mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-14 19:26:59 -04:00
Merge pull request #1442 from MarekSpodBiedry/issue-1360
fix(media-browser): drag every selected file to the Timeline, not just one (#1360)
This commit is contained in:
commit
9247622550
2 files changed
+70
-11
No files matched your search
@@ -447,26 +447,38 @@ fn entry_interactions(app: &mut FilmcraftApp, ui: &egui::Ui, resp: &egui::Respon
|
||||
});
|
||||
}
|
||||
|
||||
/// Dragging a file imports it (the selection when the file is in it) and carries the new item, so
|
||||
/// the Project panel, Timeline and monitors accept it; a drop elsewhere undoes the import.
|
||||
/// Dragging a file imports it (the selection when the file is in it) and carries its items, so
|
||||
/// the Project panel, Timeline and monitors accept them; a drop elsewhere undoes the import.
|
||||
fn start_file_drag(app: &mut FilmcraftApp, ui: &egui::Ui, e: &Entry) {
|
||||
let paths: Vec<String> = if app.session.browser.selection.contains(&e.path) { app.session.browser.selection.clone() } else { vec![e.path.clone()] };
|
||||
// files already in the project are dragged as they are
|
||||
if let Some(i) = mb::item_for_path(&app.session, &e.path) {
|
||||
crate::panels::start_drag_item(ui, i);
|
||||
return;
|
||||
}
|
||||
// files already in the project are dragged as they are; only the others are imported
|
||||
let known: Vec<ItemId> = paths.iter().filter_map(|p| mb::item_for_path(&app.session, p)).collect();
|
||||
let fresh: Vec<String> = paths.iter().filter(|p| mb::item_for_path(&app.session, p).is_none()).cloned().collect();
|
||||
let before = app.session.history.undo.len();
|
||||
let Ok(r) = app.session.execute("mediaBrowser.import", json!({"paths": paths})) else { return };
|
||||
let items: Vec<u64> = r["items"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default();
|
||||
if let Some(first) = items.first() {
|
||||
crate::panels::start_drag_item(ui, ItemId(*first));
|
||||
let mut items: Vec<u64> = Vec::new();
|
||||
if !fresh.is_empty() {
|
||||
let Ok(r) = app.session.execute("mediaBrowser.import", json!({"paths": fresh})) else { return };
|
||||
items = r["items"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default();
|
||||
}
|
||||
let all = file_drag_items(&known, &items);
|
||||
let Some(first) = all.first().copied() else { return };
|
||||
// the drop targets place the whole Project selection when the dragged item is one of it
|
||||
if all.len() > 1 {
|
||||
app.session.state.project_selection = all;
|
||||
}
|
||||
crate::panels::start_drag_item(ui, first);
|
||||
if !items.is_empty() {
|
||||
let pos = ui.ctx().pointer_latest_pos().unwrap_or_default();
|
||||
let after = app.session.history.undo.len();
|
||||
with_cache(ui.ctx(), |c| c.drag = Some((items, after.max(before), pos)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Every item a Media Browser drag carries: the selected files already in the project, then the newly imported ones.
|
||||
fn file_drag_items(known: &[ItemId], imported: &[u64]) -> Vec<ItemId> {
|
||||
known.iter().copied().chain(imported.iter().map(|i| ItemId(*i))).collect()
|
||||
}
|
||||
|
||||
/// After a file drag ends: keep the import when it landed on a panel that takes items.
|
||||
fn finish_drag(app: &mut FilmcraftApp, ui: &egui::Ui) {
|
||||
let Some((items, hist, _)) = with_cache(ui.ctx(), |c| c.drag.clone()) else { return };
|
||||
@@ -854,3 +866,16 @@ pub fn dialogs(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
app.ui.media_browser.edit_columns = if close || crate::widgets::escape_closes(ctx) { None } else { Some(cols) };
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn dragging_several_files_carries_all_of_them() {
|
||||
let all = file_drag_items(&[ItemId(7)], &[11, 12]);
|
||||
assert_eq!(all, vec![ItemId(7), ItemId(11), ItemId(12)]);
|
||||
assert_eq!(file_drag_items(&[], &[3]), vec![ItemId(3)]);
|
||||
assert!(file_drag_items(&[], &[]).is_empty());
|
||||
}
|
||||
}
|
||||
@@ -760,6 +760,40 @@ fn media_browser_drag_imports_into_the_project_panel() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Dragging several selected Media Browser files onto the Timeline (#1360) places every one of
|
||||
/// them, whether it was already in the project or is imported by the drag.
|
||||
#[test]
|
||||
fn media_browser_drag_places_every_selected_file() {
|
||||
let dir = media_dir("drag-many");
|
||||
let mut session = Session::default();
|
||||
session.execute("file.openDemoProject", json!({})).unwrap();
|
||||
let mut d = Driver::with(session);
|
||||
d.ok("ui.set", json!({"workspace": "Editing"}));
|
||||
d.ok("ui.panel.show", json!({"panel": "MediaBrowser"}));
|
||||
d.exec("mediaBrowser.navigate", json!({"path": dir.to_string_lossy()}));
|
||||
d.frames(3);
|
||||
d.wait_for("mediaBrowser.entry.b-roll.wav");
|
||||
let listed = d.exec("mediaBrowser.list", json!({}));
|
||||
let path = |name: &str| listed["entries"].as_array().unwrap().iter().find(|e| e["name"] == json!(name)).unwrap()["path"].clone();
|
||||
let (a, b) = (path("a-interview.wav"), path("b-roll.wav"));
|
||||
// one file is already in the project, the other is imported by the drag
|
||||
d.exec("mediaBrowser.import", json!({"paths": [a]}));
|
||||
d.exec("mediaBrowser.select", json!({"paths": [a, b]}));
|
||||
d.frames(2);
|
||||
let n0 = d.app().session.project.items.len();
|
||||
let clips = |d: &mut Driver| {
|
||||
d.app().session.active_sequence().map(|q| q.video_tracks.iter().chain(&q.audio_tracks).map(|t| t.items.len()).sum::<usize>()).unwrap_or(0)
|
||||
};
|
||||
let c0 = clips(&mut d);
|
||||
let e = d.rect("mediaBrowser.entry.b-roll.wav");
|
||||
let tl = d.rect("panel.Timeline");
|
||||
d.ok("ui.drag", json!({"from": {"x": e[0] + 40.0, "y": e[1] + e[3] / 2.0}, "to": {"x": tl[0] + tl[2] * 0.6, "y": tl[1] + tl[3] * 0.75}, "steps": 20}));
|
||||
d.frames(6);
|
||||
assert_eq!(d.app().session.project.items.len(), n0 + 1, "only the new file is imported");
|
||||
assert_eq!(clips(&mut d), c0 + 2, "both files are placed; status: {}", d.app().ui.status);
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Dragging one of several selected Project items onto the Timeline places all of them, back to
|
||||
/// back, as one undo step; an item outside the selection goes alone.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user