Merge pull request #239 from storytold/fix/230-viewer-selected-layer-first
Viewer: a press on a selected layer behind others acts on it (#230)
This commit is contained in:
commit
bb3e63e8f3
|
|
@ -909,10 +909,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
|||
// becomes the target, using the same visible/unlocked hit test as left-click.
|
||||
pick(app, &ectx, map.to_comp(pos), false);
|
||||
let point = map.to_comp(pos);
|
||||
let hits: Vec<(u64, String)> = selectable_layers(&comp, time)
|
||||
.filter(|l| layer_quad(&ectx, l).is_some_and(|(_, q, _)| point_in_quad(point, &q)))
|
||||
.map(|l| (l.id.0, l.name.clone()))
|
||||
.collect();
|
||||
let hits: Vec<(u64, String)> = layers_at(&ectx, point).map(|l| (l.id.0, l.name.clone())).collect();
|
||||
ui.data_mut(|d| d.insert_temp(menu_hits_id, hits));
|
||||
}
|
||||
let context_layers: Vec<u64> =
|
||||
|
|
@ -1754,17 +1751,29 @@ pub(crate) fn selectable_layers(comp: &Comp, time: Tick) -> impl Iterator<Item =
|
|||
.filter(move |l| l.is_active_at(time) && l.switches.video && !l.switches.locked && !l.is_camera() && !l.is_light() && (!any_solo || l.switches.solo))
|
||||
}
|
||||
|
||||
/// Topmost selectable layer under a comp point.
|
||||
pub(crate) fn layer_at<'a>(ectx: &EvalCtx<'a>, cpt: [f64; 2]) -> Option<&'a Layer> {
|
||||
selectable_layers(ectx.comp, ectx.time).find(|l| layer_quad(ectx, l).is_some_and(|(_, q, _)| point_in_quad(cpt, &q)))
|
||||
/// Selectable layers under a comp point, front to back.
|
||||
fn layers_at<'a, 'b>(ectx: &'b EvalCtx<'a>, cpt: [f64; 2]) -> impl Iterator<Item = &'a Layer> + 'b {
|
||||
selectable_layers(ectx.comp, ectx.time).filter(move |l| layer_quad(ectx, l).is_some_and(|(_, q, _)| point_in_quad(cpt, &q)))
|
||||
}
|
||||
|
||||
/// Topmost layer under a comp point (selects it; shift toggles). Returns the hit layer.
|
||||
/// Topmost selectable layer under a comp point.
|
||||
pub(crate) fn layer_at<'a>(ectx: &EvalCtx<'a>, cpt: [f64; 2]) -> Option<&'a Layer> {
|
||||
layers_at(ectx, cpt).next()
|
||||
}
|
||||
|
||||
/// The layer a press at a comp point acts on, selected (Shift toggles the topmost layer). As in
|
||||
/// After Effects, a selected layer under the point comes before the layers in front of it, so it
|
||||
/// can be dragged where they cover it; otherwise the topmost layer (#230).
|
||||
pub(crate) fn pick(app: &mut EffectcraftApp, ectx: &EvalCtx, cpt: [f64; 2], toggle: bool) -> Option<LayerId> {
|
||||
let hit = layer_at(ectx, cpt).map(|l| l.id)?;
|
||||
if toggle {
|
||||
let hit = layer_at(ectx, cpt)?.id;
|
||||
let _ = app.session.execute("layer.select", json!({"layers": [hit.0], "toggle": true}));
|
||||
} else if !app.session.state.selected_layers.contains(&hit) {
|
||||
return Some(hit);
|
||||
}
|
||||
let selected = &app.session.state.selected_layers;
|
||||
let hits: Vec<LayerId> = layers_at(ectx, cpt).map(|l| l.id).collect();
|
||||
let hit = *hits.iter().find(|id| selected.contains(id)).or(hits.first())?;
|
||||
if !selected.contains(&hit) {
|
||||
let _ = app.session.execute("layer.select", json!({"layers": [hit.0]}));
|
||||
}
|
||||
Some(hit)
|
||||
|
|
|
|||
|
|
@ -134,6 +134,32 @@ fn viewer_click_ignores_inactive_solo_layers() {
|
|||
assert_eq!(h.state().session.state.selected_layers, vec![back]);
|
||||
}
|
||||
|
||||
/// A selected layer under the pointer takes the press before the layers in front of it, as in
|
||||
/// After Effects: it stays selected and a drag moves it. With none selected there, the topmost
|
||||
/// layer does (#230).
|
||||
#[test]
|
||||
fn viewer_press_prefers_a_selected_layer_under_the_pointer() {
|
||||
let mut h = selection_harness();
|
||||
let comp = h.state().session.active_comp().unwrap();
|
||||
let (front, back) = (comp.layers[0].id, comp.layers[1].id);
|
||||
h.state_mut().session.execute("layer.select", json!({"layers": [back.0]})).unwrap();
|
||||
h.run_steps(3);
|
||||
let at = screen(&h, [320.0, 180.0]);
|
||||
click(&mut h, at);
|
||||
assert_eq!(h.state().session.state.selected_layers, vec![back], "a click keeps the selected layer behind");
|
||||
h.run_steps(40);
|
||||
let (front0, back0) = (position_and_anchor(&h, front).0, position_and_anchor(&h, back).0);
|
||||
drag(&mut h, at, at + vec2(60.0, 0.0));
|
||||
assert_eq!(h.state().session.state.selected_layers, vec![back]);
|
||||
assert_eq!(position_and_anchor(&h, front).0, front0, "the layer in front stays");
|
||||
assert!(position_and_anchor(&h, back).0[0] > back0[0] + 10.0, "the selected layer behind moves");
|
||||
h.state_mut().session.execute("edit.deselectAll", json!({})).unwrap();
|
||||
h.run_steps(40);
|
||||
let at = screen(&h, [300.0, 180.0]);
|
||||
click(&mut h, at);
|
||||
assert_eq!(h.state().session.state.selected_layers, vec![front], "nothing selected: the topmost layer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bottom_bar_in_after_effects_order() {
|
||||
let h = harness();
|
||||
|
|
|
|||
Loading…
Reference in New Issue