egui/widgets/
image_button.rs
1use crate::{
2 widgets, Color32, Image, Rect, Response, Rounding, Sense, Ui, Vec2, Widget, WidgetInfo,
3 WidgetType,
4};
5
6#[must_use = "You should put this widget in a ui with `ui.add(widget);`"]
8#[derive(Clone, Debug)]
9pub struct ImageButton<'a> {
10 pub(crate) image: Image<'a>,
11 sense: Sense,
12 frame: bool,
13 selected: bool,
14}
15
16impl<'a> ImageButton<'a> {
17 pub fn new(image: impl Into<Image<'a>>) -> Self {
18 Self {
19 image: image.into(),
20 sense: Sense::click(),
21 frame: true,
22 selected: false,
23 }
24 }
25
26 #[inline]
28 pub fn uv(mut self, uv: impl Into<Rect>) -> Self {
29 self.image = self.image.uv(uv);
30 self
31 }
32
33 #[inline]
35 pub fn tint(mut self, tint: impl Into<Color32>) -> Self {
36 self.image = self.image.tint(tint);
37 self
38 }
39
40 #[inline]
42 pub fn selected(mut self, selected: bool) -> Self {
43 self.selected = selected;
44 self
45 }
46
47 #[inline]
49 pub fn frame(mut self, frame: bool) -> Self {
50 self.frame = frame;
51 self
52 }
53
54 #[inline]
57 pub fn sense(mut self, sense: Sense) -> Self {
58 self.sense = sense;
59 self
60 }
61
62 #[inline]
66 pub fn rounding(mut self, rounding: impl Into<Rounding>) -> Self {
67 self.image = self.image.rounding(rounding.into());
68 self
69 }
70}
71
72impl<'a> Widget for ImageButton<'a> {
73 fn ui(self, ui: &mut Ui) -> Response {
74 let padding = if self.frame {
75 Vec2::splat(ui.spacing().button_padding.x)
77 } else {
78 Vec2::ZERO
79 };
80
81 let available_size_for_image = ui.available_size() - 2.0 * padding;
82 let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image);
83 let original_image_size = tlr.as_ref().ok().and_then(|t| t.size());
84 let image_size = self
85 .image
86 .calc_size(available_size_for_image, original_image_size);
87
88 let padded_size = image_size + 2.0 * padding;
89 let (rect, response) = ui.allocate_exact_size(padded_size, self.sense);
90 response.widget_info(|| WidgetInfo::new(WidgetType::ImageButton));
91
92 if ui.is_rect_visible(rect) {
93 let (expansion, rounding, fill, stroke) = if self.selected {
94 let selection = ui.visuals().selection;
95 (
96 Vec2::ZERO,
97 self.image.image_options().rounding,
98 selection.bg_fill,
99 selection.stroke,
100 )
101 } else if self.frame {
102 let visuals = ui.style().interact(&response);
103 let expansion = Vec2::splat(visuals.expansion);
104 (
105 expansion,
106 self.image.image_options().rounding,
107 visuals.weak_bg_fill,
108 visuals.bg_stroke,
109 )
110 } else {
111 Default::default()
112 };
113
114 ui.painter()
116 .rect_filled(rect.expand2(expansion), rounding, fill);
117
118 let image_rect = ui
119 .layout()
120 .align_size_within_rect(image_size, rect.shrink2(padding));
121 let image_options = self.image.image_options().clone();
123
124 widgets::image::paint_texture_load_result(ui, &tlr, image_rect, None, &image_options);
125
126 ui.painter()
128 .rect_stroke(rect.expand2(expansion), rounding, stroke);
129 }
130
131 widgets::image::texture_load_result_response(&self.image.source(ui.ctx()), &tlr, response)
132 }
133}