diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 59c15348..089a290c 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -100,6 +100,20 @@ 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. +/// +/// Responds to primary clicks. +/// +/// Returns `None` if the menu is not open. +pub fn menu_image_button( + ui: &mut Ui, + texture_id: TextureId, + image_size: impl Into, + add_contents: impl FnOnce(&mut Ui) -> R, +) -> InnerResponse> { + stationary_menu_image_impl(ui, texture_id, image_size, Box::new(add_contents)) +} + /// Construct a nested sub menu in another menu. /// /// Opens on hover. @@ -177,6 +191,25 @@ fn stationary_menu_impl<'c, R>( InnerResponse::new(inner.map(|r| r.inner), button_response) } +/// Build a top level menu with an image button. +/// +/// Responds to primary clicks. +fn stationary_menu_image_impl<'c, R>( + ui: &mut Ui, + texture_id: TextureId, + image_size: impl Into, + 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(ImageButton::new(texture_id, image_size)); + 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) +} + /// Response to secondary clicks (right-clicks) by showing the given menu. pub(crate) fn context_menu( response: &Response, diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index f8a68563..a8ac5c92 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -2157,6 +2157,43 @@ impl Ui { menu::menu_button(self, title, add_contents) } } + + #[inline] + /// Create a menu button with an image that when clicked will show the given menu. + /// + /// If called from within a menu this will instead create a button for a sub-menu. + /// + /// ``` + /// use egui_extras; + /// + /// let img = egui_extrasRetainedImage::from_svg_bytes_with_size( + /// "rss", + /// include_bytes!("rss.svg"), + /// egui_extras::image::FitTo::Size(24, 24), + /// ); + /// + /// ui.menu_image_button(img.texture_id(ctx), img.size_vec2(), |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`]. + pub fn menu_image_button( + &mut self, + texture_id: TextureId, + image_size: 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_image_button(self, texture_id, image_size, add_contents) + } + } } // ----------------------------------------------------------------------------