From f00b6f88591ef5cc8832bcc415a57952b021cbcd Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Sun, 12 Apr 2026 23:40:59 +0300 Subject: [PATCH] correctly deselect nested menu items of orphaned menus --- fyrox-ui/src/menu.rs | 71 ++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/fyrox-ui/src/menu.rs b/fyrox-ui/src/menu.rs index 00b11738b..197a68470 100644 --- a/fyrox-ui/src/menu.rs +++ b/fyrox-ui/src/menu.rs @@ -269,7 +269,7 @@ impl Control for Menu { } } else if let Some(WidgetMessage::KeyDown(key_code)) = message.data() { if !message.handled() { - if keyboard_navigation(ui, *key_code, self, self.handle) { + if keyboard_navigation(ui, *key_code, self, self.handle.to_variant()) { message.set_handled(true); } else if *key_code == KeyCode::Escape { ui.send(self.handle, MenuMessage::Deactivate); @@ -463,6 +463,27 @@ impl MenuItem { WidgetMessage::Visibility(!self.items_container.is_empty()), ); } + + fn close_menu_chain(&self, ui: &UserInterface) { + let mut menu_item_ref_container = Some(self); + while let Some(menu_item_ref) = menu_item_ref_container { + let popup_handle = + menu_item_ref.find_by_criteria_up(ui, |n| n.has_component::()); + + ui.send( + menu_item_ref.handle, + MenuItemMessage::Close { deselect: true }, + ); + + if let Ok(panel) = ui.try_get_of_type::(popup_handle) { + // Continue search from parent menu item of popup. + menu_item_ref_container = ui.try_get(panel.parent_menu_item).ok(); + } else { + // Prevent infinite loops. + break; + } + } + } } // MenuItem uses popup to show its content, popup can be top-most only if it is @@ -475,7 +496,7 @@ fn find_menu(from: Handle, ui: &UserInterface) -> Handle { while handle.is_some() { if let Some((_, panel)) = ui.find_component_up::(handle) { // Continue search from parent menu item of popup. - handle = panel.parent_menu_item; + handle = panel.parent_menu_item.to_base(); } else { // Maybe we have Menu as parent for MenuItem. return ui.find_handle_up(handle, &mut |n| n.cast::().is_some()); @@ -500,25 +521,6 @@ fn is_any_menu_item_contains_point(ui: &UserInterface, pt: Vector2) -> bool false } -fn close_menu_chain(from: Handle, ui: &UserInterface) { - let mut handle = from; - while handle.is_some() { - let popup_handle = ui.find_handle_up(handle, &mut |n| n.has_component::()); - - if let Ok(panel) = ui.try_get_of_type::(popup_handle) { - if *panel.popup.is_open { - ui.send(popup_handle, PopupMessage::Close); - } - - // Continue search from parent menu item of popup. - handle = panel.parent_menu_item; - } else { - // Prevent infinite loops. - break; - } - } -} - uuid_provider!(MenuItem = "72e002c6-6060-4583-b5b7-0c5500244fef"); impl Control for MenuItem { @@ -564,7 +566,7 @@ impl Control for MenuItem { ui.send(menu, MenuMessage::Deactivate); } else { // Or close the menu chain if menu item is in "orphaned" state. - close_menu_chain(self.parent(), ui); + self.close_menu_chain(ui); } } message.set_handled(true); @@ -722,7 +724,7 @@ impl Control for MenuItem { if let Some(panel) = node.component_ref::() { // Once we found popup in chain, we must extract handle // of parent menu item to continue search. - handle = panel.parent_menu_item; + handle = panel.parent_menu_item.to_base(); } else { handle = node.parent(); } @@ -756,12 +758,8 @@ impl Control for MenuItem { if !is_any_menu_item_contains_point(ui, ui.cursor_position()) && find_menu(self.parent(), ui).is_none() { - if *panel.popup.is_open { - ui.send(*self.items_panel, PopupMessage::Close); - } - // Close all other popups. - close_menu_chain(self.parent(), ui); + self.close_menu_chain(ui); } } } @@ -1138,7 +1136,7 @@ pub struct ContextMenu { #[component(include)] pub popup: Popup, /// Parent menu item of the context menu. Allows you to build chained context menus. - pub parent_menu_item: Handle, + pub parent_menu_item: Handle, } impl ConstructorProvider for ContextMenu { @@ -1179,13 +1177,8 @@ impl Control for ContextMenu { if let Some(WidgetMessage::KeyDown(key_code)) = message.data() { if !message.handled() { - if let Ok(parent_menu_item) = ui.try_get_node(self.parent_menu_item) { - if keyboard_navigation( - ui, - *key_code, - parent_menu_item.deref(), - self.parent_menu_item, - ) { + if let Ok(parent_menu_item) = ui.try_get(self.parent_menu_item) { + if keyboard_navigation(ui, *key_code, parent_menu_item, self.parent_menu_item) { message.set_handled(true); } } @@ -1197,7 +1190,7 @@ impl Control for ContextMenu { /// Creates [`ContextMenu`] widgets. pub struct ContextMenuBuilder { popup_builder: PopupBuilder, - parent_menu_item: Handle, + parent_menu_item: Handle, } impl ContextMenuBuilder { @@ -1210,7 +1203,7 @@ impl ContextMenuBuilder { } /// Sets the desired parent menu item. - pub fn with_parent_menu_item(mut self, parent_menu_item: Handle) -> Self { + pub fn with_parent_menu_item(mut self, parent_menu_item: Handle) -> Self { self.parent_menu_item = parent_menu_item; self } @@ -1234,7 +1227,7 @@ fn keyboard_navigation( ui: &UserInterface, key_code: KeyCode, parent_menu_item: &dyn Control, - parent_menu_item_handle: Handle, + parent_menu_item_handle: Handle, ) -> bool { let Some(items_container) = parent_menu_item .query_component_ref(TypeId::of::())