From 8220d96929de481787e3eea93d8eeed2e61b6cd7 Mon Sep 17 00:00:00 2001 From: tylerjenningsw Date: Sun, 25 Oct 2026 13:34:16 -0400 Subject: [PATCH 1/2] Menus: tests that hover switching leaves submenus, a disabled bar and the arrow keys alone (#1290) --- crates/ui-egui/tests/it/ui_menus.rs | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/crates/ui-egui/tests/it/ui_menus.rs b/crates/ui-egui/tests/it/ui_menus.rs index c76f7fafd..dd750e81b 100644 --- a/crates/ui-egui/tests/it/ui_menus.rs +++ b/crates/ui-egui/tests/it/ui_menus.rs @@ -373,3 +373,48 @@ fn outside_presses_and_focus_loss_close_the_menus() { h.run_steps(2); assert!(!shown(&h, "Open Project"), "losing focus closes the menus"); } + +/// From an open submenu, the pointer reaching another title switches menus too (#1290). +#[test] +fn hovering_a_title_from_an_open_submenu_switches_menus() { + let mut h = harness(); + let (file, edit) = (rect(&h, "menu.File"), rect(&h, "menu.Edit")); + click_at(&mut h, file.center()); + let new = h.query_by(|n| n.label().is_some_and(|l| l.trim_end_matches('\u{23f5}').trim() == "New")).expect("File ▸ New").rect(); + move_to(&mut h, new.center()); + h.run_steps(5); + assert!(shown(&h, "New Project from Template"), "the New submenu is open"); + move_to(&mut h, edit.center()); + assert!(shown(&h, "Keyboard Shortcuts"), "hovering Edit opened it"); + assert!(!shown(&h, "New Project from Template") && !shown(&h, "Open Project"), "and closed File and its submenu"); +} + +/// A title the pointer can't reach (the app is disabled under a dialog) doesn't switch menus +/// (#1290). +#[test] +fn a_disabled_title_does_not_switch_menus() { + let mut h = harness(); + let (file, edit) = (rect(&h, "menu.File"), rect(&h, "menu.Edit")); + click_at(&mut h, file.center()); + h.state_mut().dialog = Some(effectcraft_ui_egui::Dialog::About); + h.run_steps(2); + move_to(&mut h, edit.center()); + assert!(!shown(&h, "Keyboard Shortcuts"), "Edit didn't open"); +} + +/// A pointer resting on a title doesn't undo Left / Right: only a moving pointer switches +/// (#1290). +#[test] +fn a_resting_pointer_does_not_override_the_arrow_keys() { + if cfg!(target_os = "macos") { + // (Right / Left between menus are tested where the in-window bar is the app's menu.) + return; + } + let mut h = harness(); + let file = rect(&h, "menu.File"); + click_at(&mut h, file.center()); + key(&mut h, Key::ArrowRight); + h.run_steps(3); + assert!(shown(&h, "Keyboard Shortcuts"), "Right opened Edit with the pointer still on File"); + assert!(!shown(&h, "Open Project")); +} From 238361e59f4ef00fd9cc7272344326d1656dceb9 Mon Sep 17 00:00:00 2001 From: tylerjenningsw Date: Sun, 25 Oct 2026 13:39:06 -0400 Subject: [PATCH 2/2] Menus: a pointer coming back into the window over another title switches menus (#1290) --- crates/ui-egui/src/menus/bar.rs | 13 ++++++++++++- crates/ui-egui/tests/it/ui_menus.rs | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/menus/bar.rs b/crates/ui-egui/src/menus/bar.rs index 1a7f6e2a3..d4d36d2e1 100644 --- a/crates/ui-egui/src/menus/bar.rs +++ b/crates/ui-egui/src/menus/bar.rs @@ -14,6 +14,12 @@ fn browse_menu_id() -> egui::Id { egui::Id::unique("menu-bar-browse") } +/// The id under which [`menu_bar`] remembers where it last saw the pointer (`None`: outside the +/// window). +fn last_pointer_id() -> egui::Id { + egui::Id::unique("menu-bar-last-pointer") +} + /// Draw the in-window menu bar (the engine's AE menu tree). /// /// The titles open on the press, not the release (native menu bars): while one is held the menus @@ -34,6 +40,12 @@ pub fn menu_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui) { if ctx.data(|d| d.get_temp(browse_menu_id())).unwrap_or(false) { nav.set_browsing(); } + // Whether the pointer has moved since the last frame. Kept by position rather than egui's + // `delta`, which is zero for the first position after the pointer comes back into the window: + // coming back in over another title must switch to it too (#1290). + let pointer = ctx.pointer_hover_pos(); + let last = ctx.data_mut(|d| std::mem::replace(d.get_temp_mut_or(last_pointer_id(), None), pointer)); + let moved = pointer.is_some() && pointer != last; let mut drawn = 0; ui.horizontal_centered(|ui| { ui.add_space(6.0); @@ -53,7 +65,6 @@ pub fn menu_bar(app: &mut EffectcraftApp, ui: &mut egui::Ui) { // then happened somewhere else), and toggle the menu. Hovering another title // switches to it once the pointer has moved: merely resting there must not take a // keyboard-driven menu over. - let moved = ctx.input(|i| i.pointer.delta() != egui::Vec2::ZERO); // With the button down elsewhere nothing is hovered, so the title under the // pointer takes the highlight by hand: the hover-switch to it shows, one frame // later like egui's own hover (the open title keeps its open look). diff --git a/crates/ui-egui/tests/it/ui_menus.rs b/crates/ui-egui/tests/it/ui_menus.rs index dd750e81b..38dcd15bc 100644 --- a/crates/ui-egui/tests/it/ui_menus.rs +++ b/crates/ui-egui/tests/it/ui_menus.rs @@ -418,3 +418,18 @@ fn a_resting_pointer_does_not_override_the_arrow_keys() { assert!(shown(&h, "Keyboard Shortcuts"), "Right opened Edit with the pointer still on File"); assert!(!shown(&h, "Open Project")); } + +/// Leaving the window keeps the menu open, and coming back in over another title switches to it +/// (#1290): egui reports no pointer movement for that first position. +#[test] +fn pointer_reentering_over_another_title_switches_menus() { + let mut h = harness(); + let (file, edit) = (rect(&h, "menu.File"), rect(&h, "menu.Edit")); + click_at(&mut h, file.center()); + h.input_mut().events.push(Event::PointerGone); + h.run_steps(2); + assert!(shown(&h, "Open Project"), "leaving the window kept File open"); + move_to(&mut h, edit.center()); + assert!(shown(&h, "Keyboard Shortcuts"), "coming back over Edit opened it"); + assert!(!shown(&h, "Open Project"), "and closed File"); +}