use crate::rust_view_ui; use egui::color_picker::{Alpha, color_picker_color32}; use egui::containers::menu::{MenuConfig, SubMenuButton}; use egui::{ Align, Align2, Atom, Button, ComboBox, Frame, Id, Layout, Popup, PopupCloseBehavior, RectAlign, RichText, Tooltip, Ui, UiBuilder, include_image, }; /// Showcase [`Popup`]. #[derive(Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct PopupsDemo { align4: RectAlign, gap: f32, #[cfg_attr(feature = "serde", serde(skip))] close_behavior: PopupCloseBehavior, popup_open: bool, checked: bool, color: egui::Color32, } impl Default for PopupsDemo { fn default() -> Self { Self { align4: RectAlign::default(), gap: 4.0, close_behavior: PopupCloseBehavior::CloseOnClick, popup_open: false, checked: true, color: egui::Color32::RED, } } } impl PopupsDemo { fn apply_options<'a>(&self, popup: Popup<'a>) -> Popup<'a> { popup .align(self.align4) .gap(self.gap) .close_behavior(self.close_behavior) } fn nested_menus(&mut self, ui: &mut Ui) { ui.set_max_width(200.0); // To make sure we wrap long text if ui.button("Open…").clicked() { ui.close(); } ui.menu_button("Popups can have submenus", |ui| { ui.menu_button("SubMenu", |ui| { if ui.button("Open…").clicked() { ui.close(); } let _ = ui.button("Item"); ui.menu_button("Recursive", |ui| self.nested_menus(ui)); }); ui.menu_button("SubMenu", |ui| { if ui.button("Open…").clicked() { ui.close(); } let _ = ui.button("Item"); }); let _ = ui.button("Item"); if ui.button("Open…").clicked() { ui.close(); } }); ui.add_enabled_ui(false, |ui| { ui.menu_button("SubMenus can be disabled", |_| {}); }); ui.menu_image_text_button( include_image!("../../data/icon.png"), "I have an icon!", |ui| { let _ = ui.button("Item1"); let _ = ui.button("Item2"); let _ = ui.button("Item3"); let _ = ui.button("Item4"); if ui.button("Open…").clicked() { ui.close(); } }, ); let _ = ui.button("Very long text for this item that should be wrapped"); SubMenuButton::new("Always CloseOnClickOutside") .config(MenuConfig::new().close_behavior(PopupCloseBehavior::CloseOnClickOutside)) .ui(ui, |ui| { ui.checkbox(&mut self.checked, "Checkbox"); // Customized color SubMenuButton let is_bright = self.color.intensity() > 0.5; let text_color = if is_bright { egui::Color32::BLACK } else { egui::Color32::WHITE }; let button = Button::new(( RichText::new("Background").color(text_color), Atom::grow(), RichText::new(SubMenuButton::RIGHT_ARROW).color(text_color), )) .fill(self.color); SubMenuButton::from_button(button).ui(ui, |ui| { ui.spacing_mut().slider_width = 200.0; color_picker_color32(ui, &mut self.color, Alpha::Opaque); }); if self.checked { ui.menu_button("Only visible when checked", |ui| { if ui.button("Remove myself").clicked() { self.checked = false; } }); } if ui.button("Open…").clicked() { ui.close(); } }); } } impl crate::Demo for PopupsDemo { fn name(&self) -> &'static str { "\u{2755} Popups" } fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) { egui::Window::new(self.name()) .open(open) .resizable(false) .default_width(250.0) .constrain(false) .constrain_to(ui.available_rect_before_wrap()) .show(ui, |ui| { use crate::View as _; self.ui(ui); }); } } impl crate::View for PopupsDemo { fn ui(&mut self, ui: &mut egui::Ui) { let response = Frame::group(ui.style()) .show(ui, |ui| { ui.set_width(ui.available_width()); ui.vertical_centered(|ui| ui.button("Click, right-click and hover me!")) .inner }) .inner; self.apply_options(Popup::menu(&response).id(Id::new("menu"))) .show(|ui| self.nested_menus(ui)); self.apply_options(Popup::context_menu(&response).id(Id::new("context_menu"))) .show(|ui| self.nested_menus(ui)); if self.popup_open { self.apply_options(Popup::from_response(&response).id(Id::new("popup"))) .show(|ui| { ui.label("Popup contents"); }); } let mut tooltip = Tooltip::for_enabled(&response); tooltip.popup = self.apply_options(tooltip.popup); tooltip.show(|ui| { ui.label("Tooltips are popups, too!"); }); Frame::canvas(ui.style()).show(ui, |ui| { let mut reset_btn_ui = ui.new_child( UiBuilder::new() .max_rect(ui.max_rect()) .layout(Layout::right_to_left(Align::Min)), ); if reset_btn_ui .button("⟲") .on_hover_text("Reset to defaults") .clicked() { *self = Self::default(); } ui.set_width(ui.available_width()); ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace); ui.spacing_mut().item_spacing.x = 0.0; let align_combobox = |ui: &mut Ui, label: &str, align: &mut Align2| { let aligns = [ (Align2::LEFT_TOP, "LEFT_TOP"), (Align2::LEFT_CENTER, "LEFT_CENTER"), (Align2::LEFT_BOTTOM, "LEFT_BOTTOM"), (Align2::CENTER_TOP, "CENTER_TOP"), (Align2::CENTER_CENTER, "CENTER_CENTER"), (Align2::CENTER_BOTTOM, "CENTER_BOTTOM"), (Align2::RIGHT_TOP, "RIGHT_TOP"), (Align2::RIGHT_CENTER, "RIGHT_CENTER"), (Align2::RIGHT_BOTTOM, "RIGHT_BOTTOM"), ]; ComboBox::new(label, "") .selected_text(aligns.iter().find(|(a, _)| a == align).unwrap().1) .show_ui(ui, |ui| { for (align2, name) in &aligns { ui.selectable_value(align, *align2, *name); } }); }; rust_view_ui(ui, "let align = RectAlign {"); ui.horizontal(|ui| { rust_view_ui(ui, " parent: Align2::"); align_combobox(ui, "parent", &mut self.align4.parent); rust_view_ui(ui, ","); }); ui.horizontal(|ui| { rust_view_ui(ui, " child: Align2::"); align_combobox(ui, "child", &mut self.align4.child); rust_view_ui(ui, ","); }); rust_view_ui(ui, "};"); ui.horizontal(|ui| { rust_view_ui(ui, "let align = RectAlign::"); let presets = [ (RectAlign::TOP_START, "TOP_START"), (RectAlign::TOP, "TOP"), (RectAlign::TOP_END, "TOP_END"), (RectAlign::RIGHT_START, "RIGHT_START"), (RectAlign::RIGHT, "RIGHT"), (RectAlign::RIGHT_END, "RIGHT_END"), (RectAlign::BOTTOM_START, "BOTTOM_START"), (RectAlign::BOTTOM, "BOTTOM"), (RectAlign::BOTTOM_END, "BOTTOM_END"), (RectAlign::LEFT_START, "LEFT_START"), (RectAlign::LEFT, "LEFT"), (RectAlign::LEFT_END, "LEFT_END"), ]; ComboBox::new("Preset", "") .selected_text( presets .iter() .find(|(a, _)| a == &self.align4) .map_or("