@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
@@ -147,12 +147,6 @@ pub struct TextInputCore {
147
147
  focus_visibility_guard: RefCell<Option<SubscriptionGuard>>,
148
148
  }
149
149
 
150
- impl Default for TextInputCore {
151
- fn default() -> Self {
152
- Self::new()
153
- }
154
- }
155
-
156
150
  impl TextInputCore {
157
151
  pub fn new() -> Self {
158
152
  Self::with_profile(TextInputProfile::single_line())
@@ -200,7 +194,7 @@ impl TextInputCore {
200
194
  };
201
195
 
202
196
  let presenter = create_presenter(profile, None);
203
- presenter.bind(root.clone(), editor_text.clone(), placeholder_host.clone());
197
+ presenter.bind(editor_text.clone(), placeholder_host.clone());
204
198
 
205
199
  let this = Self {
206
200
  self_weak: RefCell::new(Weak::new()),
@@ -357,7 +351,15 @@ impl TextInputCore {
357
351
  self.selection_range(end, end)
358
352
  }
359
353
 
360
- pub fn colors(&self, colors: Option<TextInputColors>) -> &Self {
354
+ pub fn colors(&self, colors: TextInputColors) -> &Self {
355
+ self.set_colors(Some(colors))
356
+ }
357
+
358
+ pub fn clear_colors(&self) -> &Self {
359
+ self.set_colors(None)
360
+ }
361
+
362
+ fn set_colors(&self, colors: Option<TextInputColors>) -> &Self {
361
363
  self.colors_value.set(colors);
362
364
  self.sync_theme_state();
363
365
  self
@@ -423,14 +425,18 @@ impl TextInputCore {
423
425
  self
424
426
  }
425
427
 
426
- pub fn template(&self, template: Option<Rc<dyn TextInputTemplate>>) -> &Self {
428
+ pub fn template(&self, template: Rc<dyn TextInputTemplate>) -> &Self {
429
+ self.set_template(Some(template))
430
+ }
431
+
432
+ pub fn clear_template(&self) -> &Self {
433
+ self.set_template(None)
434
+ }
435
+
436
+ fn set_template(&self, template: Option<Rc<dyn TextInputTemplate>>) -> &Self {
427
437
  *self.template.borrow_mut() = template.clone();
428
438
  let presenter = create_presenter(self.profile, template);
429
- presenter.bind(
430
- self.root.clone(),
431
- self.editor_text.clone(),
432
- self.placeholder_host.clone(),
433
- );
439
+ presenter.bind(self.editor_text.clone(), self.placeholder_host.clone());
434
440
  *self.presenter.borrow_mut() = presenter;
435
441
  self.sync_theme_state();
436
442
  self
@@ -629,7 +635,7 @@ impl TextInputCore {
629
635
  theme.fonts.size_body
630
636
  };
631
637
  let line_height = resolved_font_size + theme.spacing.sm;
632
- self.presenter.borrow().apply(
638
+ let host_style = self.presenter.borrow().present(
633
639
  theme.clone(),
634
640
  &TextInputVisualState {
635
641
  multiline: self.profile.multiline,
@@ -638,6 +644,7 @@ impl TextInputCore {
638
644
  },
639
645
  self.colors_value.get(),
640
646
  );
647
+ self.root.apply_presenter_style(host_style);
641
648
 
642
649
  let colors = self.colors_value.get();
643
650
  let text_color = if is_enabled(&self.root) {
@@ -1013,7 +1020,18 @@ impl TextInputCore {
1013
1020
  && is_enabled(&self.root)
1014
1021
  && focus_visibility::keyboard_focus_visible()
1015
1022
  {
1016
- focus_adorner::show_standard(&self.root, current_theme().spacing.sm);
1023
+ let corners = self
1024
+ .root
1025
+ .resolved_host_style()
1026
+ .corners
1027
+ .unwrap_or_else(|| crate::Corners::all(current_theme().spacing.sm));
1028
+ focus_adorner::show_standard_corners(
1029
+ &self.root,
1030
+ corners.top_left,
1031
+ corners.top_right,
1032
+ corners.bottom_right,
1033
+ corners.bottom_left,
1034
+ );
1017
1035
  return;
1018
1036
  }
1019
1037
  focus_adorner::hide_owner(&self.root);
@@ -1052,12 +1070,6 @@ impl TextInputCore {
1052
1070
  }
1053
1071
  }
1054
1072
 
1055
- impl HasFlexBoxRoot for TextInputCore {
1056
- fn flex_box_root(&self) -> &FlexBox {
1057
- &self.root
1058
- }
1059
- }
1060
-
1061
1073
  impl TextInputCore {
1062
1074
  pub(crate) fn build_control(&self) {
1063
1075
  self.root.build();
@@ -1072,3 +1084,9 @@ impl TextInputCore {
1072
1084
  self.editor_text.clone()
1073
1085
  }
1074
1086
  }
1087
+
1088
+ impl HasFlexBoxRoot for TextInputCore {
1089
+ fn flex_box_root(&self) -> &FlexBox {
1090
+ &self.root
1091
+ }
1092
+ }
@@ -1,6 +1,6 @@
1
1
  use crate::controls::TextInputColors;
2
2
  use crate::ffi::{CursorStyle, Unit};
3
- use crate::node::{FlexBox, TextCore};
3
+ use crate::node::{Border, Corners, EdgeInsets, FlexBox, PresenterHostStyle, TextCore};
4
4
  use crate::theme::Theme;
5
5
  use std::cell::RefCell;
6
6
  use std::rc::Rc;
@@ -13,8 +13,13 @@ pub struct TextInputVisualState {
13
13
  }
14
14
 
15
15
  pub trait TextInputPresenter {
16
- fn bind(&self, host: FlexBox, editor_host: TextCore, placeholder_host: FlexBox);
17
- fn apply(&self, theme: Theme, state: &TextInputVisualState, colors: Option<TextInputColors>);
16
+ fn bind(&self, editor_host: TextCore, placeholder_host: FlexBox);
17
+ fn present(
18
+ &self,
19
+ theme: Theme,
20
+ state: &TextInputVisualState,
21
+ colors: Option<TextInputColors>,
22
+ ) -> PresenterHostStyle;
18
23
  }
19
24
 
20
25
  pub trait TextInputTemplate {
@@ -23,7 +28,6 @@ pub trait TextInputTemplate {
23
28
 
24
29
  #[derive(Clone, Default)]
25
30
  pub struct DefaultTextInputPresenter {
26
- host: RefCell<Option<FlexBox>>,
27
31
  editor_host: RefCell<Option<TextCore>>,
28
32
  placeholder_host: RefCell<Option<FlexBox>>,
29
33
  }
@@ -35,21 +39,22 @@ impl DefaultTextInputPresenter {
35
39
  }
36
40
 
37
41
  impl TextInputPresenter for DefaultTextInputPresenter {
38
- fn bind(&self, host: FlexBox, editor_host: TextCore, placeholder_host: FlexBox) {
39
- *self.host.borrow_mut() = Some(host);
42
+ fn bind(&self, editor_host: TextCore, placeholder_host: FlexBox) {
40
43
  *self.editor_host.borrow_mut() = Some(editor_host);
41
44
  *self.placeholder_host.borrow_mut() = Some(placeholder_host);
42
45
  }
43
46
 
44
- fn apply(&self, theme: Theme, state: &TextInputVisualState, colors: Option<TextInputColors>) {
45
- let Some(host) = self.host.borrow().clone() else {
46
- return;
47
- };
47
+ fn present(
48
+ &self,
49
+ theme: Theme,
50
+ state: &TextInputVisualState,
51
+ colors: Option<TextInputColors>,
52
+ ) -> PresenterHostStyle {
48
53
  let Some(editor_host) = self.editor_host.borrow().clone() else {
49
- return;
54
+ return PresenterHostStyle::new();
50
55
  };
51
56
  let Some(placeholder_host) = self.placeholder_host.borrow().clone() else {
52
- return;
57
+ return PresenterHostStyle::new();
53
58
  };
54
59
 
55
60
  let horizontal_padding = theme.spacing.md;
@@ -73,22 +78,23 @@ impl TextInputPresenter for DefaultTextInputPresenter {
73
78
  .map(|value| value.border_color())
74
79
  .unwrap_or(theme.colors.border);
75
80
 
76
- host.bg_color(bg)
77
- .corner_radius(theme.spacing.sm)
78
- .border(1.0, border_color)
79
- .padding(
80
- horizontal_padding,
81
- vertical_padding,
82
- horizontal_padding,
83
- vertical_padding,
84
- )
85
- .cursor(shell_cursor)
86
- .opacity(if state.enabled { 1.0 } else { 0.6 });
87
81
  editor_host.cursor(editable_cursor);
88
82
  placeholder_host
89
83
  .position(horizontal_padding, vertical_padding)
90
84
  .width(100.0, Unit::Percent)
91
85
  .cursor(editable_cursor);
86
+ PresenterHostStyle::new()
87
+ .background(bg)
88
+ .corners(Corners::all(theme.spacing.sm))
89
+ .border(Border::solid(1.0, border_color))
90
+ .padding(EdgeInsets::new(
91
+ horizontal_padding,
92
+ vertical_padding,
93
+ horizontal_padding,
94
+ vertical_padding,
95
+ ))
96
+ .cursor(shell_cursor)
97
+ .opacity(if state.enabled { 1.0 } else { 0.6 })
92
98
  }
93
99
  }
94
100
 
@@ -5,7 +5,7 @@ use crate::ffi::{
5
5
  SemanticCheckedState, SemanticRole, Unit,
6
6
  };
7
7
  use crate::node::{
8
- flex_box, row, Border, Child, FlexBox, HasFlexBoxRoot, Node, NodeRef, TextNode, WeakNodeRef,
8
+ flex_box, row, Child, FlexBox, HasFlexBoxRoot, Node, NodeRef, TextNode, WeakNodeRef,
9
9
  };
10
10
  use crate::theme::{current_theme, subscribe};
11
11
  use std::cell::{Cell, RefCell};
@@ -106,6 +106,7 @@ pub struct ComboBoxChangedEventArgs<T> {
106
106
  }
107
107
 
108
108
  pub mod anti_selection_area;
109
+ mod appearance;
109
110
  pub mod button;
110
111
  pub mod checkbox;
111
112
  pub mod combobox;
@@ -130,6 +131,10 @@ pub mod text_area;
130
131
  pub mod text_input;
131
132
 
132
133
  pub use anti_selection_area::AntiSelectionArea;
134
+ pub use appearance::{
135
+ ContextMenuAppearance, ContextMenuItemAppearance, DialogAppearance, OverlayBackdropAppearance,
136
+ PopupAppearance, SurfaceAppearance,
137
+ };
133
138
  pub use button::Button;
134
139
  pub use checkbox::Checkbox;
135
140
  pub use combobox::{ComboBox, ComboBoxCommitMode, ComboBoxFilterMode, ComboBoxItem};
@@ -142,7 +147,7 @@ pub use control_template_set::{
142
147
  };
143
148
  pub use control_tokens::{
144
149
  ButtonColors, DropdownColors, DropdownSizing, LabeledControlColors, LabeledControlSizing,
145
- SliderColors, SliderSizing, TextInputColors,
150
+ ProgressBarColors, ProgressBarSizing, SliderColors, SliderSizing, TextInputColors,
146
151
  };
147
152
  pub use dialog::{Dialog, DialogShownEventArgs};
148
153
  pub use dropdown::{Dropdown, DropdownItem};
@@ -5,7 +5,6 @@ use crate::ffi::{CursorStyle, HandleValue, SemanticRole};
5
5
  use crate::navigation;
6
6
  use crate::node::{text, NodeHandle, NodeRef, WeakFlexBox};
7
7
  use crate::platform;
8
- use crate::signal::SubscriptionGuard;
9
8
  use crate::theme::{current_theme, subscribe};
10
9
  use crate::{focus_adorner, focus_visibility};
11
10
  use std::cell::{Cell, RefCell};
@@ -43,8 +42,6 @@ pub struct NavLink {
43
42
  enter_pressed_open_in_new_tab: Rc<Cell<bool>>,
44
43
  preview_pinned_for_context_menu: Rc<Cell<bool>>,
45
44
  text_color_override: Rc<Cell<Option<u32>>>,
46
- theme_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
47
- focus_visibility_guard: Rc<RefCell<Option<SubscriptionGuard>>>,
48
45
  }
49
46
 
50
47
  impl NavLink {
@@ -62,7 +59,6 @@ impl NavLink {
62
59
  label_node
63
60
  .font_family(theme.fonts.body_family.clone())
64
61
  .font_size(15.0)
65
- .text_color(theme.colors.accent)
66
62
  .cursor(CursorStyle::Pointer);
67
63
  root.justify_content(JustifyContent::Start)
68
64
  .align_items(AlignItems::Center)
@@ -90,8 +86,6 @@ impl NavLink {
90
86
  enter_pressed_open_in_new_tab: Rc::new(Cell::new(false)),
91
87
  preview_pinned_for_context_menu: Rc::new(Cell::new(false)),
92
88
  text_color_override: Rc::new(Cell::new(None)),
93
- theme_guard: Rc::new(RefCell::new(None)),
94
- focus_visibility_guard: Rc::new(RefCell::new(None)),
95
89
  };
96
90
  let node_ref = link.root.retained_node_ref();
97
91
  let pin_target = link.event_target();
@@ -197,15 +191,20 @@ impl NavLink {
197
191
 
198
192
  fn install_subscriptions(&self) {
199
193
  let target = self.event_target();
200
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
194
+ let theme_guard = subscribe(move |_theme| {
201
195
  target.sync_visual_state();
202
196
  target.sync_focus_chrome();
203
- }));
197
+ });
198
+ self.root
199
+ .retained_node_ref()
200
+ .retain_attachment(Rc::new(theme_guard));
204
201
  let target = self.event_target();
205
- *self.focus_visibility_guard.borrow_mut() =
206
- Some(focus_visibility::subscribe(move |_visible| {
207
- target.sync_focus_chrome();
208
- }));
202
+ let focus_guard = focus_visibility::subscribe(move |_visible| {
203
+ target.sync_focus_chrome();
204
+ });
205
+ self.root
206
+ .retained_node_ref()
207
+ .retain_attachment(Rc::new(focus_guard));
209
208
  }
210
209
 
211
210
  fn event_target(&self) -> NavLinkEventTarget {
@@ -263,6 +262,10 @@ impl NavLink {
263
262
  self
264
263
  }
265
264
 
265
+ pub fn label_node(&self) -> TextNode {
266
+ self.label.clone()
267
+ }
268
+
266
269
  pub fn open_in_new_tab(&self, open: bool) -> &Self {
267
270
  self.open_in_new_tab.set(open);
268
271
  self
@@ -289,6 +292,7 @@ impl Node for NavLink {
289
292
 
290
293
  fn build_self(&self) {
291
294
  self.root.build_self();
295
+ self.sync_visual_state();
292
296
  }
293
297
  }
294
298
 
@@ -403,13 +407,15 @@ impl NavLinkEventTarget {
403
407
  }
404
408
 
405
409
  fn sync_visual_state(&self) {
406
- let color = self.text_color_override.get().unwrap_or_else(|| {
407
- if self.hovered.get() {
408
- current_theme().colors.accent_hovered
409
- } else {
410
- current_theme().colors.accent
411
- }
412
- });
410
+ let theme = current_theme();
411
+ let color = if self.hovered.get() {
412
+ theme.colors.accent_hovered
413
+ } else {
414
+ self.text_color_override
415
+ .get()
416
+ .or_else(|| self.label.configured_text_color())
417
+ .unwrap_or(theme.colors.accent)
418
+ };
413
419
  if self.label.handle() != NodeHandle::INVALID {
414
420
  ui::set_text_color(self.label.handle().raw(), color);
415
421
  }
@@ -1,6 +1,6 @@
1
1
  use super::*;
2
2
  use crate::ffi::{FlexDirection, PositionType, Unit};
3
- use crate::node::{portal, Border, Child};
3
+ use crate::node::{portal, Child};
4
4
  use crate::popup_presenter::{PopupPlacement, PopupPresenter};
5
5
  use std::cell::Cell;
6
6
 
@@ -10,6 +10,7 @@ pub struct Popup {
10
10
  surface_node: FlexBox,
11
11
  presenter: PopupPresenter,
12
12
  dismiss_on_backdrop_click: Rc<Cell<bool>>,
13
+ appearance_value: Rc<RefCell<Option<PopupAppearance>>>,
13
14
  }
14
15
 
15
16
  impl Default for Popup {
@@ -46,6 +47,7 @@ impl Popup {
46
47
  surface_node,
47
48
  presenter,
48
49
  dismiss_on_backdrop_click,
50
+ appearance_value: Rc::new(RefCell::new(None)),
49
51
  }
50
52
  }
51
53
 
@@ -77,13 +79,15 @@ impl Popup {
77
79
  self
78
80
  }
79
81
 
80
- pub fn backdrop_color(&self, color: u32) -> &Self {
81
- self.presenter.backdrop_color(color);
82
+ pub fn appearance(&self, appearance: PopupAppearance) -> &Self {
83
+ self.appearance_value.replace(Some(appearance));
84
+ self.sync_appearance();
82
85
  self
83
86
  }
84
87
 
85
- pub fn background_blur(&self, sigma: f32) -> &Self {
86
- self.presenter.background_blur(sigma);
88
+ pub fn clear_appearance(&self) -> &Self {
89
+ self.appearance_value.replace(None);
90
+ self.sync_appearance();
87
91
  self
88
92
  }
89
93
 
@@ -105,42 +109,16 @@ impl Popup {
105
109
  self
106
110
  }
107
111
 
108
- pub fn panel_color(&self, color: u32) -> &Self {
109
- self.surface_node.bg_color(color);
110
- self
111
- }
112
-
113
- pub fn panel_background_blur(&self, sigma: f32) -> &Self {
114
- self.surface_node.background_blur(sigma);
115
- self
116
- }
117
-
118
- pub fn panel_corner_radius(&self, radius: f32) -> &Self {
119
- self.surface_node.corner_radius(radius);
120
- self
121
- }
122
-
123
- pub fn panel_border(&self, width: f32, color: u32) -> &Self {
124
- self.surface_node.border(width, color);
125
- self
126
- }
127
-
128
- pub fn panel_border_config(&self, border: Border) -> &Self {
129
- self.surface_node.border_config(border);
130
- self
131
- }
132
-
133
- pub fn panel_shadow(
134
- &self,
135
- color: u32,
136
- offset_x: f32,
137
- offset_y: f32,
138
- blur_sigma: f32,
139
- spread: f32,
140
- ) -> &Self {
112
+ fn sync_appearance(&self) {
113
+ let appearance = self.appearance_value.borrow().clone().unwrap_or_default();
114
+ let panel = appearance.panel.unwrap_or_default();
115
+ let backdrop = appearance.backdrop.unwrap_or_default();
141
116
  self.surface_node
142
- .drop_shadow(color, offset_x, offset_y, blur_sigma, spread);
143
- self
117
+ .apply_presenter_style(panel.presenter_host_style())
118
+ .background_blur(panel.background_blur.unwrap_or(0.0));
119
+ self.presenter
120
+ .backdrop_color(backdrop.color.unwrap_or(0x00000000))
121
+ .background_blur(backdrop.blur.unwrap_or(0.0));
144
122
  }
145
123
 
146
124
  pub fn show_anchored(
@@ -189,3 +167,9 @@ impl Node for Popup {
189
167
  self.root.dispose();
190
168
  }
191
169
  }
170
+
171
+ impl crate::node::HasFlexBoxRoot for Popup {
172
+ fn flex_box_root(&self) -> &FlexBox {
173
+ &self.root
174
+ }
175
+ }