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