@effindomv2/fui-rs 0.1.8 → 0.1.10

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 (37) 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 +25 -19
  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 +5 -0
  31. package/src/node/flex_box.rs +248 -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/scroll_view.rs +3 -0
  36. package/src/node/text_node.rs +42 -0
  37. package/src/popup_presenter.rs +32 -0
@@ -1,7 +1,28 @@
1
1
  use super::*;
2
- use crate::logger;
3
2
  use crate::node::WeakFlexBox;
4
- use crate::signal::SubscriptionGuard;
3
+ use crate::Border;
4
+
5
+ #[derive(Clone, Copy)]
6
+ struct ResolvedProgressBarSizing {
7
+ length: f32,
8
+ thickness: f32,
9
+ }
10
+
11
+ fn resolve_sizing(sizing: Option<ProgressBarSizing>) -> ResolvedProgressBarSizing {
12
+ let sizing = sizing.unwrap_or_default();
13
+ ResolvedProgressBarSizing {
14
+ length: if sizing.has_length() {
15
+ sizing.length_px()
16
+ } else {
17
+ PROGRESS_LENGTH
18
+ },
19
+ thickness: if sizing.has_thickness() {
20
+ sizing.thickness_px()
21
+ } else {
22
+ PROGRESS_THICKNESS
23
+ },
24
+ }
25
+ }
5
26
 
6
27
  #[derive(Clone)]
7
28
  pub struct ProgressBar {
@@ -10,16 +31,10 @@ pub struct ProgressBar {
10
31
  min: Rc<Cell<f32>>,
11
32
  max: Rc<Cell<f32>>,
12
33
  value: Rc<Cell<f32>>,
13
- length: Rc<Cell<f32>>,
14
- thickness: Rc<Cell<f32>>,
15
- track_color_value: Rc<Cell<u32>>,
16
- fill_color_value: Rc<Cell<u32>>,
17
- track_color_overridden: Rc<Cell<bool>>,
18
- fill_color_overridden: Rc<Cell<bool>>,
19
- corner_radius_overridden: Rc<Cell<bool>>,
34
+ sizing_value: Rc<Cell<Option<ProgressBarSizing>>>,
35
+ colors_value: Rc<Cell<Option<ProgressBarColors>>>,
20
36
  weak_root: WeakFlexBox,
21
37
  weak_fill: WeakFlexBox,
22
- theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
23
38
  }
24
39
 
25
40
  impl Default for ProgressBar {
@@ -41,14 +56,8 @@ impl ProgressBar {
41
56
  min: Rc::new(Cell::new(0.0)),
42
57
  max: Rc::new(Cell::new(100.0)),
43
58
  value: Rc::new(Cell::new(0.0)),
44
- length: Rc::new(Cell::new(PROGRESS_LENGTH)),
45
- thickness: Rc::new(Cell::new(PROGRESS_THICKNESS)),
46
- track_color_value: Rc::new(Cell::new(0)),
47
- fill_color_value: Rc::new(Cell::new(0)),
48
- track_color_overridden: Rc::new(Cell::new(false)),
49
- fill_color_overridden: Rc::new(Cell::new(false)),
50
- corner_radius_overridden: Rc::new(Cell::new(false)),
51
- theme_guard: Rc::new(RefCell::new(None)),
59
+ sizing_value: Rc::new(Cell::new(None)),
60
+ colors_value: Rc::new(Cell::new(None)),
52
61
  };
53
62
  control.install_visual_subscriptions();
54
63
  control.set_value(0.0);
@@ -79,51 +88,33 @@ impl ProgressBar {
79
88
  self
80
89
  }
81
90
 
82
- pub fn length(&self, value: f32) -> &Self {
83
- if value <= 0.0 {
84
- logger::warn(
85
- "Layout",
86
- &format!("ProgressBar.length() received {value}; clamping to 1.0."),
87
- );
88
- }
89
- self.length.set(if value > 0.0 { value } else { 1.0 });
91
+ pub fn sizing(&self, sizing: ProgressBarSizing) -> &Self {
92
+ self.sizing_value.set(Some(sizing));
90
93
  self.sync_geometry();
94
+ self.sync_visual_state();
91
95
  self
92
96
  }
93
97
 
94
- pub fn thickness(&self, value: f32) -> &Self {
95
- if value <= 0.0 {
96
- logger::warn(
97
- "Layout",
98
- &format!("ProgressBar.thickness() received {value}; clamping to 1.0."),
99
- );
100
- }
101
- self.thickness.set(if value > 0.0 { value } else { 1.0 });
102
- if !self.corner_radius_overridden.get() {
103
- let radius = self.thickness.get() * 0.5;
104
- self.root.corner_radius(radius);
105
- }
98
+ pub fn clear_sizing(&self) -> &Self {
99
+ self.sizing_value.set(None);
106
100
  self.sync_geometry();
107
101
  self.sync_visual_state();
108
102
  self
109
103
  }
110
104
 
111
- pub fn track_color(&self, color: u32) -> &Self {
112
- self.track_color_overridden.set(true);
113
- self.track_color_value.set(color);
105
+ pub fn colors(&self, colors: ProgressBarColors) -> &Self {
106
+ self.colors_value.set(Some(colors));
114
107
  self.sync_visual_state();
115
108
  self
116
109
  }
117
110
 
118
- pub fn fill_color(&self, color: u32) -> &Self {
119
- self.fill_color_overridden.set(true);
120
- self.fill_color_value.set(color);
111
+ pub fn clear_colors(&self) -> &Self {
112
+ self.colors_value.set(None);
121
113
  self.sync_visual_state();
122
114
  self
123
115
  }
124
116
 
125
117
  pub fn corner_radius(&self, radius: f32) -> &Self {
126
- self.corner_radius_overridden.set(true);
127
118
  self.root.corner_radius(radius);
128
119
  self.fill.corner_radius(radius);
129
120
  self
@@ -136,17 +127,9 @@ impl ProgressBar {
136
127
  fn install_visual_subscriptions(&self) {
137
128
  let weak_root = self.weak_root.clone();
138
129
  let weak_fill = self.weak_fill.clone();
139
- let min = self.min.clone();
140
- let max = self.max.clone();
141
- let value = self.value.clone();
142
- let length = self.length.clone();
143
- let thickness = self.thickness.clone();
144
- let track_color_value = self.track_color_value.clone();
145
- let fill_color_value = self.fill_color_value.clone();
146
- let track_color_overridden = self.track_color_overridden.clone();
147
- let fill_color_overridden = self.fill_color_overridden.clone();
148
- let corner_radius_overridden = self.corner_radius_overridden.clone();
149
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
130
+ let sizing_value = self.sizing_value.clone();
131
+ let colors_value = self.colors_value.clone();
132
+ let guard = subscribe(move |_theme| {
150
133
  let Some(root) = weak_root.upgrade() else {
151
134
  return;
152
135
  };
@@ -156,23 +139,13 @@ impl ProgressBar {
156
139
  sync_progress_visual_state(
157
140
  &root,
158
141
  &fill,
159
- thickness.get(),
160
- track_color_value.get(),
161
- fill_color_value.get(),
162
- track_color_overridden.get(),
163
- fill_color_overridden.get(),
164
- corner_radius_overridden.get(),
165
- );
166
- sync_progress_geometry(
167
- &root,
168
- &fill,
169
- min.get(),
170
- max.get(),
171
- value.get(),
172
- length.get(),
173
- thickness.get(),
142
+ resolve_sizing(sizing_value.get()),
143
+ colors_value.get(),
174
144
  );
175
- }));
145
+ });
146
+ self.root
147
+ .retained_node_ref()
148
+ .retain_attachment(Rc::new(guard));
176
149
  }
177
150
 
178
151
  fn handle_theme_changed(&self) {
@@ -192,8 +165,7 @@ impl ProgressBar {
192
165
  self.min.get(),
193
166
  self.max.get(),
194
167
  self.value.get(),
195
- self.length.get(),
196
- self.thickness.get(),
168
+ resolve_sizing(self.sizing_value.get()),
197
169
  );
198
170
  }
199
171
 
@@ -201,12 +173,8 @@ impl ProgressBar {
201
173
  sync_progress_visual_state(
202
174
  &self.root,
203
175
  &self.fill,
204
- self.thickness.get(),
205
- self.track_color_value.get(),
206
- self.fill_color_value.get(),
207
- self.track_color_overridden.get(),
208
- self.fill_color_overridden.get(),
209
- self.corner_radius_overridden.get(),
176
+ resolve_sizing(self.sizing_value.get()),
177
+ self.colors_value.get(),
210
178
  );
211
179
  }
212
180
  }
@@ -235,8 +203,7 @@ fn sync_progress_geometry(
235
203
  min: f32,
236
204
  max: f32,
237
205
  current_value: f32,
238
- length: f32,
239
- thickness: f32,
206
+ sizing: ResolvedProgressBarSizing,
240
207
  ) {
241
208
  let range = max - min;
242
209
  let fraction = if range > 0.0 {
@@ -244,44 +211,48 @@ fn sync_progress_geometry(
244
211
  } else {
245
212
  0.0
246
213
  };
247
- let fill_length = length * fraction;
248
- root.width(length, Unit::Pixel)
249
- .height(thickness, Unit::Pixel)
214
+ let fill_length = sizing.length * fraction;
215
+ root.width(sizing.length, Unit::Pixel)
216
+ .height(sizing.thickness, Unit::Pixel)
250
217
  .semantic_value_range(current_value, min, max)
251
218
  .default_semantic_label(format!(
252
219
  "Progress bar, value {}, range {} to {}",
253
220
  current_value, min, max
254
221
  ));
255
222
  fill.width(fill_length, Unit::Pixel)
256
- .height(thickness, Unit::Pixel);
223
+ .height(sizing.thickness, Unit::Pixel);
257
224
  }
258
225
 
259
226
  fn sync_progress_visual_state(
260
227
  root: &FlexBox,
261
228
  fill: &FlexBox,
262
- thickness: f32,
263
- track_color_value: u32,
264
- fill_color_value: u32,
265
- track_color_overridden: bool,
266
- fill_color_overridden: bool,
267
- corner_radius_overridden: bool,
229
+ sizing: ResolvedProgressBarSizing,
230
+ colors: Option<ProgressBarColors>,
268
231
  ) {
269
232
  let theme = current_theme();
270
- let track_color = if track_color_overridden {
271
- track_color_value
272
- } else {
273
- theme.colors.scrollbar_track
274
- };
275
- let fill_color = if fill_color_overridden {
276
- fill_color_value
277
- } else {
278
- theme.colors.accent
279
- };
280
- if !corner_radius_overridden {
281
- let radius = thickness * 0.5;
282
- root.corner_radius(radius);
283
- fill.corner_radius(radius);
284
- }
285
- root.bg_color(track_color).border(1.0, theme.colors.border);
286
- fill.bg_color(fill_color);
233
+ let track_color = colors
234
+ .filter(ProgressBarColors::has_track)
235
+ .map(|colors| colors.track_color())
236
+ .unwrap_or(theme.colors.scrollbar_track);
237
+ let fill_color = colors
238
+ .filter(ProgressBarColors::has_fill)
239
+ .map(|colors| colors.fill_color())
240
+ .unwrap_or(theme.colors.accent);
241
+ root.apply_presenter_style(
242
+ crate::PresenterHostStyle::new()
243
+ .background(track_color)
244
+ .border(Border::solid(1.0, theme.colors.border))
245
+ .corners(crate::Corners::all(sizing.thickness * 0.5)),
246
+ );
247
+ let corners = root
248
+ .resolved_host_style()
249
+ .corners
250
+ .unwrap_or_else(|| crate::Corners::all(sizing.thickness * 0.5));
251
+ fill.corners(
252
+ corners.top_left,
253
+ corners.top_right,
254
+ corners.bottom_right,
255
+ corners.bottom_left,
256
+ )
257
+ .bg_color(fill_color);
287
258
  }
@@ -102,7 +102,15 @@ impl RadioButton {
102
102
  self.checked.get()
103
103
  }
104
104
 
105
- pub fn sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
105
+ pub fn sizing(&self, sizing: LabeledControlSizing) -> &Self {
106
+ self.set_sizing(Some(sizing))
107
+ }
108
+
109
+ pub fn clear_sizing(&self) -> &Self {
110
+ self.set_sizing(None)
111
+ }
112
+
113
+ fn set_sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
106
114
  self.sizing_value.set(sizing);
107
115
  self.base.set_label_font_size_override(
108
116
  sizing
@@ -118,7 +126,15 @@ impl RadioButton {
118
126
  self
119
127
  }
120
128
 
121
- pub fn template(&self, template: Option<Rc<dyn RadioIndicatorTemplate>>) -> &Self {
129
+ pub fn template(&self, template: Rc<dyn RadioIndicatorTemplate>) -> &Self {
130
+ self.set_template(Some(template))
131
+ }
132
+
133
+ pub fn clear_template(&self) -> &Self {
134
+ self.set_template(None)
135
+ }
136
+
137
+ fn set_template(&self, template: Option<Rc<dyn RadioIndicatorTemplate>>) -> &Self {
122
138
  self.template_override.replace(template.clone());
123
139
  self.replace_indicator_presenter(create_indicator_presenter(
124
140
  template,
@@ -128,7 +144,15 @@ impl RadioButton {
128
144
  self
129
145
  }
130
146
 
131
- pub fn colors(&self, colors: Option<LabeledControlColors>) -> &Self {
147
+ pub fn colors(&self, colors: LabeledControlColors) -> &Self {
148
+ self.set_colors(Some(colors))
149
+ }
150
+
151
+ pub fn clear_colors(&self) -> &Self {
152
+ self.set_colors(None)
153
+ }
154
+
155
+ fn set_colors(&self, colors: Option<LabeledControlColors>) -> &Self {
132
156
  self.colors_value.set(colors);
133
157
  self.base.colors(colors);
134
158
  self.sync_visual(self.base.snapshot_state(), false, false);
@@ -280,4 +280,10 @@ impl Node for RadioGroup {
280
280
  }
281
281
  }
282
282
 
283
+ impl crate::node::HasFlexBoxRoot for RadioGroup {
284
+ fn flex_box_root(&self) -> &FlexBox {
285
+ &self.root
286
+ }
287
+ }
288
+
283
289
  pub(crate) type WeakRadioGroupEventTarget = Weak<RadioGroupEventTarget>;
@@ -4,7 +4,6 @@ use super::internal::slider_presenter::{
4
4
  use super::*;
5
5
  use crate::logger;
6
6
  use crate::node::WeakFlexBox;
7
- use crate::signal::SubscriptionGuard;
8
7
  use crate::{focus_adorner, focus_visibility};
9
8
 
10
9
  const DEFAULT_SLIDER_LENGTH: f32 = 180.0;
@@ -53,8 +52,6 @@ pub struct Slider {
53
52
  focused_state: Rc<Cell<bool>>,
54
53
  weak_root: Rc<WeakNodeRef>,
55
54
  weak_flex_root: WeakFlexBox,
56
- theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
57
- focus_visibility_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
58
55
  }
59
56
 
60
57
  impl Default for Slider {
@@ -98,8 +95,6 @@ impl Slider {
98
95
  hovered_state: Rc::new(Cell::new(false)),
99
96
  dragging_state: Rc::new(Cell::new(false)),
100
97
  focused_state: Rc::new(Cell::new(false)),
101
- theme_guard: Rc::new(RefCell::new(None)),
102
- focus_visibility_guard: Rc::new(RefCell::new(None)),
103
98
  };
104
99
  control.install_visual_subscriptions();
105
100
  control.bind_events();
@@ -229,7 +224,15 @@ impl Slider {
229
224
  self
230
225
  }
231
226
 
232
- pub fn sizing(&self, sizing: Option<SliderSizing>) -> &Self {
227
+ pub fn sizing(&self, sizing: SliderSizing) -> &Self {
228
+ self.set_sizing(Some(sizing))
229
+ }
230
+
231
+ pub fn clear_sizing(&self) -> &Self {
232
+ self.set_sizing(None)
233
+ }
234
+
235
+ fn set_sizing(&self, sizing: Option<SliderSizing>) -> &Self {
233
236
  self.sizing_value.set(sizing);
234
237
  self.replace_presenter(create_slider_presenter(
235
238
  self.template_override.borrow().clone(),
@@ -243,13 +246,29 @@ impl Slider {
243
246
  self
244
247
  }
245
248
 
246
- pub fn colors(&self, colors: Option<SliderColors>) -> &Self {
249
+ pub fn colors(&self, colors: SliderColors) -> &Self {
250
+ self.set_colors(Some(colors))
251
+ }
252
+
253
+ pub fn clear_colors(&self) -> &Self {
254
+ self.set_colors(None)
255
+ }
256
+
257
+ fn set_colors(&self, colors: Option<SliderColors>) -> &Self {
247
258
  self.colors_value.set(colors);
248
259
  self.sync_presentation();
249
260
  self
250
261
  }
251
262
 
252
- pub fn template(&self, template: Option<Rc<dyn SliderTemplate>>) -> &Self {
263
+ pub fn template(&self, template: Rc<dyn SliderTemplate>) -> &Self {
264
+ self.set_template(Some(template))
265
+ }
266
+
267
+ pub fn clear_template(&self) -> &Self {
268
+ self.set_template(None)
269
+ }
270
+
271
+ fn set_template(&self, template: Option<Rc<dyn SliderTemplate>>) -> &Self {
253
272
  self.template_override.replace(template.clone());
254
273
  self.replace_presenter(create_slider_presenter(template, self.sizing_value.get()));
255
274
  let thumb_size = self.slider_presenter.borrow().metrics().thumb_size;
@@ -287,15 +306,20 @@ impl Slider {
287
306
 
288
307
  fn install_visual_subscriptions(&self) {
289
308
  let target = SliderEventTarget::from_slider(self);
290
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
309
+ let theme_guard = subscribe(move |_theme| {
291
310
  target.handle_theme_changed();
292
- }));
311
+ });
312
+ self.root
313
+ .retained_node_ref()
314
+ .retain_attachment(Rc::new(theme_guard));
293
315
 
294
316
  let target = SliderEventTarget::from_slider(self);
295
- *self.focus_visibility_guard.borrow_mut() =
296
- Some(focus_visibility::subscribe(move |_visible| {
297
- target.sync_focus_chrome();
298
- }));
317
+ let focus_guard = focus_visibility::subscribe(move |_visible| {
318
+ target.sync_focus_chrome();
319
+ });
320
+ self.root
321
+ .retained_node_ref()
322
+ .retain_attachment(Rc::new(focus_guard));
299
323
  }
300
324
 
301
325
  fn set_value_inner(&self, value: f32, emit: bool, announce: bool) {
@@ -115,7 +115,15 @@ impl Switch {
115
115
  self
116
116
  }
117
117
 
118
- pub fn sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
118
+ pub fn sizing(&self, sizing: LabeledControlSizing) -> &Self {
119
+ self.set_sizing(Some(sizing))
120
+ }
121
+
122
+ pub fn clear_sizing(&self) -> &Self {
123
+ self.set_sizing(None)
124
+ }
125
+
126
+ fn set_sizing(&self, sizing: Option<LabeledControlSizing>) -> &Self {
119
127
  self.sizing_value.set(sizing);
120
128
  self.base.set_label_font_size_override(
121
129
  sizing
@@ -131,7 +139,15 @@ impl Switch {
131
139
  self
132
140
  }
133
141
 
134
- pub fn template(&self, template: Option<Rc<dyn SwitchIndicatorTemplate>>) -> &Self {
142
+ pub fn template(&self, template: Rc<dyn SwitchIndicatorTemplate>) -> &Self {
143
+ self.set_template(Some(template))
144
+ }
145
+
146
+ pub fn clear_template(&self) -> &Self {
147
+ self.set_template(None)
148
+ }
149
+
150
+ fn set_template(&self, template: Option<Rc<dyn SwitchIndicatorTemplate>>) -> &Self {
135
151
  self.template_override.replace(template.clone());
136
152
  self.replace_indicator_presenter(create_indicator_presenter(
137
153
  template,
@@ -141,7 +157,15 @@ impl Switch {
141
157
  self
142
158
  }
143
159
 
144
- pub fn colors(&self, colors: Option<LabeledControlColors>) -> &Self {
160
+ pub fn colors(&self, colors: LabeledControlColors) -> &Self {
161
+ self.set_colors(Some(colors))
162
+ }
163
+
164
+ pub fn clear_colors(&self) -> &Self {
165
+ self.set_colors(None)
166
+ }
167
+
168
+ fn set_colors(&self, colors: Option<LabeledControlColors>) -> &Self {
145
169
  self.colors_value.set(colors);
146
170
  self.base.colors(colors);
147
171
  self.sync_visual(self.base.snapshot_state(), false, false);