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 .expect("Border could not be constructed, please raise an issue on Github emmabritton/pixel-graphics-lib");
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 .expect("Shadow could not be constructed, please raise an issue on Github emmabritton/pixel-graphics-lib");
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 pub fn on_mouse_click(&mut self, down: Coord, up: Coord) -> bool {
94 if self.state != ViewState::Disabled {
95 self.bounds.contains(down) && self.bounds.contains(up)
96 } else {
97 false
98 }
99 }
100}
101
102impl PixelView for IconButton {
103 fn set_position(&mut self, top_left: Coord) {
104 self.bounds = self.bounds.move_to(top_left);
105 let (icon_xy, border, shadow, tooltip) = Self::layout(
106 &self.bounds,
107 &self.style,
108 &self.tooltip_text,
109 self.tooltip_positioning,
110 (self.icon.width(), self.icon.height()),
111 );
112 self.icon_xy = icon_xy;
113 self.border = border;
114 self.shadow = shadow;
115 self.tooltip = tooltip;
116 }
117
118 fn bounds(&self) -> &Rect {
119 &self.bounds
120 }
121
122 fn render(&self, graphics: &mut Graphics, mouse: &MouseData) {
123 let (error, disabled) = self.state.get_err_dis();
124 let hovering = self.bounds.contains(mouse.xy);
125 if let Some(color) = self.style.shadow.get(hovering, error, disabled) {
126 self.shadow.with_color(color).render(graphics);
127 }
128 if let Some(color) = self.style.border.get(hovering, error, disabled) {
129 self.border.with_color(color).render(graphics);
130 }
131 graphics.draw_indexed_image(self.icon_xy, &self.icon);
132 if !disabled && hovering {
133 self.tooltip.render(graphics, mouse);
134 }
135 }
136
137 fn update(&mut self, _: &Timing) {}
138
139 #[inline]
140 fn set_state(&mut self, state: ViewState) {
141 self.state = state;
142 }
143
144 #[inline]
145 fn get_state(&self) -> ViewState {
146 self.state
147 }
148}
149
150impl LayoutView for IconButton {
151 fn set_bounds(&mut self, bounds: Rect) {
152 self.bounds = bounds.clone();
153 self.set_position(bounds.top_left());
154 }
155}