egui/widgets/
image_button.rs

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