From cbc1ce26e2afefa305e74de4b6805b34089512a4 Mon Sep 17 00:00:00 2001 From: Vikram Bhaskaran Date: Tue, 13 Oct 2026 00:35:59 +1100 Subject: [PATCH] Cover timeline right-click Pre-compose grouping the whole selection (#985, #1003) Right-clicking selected layers and choosing Pre-compose grouped only the right-clicked layer in 0.8.0 while Layer > Pre-compose grouped all of them. The menu plumbing was fixed since; add a regression test driving the dialog from both the outline row and the layer bar and asserting both selected layers end up nested in the new composition. --- crates/ui-egui/tests/ui_workflow_selection.rs | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/tests/ui_workflow_selection.rs b/crates/ui-egui/tests/ui_workflow_selection.rs index 697ff2a26..6b46277df 100644 --- a/crates/ui-egui/tests/ui_workflow_selection.rs +++ b/crates/ui-egui/tests/ui_workflow_selection.rs @@ -1,6 +1,6 @@ //! User-reported selection, Project deletion, and render-queue drop regressions. use effectcraft_engine::{Session, project::LayerId}; -use effectcraft_ui_egui::{EffectcraftApp, dock::PanelKind}; +use effectcraft_ui_egui::{Dialog, EffectcraftApp, dock::PanelKind}; use egui::{Event, Modifiers, PointerButton, Pos2, Rect, pos2, vec2}; use egui_kittest::Harness; use egui_kittest::kittest::Queryable as _; @@ -508,3 +508,44 @@ fn project_context_move_to_root_acts_on_the_whole_selection() { assert_eq!(project.item(effectcraft_engine::project::ItemId(*id)).unwrap().parent, None); } } + +/// Timeline right-click "Pre-compose…" groups every selected layer into one composition, +/// like Layer > Pre-compose does, from both the outline row and the layer bar +/// (#985, #1003). +#[test] +fn timeline_context_menu_precompose_groups_the_whole_selection() { + let right_click = |h: &mut Harness<'_, EffectcraftApp>, p: Pos2| { + for pressed in [true, false] { + h.input_mut().events.push(Event::PointerMoved(p)); + h.input_mut().events.push(Event::PointerButton { pos: p, button: PointerButton::Secondary, pressed, modifiers: Modifiers::NONE }); + h.step(); + } + h.run_steps(3); + }; + let row = |h: &Harness<'_, EffectcraftApp>, id: LayerId| { + let r = rect(h, &format!("timeline.layer.{}.row", id.0)); + pos2(r.min.x + 210.0, r.center().y) + }; + let bar = |h: &Harness<'_, EffectcraftApp>, id: LayerId| rect(h, &format!("timeline.layer.{}.bar", id.0)).center(); + for via_bar in [false, true] { + let mut h = harness(); + let ids: Vec = h.state().session.active_comp().unwrap().layers.iter().map(|l| l.id).collect(); + let mut want: Vec = + h.state().session.active_comp().unwrap().layers.iter().filter(|l| l.id == ids[0] || l.id == ids[1]).map(|l| l.name.clone()).collect(); + want.sort(); + h.state_mut().session.execute("layer.select", json!({"layers":[ids[0].0, ids[1].0]})).unwrap(); + h.run_steps(3); + let p = if via_bar { bar(&h, ids[1]) } else { row(&h, ids[1]) }; + right_click(&mut h, p); + h.get_by_label("Pre-compose…").click(); + h.run_steps(3); + assert_eq!(h.state().dialog, Some(Dialog::Precompose), "via_bar={via_bar}: Pre-compose opens its dialog"); + let ok = rect(&h, "dialog.precompose.ok").center(); + click(&mut h, ok, Modifiers::NONE); + h.run_steps(3); + // The dialog opens the new composition holding both nested layers. + let mut nested: Vec = h.state().session.active_comp().unwrap().layers.iter().map(|l| l.name.clone()).collect(); + nested.sort(); + assert_eq!(nested, want, "via_bar={via_bar}: both selected layers are nested"); + } +}