mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-14 19:26:59 -04:00
Merge pull request #1240 from Ni-zav/fix/1163-alt-marquee-select-unlinked-s20261012T0220Z-a3
timeline: Alt marquee selects clips without linked partners (#1163)
This commit is contained in:
commit
bdc10001fe
2 files changed
+36
-6
No files matched your search
@@ -168,6 +168,7 @@ pub enum Drag {
|
||||
start: Pos2,
|
||||
before: Vec<ClipId>,
|
||||
add: bool,
|
||||
individual: bool,
|
||||
},
|
||||
Divider,
|
||||
ZoomBar {
|
||||
@@ -2438,7 +2439,7 @@ fn interact(app: &mut FilmcraftApp, ui: &mut egui::Ui, seq: &Sequence, layout: &
|
||||
// the video and audio tracks, #683)
|
||||
(_, Grab::Other(hit @ (Hit::Empty { .. } | Hit::None))) if layout.content.contains(p) => {
|
||||
if resp.drag_started() {
|
||||
Some(Drag::Marquee { start: p, before: app.session.state.selection.clone(), add: mods.shift || mods.command })
|
||||
Some(Drag::Marquee { start: p, before: app.session.state.selection.clone(), add: mods.shift || mods.command, individual: mods.alt })
|
||||
} else {
|
||||
app.session.state.selection.clear();
|
||||
app.session.state.edit_points.clear();
|
||||
@@ -2581,20 +2582,22 @@ fn interact(app: &mut FilmcraftApp, ui: &mut egui::Ui, seq: &Sequence, layout: &
|
||||
Some(Drag::Transition { id, edge, from, start, duration })
|
||||
}
|
||||
Drag::Volume(v) => Some(Drag::Volume(super::timeline_volume::drag(app, ui, seq, layout, v, p.y))),
|
||||
Drag::Marquee { start, before, add } => {
|
||||
Drag::Marquee { start, before, add, individual } => {
|
||||
let r = Rect::from_two_pos(start, p);
|
||||
ui.painter().rect_filled(r, 0.0, Color32::from_white_alpha(18));
|
||||
ui.painter().rect_stroke(r, 0.0, Stroke::new(1.0, Color32::from_white_alpha(140)), StrokeKind::Inside);
|
||||
// the clips in the box are selected as it grows and shrinks; only the release runs
|
||||
// the command, so the drag is one selection change
|
||||
let mut sel = if add { before.clone() } else { Vec::new() };
|
||||
for c in filmcraft_engine::commands::with_links(&app.session, &marquee_clips(seq, layout, r)) {
|
||||
let clips = marquee_clips(seq, layout, r);
|
||||
let clips = if individual { clips } else { filmcraft_engine::commands::with_links(&app.session, &clips) };
|
||||
for c in clips {
|
||||
if !sel.contains(&c) {
|
||||
sel.push(c);
|
||||
}
|
||||
}
|
||||
app.session.state.selection = sel;
|
||||
Some(Drag::Marquee { start, before, add })
|
||||
Some(Drag::Marquee { start, before, add, individual })
|
||||
}
|
||||
other => Some(other),
|
||||
};
|
||||
@@ -2644,13 +2647,13 @@ fn interact(app: &mut FilmcraftApp, ui: &mut egui::Ui, seq: &Sequence, layout: &
|
||||
let moved = seq.all_tracks().find_map(|tr| tr.transitions.iter().find(|x| x.id == id)).is_some_and(|x| (x.start, x.duration) != (start, duration));
|
||||
moved.then(|| app.session.execute("sequence.setTransition", json!({"transition": id.0, "start": start.0, "duration": duration.0})))
|
||||
}
|
||||
Drag::Marquee { start, before, add } => {
|
||||
Drag::Marquee { start, before, add, individual } => {
|
||||
app.session.state.selection = before;
|
||||
// egui ends a drag on Escape: that cancels the box, as in the Project panel
|
||||
match resp.interact_pointer_pos() {
|
||||
Some(end) if !ctx.input(|i| i.key_pressed(egui::Key::Escape)) => {
|
||||
let ids: Vec<u64> = marquee_clips(seq, layout, Rect::from_two_pos(start, end)).iter().map(|c| c.0).collect();
|
||||
Some(app.session.execute("timeline.select", json!({"clips": ids, "add": add})))
|
||||
Some(app.session.execute("timeline.select", json!({"clips": ids, "add": add, "linked": !individual})))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
||||
@@ -121,6 +121,33 @@ fn the_selection_follows_the_box_while_it_is_dragged() {
|
||||
assert!(d.selected(b) && !d.selected(a), "the release keeps what the box showed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_box_selects_only_video_and_delete_keeps_linked_audio() {
|
||||
let mut d = Driver::demo();
|
||||
let (_, video, _, rv, start) = d.setup();
|
||||
let audio = {
|
||||
let seq = d.harness.state().session.active_sequence().unwrap();
|
||||
let link = seq.find_item(ClipId(video)).unwrap().1.link;
|
||||
seq.audio_tracks.iter().flat_map(|tr| &tr.items).find(|clip| clip.link == link).unwrap().id.0
|
||||
};
|
||||
d.exec("timeline.select", json!({"clips": []}));
|
||||
d.modifiers = egui::Modifiers::ALT;
|
||||
d.send(egui::Event::PointerMoved(start));
|
||||
d.button(start, true);
|
||||
let over = egui::pos2(rv.center().x, start.y + 2.0);
|
||||
d.move_to(start, over);
|
||||
assert!(d.selected(video));
|
||||
assert!(!d.selected(audio), "Alt excludes the linked audio during the drag");
|
||||
d.button(over, false);
|
||||
d.modifiers = egui::Modifiers::NONE;
|
||||
d.frames(2);
|
||||
assert!(d.selected(video) && !d.selected(audio), "release preserves the individual selection");
|
||||
d.exec("edit.clear", json!({}));
|
||||
let seq = d.harness.state().session.active_sequence().unwrap();
|
||||
assert!(seq.find_item(ClipId(video)).is_none());
|
||||
assert!(seq.find_item(ClipId(audio)).is_some(), "Delete leaves the unselected linked audio");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_adds_to_the_selection_the_drag_began_with() {
|
||||
let mut d = Driver::demo();
|
||||
|
||||
Reference in new issue
Block a user