tuix_widgets/buttons/
checkbox.rs1#![allow(dead_code)]
2
3use crate::common::*;
4use crate::ButtonEvent;
5
6const ICON_CHECK: &str = "\u{2713}";
7
8const CHECKBOX_STYLE: &str = r#"
10 checkbox {
11 font: icons,
12 width: 20px;
13 height: 20px;
14 background-color: white;
15 border-width: 1px;
16 border-color: black;
17 border-radius: 3px;
18 transition: background-color 0.1 0.0;
19 }
20
21 checkbox:checked {
22 background-color: #ff5e1a;
23 border-color: #ff5e1a;
24 transition: background-color 0.1 0.0;
25 }
26"#;
27
28#[derive(Clone, Copy, Debug, PartialEq)]
29pub enum CheckboxEvent {
30 Check,
31 Uncheck,
32 Switch,
33 Checked,
34 Unchecked,
35}
36
37pub struct Atom<T> {
38 key: &'static str,
39 default: T,
40}
41
42impl<T> Atom<T> {
43 pub const fn new(key: &'static str, default: T) -> Self {
44 Self {
45 key,
46 default,
47 }
48 }
49}
50
51#[derive(Default)]
52pub struct Checkbox {
53 icon_unchecked: Option<String>,
54 icon_checked: Option<String>,
55
56 checked: bool,
57
58 on_checked: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
59 on_unchecked: Option<Box<dyn Fn(&mut Self, &mut State, Entity)>>,
60
61 key: Code,
62}
63
64impl Checkbox {
65 pub fn new(checked: bool) -> Self {
66 Self {
67 icon_unchecked: Some(String::new()),
68 icon_checked: Some(ICON_CHECK.to_string()),
69
70 on_checked: None,
71 on_unchecked: None,
72
73 checked,
74
75 key: Code::Space,
76 }
77 }
78
79 pub fn with_icon_checked(mut self, icon_checked: &str) -> Self {
80 self.icon_checked = Some(icon_checked.to_string());
81
82 self
83 }
84
85 pub fn with_icon_unchecked(mut self, icon_unchecked: &str) -> Self {
86 self.icon_unchecked = Some(icon_unchecked.to_string());
87
88 self
89 }
90
91 pub fn on_checked<F>(mut self, callback: F) -> Self
92 where
93 F: 'static + Fn(&mut Self, &mut State, Entity)
94 {
95 self.on_checked = Some(Box::new(callback));
96
97 self
98 }
99
100 pub fn on_unchecked<F>(mut self, callback: F) -> Self
101 where
102 F: 'static + Fn(&mut Self, &mut State, Entity)
103 {
104 self.on_unchecked = Some(Box::new(callback));
105
106 self
107 }
108}
109
110impl Widget for Checkbox {
111 type Ret = Entity;
112 type Data = bool;
113 fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
114 entity
115 .set_font(state, "icons")
116 .set_child_space(state, Stretch(1.0));
117
118 if self.checked {
119 entity.set_checked(state, true);
120
121 if let Some(icon_checked) = &self.icon_checked {
122 entity.set_text(state, &icon_checked);
123 }
124
125 state.insert_event(
126 Event::new(CheckboxEvent::Checked)
127 .target(entity)
128 .origin(entity),
129 );
130
131 } else {
132 entity.set_checked(state, false);
133
134 if let Some(icon_unchecked) = &self.icon_unchecked {
135 entity.set_text(state, &icon_unchecked);
136 }
137
138 state.insert_event(
139 Event::new(CheckboxEvent::Unchecked)
140 .target(entity)
141 .origin(entity),
142 );
143 }
144
145 entity.set_element(state, "checkbox")
146 }
147
148 fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
149
150 if let Some(checkbox_event) = event.message.downcast::<CheckboxEvent>() {
151 match checkbox_event {
152 CheckboxEvent::Switch => {
153 if event.target == entity {
154 if self.checked {
155
156 entity.set_checked(state, false);
157
158 if let Some(icon_unchecked) = &self.icon_unchecked {
159 entity.set_text(state, &icon_unchecked);
160 }
161
162 state.insert_event(
163 Event::new(CheckboxEvent::Unchecked)
164 .target(entity)
165 .origin(entity),
166 );
167 } else {
168
169 entity.set_checked(state, true);
170
171 if let Some(icon_checked) = &self.icon_checked {
172 entity.set_text(state, &icon_checked);
173 }
174
175 state.insert_event(
176 Event::new(CheckboxEvent::Checked)
177 .target(entity)
178 .origin(entity),
179 );
180 }
181 }
182 }
183
184 CheckboxEvent::Check => {
185 self.checked = true;
186 entity.set_checked(state, true);
187 }
188
189 CheckboxEvent::Uncheck => {
190 self.checked = false;
191 entity.set_checked(state, false);
192 }
193
194 CheckboxEvent::Checked => {
195 self.checked = true;
196
197 entity.set_checked(state, true);
198
199 if let Some(callback) = self.on_checked.take() {
200 (callback)(self, state, entity);
201 self.on_checked = Some(callback);
202 }
203 }
204
205 CheckboxEvent::Unchecked => {
206 self.checked = false;
207
208 entity.set_checked(state, false);
209
210 if let Some(callback) = self.on_unchecked.take() {
211 (callback)(self, state, entity);
212 self.on_unchecked = Some(callback);
213 }
214 }
215 }
216 }
217
218 if let Some(window_event) = event.message.downcast::<WindowEvent>() {
219 match window_event {
220 WindowEvent::MouseDown(button) if *button == MouseButton::Left => {
221 if entity == event.target && !entity.is_disabled(state) {
222 state.capture(entity);
223 }
224 }
225
226 WindowEvent::MouseUp(button) if *button == MouseButton::Left => {
227 if entity == event.target && state.mouse.left.pressed == entity {
228 state.release(entity);
229 entity.set_active(state, false);
230 if !entity.is_disabled(state) {
231 if state.hovered == entity {
232 state.insert_event(
233 Event::new(CheckboxEvent::Switch)
234 .target(entity)
235 .origin(entity),
236 );
237 }
238 }
239 }
240 }
241
242 WindowEvent::KeyDown(code, _) if *code == self.key => {
243 if state.focused == entity && !entity.is_disabled(state) {
244 state.insert_event(
245 Event::new(ButtonEvent::Pressed)
246 .target(entity)
247 .origin(entity),
248 );
249 }
250 }
251
252 WindowEvent::KeyUp(code, _) if *code == self.key => {
253 state.insert_event(
254 Event::new(ButtonEvent::Released)
255 .target(entity)
256 .origin(entity),
257 );
258 }
259
260 _ => {}
261 }
262 }
263
264 }
265}
266
267