diff --git a/egui/src/widgets/button.rs b/egui/src/widgets/button.rs index d9ff8e56158..e5866e56980 100644 --- a/egui/src/widgets/button.rs +++ b/egui/src/widgets/button.rs @@ -29,6 +29,7 @@ pub struct Button { small: bool, frame: Option, min_size: Vec2, + image: Option, } impl Button { @@ -42,6 +43,27 @@ impl Button { small: false, frame: None, min_size: Vec2::ZERO, + image: None, + } + } + + /// Creates a button with an image to the left of the text. The size of the image as displayed is defined by the size Vec2 provided. + #[allow(clippy::needless_pass_by_value)] + pub fn image_and_text( + texture_id: TextureId, + size: impl Into, + text: impl Into, + ) -> Self { + Self { + text: text.into(), + fill: None, + stroke: None, + sense: Sense::click(), + small: false, + frame: None, + wrap: None, + min_size: Vec2::ZERO, + image: Some(widgets::Image::new(texture_id, size)), } } @@ -123,6 +145,7 @@ impl Widget for Button { small, frame, min_size, + image, } = self; let frame = frame.unwrap_or_else(|| ui.visuals().button_frame); @@ -142,15 +165,27 @@ impl Widget for Button { } desired_size = desired_size.at_least(min_size); + if let Some(image) = image { + desired_size.x += image.size().x + ui.spacing().icon_spacing; + desired_size.y = desired_size.y.max(image.size().y + 2.0 * button_padding.y); + } + let (rect, response) = ui.allocate_at_least(desired_size, sense); response.widget_info(|| WidgetInfo::labeled(WidgetType::Button, text.text())); if ui.is_rect_visible(rect) { let visuals = ui.style().interact(&response); - let text_pos = ui - .layout() - .align_size_within_rect(text.size(), rect.shrink2(button_padding)) - .min; + let text_pos = if let Some(image) = image { + let icon_spacing = ui.spacing().icon_spacing; + pos2( + rect.min.x + button_padding.x + image.size().x + icon_spacing, + rect.center().y - 0.5 * text.size().y, + ) + } else { + ui.layout() + .align_size_within_rect(text.size(), rect.shrink2(button_padding)) + .min + }; if frame { let fill = fill.unwrap_or(visuals.bg_fill); @@ -166,6 +201,14 @@ impl Widget for Button { text.paint_with_visuals(ui.painter(), text_pos, visuals); } + if let Some(image) = image { + let image_rect = Rect::from_min_size( + pos2(rect.min.x, rect.center().y - 0.5 - (image.size().y / 2.0)), + image.size(), + ); + image.paint_at(ui, image_rect); + } + response } } diff --git a/egui_glium/examples/native_texture.rs b/egui_glium/examples/native_texture.rs index db5da4ad7fe..c380d66f833 100644 --- a/egui_glium/examples/native_texture.rs +++ b/egui_glium/examples/native_texture.rs @@ -52,6 +52,8 @@ fn main() { let glium_texture = std::rc::Rc::new(glium_texture); // Allocate egui's texture id for GL texture let texture_id = egui_glium.painter.register_native_texture(glium_texture); + // Setup button image size for reasonable image size for button container. + let button_image_size = egui::Vec2::new(32_f32, 32_f32); event_loop.run(move |event, _, control_flow| { let mut redraw = || { @@ -59,7 +61,14 @@ fn main() { let (needs_repaint, shapes) = egui_glium.run(&display, |egui_ctx| { egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| { - if ui.button("Quit").clicked() { + if ui + .add(egui::Button::image_and_text( + texture_id, + button_image_size, + "Quit", + )) + .clicked() + { quit = true; } });