From 6202b2a82209f6b87c4a0816ea5f3006acb08edf Mon Sep 17 00:00:00 2001 From: Nicolas Bircks Date: Mon, 1 Jul 2024 13:31:13 +0000 Subject: [PATCH 1/5] add text_image menu button --- crates/egui/src/menu.rs | 31 +++++++++++++++++++++++++++++++ crates/egui/src/ui.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 4325f07d8810..80121bf2d48c 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -118,6 +118,19 @@ pub fn menu_image_button( stationary_menu_image_impl(ui, image_button, Box::new(add_contents)) } +/// Construct a top level menu with an image and a text in a menu bar. This would be e.g. "File", "Edit" etc. +/// +/// Responds to primary clicks. +/// +/// Returns `None` if the menu is not open. +pub fn menu_text_image_button( + ui: &mut Ui, + image_button: Button<'_>, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse> { + stationary_menu_text_image_impl(ui, image_button, Box::new(add_contents)) +} + /// Construct a nested sub menu in another menu. /// /// Opens on hover. @@ -230,6 +243,24 @@ fn stationary_menu_image_impl<'c, R>( InnerResponse::new(inner.map(|r| r.inner), button_response) } +/// Build a top level menu with an text and image button. +/// +/// Responds to primary clicks. +fn stationary_menu_text_image_impl<'c, R>( + ui: &mut Ui, + image_button: Button<'_>, + add_contents: Box R + 'c>, +) -> InnerResponse> { + let bar_id = ui.id(); + + let mut bar_state = BarState::load(ui.ctx(), bar_id); + let button_response = ui.add(image_button); + let inner = bar_state.bar_menu(&button_response, add_contents); + + bar_state.store(ui.ctx(), bar_id); + 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. diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index f24a8a3792ad..0afcf16474b3 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2567,6 +2567,38 @@ impl Ui { menu::menu_image_button(self, ImageButton::new(image), add_contents) } } + + /// Create a menu button with an image and a text that when clicked will show the given menu. + /// + /// If called from within a menu this will instead create a button for a sub-menu. + /// + /// ```ignore + /// let img = egui::include_image!("../assets/ferris.png"); + /// let title = "My Menu"; + /// + /// ui.menu_text_image_button(title, img, |ui| { + /// ui.menu_button("My sub-menu", |ui| { + /// if ui.button("Close the menu").clicked() { + /// ui.close_menu(); + /// } + /// }); + /// }); + /// ``` + /// + /// See also: [`Self::close_menu`] and [`Response::context_menu`]. + #[inline] + pub fn menu_text_image_button<'a, R>( + &mut self, + title: impl Into, + image: impl Into>, + add_contents: impl FnOnce(&mut Ui) -> R, + ) -> InnerResponse> { + if let Some(menu_state) = self.menu_state.clone() { + menu::submenu_button(self, menu_state, String::new(), add_contents) + } else { + menu::menu_text_image_button(self, Button::image_and_text(image, title), add_contents) + } + } } // ---------------------------------------------------------------------------- From 168fc4584cd8d5cc7a427e60c9fdb2c0a4273c77 Mon Sep 17 00:00:00 2001 From: Nicolas Bircks Date: Mon, 1 Jul 2024 18:35:03 +0000 Subject: [PATCH 2/5] fix title --- crates/egui/src/ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 0afcf16474b3..9d5c10d62885 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2594,7 +2594,7 @@ impl Ui { add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse> { if let Some(menu_state) = self.menu_state.clone() { - menu::submenu_button(self, menu_state, String::new(), add_contents) + menu::submenu_button(self, menu_state, title, add_contents) } else { menu::menu_text_image_button(self, Button::image_and_text(image, title), add_contents) } From c9d3103dfbf07a71cf7442fd0f193ced290d93d2 Mon Sep 17 00:00:00 2001 From: Nicolas Bircks Date: Tue, 2 Jul 2024 11:09:40 +0200 Subject: [PATCH 3/5] removing duplicated code --- crates/egui/src/menu.rs | 45 +++++++---------------------------------- crates/egui/src/ui.rs | 4 ++-- 2 files changed, 9 insertions(+), 40 deletions(-) diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 80121bf2d48c..1097897bf167 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -105,30 +105,17 @@ pub fn menu_button( stationary_menu_impl(ui, title, Box::new(add_contents)) } -/// Construct a top level menu with an image in a menu bar. This would be e.g. "File", "Edit" etc. +/// Construct a top level menu with a custom button in a menu bar. /// /// Responds to primary clicks. /// /// Returns `None` if the menu is not open. -pub fn menu_image_button( +pub fn menu_custom_button( ui: &mut Ui, - image_button: ImageButton<'_>, + button: Button<'_>, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse> { - stationary_menu_image_impl(ui, image_button, Box::new(add_contents)) -} - -/// Construct a top level menu with an image and a text in a menu bar. This would be e.g. "File", "Edit" etc. -/// -/// Responds to primary clicks. -/// -/// Returns `None` if the menu is not open. -pub fn menu_text_image_button( - ui: &mut Ui, - image_button: Button<'_>, - add_contents: impl FnOnce(&mut Ui) -> R, -) -> InnerResponse> { - stationary_menu_text_image_impl(ui, image_button, Box::new(add_contents)) + stationary_menu_button_impl(ui, button, Box::new(add_contents)) } /// Construct a nested sub menu in another menu. @@ -228,33 +215,15 @@ fn stationary_menu_impl<'c, R>( /// Build a top level menu with an image button. /// /// Responds to primary clicks. -fn stationary_menu_image_impl<'c, R>( - ui: &mut Ui, - image_button: ImageButton<'_>, - add_contents: Box R + 'c>, -) -> InnerResponse> { - let bar_id = ui.id(); - - let mut bar_state = BarState::load(ui.ctx(), bar_id); - let button_response = ui.add(image_button); - let inner = bar_state.bar_menu(&button_response, add_contents); - - bar_state.store(ui.ctx(), bar_id); - InnerResponse::new(inner.map(|r| r.inner), button_response) -} - -/// Build a top level menu with an text and image button. -/// -/// Responds to primary clicks. -fn stationary_menu_text_image_impl<'c, R>( +fn stationary_menu_button_impl<'c, R>( ui: &mut Ui, - image_button: Button<'_>, + button: Button<'_>, add_contents: Box R + 'c>, ) -> InnerResponse> { let bar_id = ui.id(); let mut bar_state = BarState::load(ui.ctx(), bar_id); - let button_response = ui.add(image_button); + let button_response = ui.add(button); let inner = bar_state.bar_menu(&button_response, add_contents); bar_state.store(ui.ctx(), bar_id); diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 9d5c10d62885..621fc6a7c019 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2564,7 +2564,7 @@ impl Ui { if let Some(menu_state) = self.menu_state.clone() { menu::submenu_button(self, menu_state, String::new(), add_contents) } else { - menu::menu_image_button(self, ImageButton::new(image), add_contents) + menu::menu_custom_button(self, Button::image(image), add_contents) } } @@ -2596,7 +2596,7 @@ impl Ui { if let Some(menu_state) = self.menu_state.clone() { menu::submenu_button(self, menu_state, title, add_contents) } else { - menu::menu_text_image_button(self, Button::image_and_text(image, title), add_contents) + menu::menu_custom_button(self, Button::image_and_text(image, title), add_contents) } } } From dc5f146e2bf5119856e6b23fe785ac548ab923c8 Mon Sep 17 00:00:00 2001 From: Nicolas Bircks Date: Tue, 9 Jul 2024 08:18:09 +0200 Subject: [PATCH 4/5] Update crates/egui/src/ui.rs Co-authored-by: Emil Ernerfeldt --- crates/egui/src/ui.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index bbc97ea1d0da..d63d8961e6d6 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2587,10 +2587,10 @@ impl Ui { /// /// See also: [`Self::close_menu`] and [`Response::context_menu`]. #[inline] - pub fn menu_text_image_button<'a, R>( + pub fn menu_image_text_button<'a, R>( &mut self, - title: impl Into, image: impl Into>, + title: impl Into, add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse> { if let Some(menu_state) = self.menu_state.clone() { From fea0e0671893193d629f24f06e0cf7a5e7faecde Mon Sep 17 00:00:00 2001 From: Nicolas Bircks Date: Tue, 9 Jul 2024 10:29:48 +0200 Subject: [PATCH 5/5] Fix review findings --- crates/egui/src/menu.rs | 18 ++++++++++++++++++ crates/egui/src/ui.rs | 11 ++++++++--- crates/egui/src/widgets/image_button.rs | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 47bf72dd3425..3801f89b1787 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -118,6 +118,24 @@ pub fn menu_custom_button( stationary_menu_button_impl(ui, button, Box::new(add_contents)) } +/// Construct a top level menu with an image in a menu bar. This would be e.g. "File", "Edit" etc. +/// +/// Responds to primary clicks. +/// +/// Returns `None` if the menu is not open. +#[deprecated = "Use `menu_custom_button` instead"] +pub fn menu_image_button( + ui: &mut Ui, + image_button: ImageButton<'_>, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse> { + stationary_menu_button_impl( + ui, + Button::image(image_button.image), + Box::new(add_contents), + ) +} + /// Construct a nested sub menu in another menu. /// /// Opens on hover. diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index d63d8961e6d6..74eecc846e43 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2543,16 +2543,19 @@ impl Ui { /// If called from within a menu this will instead create a button for a sub-menu. /// /// ```ignore + /// # egui::__run_test_ui(|ui| { /// let img = egui::include_image!("../assets/ferris.png"); /// - /// ui.menu_image_button(img, |ui| { + /// ui.menu_image_button(title, img, |ui| { /// ui.menu_button("My sub-menu", |ui| { /// if ui.button("Close the menu").clicked() { /// ui.close_menu(); /// } /// }); /// }); + /// # }); /// ``` + /// /// /// See also: [`Self::close_menu`] and [`Response::context_menu`]. #[inline] @@ -2572,17 +2575,19 @@ impl Ui { /// /// If called from within a menu this will instead create a button for a sub-menu. /// - /// ```ignore + /// ``` + /// # egui::__run_test_ui(|ui| { /// let img = egui::include_image!("../assets/ferris.png"); /// let title = "My Menu"; /// - /// ui.menu_text_image_button(title, img, |ui| { + /// ui.menu_image_text_button(img, title, |ui| { /// ui.menu_button("My sub-menu", |ui| { /// if ui.button("Close the menu").clicked() { /// ui.close_menu(); /// } /// }); /// }); + /// # }); /// ``` /// /// See also: [`Self::close_menu`] and [`Response::context_menu`]. diff --git a/crates/egui/src/widgets/image_button.rs b/crates/egui/src/widgets/image_button.rs index 8b206757efa7..79460b456be3 100644 --- a/crates/egui/src/widgets/image_button.rs +++ b/crates/egui/src/widgets/image_button.rs @@ -4,7 +4,7 @@ use crate::*; #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] #[derive(Clone, Debug)] pub struct ImageButton<'a> { - image: Image<'a>, + pub(crate) image: Image<'a>, sense: Sense, frame: bool, selected: bool,