1use crate::buffer_graphics_lib::shapes::polyline::Polyline;
2use crate::ui::prelude::*;
3use crate::ui::tooltip::Tooltip;
4use crate::ui::{PixelView, ViewState};
5use crate::Timing;
6
7#[derive(Debug)]
8pub struct IconButton {
9 tooltip: Tooltip,
10 icon: IndexedImage,
11 icon_xy: Coord,
12 bounds: Rect,
13 border: Polyline,
14 shadow: Polyline,
15 style: IconButtonStyle,
16 state: ViewState,
17 tooltip_text: String,
18 tooltip_positioning: Positioning,
19}
20
21impl IconButton {
22 pub fn new<P: Into<Coord>>(
23 xy: P,
24 tooltip_text: &str,
25 tooltip_positioning: Positioning,
26 icon: IndexedImage,
27 style: &IconButtonStyle,
28 ) -> Self {
29 let xy = xy.into();
30 let (w, h) = icon.size();
31 let bounds = Rect::new_with_size(
32 xy,
33 w as usize + style.padding + style.padding,
34 h as usize + style.padding + style.padding,
35 );
36 let (icon_xy, border, shadow, tooltip) =
37 Self::layout(&bounds, style, tooltip_text, tooltip_positioning, (w, h));
38 Self {
39 tooltip,
40 icon,
41 icon_xy,
42 bounds,
43 border,
44 shadow,
45 style: style.clone(),
46 state: ViewState::Normal,
47 tooltip_text: tooltip_text.to_string(),
48 tooltip_positioning,
49 }
50 }
51
52 fn layout(
53 bounds: &Rect,
54 style: &IconButtonStyle,
55 tooltip_text: &str,
56 tooltip_positioning: Positioning,
57 (w, h): (u8, u8),
58 ) -> (Coord, Polyline, Polyline, Tooltip) {
59 let border = Polyline::rounded_rect(
60 bounds.left(),
61 bounds.top(),
62 bounds.right(),
63 bounds.bottom(),
64 style.rounding,
65 WHITE,
66 )
67 .unwrap();
68 let shadow = Polyline::rounded_rect(
69 bounds.left() + 1,
70 bounds.top() + 1,
71 bounds.right() + 1,
72 bounds.bottom() + 1,
73 style.rounding,
74 WHITE,
75 )
76 .unwrap();
77 let tooltip = Tooltip::new(
78 bounds.top_left() + (w, h),
79 tooltip_text,
80 tooltip_positioning,
81 &style.tooltip,
82 );
83 (
84 bounds.top_left() + (style.padding, style.padding) + (1, 1),
85 border,
86 shadow,
87 tooltip,
88 )
89 }
90}
91
92impl IconButton {
93 #[must_use]
94 pub fn on_mouse_click(&mut self, down: Coord, up: Coord) -> bool {
95 if self.state != ViewState::Disabled {
96 self.bounds.contains(down) && self.bounds.contains(up)
97 } else {
98 false
99 }
100 }
101}
102
103impl PixelView for IconButton {
104 fn set_position(&mut self, top_left: Coord) {
105 self.bounds = self.bounds.move_to(top_left);
106 let (icon_xy, border, shadow, tooltip) = Self::layout(
107 &self.bounds,
108 &self.style,
109 &self.tooltip_text,
110 self.tooltip_positioning,
111 (self.icon.width(), self.icon.height()),
112 );
113 self.icon_xy = icon_xy;
114 self.border = border;
115 self.shadow = shadow;
116 self.tooltip = tooltip;
117 }
118
119 #[must_use]
120 fn bounds(&self) -> &Rect {
121 &self.bounds
122 }
123
124 fn render(&self, graphics: &mut Graphics, mouse: &MouseData) {
125 let (error, disabled) = self.state.get_err_dis();
126 let hovering = self.bounds.contains(mouse.xy);
127 if let Some(color) = self.style.shadow.get(hovering, error, disabled) {
128 self.shadow.with_color(color).render(graphics);
129 }
130 if let Some(color) = self.style.border.get(hovering, error, disabled) {
131 self.border.with_color(color).render(graphics);
132 }
133 graphics.draw_indexed_image(self.icon_xy, &self.icon);
134 if !disabled && hovering {
135 self.tooltip.render(graphics, mouse);
136 }
137 }
138
139 fn update(&mut self, _: &Timing) {}
140
141 #[inline]
142 fn set_state(&mut self, state: ViewState) {
143 self.state = state;
144 }
145
146 #[inline]
147 #[must_use]
148 fn get_state(&self) -> ViewState {
149 self.state
150 }
151}
152
153impl LayoutView for IconButton {
154 fn set_bounds(&mut self, bounds: Rect) {
155 self.bounds = bounds.clone();
156 self.set_position(bounds.top_left());
157 }
158}