diff --git a/egui/src/containers/combo_box.rs b/egui/src/containers/combo_box.rs index bbc3809b9cc..fa90d86ac4d 100644 --- a/egui/src/containers/combo_box.rs +++ b/egui/src/containers/combo_box.rs @@ -1,6 +1,9 @@ use crate::{style::WidgetVisuals, *}; use epaint::Shape; +/// A function that paints the `ComboBox` icon +pub type IconPainter = Box; + /// A drop-down selection menu with a descriptive label. /// /// ``` @@ -24,6 +27,7 @@ pub struct ComboBox { label: Option, selected_text: WidgetText, width: Option, + icon: Option, } impl ComboBox { @@ -34,6 +38,7 @@ impl ComboBox { label: Some(label.into()), selected_text: Default::default(), width: None, + icon: None, } } @@ -45,6 +50,7 @@ impl ComboBox { label: Some(label), selected_text: Default::default(), width: None, + icon: None, } } @@ -55,6 +61,7 @@ impl ComboBox { label: Default::default(), selected_text: Default::default(), width: None, + icon: None, } } @@ -70,6 +77,41 @@ impl ComboBox { self } + /// Use the provided function to render a different `ComboBox` icon. + /// Defaults to a triangle that expands when the cursor is hovering over the `ComboBox`. + /// + /// For example: + /// ``` + /// # egui::__run_test_ui(|ui| { + /// # let text = "Selected text"; + /// pub fn filled_triangle( + /// ui: &egui::Ui, + /// rect: egui::Rect, + /// visuals: &egui::style::WidgetVisuals, + /// _is_open: bool, + /// ) { + /// let rect = egui::Rect::from_center_size( + /// rect.center(), + /// egui::Vec2::new(rect.width() * 0.6, rect.height() * 0.4), + /// ); + /// ui.painter().add(egui::Shape::convex_polygon( + /// vec![rect.left_top(), rect.right_top(), rect.center_bottom()], + /// visuals.fg_stroke.color, + /// visuals.fg_stroke, + /// )); + /// } + /// + /// egui::ComboBox::from_id_source("my-combobox") + /// .selected_text(text) + /// .icon(filled_triangle) + /// .show_ui(ui, |_ui| {}); + /// # }); + /// ``` + pub fn icon(mut self, icon_fn: impl FnOnce(&Ui, Rect, &WidgetVisuals, bool) + 'static) -> Self { + self.icon = Some(Box::new(icon_fn)); + self + } + /// Show the combo box, with the given ui code for the menu contents. /// /// Returns `InnerResponse { inner: None }` if the combo box is closed. @@ -91,6 +133,7 @@ impl ComboBox { label, selected_text, width, + icon, } = self; let button_id = ui.make_persistent_id(id_source); @@ -99,7 +142,7 @@ impl ComboBox { if let Some(width) = width { ui.spacing_mut().slider_width = width; // yes, this is ugly. Will remove later. } - let mut ir = combo_box_dyn(ui, button_id, selected_text, menu_contents); + let mut ir = combo_box_dyn(ui, button_id, selected_text, menu_contents, icon); if let Some(label) = label { ir.response .widget_info(|| WidgetInfo::labeled(WidgetType::ComboBox, label.text())); @@ -165,6 +208,7 @@ fn combo_box_dyn<'c, R>( button_id: Id, selected_text: WidgetText, menu_contents: Box R + 'c>, + icon: Option, ) -> InnerResponse> { let popup_id = button_id.with("popup"); @@ -192,7 +236,17 @@ fn combo_box_dyn<'c, R>( } else { ui.style().interact(&response) }; - paint_icon(ui.painter(), icon_rect.expand(visuals.expansion), visuals); + + if let Some(icon) = icon { + icon( + ui, + icon_rect.expand(visuals.expansion), + visuals, + is_popup_open, + ); + } else { + paint_default_icon(ui.painter(), icon_rect.expand(visuals.expansion), visuals); + } let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size(), rect); galley.paint_with_visuals(ui.painter(), text_rect.min, visuals); @@ -262,7 +316,7 @@ fn button_frame( response } -fn paint_icon(painter: &Painter, rect: Rect, visuals: &WidgetVisuals) { +fn paint_default_icon(painter: &Painter, rect: Rect, visuals: &WidgetVisuals) { let rect = Rect::from_center_size( rect.center(), vec2(rect.width() * 0.7, rect.height() * 0.45),