From 32a63da5800fc1af0400e2d018c79cb09eec673f Mon Sep 17 00:00:00 2001 From: "David M. Lary" Date: Tue, 22 Aug 2023 08:35:15 -0500 Subject: [PATCH] Added Context::is_context_menu_open() (#3267) I encountered a case where I needed to know if an egui context menu was open, even if the mouse cursor was not in an egui area (full details in discord). I found that every resource I needed to detect that the menu was open was either private, or pub(crate). While it is possible to wrap the `Response::context_menu()` in code to do state-tracking, it becomes necessary to duplicate that code in every place you use a context menu. In this commit, I add `Context::is_context_menu_open()`. Named similarly to `Context::is_pointer_over_area()`, this method will return true if any context menu is open. This is possible because the context menu uses a temp var with a fixed Id to store its state. This method just fetches the state, and interrogates it to see if there is a menu present. One helper method, `BarState::has_root()`, was added as all the fields needed to perform the check were private to the `menu` module. I've also updated the Context Menu demo to show the result of `is_context_menu_open()` to verify the code functions as expected. --- crates/egui/src/context.rs | 8 ++++++++ crates/egui/src/menu.rs | 8 +++++++- crates/egui_demo_lib/src/demo/context_menu.rs | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index d8d5a0f5..c79e2683 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1389,6 +1389,14 @@ impl Context { pub fn highlight_widget(&self, id: Id) { self.frame_state_mut(|fs| fs.highlight_next_frame.insert(id)); } + + /// Is an egui context menu open? + pub fn is_context_menu_open(&self) -> bool { + self.data(|d| { + d.get_temp::(menu::CONTEXT_MENU_ID_STR.into()) + .map_or(false, |state| state.has_root()) + }) + } } // Ergonomic methods to forward some calls often used in 'if let' without holding the borrow diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 5e27e953..0364cf46 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -48,6 +48,10 @@ impl BarState { MenuRoot::stationary_click_interaction(response, &mut self.open_menu, response.id); self.open_menu.show(response, add_contents) } + + pub(crate) fn has_root(&self) -> bool { + self.open_menu.inner.is_some() + } } impl std::ops::Deref for BarState { @@ -212,12 +216,14 @@ fn stationary_menu_image_impl<'c, R>( InnerResponse::new(inner.map(|r| r.inner), button_response) } +pub(crate) const CONTEXT_MENU_ID_STR: &str = "__egui::context_menu"; + /// Response to secondary clicks (right-clicks) by showing the given menu. pub(crate) fn context_menu( response: &Response, add_contents: impl FnOnce(&mut Ui), ) -> Option> { - let menu_id = Id::new("__egui::context_menu"); + let menu_id = Id::new(CONTEXT_MENU_ID_STR); let mut bar_state = BarState::load(&response.ctx, menu_id); MenuRoot::context_click_interaction(response, &mut bar_state, response.id); diff --git a/crates/egui_demo_lib/src/demo/context_menu.rs b/crates/egui_demo_lib/src/demo/context_menu.rs index f8b6bc58..1e1487ce 100644 --- a/crates/egui_demo_lib/src/demo/context_menu.rs +++ b/crates/egui_demo_lib/src/demo/context_menu.rs @@ -66,6 +66,11 @@ impl super::View for ContextMenus { ui.menu_button("Click for menu", Self::nested_menus); ui.button("Right-click for menu") .context_menu(Self::nested_menus); + if ui.ctx().is_context_menu_open() { + ui.label("Context menu is open"); + } else { + ui.label("Context menu is closed"); + } }); ui.separator();