egui/widgets/
image_button.rs1use crate::{
2 widgets, Color32, CornerRadius, Image, Rect, Response, 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 alt_text: Option<String>,
15}
16
17impl<'a> ImageButton<'a> {
18 pub fn new(image: impl Into<Image<'a>>) -> Self {
19 Self {
20 image: image.into(),
21 sense: Sense::click(),
22 frame: true,
23 selected: false,
24 alt_text: None,
25 }
26 }
27
28 #[inline]
30 pub fn uv(mut self, uv: impl Into<Rect>) -> Self {
31 self.image = self.image.uv(uv);
32 self
33 }
34
35 #[inline]
37 pub fn tint(mut self, tint: impl Into<Color32>) -> Self {
38 self.image = self.image.tint(tint);
39 self
40 }
41
42 #[inline]
44 pub fn selected(mut self, selected: bool) -> Self {
45 self.selected = selected;
46 self
47 }
48
49 #[inline]
51 pub fn frame(mut self, frame: bool) -> Self {
52 self.frame = frame;
53 self
54 }
55
56 #[inline]
59 pub fn sense(mut self, sense: Sense) -> Self {
60 self.sense = sense;
61 self
62 }
63
64 #[inline]
69 pub fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
70 self.image = self.image.corner_radius(corner_radius.into());
71 self
72 }
73
74 #[inline]
79 #[deprecated = "Renamed to `corner_radius`"]
80 pub fn rounding(self, corner_radius: impl Into<CornerRadius>) -> Self {
81 self.corner_radius(corner_radius)
82 }
83}
84
85impl Widget for ImageButton<'_> {
86 fn ui(self, ui: &mut Ui) -> Response {
87 let padding = if self.frame {
88 Vec2::splat(ui.spacing().button_padding.x)
90 } else {
91 Vec2::ZERO
92 };
93
94 let available_size_for_image = ui.available_size() - 2.0 * padding;
95 let tlr = self.image.load_for_size(ui.ctx(), available_size_for_image);
96 let original_image_size = tlr.as_ref().ok().and_then(|t| t.size());
97 let image_size = self
98 .image
99 .calc_size(available_size_for_image, original_image_size);
100
101 let padded_size = image_size + 2.0 * padding;
102 let (rect, response) = ui.allocate_exact_size(padded_size, self.sense);
103 response.widget_info(|| {
104 let mut info = WidgetInfo::new(WidgetType::ImageButton);
105 info.label = self.alt_text.clone();
106 info
107 });
108
109 if ui.is_rect_visible(rect) {
110 let (expansion, rounding, fill, stroke) = if self.selected {
111 let selection = ui.visuals().selection;
112 (
113 Vec2::ZERO,
114 self.image.image_options().corner_radius,
115 selection.bg_fill,
116 selection.stroke,
117 )
118 } else if self.frame {
119 let visuals = ui.style().interact(&response);
120 let expansion = Vec2::splat(visuals.expansion);
121 (
122 expansion,
123 self.image.image_options().corner_radius,
124 visuals.weak_bg_fill,
125 visuals.bg_stroke,
126 )
127 } else {
128 Default::default()
129 };
130
131 ui.painter()
133 .rect_filled(rect.expand2(expansion), rounding, fill);
134
135 let image_rect = ui
136 .layout()
137 .align_size_within_rect(image_size, rect.shrink2(padding));
138 let image_options = self.image.image_options().clone();
140
141 widgets::image::paint_texture_load_result(
142 ui,
143 &tlr,
144 image_rect,
145 None,
146 &image_options,
147 self.alt_text.as_deref(),
148 );
149
150 ui.painter().rect_stroke(
152 rect.expand2(expansion),
153 rounding,
154 stroke,
155 epaint::StrokeKind::Inside,
156 );
157 }
158
159 widgets::image::texture_load_result_response(&self.image.source(ui.ctx()), &tlr, response)
160 }
161}