@effindomv2/fui-rs 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/Cargo.toml +1 -1
  2. package/package.json +1 -1
  3. package/src/bridge_callbacks.rs +1 -0
  4. package/src/controls/appearance.rs +200 -0
  5. package/src/controls/button.rs +48 -122
  6. package/src/controls/checkbox.rs +27 -3
  7. package/src/controls/combobox.rs +61 -23
  8. package/src/controls/context_menu.rs +156 -256
  9. package/src/controls/control_template_set.rs +2 -3
  10. package/src/controls/control_tokens.rs +76 -0
  11. package/src/controls/dialog.rs +80 -78
  12. package/src/controls/dropdown.rs +46 -5
  13. package/src/controls/form.rs +6 -0
  14. package/src/controls/internal/button_presenter.rs +21 -20
  15. package/src/controls/internal/pressable_labeled_control.rs +11 -11
  16. package/src/controls/internal/text_input_core.rs +40 -22
  17. package/src/controls/internal/text_input_presenter.rs +29 -23
  18. package/src/controls/mod.rs +7 -2
  19. package/src/controls/nav_link.rs +15 -11
  20. package/src/controls/popup.rs +24 -40
  21. package/src/controls/progress_bar.rs +80 -109
  22. package/src/controls/radio_button.rs +27 -3
  23. package/src/controls/radio_group.rs +6 -0
  24. package/src/controls/slider.rs +38 -14
  25. package/src/controls/switch.rs +27 -3
  26. package/src/controls/tests.rs +796 -56
  27. package/src/controls/text_area.rs +12 -2
  28. package/src/controls/text_input.rs +19 -3
  29. package/src/lib.rs +44 -38
  30. package/src/node/core.rs +2 -0
  31. package/src/node/flex_box.rs +245 -2
  32. package/src/node/grid.rs +5 -0
  33. package/src/node/mod.rs +48 -0
  34. package/src/node/presenter_host_style.rs +177 -0
  35. package/src/node/text_node.rs +35 -0
  36. package/src/popup_presenter.rs +32 -0
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fui-rs"
3
- version = "0.1.8"
3
+ version = "0.1.9"
4
4
  edition = "2021"
5
5
  license = "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial"
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/fui-rs",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "private": false,
5
5
  "license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
6
6
  "description": "EffinDom v2 Rust frontend framework SDK and host generators",
@@ -179,6 +179,7 @@ pub extern "C" fn __fui_on_font_loaded(font_id: u32) {
179
179
  assets::on_font_loaded(font_id);
180
180
  crate::typography::notify_font_loaded(font_id);
181
181
  crate::text::notify_font_loaded(font_id);
182
+ crate::frame_scheduler::mark_needs_commit();
182
183
  }
183
184
 
184
185
  #[no_mangle]
@@ -0,0 +1,200 @@
1
+ use crate::{Border, Corners, EdgeInsets, FontFamily, FontStyle, FontWeight, Shadow};
2
+
3
+ #[derive(Clone, Debug, Default, PartialEq)]
4
+ pub struct SurfaceAppearance {
5
+ pub(crate) background: Option<u32>,
6
+ pub(crate) background_blur: Option<f32>,
7
+ pub(crate) border: Option<Border>,
8
+ pub(crate) corners: Option<Corners>,
9
+ pub(crate) shadow: Option<Shadow>,
10
+ }
11
+
12
+ impl SurfaceAppearance {
13
+ pub fn new() -> Self {
14
+ Self::default()
15
+ }
16
+ pub fn background(mut self, value: u32) -> Self {
17
+ self.background = Some(value);
18
+ self
19
+ }
20
+ pub fn background_blur(mut self, value: f32) -> Self {
21
+ self.background_blur = Some(value.max(0.0));
22
+ self
23
+ }
24
+ pub fn border(mut self, value: Border) -> Self {
25
+ self.border = Some(value);
26
+ self
27
+ }
28
+ pub fn corners(mut self, value: Corners) -> Self {
29
+ self.corners = Some(value);
30
+ self
31
+ }
32
+ pub fn shadow(mut self, value: Shadow) -> Self {
33
+ self.shadow = Some(value);
34
+ self
35
+ }
36
+
37
+ pub(crate) fn presenter_host_style(&self) -> crate::PresenterHostStyle {
38
+ let mut style = crate::PresenterHostStyle::new();
39
+ style.background = self.background;
40
+ style.border = self.border;
41
+ style.corners = self.corners;
42
+ style.shadow = self.shadow;
43
+ style
44
+ }
45
+ }
46
+
47
+ #[derive(Clone, Debug, Default, PartialEq)]
48
+ pub struct OverlayBackdropAppearance {
49
+ pub(crate) color: Option<u32>,
50
+ pub(crate) blur: Option<f32>,
51
+ }
52
+
53
+ impl OverlayBackdropAppearance {
54
+ pub fn new() -> Self {
55
+ Self::default()
56
+ }
57
+ pub fn color(mut self, value: u32) -> Self {
58
+ self.color = Some(value);
59
+ self
60
+ }
61
+ pub fn blur(mut self, value: f32) -> Self {
62
+ self.blur = Some(value.max(0.0));
63
+ self
64
+ }
65
+ }
66
+
67
+ #[derive(Clone, Debug, Default, PartialEq)]
68
+ pub struct PopupAppearance {
69
+ pub(crate) panel: Option<SurfaceAppearance>,
70
+ pub(crate) backdrop: Option<OverlayBackdropAppearance>,
71
+ }
72
+
73
+ impl PopupAppearance {
74
+ pub fn new() -> Self {
75
+ Self::default()
76
+ }
77
+ pub fn panel(mut self, value: SurfaceAppearance) -> Self {
78
+ self.panel = Some(value);
79
+ self
80
+ }
81
+ pub fn backdrop(mut self, value: OverlayBackdropAppearance) -> Self {
82
+ self.backdrop = Some(value);
83
+ self
84
+ }
85
+ }
86
+
87
+ #[derive(Clone, Debug, Default, PartialEq)]
88
+ pub struct DialogAppearance {
89
+ pub(crate) card: Option<SurfaceAppearance>,
90
+ pub(crate) backdrop: Option<OverlayBackdropAppearance>,
91
+ }
92
+
93
+ impl DialogAppearance {
94
+ pub fn new() -> Self {
95
+ Self::default()
96
+ }
97
+ pub fn card(mut self, value: SurfaceAppearance) -> Self {
98
+ self.card = Some(value);
99
+ self
100
+ }
101
+ pub fn backdrop(mut self, value: OverlayBackdropAppearance) -> Self {
102
+ self.backdrop = Some(value);
103
+ self
104
+ }
105
+ }
106
+
107
+ #[derive(Clone, Debug, Default, PartialEq)]
108
+ pub struct ContextMenuItemAppearance {
109
+ pub(crate) height: Option<f32>,
110
+ pub(crate) padding: Option<EdgeInsets>,
111
+ pub(crate) background: Option<u32>,
112
+ pub(crate) hover_background: Option<u32>,
113
+ pub(crate) text_color: Option<u32>,
114
+ pub(crate) corners: Option<Corners>,
115
+ pub(crate) font_family: Option<FontFamily>,
116
+ pub(crate) font_weight: Option<FontWeight>,
117
+ pub(crate) font_style: Option<FontStyle>,
118
+ pub(crate) font_size: Option<f32>,
119
+ }
120
+
121
+ impl ContextMenuItemAppearance {
122
+ pub fn new() -> Self {
123
+ Self::default()
124
+ }
125
+ pub fn height(mut self, value: f32) -> Self {
126
+ self.height = Some(value.max(1.0));
127
+ self
128
+ }
129
+ pub fn padding(mut self, value: EdgeInsets) -> Self {
130
+ self.padding = Some(value);
131
+ self
132
+ }
133
+ pub fn background(mut self, value: u32) -> Self {
134
+ self.background = Some(value);
135
+ self
136
+ }
137
+ pub fn hover_background(mut self, value: u32) -> Self {
138
+ self.hover_background = Some(value);
139
+ self
140
+ }
141
+ pub fn text_color(mut self, value: u32) -> Self {
142
+ self.text_color = Some(value);
143
+ self
144
+ }
145
+ pub fn corners(mut self, value: Corners) -> Self {
146
+ self.corners = Some(value);
147
+ self
148
+ }
149
+ pub fn font_family(mut self, value: FontFamily) -> Self {
150
+ self.font_family = Some(value);
151
+ self
152
+ }
153
+ pub fn font_weight(mut self, value: FontWeight) -> Self {
154
+ self.font_weight = Some(value);
155
+ self
156
+ }
157
+ pub fn font_style(mut self, value: FontStyle) -> Self {
158
+ self.font_style = Some(value);
159
+ self
160
+ }
161
+ pub fn font_size(mut self, value: f32) -> Self {
162
+ self.font_size = Some(value.max(1.0));
163
+ self
164
+ }
165
+ }
166
+
167
+ #[derive(Clone, Debug, Default, PartialEq)]
168
+ pub struct ContextMenuAppearance {
169
+ pub(crate) width: Option<f32>,
170
+ pub(crate) panel: Option<SurfaceAppearance>,
171
+ pub(crate) backdrop: Option<OverlayBackdropAppearance>,
172
+ pub(crate) item: Option<ContextMenuItemAppearance>,
173
+ pub(crate) separator_color: Option<u32>,
174
+ }
175
+
176
+ impl ContextMenuAppearance {
177
+ pub fn new() -> Self {
178
+ Self::default()
179
+ }
180
+ pub fn width(mut self, value: f32) -> Self {
181
+ self.width = Some(value.max(1.0));
182
+ self
183
+ }
184
+ pub fn panel(mut self, value: SurfaceAppearance) -> Self {
185
+ self.panel = Some(value);
186
+ self
187
+ }
188
+ pub fn backdrop(mut self, value: OverlayBackdropAppearance) -> Self {
189
+ self.backdrop = Some(value);
190
+ self
191
+ }
192
+ pub fn item(mut self, value: ContextMenuItemAppearance) -> Self {
193
+ self.item = Some(value);
194
+ self
195
+ }
196
+ pub fn separator_color(mut self, value: u32) -> Self {
197
+ self.separator_color = Some(value);
198
+ self
199
+ }
200
+ }
@@ -4,7 +4,6 @@ use super::internal::button_presenter::{
4
4
  use super::*;
5
5
  use crate::ffi::CursorStyle;
6
6
  use crate::node::{TextCore, WeakFlexBox};
7
- use crate::signal::SubscriptionGuard;
8
7
  use crate::{focus_adorner, focus_visibility};
9
8
  use std::rc::Rc;
10
9
 
@@ -22,16 +21,10 @@ pub struct Button {
22
21
  pressed_state: Rc<Cell<bool>>,
23
22
  focused_state: Rc<Cell<bool>>,
24
23
  keyboard_armed_key: Rc<RefCell<Option<String>>>,
25
- background_override: Rc<Cell<Option<u32>>>,
26
- hover_background_override: Rc<Cell<Option<u32>>>,
27
- pressed_background_override: Rc<Cell<Option<u32>>>,
28
- border_override: Rc<Cell<Option<Border>>>,
29
24
  font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
30
25
  font_size_override: Rc<Cell<Option<f32>>>,
31
26
  text_color_override: Rc<Cell<Option<u32>>>,
32
27
  colors_value: Rc<Cell<Option<ButtonColors>>>,
33
- theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
34
- focus_visibility_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
35
28
  }
36
29
 
37
30
  impl Button {
@@ -41,10 +34,7 @@ impl Button {
41
34
  let presenter = create_button_presenter(None);
42
35
  let label_node = presenter.label_node();
43
36
  label_node.text(label.clone());
44
- root.flex_direction(crate::FlexDirection::Row)
45
- .justify_content(JustifyContent::Center)
46
- .align_items(AlignItems::Center)
47
- .interactive(true)
37
+ root.interactive(true)
48
38
  .focusable(true, 0)
49
39
  .semantic_role(SemanticRole::Button)
50
40
  .semantic_label(label.clone())
@@ -64,16 +54,10 @@ impl Button {
64
54
  pressed_state: Rc::new(Cell::new(false)),
65
55
  focused_state: Rc::new(Cell::new(false)),
66
56
  keyboard_armed_key: Rc::new(RefCell::new(None)),
67
- background_override: Rc::new(Cell::new(None)),
68
- hover_background_override: Rc::new(Cell::new(None)),
69
- pressed_background_override: Rc::new(Cell::new(None)),
70
- border_override: Rc::new(Cell::new(None)),
71
57
  font_family_override: Rc::new(RefCell::new(None)),
72
58
  font_size_override: Rc::new(Cell::new(None)),
73
59
  text_color_override: Rc::new(Cell::new(None)),
74
60
  colors_value: Rc::new(Cell::new(None)),
75
- theme_guard: Rc::new(RefCell::new(None)),
76
- focus_visibility_guard: Rc::new(RefCell::new(None)),
77
61
  };
78
62
  control.install_visual_subscriptions();
79
63
  control.sync_visual_state();
@@ -172,13 +156,29 @@ impl Button {
172
156
  self
173
157
  }
174
158
 
175
- pub fn template(&self, template: Option<Rc<dyn ButtonTemplate>>) -> &Self {
159
+ pub fn template(&self, template: Rc<dyn ButtonTemplate>) -> &Self {
160
+ self.set_template(Some(template))
161
+ }
162
+
163
+ pub fn clear_template(&self) -> &Self {
164
+ self.set_template(None)
165
+ }
166
+
167
+ fn set_template(&self, template: Option<Rc<dyn ButtonTemplate>>) -> &Self {
176
168
  self.template.replace(template.clone());
177
169
  self.replace_presenter(create_button_presenter(template));
178
170
  self
179
171
  }
180
172
 
181
- pub fn colors(&self, colors: Option<ButtonColors>) -> &Self {
173
+ pub fn colors(&self, colors: ButtonColors) -> &Self {
174
+ self.set_colors(Some(colors))
175
+ }
176
+
177
+ pub fn clear_colors(&self) -> &Self {
178
+ self.set_colors(None)
179
+ }
180
+
181
+ fn set_colors(&self, colors: Option<ButtonColors>) -> &Self {
182
182
  self.colors_value.set(colors);
183
183
  self.sync_visual_state();
184
184
  self
@@ -212,48 +212,6 @@ impl Button {
212
212
  self
213
213
  }
214
214
 
215
- pub fn corner_radius(&self, radius: f32) -> &Self {
216
- self.root.corner_radius(radius);
217
- self.sync_focus_chrome();
218
- self
219
- }
220
-
221
- pub fn corners(&self, tl: f32, tr: f32, br: f32, bl: f32) -> &Self {
222
- self.root.corners(tl, tr, br, bl);
223
- self.sync_focus_chrome();
224
- self
225
- }
226
-
227
- pub fn hover_bg_color(&self, color: u32) -> &Self {
228
- self.hover_background_override.set(Some(color));
229
- self.sync_visual_state();
230
- self
231
- }
232
-
233
- pub fn pressed_bg_color(&self, color: u32) -> &Self {
234
- self.pressed_background_override.set(Some(color));
235
- self.sync_visual_state();
236
- self
237
- }
238
-
239
- pub fn bg_color(&self, color: u32) -> &Self {
240
- self.background_override.set(Some(color));
241
- self.sync_visual_state();
242
- self
243
- }
244
-
245
- pub fn border(&self, width: f32, color: u32) -> &Self {
246
- self.border_override.set(Some(Border::solid(width, color)));
247
- self.sync_visual_state();
248
- self
249
- }
250
-
251
- pub fn border_config(&self, border: Border) -> &Self {
252
- self.border_override.set(Some(border));
253
- self.sync_visual_state();
254
- self
255
- }
256
-
257
215
  fn set_explicit_text_color(&self, color: u32) {
258
216
  self.text_color_override.set(Some(color));
259
217
  self.sync_visual_state();
@@ -271,16 +229,21 @@ impl Button {
271
229
 
272
230
  fn install_visual_subscriptions(&self) {
273
231
  let event_target = self.event_target();
274
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
232
+ let theme_guard = subscribe(move |_theme| {
275
233
  event_target.sync_visual_state();
276
234
  event_target.sync_focus_chrome();
277
- }));
235
+ });
236
+ self.root
237
+ .retained_node_ref()
238
+ .retain_attachment(Rc::new(theme_guard));
278
239
 
279
240
  let event_target = self.event_target();
280
- *self.focus_visibility_guard.borrow_mut() =
281
- Some(focus_visibility::subscribe(move |_visible| {
282
- event_target.sync_focus_chrome();
283
- }));
241
+ let focus_guard = focus_visibility::subscribe(move |_visible| {
242
+ event_target.sync_focus_chrome();
243
+ });
244
+ self.root
245
+ .retained_node_ref()
246
+ .retain_attachment(Rc::new(focus_guard));
284
247
  }
285
248
 
286
249
  fn event_target(&self) -> ButtonEventTarget {
@@ -294,10 +257,6 @@ impl Button {
294
257
  pressed_state: self.pressed_state.clone(),
295
258
  focused_state: self.focused_state.clone(),
296
259
  keyboard_armed_key: self.keyboard_armed_key.clone(),
297
- background_override: self.background_override.clone(),
298
- hover_background_override: self.hover_background_override.clone(),
299
- pressed_background_override: self.pressed_background_override.clone(),
300
- border_override: self.border_override.clone(),
301
260
  font_family_override: self.font_family_override.clone(),
302
261
  font_size_override: self.font_size_override.clone(),
303
262
  text_color_override: self.text_color_override.clone(),
@@ -339,22 +298,11 @@ impl Button {
339
298
  self.pressed_state.get(),
340
299
  self.focused_state.get(),
341
300
  self.is_enabled(),
342
- self.background_override.get(),
343
- self.hover_background_override.get(),
344
- self.pressed_background_override.get(),
345
- self.border_override.get(),
346
301
  self.font_family_override.borrow().clone(),
347
302
  self.font_size_override.get(),
348
303
  self.text_color_override.get(),
349
304
  self.colors_value.get(),
350
305
  );
351
- self.root.cursor(if self.is_enabled() {
352
- CursorStyle::Pointer
353
- } else {
354
- CursorStyle::Default
355
- });
356
- self.root
357
- .opacity(if self.is_enabled() { 1.0 } else { 0.38 });
358
306
  }
359
307
 
360
308
  fn sync_focus_chrome(&self) {
@@ -418,18 +366,13 @@ fn sync_button_visual_state(
418
366
  pressed: bool,
419
367
  focused: bool,
420
368
  enabled: bool,
421
- background_override: Option<u32>,
422
- hover_background_override: Option<u32>,
423
- pressed_background_override: Option<u32>,
424
- border_override: Option<Border>,
425
369
  font_family_override: Option<crate::FontFamily>,
426
370
  font_size_override: Option<f32>,
427
371
  text_color_override: Option<u32>,
428
372
  colors: Option<ButtonColors>,
429
373
  ) {
430
374
  let presenter = presenter.borrow().clone();
431
- presenter.apply(
432
- root,
375
+ let mut host_style = presenter.present(
433
376
  current_theme(),
434
377
  ButtonVisualState {
435
378
  hovered,
@@ -439,21 +382,13 @@ fn sync_button_visual_state(
439
382
  },
440
383
  colors,
441
384
  );
442
- let override_background = if !enabled {
443
- background_override
444
- } else if pressed {
445
- pressed_background_override.or(background_override)
446
- } else if hovered {
447
- hover_background_override.or(background_override)
385
+ host_style.cursor = Some(if enabled {
386
+ CursorStyle::Pointer
448
387
  } else {
449
- background_override
450
- };
451
- if let Some(color) = override_background {
452
- root.bg_color(color);
453
- }
454
- if let Some(border) = border_override {
455
- root.border_config(border);
456
- }
388
+ CursorStyle::Default
389
+ });
390
+ host_style.opacity = Some(if enabled { 1.0 } else { 0.38 });
391
+ root.apply_presenter_style(host_style);
457
392
  if let Some(family) = font_family_override {
458
393
  presenter.label_node().font_family(family);
459
394
  }
@@ -467,8 +402,17 @@ fn sync_button_visual_state(
467
402
 
468
403
  fn sync_button_focus_chrome(root: &FlexBox, focused: bool, enabled: bool) {
469
404
  if focused && enabled && focus_visibility::keyboard_focus_visible() {
470
- let radius = current_theme().spacing.sm;
471
- focus_adorner::show_standard_corners(root, radius, radius, radius, radius);
405
+ let corners = root
406
+ .resolved_host_style()
407
+ .corners
408
+ .unwrap_or_else(|| crate::Corners::all(current_theme().spacing.sm));
409
+ focus_adorner::show_standard_corners(
410
+ root,
411
+ corners.top_left,
412
+ corners.top_right,
413
+ corners.bottom_right,
414
+ corners.bottom_left,
415
+ );
472
416
  return;
473
417
  }
474
418
  focus_adorner::hide_owner(root);
@@ -497,10 +441,6 @@ struct ButtonEventTarget {
497
441
  pressed_state: Rc<Cell<bool>>,
498
442
  focused_state: Rc<Cell<bool>>,
499
443
  keyboard_armed_key: Rc<RefCell<Option<String>>>,
500
- background_override: Rc<Cell<Option<u32>>>,
501
- hover_background_override: Rc<Cell<Option<u32>>>,
502
- pressed_background_override: Rc<Cell<Option<u32>>>,
503
- border_override: Rc<Cell<Option<Border>>>,
504
444
  font_family_override: Rc<RefCell<Option<crate::FontFamily>>>,
505
445
  font_size_override: Rc<Cell<Option<f32>>>,
506
446
  text_color_override: Rc<Cell<Option<u32>>>,
@@ -525,25 +465,11 @@ impl ButtonEventTarget {
525
465
  self.pressed_state.get(),
526
466
  self.focused_state.get(),
527
467
  root.retained_node_ref().is_enabled_for_routing(),
528
- self.background_override.get(),
529
- self.hover_background_override.get(),
530
- self.pressed_background_override.get(),
531
- self.border_override.get(),
532
468
  self.font_family_override.borrow().clone(),
533
469
  self.font_size_override.get(),
534
470
  self.text_color_override.get(),
535
471
  self.colors_value.get(),
536
472
  );
537
- root.cursor(if root.retained_node_ref().is_enabled_for_routing() {
538
- CursorStyle::Pointer
539
- } else {
540
- CursorStyle::Default
541
- });
542
- root.opacity(if root.retained_node_ref().is_enabled_for_routing() {
543
- 1.0
544
- } else {
545
- 0.38
546
- });
547
473
  }
548
474
 
549
475
  fn sync_focus_chrome(&self) {
@@ -160,7 +160,15 @@ impl Checkbox {
160
160
  self
161
161
  }
162
162
 
163
- pub fn sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
163
+ pub fn sizing(&self, sizing: LabeledControlSizing) -> &Self {
164
+ self.set_sizing(Some(sizing))
165
+ }
166
+
167
+ pub fn clear_sizing(&self) -> &Self {
168
+ self.set_sizing(None)
169
+ }
170
+
171
+ fn set_sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
164
172
  self.sizing_value.set(sizing);
165
173
  self.base.set_label_font_size_override(
166
174
  sizing
@@ -176,7 +184,15 @@ impl Checkbox {
176
184
  self
177
185
  }
178
186
 
179
- pub fn template(&self, template: Option<Rc<dyn CheckboxIndicatorTemplate>>) -> &Self {
187
+ pub fn template(&self, template: Rc<dyn CheckboxIndicatorTemplate>) -> &Self {
188
+ self.set_template(Some(template))
189
+ }
190
+
191
+ pub fn clear_template(&self) -> &Self {
192
+ self.set_template(None)
193
+ }
194
+
195
+ fn set_template(&self, template: Option<Rc<dyn CheckboxIndicatorTemplate>>) -> &Self {
180
196
  self.template_override.replace(template.clone());
181
197
  self.replace_indicator_presenter(create_indicator_presenter(
182
198
  template,
@@ -186,7 +202,15 @@ impl Checkbox {
186
202
  self
187
203
  }
188
204
 
189
- pub fn colors(&self, colors: Option<LabeledControlColors>) -> &Self {
205
+ pub fn colors(&self, colors: LabeledControlColors) -> &Self {
206
+ self.set_colors(Some(colors))
207
+ }
208
+
209
+ pub fn clear_colors(&self) -> &Self {
210
+ self.set_colors(None)
211
+ }
212
+
213
+ fn set_colors(&self, colors: Option<LabeledControlColors>) -> &Self {
190
214
  self.colors_value.set(colors);
191
215
  self.base.colors(colors);
192
216
  self.sync_visual(self.base.snapshot_state(), false, false);