diff --git a/crates/ui-egui/tests/it/ui_effect_controls.rs b/crates/ui-egui/tests/it/ui_effect_controls.rs index 159dc8a12..ff76a1cb7 100644 --- a/crates/ui-egui/tests/it/ui_effect_controls.rs +++ b/crates/ui-egui/tests/it/ui_effect_controls.rs @@ -156,12 +156,18 @@ fn effect_header_arrows_only_disclose_selected_headers_on_the_displayed_layer() #[test] fn effect_header_arrows_ignore_hidden_panels_with_stale_focus() { - for hidden in ["closed", "maximized", "home"] { + // A maximized frame hides every other frame and every floating window, so a floating Effect + // Controls is hidden too. + for hidden in ["closed", "maximized", "floating under maximized", "home"] { let (mut h, lid, [(fx, _), _, _]) = header_arrow_harness(); h.state_mut().session.state.selected_props = vec![(lid, fx)]; match hidden { "closed" => h.state_mut().close_panel(PanelKind::EffectControls), "maximized" => h.state_mut().toggle_maximize(PanelKind::Composition), + "floating under maximized" => { + h.state_mut().apply_dock(craft_ui::docking::Action::Float { panel: PanelKind::EffectControls, rect: [40.0, 40.0, 600.0, 400.0] }).unwrap(); + h.state_mut().toggle_maximize(PanelKind::Composition); + } _ => h.state_mut().ui.start_screen = true, } h.run_steps(2); @@ -176,17 +182,28 @@ fn effect_header_arrows_ignore_hidden_panels_with_stale_focus() { } #[test] -fn effect_header_arrows_work_in_floating_panel_beside_maximized_panel() { - let (mut h, lid, [(fx, prop), _, _]) = header_arrow_harness(); - h.state_mut().apply_dock(craft_ui::docking::Action::Float { panel: PanelKind::EffectControls, rect: [40.0, 40.0, 600.0, 400.0] }).unwrap(); - h.state_mut().toggle_maximize(PanelKind::Composition); - h.state_mut().session.state.selected_props = vec![(lid, fx)]; - h.run_steps(3); - assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_some()); - header_arrow_key(&mut h, egui::Key::ArrowLeft, egui::Modifiers::NONE); - assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_none()); - header_arrow_key(&mut h, egui::Key::ArrowRight, egui::Modifiers::NONE); - assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_some()); +fn effect_header_arrows_work_wherever_effect_controls_is_on_screen() { + for place in ["floating", "tab of a maximized frame"] { + let (mut h, lid, [(fx, prop), _, _]) = header_arrow_harness(); + h.state_mut().toggle_maximize(PanelKind::EffectControls); + if place == "floating" { + h.state_mut().apply_dock(craft_ui::docking::Action::Float { panel: PanelKind::EffectControls, rect: [40.0, 40.0, 600.0, 400.0] }).unwrap(); + } else { + let placement = craft_ui::docking::Placement::Tab { before: None }; + h.state_mut().apply_dock(craft_ui::docking::Action::Move { panel: PanelKind::EffectControls, anchor: PanelKind::Composition, placement }).unwrap(); + h.state_mut().toggle_maximize(PanelKind::Composition); + assert_eq!(h.state().ui.maximized, Some(PanelKind::Composition), "the frame was maximized from its Composition tab"); + } + // The moved tab is the front one; clicking it focuses it. + h.state_mut().ui.focused = PanelKind::EffectControls; + h.state_mut().session.state.selected_props = vec![(lid, fx)]; + h.run_steps(3); + assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_some(), "{place}: Effect Controls is on screen"); + header_arrow_key(&mut h, egui::Key::ArrowLeft, egui::Modifiers::NONE); + assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_none(), "{place}: Left collapses"); + header_arrow_key(&mut h, egui::Key::ArrowRight, egui::Modifiers::NONE); + assert!(h.state().auto.find(&format!("effectControls.row.{prop}")).is_some(), "{place}: Right expands"); + } } #[test] @@ -220,13 +237,30 @@ fn effect_header_arrows_preserve_modified_shortcuts_and_keyboard_ownership() { header_arrow_key(&mut h, egui::Key::ArrowLeft, egui::Modifiers::NONE); assert!(h.state().ui.fx_closed.is_empty(), "the context menu owns the arrows"); header_arrow_key(&mut h, egui::Key::Escape, egui::Modifiers::NONE); + h.run_steps(3); + assert!(!egui::Popup::is_any_open(&h.ctx), "Escape closed the context menu"); double_click(&mut h, at); - assert!(h.ctx.egui_wants_keyboard_input(), "the rename field owns the keyboard"); + h.run_steps(3); + assert!(h.state().auto.find(&format!("effectControls.effect.{fx}.nameEdit")).is_some(), "the rename field is open"); header_arrow_key(&mut h, egui::Key::ArrowLeft, egui::Modifiers::NONE); assert!(h.state().ui.fx_closed.is_empty(), "Left moves the text caret instead of collapsing"); assert!(h.state().auto.find(&format!("effectControls.effect.{fx}.nameEdit")).is_some()); } +#[test] +fn effect_header_arrows_leave_viewer_text_editing_alone() { + let (mut h, lid, [(fx, _), _, _]) = header_arrow_harness(); + let text = h.state_mut().session.execute("layer.newText", json!({"text": "abcdef"})).unwrap()["layer"].as_u64().unwrap(); + h.state_mut().session.execute("text.edit", json!({"layer": text, "caret": 3})).unwrap(); + h.state_mut().session.state.selected_layers = vec![lid]; + h.state_mut().session.state.selected_props = vec![(lid, fx)]; + h.state_mut().show_panel(PanelKind::EffectControls); + h.run_steps(2); + assert!(h.state().session.state.text_edit.is_some(), "the text is still being edited"); + header_arrow_key(&mut h, egui::Key::ArrowLeft, egui::Modifiers::NONE); + assert!(h.state().ui.fx_closed.is_empty(), "the text edit owns the arrows"); +} + #[test] fn effect_controls_widgets_register_automation_ids() { let (app, x) = app();