egui/widgets/
image_button.rs

1use crate::*;
2
3/// A clickable image within a frame.
4#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
5#[derive(Clone, Debug)]
6pub struct ImageButton<'a> {
7    image: Image<'a>,
8    sense: Sense,
9    frame: bool,
10    selected: bool,
11}
12
13impl<'a> ImageButton<'a> {
14    pub fn new(image: impl Into<Image<'a>>) -> Self {
15        Self {
16            image: image.into(),
17            sense: Sense::click(),
18            frame: true,
19            selected: false,
20        }
21    }
22
23    /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right.
24    #[inline]
25    pub fn uv(mut self, uv: impl Into<Rect>) -> Self {
26        self.image = self.image.uv(uv);
27        self
28    }
29
30    /// Multiply image color with this. Default is WHITE (no tint).
31    #[inline]
32    pub fn tint(mut self, tint: impl Into<Color32>) -> Self {
33        self.image = self.image.tint(tint);
34        self
35    }
36
37    /// If `true`, mark this button as "selected".
38    #[inline]
39    pub fn selected(mut self, selected: bool) -> Self {
40        self.selected = selected;
41        self
42    }
43
44    /// Turn off the frame
45    #[inline]
46    pub fn frame(mut self, frame: bool) -> Self {
47        self.frame = frame;
48        self
49    }
50
51    /// By default, buttons senses clicks.
52    /// Change this to a drag-button with `Sense::drag()`.
53    #[inline]
54    pub fn sense(mut self, sense: Sense) -> Self {
55        self.sense = sense;
56        self
57    }
58
59    /// Set rounding for the `ImageButton`.
60    /// If the underlying image already has rounding, this
61    /// will override that value.
62    #[inline]
63    pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
64        self.image = self.image.rounding(rounding.into());
65        self
66    }
67}
68
69impl<'a> Widget for ImageButton<'a> {
70    fn ui(self, ui: &mut Ui) -> Response {
71        let padding = if self.frame {
72            // so we can see that it is a button:
73            Vec2::splat(ui.spacing().button_padding.x)
74        } else {
75            Vec2::ZERO
76        };
77
78        let available_size_for_image = ui.available_size() - 2.0 * padding;
79        let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image);
80        let original_image_size = tlr.as_ref().ok().and_then(|t| t.size());
81        let image_size = self
82            .image
83            .calc_size(available_size_for_image, original_image_size);
84
85        let padded_size = image_size + 2.0 * padding;
86        let (rect, response) = ui.allocate_exact_size(padded_size, self.sense);
87        response.widget_info(|| WidgetInfo::new(WidgetType::ImageButton));
88
89        if ui.is_rect_visible(rect) {
90            let (expansion, rounding, fill, stroke) = if self.selected {
91                let selection = ui.visuals().selection;
92                (
93                    Vec2::ZERO,
94                    self.image.image_options().rounding,
95                    selection.bg_fill,
96                    selection.stroke,
97                )
98            } else if self.frame {
99                let visuals = ui.style().interact(&response);
100                let expansion = Vec2::splat(visuals.expansion);
101                (
102                    expansion,
103                    self.image.image_options().rounding,
104                    visuals.weak_bg_fill,
105                    visuals.bg_stroke,
106                )
107            } else {
108                Default::default()
109            };
110
111            // Draw frame background (for transparent images):
112            ui.painter()
113                .rect_filled(rect.expand2(expansion), rounding, fill);
114
115            let image_rect = ui
116                .layout()
117                .align_size_within_rect(image_size, rect.shrink2(padding));
118            // let image_rect = image_rect.expand2(expansion); // can make it blurry, so let's not
119            let image_options = self.image.image_options().clone();
120
121            widgets::image::paint_texture_load_result(ui, &tlr, image_rect, None, &image_options);
122
123            // Draw frame outline:
124            ui.painter()
125                .rect_stroke(rect.expand2(expansion), rounding, stroke);
126        }
127
128        widgets::image::texture_load_result_response(&self.image.source(ui.ctx()), &tlr, response)
129    }
130}