@effindomv2/fui-rs 0.1.7 → 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 (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 +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/helpers.rs +6 -3
  34. package/src/node/mod.rs +48 -0
  35. package/src/node/presenter_host_style.rs +177 -0
  36. package/src/node/text_node.rs +35 -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 {
@@ -90,8 +87,6 @@ impl NavLink {
90
87
  enter_pressed_open_in_new_tab: Rc::new(Cell::new(false)),
91
88
  preview_pinned_for_context_menu: Rc::new(Cell::new(false)),
92
89
  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
90
  };
96
91
  let node_ref = link.root.retained_node_ref();
97
92
  let pin_target = link.event_target();
@@ -197,15 +192,20 @@ impl NavLink {
197
192
 
198
193
  fn install_subscriptions(&self) {
199
194
  let target = self.event_target();
200
- *self.theme_guard.borrow_mut() = Some(subscribe(move |_theme| {
195
+ let theme_guard = subscribe(move |_theme| {
201
196
  target.sync_visual_state();
202
197
  target.sync_focus_chrome();
203
- }));
198
+ });
199
+ self.root
200
+ .retained_node_ref()
201
+ .retain_attachment(Rc::new(theme_guard));
204
202
  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
- }));
203
+ let focus_guard = focus_visibility::subscribe(move |_visible| {
204
+ target.sync_focus_chrome();
205
+ });
206
+ self.root
207
+ .retained_node_ref()
208
+ .retain_attachment(Rc::new(focus_guard));
209
209
  }
210
210
 
211
211
  fn event_target(&self) -> NavLinkEventTarget {
@@ -263,6 +263,10 @@ impl NavLink {
263
263
  self
264
264
  }
265
265
 
266
+ pub fn label_node(&self) -> TextNode {
267
+ self.label.clone()
268
+ }
269
+
266
270
  pub fn open_in_new_tab(&self, open: bool) -> &Self {
267
271
  self.open_in_new_tab.set(open);
268
272
  self
@@ -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
+ }