mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
Merge pull request #948 from Themolx/solo-outside-active-range
Solo: exclude other layers even when the soloed layer is outside its time range
This commit is contained in:
@@ -362,7 +362,7 @@ const MAX_COLLAPSED_DEPTH: usize = 8;
|
||||
fn visible_layers<'a>(ctx: &EvalCtx<'a>, exclude: &'a [LayerId]) -> impl Iterator<Item = &'a Layer> {
|
||||
let comp = ctx.comp;
|
||||
let time = ctx.time;
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av() && l.is_active_at(time));
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av());
|
||||
comp.layers.iter().filter(move |l| l.is_active_at(time) && l.switches.video && (!any_solo || l.switches.solo) && !exclude.contains(&l.id))
|
||||
}
|
||||
|
||||
|
||||
@@ -2094,7 +2094,7 @@ impl<'a> Renderer<'a> {
|
||||
/// applied).
|
||||
pub fn visible_layers(&self, ctx: &EvalCtx<'a>) -> Vec<&'a Layer> {
|
||||
let (comp, t) = (ctx.comp, ctx.time);
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av() && l.is_active_at(t));
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av());
|
||||
comp.layers
|
||||
.iter()
|
||||
.rev()
|
||||
|
||||
@@ -953,3 +953,21 @@ fn shape_content_bounds_are_the_visual_edges() {
|
||||
assert!(b.iter().zip([-half, -half, half, half]).all(|(a, e)| (a - e).abs() < 1e-6), "{stroke:?}: {b:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Solo is not time-dependent (as in After Effects): while any layer is soloed, the others stay
|
||||
/// excluded even when the playhead is outside the soloed layer's in-out range.
|
||||
#[test]
|
||||
fn solo_excludes_other_layers_outside_the_soloed_layers_range() {
|
||||
let (mut p, cid, comp) = setup();
|
||||
let a = solid(&mut p, &comp, [1.0, 0.0, 0.0], 200, 100);
|
||||
let mut b = solid(&mut p, &comp, [0.0, 0.0, 1.0], 200, 100);
|
||||
b.in_point = Tick::from_seconds_f64(1.0);
|
||||
b.switches.solo = true;
|
||||
let c = p.comp_mut(cid).unwrap();
|
||||
c.layers.push(b);
|
||||
c.layers.push(a);
|
||||
let first = render_frame(&p, cid, Tick::from_seconds_f64(0.5), 1.0);
|
||||
assert_eq!(first.get(100, 50), [0.0, 0.0, 0.0, 0.0], "neither A nor B before B's in point");
|
||||
let second = render_frame(&p, cid, Tick::from_seconds_f64(1.5), 1.0);
|
||||
assert_eq!(second.get(100, 50), [0.0, 0.0, 1.0, 1.0], "only B inside its range");
|
||||
}
|
||||
|
||||
@@ -873,7 +873,7 @@ fn collapsed_layer<'a>(r: &Renderer<'a>, ctx: &EvalCtx<'a>, layer: &Layer, item:
|
||||
let saved = b.outer;
|
||||
b.outer = saved * ctx.world_matrix(layer);
|
||||
let t = nctx.time;
|
||||
let any_solo = nctx.comp.layers.iter().any(|l| l.switches.solo && l.source.is_av() && l.is_active_at(t));
|
||||
let any_solo = nctx.comp.layers.iter().any(|l| l.switches.solo && l.source.is_av());
|
||||
for l in nctx.comp.layers.iter().rev() {
|
||||
if !l.is_active_at(t)
|
||||
|| l.is_light()
|
||||
|
||||
@@ -497,7 +497,7 @@ fn collapsed_items<'a>(
|
||||
let op = ctx.opacity(layer) as f32 * r.opacity_mul();
|
||||
let Some((sub, nctx)) = r.collapse_into(ctx, layer, item, op) else { return vec![] };
|
||||
let t = nctx.time;
|
||||
let any_solo = nctx.comp.layers.iter().any(|l| l.switches.solo && l.source.is_av() && l.is_active_at(t));
|
||||
let any_solo = nctx.comp.layers.iter().any(|l| l.switches.solo && l.source.is_av());
|
||||
let nested: Vec<&'a Layer> = nctx
|
||||
.comp
|
||||
.layers
|
||||
|
||||
@@ -2283,9 +2283,10 @@ fn pen_press(
|
||||
}
|
||||
|
||||
/// Visible, unlocked layers eligible for viewer hit tests, in front-to-back order.
|
||||
/// Solo scope matches the renderer: only active AV layers activate it, even when hidden or locked.
|
||||
/// Solo scope matches the renderer: any soloed AV layer activates it at every time (as in After
|
||||
/// Effects), even when hidden, locked or outside its in-out range.
|
||||
pub(crate) fn selectable_layers(comp: &Comp, time: Tick) -> impl Iterator<Item = &Layer> {
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av() && l.is_active_at(time));
|
||||
let any_solo = comp.layers.iter().any(|l| l.switches.solo && l.source.is_av());
|
||||
comp.layers
|
||||
.iter()
|
||||
.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))
|
||||
|
||||
@@ -121,8 +121,10 @@ fn viewer_click_does_not_pick_through_hidden_or_locked_solo_layers() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Solo is not time-dependent (as in After Effects): outside the soloed layer's in-out range the
|
||||
/// viewer shows nothing, so neither the soloed layer nor the others can be picked.
|
||||
#[test]
|
||||
fn viewer_click_ignores_inactive_solo_layers() {
|
||||
fn viewer_click_picks_nothing_while_the_solo_layer_is_inactive() {
|
||||
let mut h = selection_harness();
|
||||
let comp = h.state().session.active_comp().unwrap();
|
||||
let (front, back) = (comp.layers[0].id, comp.layers[1].id);
|
||||
@@ -133,7 +135,7 @@ fn viewer_click_ignores_inactive_solo_layers() {
|
||||
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]);
|
||||
assert!(h.state().session.state.selected_layers.is_empty(), "{back:?} stays excluded by solo");
|
||||
}
|
||||
|
||||
/// A selected layer under the pointer takes the press before the layers in front of it, as in
|
||||
|
||||
+2
-1
@@ -117,7 +117,8 @@ The most important missing piece: it turns every other estimate here into a meas
|
||||
- Regression evidence for [#66](https://github.com/storytold/effectcraft/issues/66): scripted
|
||||
pointer tests in `ui_viewer` and `ui_text_edit`, checked headlessly on Linux, cover viewer clicks,
|
||||
marquee selection and text picking when layers are hidden or excluded by solo. They also check
|
||||
hidden/locked solo layers and solo layers outside their active time range.
|
||||
hidden/locked solo layers, and that solo still excludes the other layers while the soloed
|
||||
layer is outside its active time range (as in After Effects, so nothing is picked there).
|
||||
- Regression evidence for [#67](https://github.com/storytold/effectcraft/issues/67): scripted
|
||||
input tests in `ui_project_delete`, checked headlessly on Linux, cover Delete/Backspace on
|
||||
Project items, multiple selection and undo. They check that an unrelated Timeline layer stays,
|
||||
|
||||
Reference in New Issue
Block a user