From 18ebac116ff8ff5f584bcad5e5662188b182088f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 15 Dec 2020 14:52:24 +0100 Subject: [PATCH] Add widget `ImageButton` --- CHANGELOG.md | 1 + egui/src/style.rs | 2 +- egui/src/widgets/button.rs | 93 ++++++++++++++++++++++++++++++++++++++ egui/src/widgets/image.rs | 35 +++++++++----- egui/src/widgets/mod.rs | 15 +++--- 5 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 egui/src/widgets/button.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 408402e9..e6b5b52a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added ⭐ +* `ImageButton` - `ui.add(ImageButton::new(...))` * `ui.vertical_centered` and `ui.vertical_centered_justified` ### Changed 🔧 diff --git a/egui/src/style.rs b/egui/src/style.rs index 9dc04ad1..66d97b86 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -159,7 +159,7 @@ impl Visuals { } /// Selected text, selected elements etc -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Selection { pub bg_fill: Srgba, diff --git a/egui/src/widgets/button.rs b/egui/src/widgets/button.rs new file mode 100644 index 00000000..be2e8c1d --- /dev/null +++ b/egui/src/widgets/button.rs @@ -0,0 +1,93 @@ +use crate::*; + +#[derive(Clone, Debug)] +pub struct ImageButton { + image: widgets::Image, + sense: Sense, + frame: bool, + selected: bool, +} + +impl ImageButton { + pub fn new(texture_id: TextureId, desired_size: impl Into) -> Self { + Self { + image: widgets::Image::new(texture_id, desired_size), + sense: Sense::click(), + frame: true, + selected: false, + } + } + + /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right. + pub fn uv(mut self, uv: impl Into) -> Self { + self.image = self.image.uv(uv); + self + } + + /// Multiply image color with this. Default is WHITE (no tint). + pub fn tint(mut self, tint: impl Into) -> Self { + self.image = self.image.tint(tint); + self + } + + /// If `true`, mark this button as "selected". + pub fn selected(mut self, selected: bool) -> Self { + self.selected = selected; + self + } + + /// Turn off the frame + pub fn frame(mut self, frame: bool) -> Self { + self.frame = frame; + self + } + + /// By default, buttons senses clicks. + /// Change this to a drag-button with `Sense::drag()`. + pub fn sense(mut self, sense: Sense) -> Self { + self.sense = sense; + self + } +} + +impl Widget for ImageButton { + fn ui(self, ui: &mut Ui) -> Response { + let Self { + image, + sense, + frame, + selected, + } = self; + + let button_padding = ui.style().spacing.button_padding; + let desired_size = image.desired_size() + 2.0 * button_padding; + let rect = ui.allocate_space(desired_size); + let id = ui.make_position_id(); + let response = ui.interact(rect, id, sense); + + if ui.clip_rect().intersects(rect) { + let visuals = ui.style().interact(&response); + + if selected { + let selection = ui.style().visuals.selection; + ui.painter() + .rect(response.rect, 0.0, selection.bg_fill, selection.stroke); + } else if frame { + ui.painter().rect( + response.rect, + visuals.corner_radius, + visuals.bg_fill, + visuals.bg_stroke, + ); + } + + let image_rect = ui.layout().align_size_within_rect( + image.desired_size(), + response.rect.shrink2(button_padding), + ); + image.paint_at(ui, image_rect); + } + + response + } +} diff --git a/egui/src/widgets/image.rs b/egui/src/widgets/image.rs index 2098f1a4..53298c73 100644 --- a/egui/src/widgets/image.rs +++ b/egui/src/widgets/image.rs @@ -39,29 +39,40 @@ impl Image { } } -impl Widget for Image { - fn ui(self, ui: &mut Ui) -> Response { +impl Image { + pub fn desired_size(&self) -> Vec2 { + self.desired_size + } + + pub fn paint_at(&self, ui: &mut Ui, rect: Rect) { use paint::*; let Self { texture_id, uv, - desired_size, + desired_size: _, bg_fill, tint, } = self; - let rect = ui.allocate_space(desired_size); - if bg_fill != Default::default() { + + if *bg_fill != Default::default() { let mut triangles = Triangles::default(); - triangles.add_colored_rect(rect, bg_fill); - ui.painter().add(PaintCmd::triangles(triangles)); - } - { - // TODO: builder pattern for Triangles - let mut triangles = Triangles::with_texture(texture_id); - triangles.add_rect_with_uv(rect, uv, tint); + triangles.add_colored_rect(rect, *bg_fill); ui.painter().add(PaintCmd::triangles(triangles)); } + { + // TODO: builder pattern for Triangles + let mut triangles = Triangles::with_texture(*texture_id); + triangles.add_rect_with_uv(rect, *uv, *tint); + ui.painter().add(PaintCmd::triangles(triangles)); + } + } +} + +impl Widget for Image { + fn ui(self, ui: &mut Ui) -> Response { + let rect = ui.allocate_space(self.desired_size); + self.paint_at(ui, rect); ui.interact_hover(rect) } } diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs index 6c9552fb..8d5cd589 100644 --- a/egui/src/widgets/mod.rs +++ b/egui/src/widgets/mod.rs @@ -8,13 +8,14 @@ use crate::*; +mod button; pub mod color_picker; mod drag_value; mod image; mod slider; pub(crate) mod text_edit; -pub use {drag_value::DragValue, image::Image, slider::*, text_edit::*}; +pub use {button::*, drag_value::DragValue, image::Image, slider::*, text_edit::*}; use paint::*; @@ -380,12 +381,6 @@ impl Widget for Button { small, frame, } = self; - - let mut button_padding = ui.style().spacing.button_padding; - if small { - button_padding.y = 0.0; - } - let font = &ui.fonts()[text_style]; let single_line = ui.layout().is_horizontal(); @@ -395,10 +390,16 @@ impl Widget for Button { font.layout_multiline(text, ui.available_width()) }; + let mut button_padding = ui.style().spacing.button_padding; + if small { + button_padding.y = 0.0; + } + let mut desired_size = galley.size + 2.0 * button_padding; if !small { desired_size.y = desired_size.y.at_least(ui.style().spacing.interact_size.y); } + let rect = ui.allocate_space(desired_size); let id = ui.make_position_id();