@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
package/src/node/mod.rs CHANGED
@@ -24,6 +24,7 @@ mod flex_box;
24
24
  mod grid;
25
25
  mod helpers;
26
26
  mod image;
27
+ mod presenter_host_style;
27
28
  mod scroll_bar;
28
29
  mod scroll_box;
29
30
  mod scroll_state;
@@ -45,6 +46,8 @@ pub use helpers::{
45
46
  scroll_view, svg, text, viewport_height, viewport_width, virtual_list, Length,
46
47
  };
47
48
  pub use image::ImageNode;
49
+ pub(crate) use presenter_host_style::HostStyleLayers;
50
+ pub use presenter_host_style::{Corners, EdgeInsets, PresenterHostStyle, Shadow};
48
51
  pub use scroll_bar::{ScrollBar, ScrollBarVisibility};
49
52
  pub use scroll_box::ScrollBox;
50
53
  pub use scroll_state::ScrollState;
@@ -170,6 +173,11 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
170
173
  self
171
174
  }
172
175
 
176
+ fn clear_padding(&self) -> &Self {
177
+ self.flex_box_root().clear_padding();
178
+ self
179
+ }
180
+
173
181
  fn margin(&self, left: f32, top: f32, right: f32, bottom: f32) -> &Self {
174
182
  self.flex_box_root().margin(left, top, right, bottom);
175
183
  self
@@ -180,6 +188,11 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
180
188
  self
181
189
  }
182
190
 
191
+ fn clear_corners(&self) -> &Self {
192
+ self.flex_box_root().clear_corners();
193
+ self
194
+ }
195
+
183
196
  fn corners(&self, tl: f32, tr: f32, br: f32, bl: f32) -> &Self {
184
197
  self.flex_box_root().corners(tl, tr, br, bl);
185
198
  self
@@ -195,16 +208,31 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
195
208
  self
196
209
  }
197
210
 
211
+ fn clear_border(&self) -> &Self {
212
+ self.flex_box_root().clear_border();
213
+ self
214
+ }
215
+
198
216
  fn bg_color(&self, color: u32) -> &Self {
199
217
  self.flex_box_root().bg_color(color);
200
218
  self
201
219
  }
202
220
 
221
+ fn clear_bg_color(&self) -> &Self {
222
+ self.flex_box_root().clear_bg_color();
223
+ self
224
+ }
225
+
203
226
  fn opacity(&self, value: f32) -> &Self {
204
227
  self.flex_box_root().opacity(value);
205
228
  self
206
229
  }
207
230
 
231
+ fn clear_opacity(&self) -> &Self {
232
+ self.flex_box_root().clear_opacity();
233
+ self
234
+ }
235
+
208
236
  fn blur(&self, sigma: f32) -> &Self {
209
237
  self.flex_box_root().blur(sigma);
210
238
  self
@@ -223,6 +251,11 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
223
251
  self
224
252
  }
225
253
 
254
+ fn clear_drop_shadow(&self) -> &Self {
255
+ self.flex_box_root().clear_drop_shadow();
256
+ self
257
+ }
258
+
226
259
  fn background_blur(&self, sigma: f32) -> &Self {
227
260
  self.flex_box_root().background_blur(sigma);
228
261
  self
@@ -270,11 +303,21 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
270
303
  self
271
304
  }
272
305
 
306
+ fn clear_justify_content(&self) -> &Self {
307
+ self.flex_box_root().clear_justify_content();
308
+ self
309
+ }
310
+
273
311
  fn align_items(&self, align: AlignItems) -> &Self {
274
312
  self.flex_box_root().align_items(align);
275
313
  self
276
314
  }
277
315
 
316
+ fn clear_align_items(&self) -> &Self {
317
+ self.flex_box_root().clear_align_items();
318
+ self
319
+ }
320
+
278
321
  fn align_self(&self, align: AlignSelf) -> &Self {
279
322
  self.flex_box_root().align_self(align);
280
323
  self
@@ -305,6 +348,11 @@ pub trait FlexBoxSurface: HasFlexBoxRoot {
305
348
  self
306
349
  }
307
350
 
351
+ fn clear_cursor(&self) -> &Self {
352
+ self.flex_box_root().clear_cursor();
353
+ self
354
+ }
355
+
308
356
  fn visibility(&self, visibility: Visibility) -> &Self {
309
357
  self.flex_box_root().visibility(visibility);
310
358
  self
@@ -0,0 +1,177 @@
1
+ use crate::ffi::{AlignItems, CursorStyle, FlexDirection, JustifyContent};
2
+ use crate::node::Border;
3
+
4
+ #[derive(Clone, Copy, Debug, PartialEq)]
5
+ pub struct EdgeInsets {
6
+ pub left: f32,
7
+ pub top: f32,
8
+ pub right: f32,
9
+ pub bottom: f32,
10
+ }
11
+
12
+ impl EdgeInsets {
13
+ pub const fn new(left: f32, top: f32, right: f32, bottom: f32) -> Self {
14
+ Self {
15
+ left,
16
+ top,
17
+ right,
18
+ bottom,
19
+ }
20
+ }
21
+
22
+ pub const fn all(value: f32) -> Self {
23
+ Self::new(value, value, value, value)
24
+ }
25
+ }
26
+
27
+ #[derive(Clone, Copy, Debug, PartialEq)]
28
+ pub struct Corners {
29
+ pub top_left: f32,
30
+ pub top_right: f32,
31
+ pub bottom_right: f32,
32
+ pub bottom_left: f32,
33
+ }
34
+
35
+ impl Corners {
36
+ pub const fn new(top_left: f32, top_right: f32, bottom_right: f32, bottom_left: f32) -> Self {
37
+ Self {
38
+ top_left,
39
+ top_right,
40
+ bottom_right,
41
+ bottom_left,
42
+ }
43
+ }
44
+
45
+ pub const fn all(value: f32) -> Self {
46
+ Self::new(value, value, value, value)
47
+ }
48
+ }
49
+
50
+ #[derive(Clone, Copy, Debug, PartialEq)]
51
+ pub struct Shadow {
52
+ pub color: u32,
53
+ pub offset_x: f32,
54
+ pub offset_y: f32,
55
+ pub blur_sigma: f32,
56
+ pub spread: f32,
57
+ }
58
+
59
+ impl Shadow {
60
+ pub fn new(color: u32, offset_x: f32, offset_y: f32, blur_sigma: f32, spread: f32) -> Self {
61
+ Self {
62
+ color,
63
+ offset_x,
64
+ offset_y,
65
+ blur_sigma: blur_sigma.max(0.0),
66
+ spread,
67
+ }
68
+ }
69
+ }
70
+
71
+ #[derive(Clone, Copy, Debug, Default, PartialEq)]
72
+ pub struct PresenterHostStyle {
73
+ pub flex_direction: Option<FlexDirection>,
74
+ pub justify_content: Option<JustifyContent>,
75
+ pub align_items: Option<AlignItems>,
76
+ pub background: Option<u32>,
77
+ pub padding: Option<EdgeInsets>,
78
+ pub corners: Option<Corners>,
79
+ pub border: Option<Border>,
80
+ pub shadow: Option<Shadow>,
81
+ pub cursor: Option<CursorStyle>,
82
+ pub opacity: Option<f32>,
83
+ }
84
+
85
+ impl PresenterHostStyle {
86
+ pub const fn new() -> Self {
87
+ Self {
88
+ flex_direction: None,
89
+ justify_content: None,
90
+ align_items: None,
91
+ background: None,
92
+ padding: None,
93
+ corners: None,
94
+ border: None,
95
+ shadow: None,
96
+ cursor: None,
97
+ opacity: None,
98
+ }
99
+ }
100
+
101
+ pub fn flex_direction(mut self, value: FlexDirection) -> Self {
102
+ self.flex_direction = Some(value);
103
+ self
104
+ }
105
+
106
+ pub fn justify_content(mut self, value: JustifyContent) -> Self {
107
+ self.justify_content = Some(value);
108
+ self
109
+ }
110
+
111
+ pub fn align_items(mut self, value: AlignItems) -> Self {
112
+ self.align_items = Some(value);
113
+ self
114
+ }
115
+
116
+ pub fn background(mut self, value: u32) -> Self {
117
+ self.background = Some(value);
118
+ self
119
+ }
120
+
121
+ pub fn padding(mut self, value: EdgeInsets) -> Self {
122
+ self.padding = Some(value);
123
+ self
124
+ }
125
+
126
+ pub fn corners(mut self, value: Corners) -> Self {
127
+ self.corners = Some(value);
128
+ self
129
+ }
130
+
131
+ pub fn border(mut self, value: Border) -> Self {
132
+ self.border = Some(value);
133
+ self
134
+ }
135
+
136
+ pub fn shadow(mut self, value: Shadow) -> Self {
137
+ self.shadow = Some(value);
138
+ self
139
+ }
140
+
141
+ pub fn cursor(mut self, value: CursorStyle) -> Self {
142
+ self.cursor = Some(value);
143
+ self
144
+ }
145
+
146
+ pub fn opacity(mut self, value: f32) -> Self {
147
+ self.opacity = Some(value.clamp(0.0, 1.0));
148
+ self
149
+ }
150
+
151
+ pub(crate) fn overlay(self, fallback: Self) -> Self {
152
+ Self {
153
+ flex_direction: self.flex_direction.or(fallback.flex_direction),
154
+ justify_content: self.justify_content.or(fallback.justify_content),
155
+ align_items: self.align_items.or(fallback.align_items),
156
+ background: self.background.or(fallback.background),
157
+ padding: self.padding.or(fallback.padding),
158
+ corners: self.corners.or(fallback.corners),
159
+ border: self.border.or(fallback.border),
160
+ shadow: self.shadow.or(fallback.shadow),
161
+ cursor: self.cursor.or(fallback.cursor),
162
+ opacity: self.opacity.or(fallback.opacity),
163
+ }
164
+ }
165
+ }
166
+
167
+ #[derive(Clone, Copy, Debug, Default)]
168
+ pub(crate) struct HostStyleLayers {
169
+ pub local: PresenterHostStyle,
170
+ pub presenter: PresenterHostStyle,
171
+ }
172
+
173
+ impl HostStyleLayers {
174
+ pub fn resolved(self) -> PresenterHostStyle {
175
+ self.local.overlay(self.presenter)
176
+ }
177
+ }
@@ -249,6 +249,9 @@ impl ScrollView {
249
249
  }
250
250
 
251
251
  pub fn focusable(&self, enabled: bool, tab_index: i32) -> &Self {
252
+ if enabled {
253
+ self.retained_node_ref().require_interactive();
254
+ }
252
255
  let mut core = self.core.borrow_mut();
253
256
  core.behavior.focusable = Some((enabled, tab_index));
254
257
  let interactive = core.behavior.enabled && core.behavior.inherited_enabled;
@@ -177,6 +177,10 @@ impl TextNode {
177
177
  self
178
178
  }
179
179
 
180
+ pub(crate) fn configured_text_color(&self) -> Option<u32> {
181
+ self.props.borrow().text_color
182
+ }
183
+
180
184
  pub fn style_runs(&self, words: Vec<u32>) -> &Self {
181
185
  {
182
186
  let mut props = self.props.borrow_mut();
@@ -418,6 +422,9 @@ impl TextNode {
418
422
  }
419
423
 
420
424
  pub fn focusable(&self, enabled: bool, tab_index: i32) -> &Self {
425
+ if enabled {
426
+ self.retained_node_ref().require_interactive();
427
+ }
421
428
  let mut core = self.core.borrow_mut();
422
429
  core.behavior.focusable = Some((enabled, tab_index));
423
430
  let interactive = core.behavior.enabled && core.behavior.inherited_enabled;
@@ -648,6 +655,9 @@ impl Node for TextNode {
648
655
  }
649
656
 
650
657
  fn build_self(&self) {
658
+ if self.props.borrow().has_font {
659
+ self.resolve_font_id();
660
+ }
651
661
  apply_text_props(
652
662
  self.handle(),
653
663
  &self.props.borrow(),
@@ -655,3 +665,35 @@ impl Node for TextNode {
655
665
  );
656
666
  }
657
667
  }
668
+
669
+ #[cfg(test)]
670
+ mod tests {
671
+ use super::*;
672
+ use crate::ffi::{self, Call};
673
+ use crate::Application;
674
+
675
+ #[test]
676
+ fn prebuild_font_family_weight_and_style_are_resolved_during_build() {
677
+ ffi::test::reset();
678
+ let theme = theme::current_theme();
679
+ let label = TextCore::new("Bold before build");
680
+ label
681
+ .font_family(theme.fonts.body_family)
682
+ .font_weight(FontWeight::Bold)
683
+ .font_style(FontStyle::Normal)
684
+ .font_size(17.0);
685
+
686
+ Application::mount(label.clone());
687
+ let handle = label.handle().raw();
688
+ let calls = ffi::test::take_calls();
689
+ assert!(calls.iter().any(|call| matches!(
690
+ call,
691
+ Call::SetFont {
692
+ handle: call_handle,
693
+ font_id: 2,
694
+ size,
695
+ } if *call_handle == handle && (*size - 17.0).abs() < f32::EPSILON
696
+ )));
697
+ Application::unmount();
698
+ }
699
+ }
@@ -322,6 +322,38 @@ impl PopupPresenter {
322
322
  }
323
323
 
324
324
  impl PopupPresenterEventTarget {
325
+ pub(crate) fn is_open(&self) -> bool {
326
+ self.state.borrow().open
327
+ }
328
+
329
+ pub(crate) fn surface_x(&self) -> f32 {
330
+ self.state.borrow().surface_x
331
+ }
332
+
333
+ pub(crate) fn surface_y(&self) -> f32 {
334
+ self.state.borrow().surface_y
335
+ }
336
+
337
+ pub(crate) fn backdrop_color(&self, color: u32) {
338
+ self.state.borrow_mut().backdrop_color = color;
339
+ self.apply_backdrop_style();
340
+ }
341
+
342
+ pub(crate) fn background_blur(&self, sigma: f32) {
343
+ self.state.borrow_mut().background_blur_sigma = sigma.max(0.0);
344
+ self.apply_backdrop_style();
345
+ }
346
+
347
+ fn apply_backdrop_style(&self) {
348
+ let Some(overlay) = self.overlay_node.upgrade() else {
349
+ return;
350
+ };
351
+ let state = *self.state.borrow();
352
+ overlay
353
+ .bg_color(state.backdrop_color)
354
+ .background_blur(state.background_blur_sigma);
355
+ }
356
+
325
357
  pub(crate) fn hide(&self) {
326
358
  let Some(root) = self.root.upgrade() else {
327
359
  return;