pixels_graphics_lib/ui/
checkbox.rs1use crate::prelude::*;
2use crate::ui::layout::LayoutView;
3use crate::ui::prelude::CheckboxStyle;
4use crate::ui::{PixelView, ViewState};
5
6#[derive(Debug)]
7pub struct Checkbox {
8 bounds: Rect,
9 text: Text,
10 checked: bool,
11 style: CheckboxStyle,
12 state: ViewState,
13}
14
15impl Checkbox {
16 pub fn new<P: Into<Coord>>(pos: P, text: &str, checked: bool, style: &CheckboxStyle) -> Self {
17 let pos = pos.into();
18 let (w, h) = style.font.measure(text);
19 let w = w + style.spacing + style.check_box.width() as usize;
20 let bounds = Rect::new_with_size(pos, w, h);
21 let text = Checkbox::layout(style, bounds.clone(), text);
22 Self {
23 bounds,
24 text,
25 checked,
26 style: style.clone(),
27 state: ViewState::Normal,
28 }
29 }
30
31 fn text(&self) -> String {
32 self.text
33 .contents()
34 .iter()
35 .map(|bytes| String::from_utf8_lossy(bytes).to_string())
36 .collect::<Vec<String>>()
37 .join("\n")
38 }
39
40 fn layout(style: &CheckboxStyle, bounds: Rect, text: &str) -> Text {
41 let text_width = bounds
42 .width()
43 .saturating_sub(style.check_box.width() as usize);
44 let pos = bounds.top_left()
45 + (
46 style.check_box.width() as usize + style.spacing,
47 bounds.height() / 2,
48 );
49 Text::new(
50 text,
51 TextPos::px(pos),
52 (
53 TRANSPARENT,
54 style.font,
55 WrappingStrategy::SpaceBeforeCol(style.font.px_to_cols(text_width)),
56 Positioning::LeftCenter,
57 ),
58 )
59 }
60}
61
62impl Checkbox {
63 #[must_use]
67 pub fn on_mouse_click(&mut self, down: Coord, up: Coord) -> Option<bool> {
68 if self.state != ViewState::Disabled {
69 if self.bounds.contains(down) && self.bounds.contains(up) {
70 self.checked = !self.checked;
71 Some(self.checked)
72 } else {
73 None
74 }
75 } else {
76 None
77 }
78 }
79
80 pub fn is_checked(&self) -> bool {
81 self.checked
82 }
83
84 pub fn set_checked(&mut self, checked: bool) {
85 self.checked = checked;
86 }
87}
88
89impl PixelView for Checkbox {
90 fn set_position(&mut self, top_left: Coord) {
91 self.set_bounds(self.bounds.move_to(top_left));
92 }
93
94 fn bounds(&self) -> &Rect {
95 &self.bounds
96 }
97
98 fn render(&self, graphics: &mut Graphics, mouse: &MouseData) {
99 let hover = self.bounds.contains(mouse.xy);
100 let (err, dir) = self.state.get_err_dis();
101 graphics.draw_indexed_image(self.bounds.top_left(), &self.style.check_box);
102 if self.checked {
103 graphics.draw_indexed_image(self.bounds.top_left(), &self.style.checked_icon);
104 }
105 if let Some(color) = self.style.text.get(hover, err, dir) {
106 self.text.with_color(color).render(graphics);
107 }
108 }
109
110 fn update(&mut self, _: &Timing) {}
111
112 fn set_state(&mut self, new_state: ViewState) {
113 self.state = new_state;
114 }
115
116 fn get_state(&self) -> ViewState {
117 self.state
118 }
119}
120
121impl LayoutView for Checkbox {
122 fn set_bounds(&mut self, bounds: Rect) {
123 let text = Checkbox::layout(
124 &self.style,
125 bounds
126 .clone()
127 .translate_by(coord!(0, self.text.formatting().font().spacing())),
128 &self.text(),
129 );
130 self.bounds = bounds;
131 self.text = text;
132 }
133}