@effindomv2/fui-rs 0.1.3

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 (115) hide show
  1. package/COMMERCIAL.md +7 -0
  2. package/Cargo.toml +34 -0
  3. package/LICENSE.md +9 -0
  4. package/README.md +88 -0
  5. package/package.json +54 -0
  6. package/scripts/build.sh +227 -0
  7. package/scripts/check-runtime-dependency.sh +41 -0
  8. package/scripts/framework-host-services.ts +40 -0
  9. package/scripts/generate-host-events.ts +17 -0
  10. package/scripts/generate-host-services.ts +28 -0
  11. package/scripts/hostgen/common.ts +73 -0
  12. package/scripts/hostgen/registry.ts +159 -0
  13. package/scripts/hostgen/rust-host-events.ts +171 -0
  14. package/scripts/hostgen/rust-host-services.ts +257 -0
  15. package/src/animation.rs +625 -0
  16. package/src/app.rs +324 -0
  17. package/src/assets.rs +572 -0
  18. package/src/bindings/mod.rs +1 -0
  19. package/src/bindings/ui.rs +705 -0
  20. package/src/bitmap.rs +317 -0
  21. package/src/bridge_callbacks.rs +242 -0
  22. package/src/context_menu_manager.rs +332 -0
  23. package/src/controls/anti_selection_area.rs +54 -0
  24. package/src/controls/button.rs +542 -0
  25. package/src/controls/checkbox.rs +372 -0
  26. package/src/controls/combobox.rs +1574 -0
  27. package/src/controls/context_menu.rs +1367 -0
  28. package/src/controls/control_template_set.rs +46 -0
  29. package/src/controls/control_tokens.rs +596 -0
  30. package/src/controls/dialog.rs +590 -0
  31. package/src/controls/dropdown.rs +1010 -0
  32. package/src/controls/form.rs +229 -0
  33. package/src/controls/internal/button_presenter.rs +142 -0
  34. package/src/controls/internal/checkbox_indicator_presenter.rs +210 -0
  35. package/src/controls/internal/dropdown_chevron_presenter.rs +134 -0
  36. package/src/controls/internal/dropdown_field_presenter.rs +269 -0
  37. package/src/controls/internal/dropdown_option_row_presenter.rs +184 -0
  38. package/src/controls/internal/mod.rs +13 -0
  39. package/src/controls/internal/pressable_indicator_presenter.rs +26 -0
  40. package/src/controls/internal/pressable_labeled_control.rs +492 -0
  41. package/src/controls/internal/radio_indicator_presenter.rs +208 -0
  42. package/src/controls/internal/selectable_popup_list.rs +597 -0
  43. package/src/controls/internal/slider_presenter.rs +282 -0
  44. package/src/controls/internal/switch_indicator_presenter.rs +235 -0
  45. package/src/controls/internal/text_input_core.rs +1074 -0
  46. package/src/controls/internal/text_input_presenter.rs +108 -0
  47. package/src/controls/mod.rs +235 -0
  48. package/src/controls/nav_link.rs +395 -0
  49. package/src/controls/popup.rs +191 -0
  50. package/src/controls/progress_bar.rs +287 -0
  51. package/src/controls/radio_button.rs +348 -0
  52. package/src/controls/radio_group.rs +283 -0
  53. package/src/controls/selection_area.rs +82 -0
  54. package/src/controls/shared.rs +68 -0
  55. package/src/controls/slider.rs +701 -0
  56. package/src/controls/switch.rs +293 -0
  57. package/src/controls/templating.rs +49 -0
  58. package/src/controls/tests.rs +3905 -0
  59. package/src/controls/text_area.rs +207 -0
  60. package/src/controls/text_input.rs +192 -0
  61. package/src/debug.rs +146 -0
  62. package/src/drag_drop.rs +424 -0
  63. package/src/drag_gesture.rs +258 -0
  64. package/src/drawing.rs +385 -0
  65. package/src/event.rs +1603 -0
  66. package/src/external_drop.rs +467 -0
  67. package/src/fetch.rs +500 -0
  68. package/src/ffi.rs +3542 -0
  69. package/src/file.rs +1677 -0
  70. package/src/focus_adorner.rs +307 -0
  71. package/src/focus_visibility.rs +121 -0
  72. package/src/frame_scheduler.rs +159 -0
  73. package/src/frame_signal.rs +64 -0
  74. package/src/generated/ffi.rs +798 -0
  75. package/src/generated/framework_host_services.rs +74 -0
  76. package/src/generated/mod.rs +2 -0
  77. package/src/host_services.rs +162 -0
  78. package/src/image_sampling.rs +78 -0
  79. package/src/keyboard_scroll.rs +137 -0
  80. package/src/keyboard_scroll_tracker.rs +385 -0
  81. package/src/lib.rs +547 -0
  82. package/src/logger.rs +56 -0
  83. package/src/mobile_text_selection_toolbar.rs +1034 -0
  84. package/src/navigation.rs +48 -0
  85. package/src/node/core.rs +2210 -0
  86. package/src/node/custom_drawable.rs +149 -0
  87. package/src/node/flex_box.rs +874 -0
  88. package/src/node/grid.rs +304 -0
  89. package/src/node/helpers.rs +442 -0
  90. package/src/node/image.rs +506 -0
  91. package/src/node/mod.rs +315 -0
  92. package/src/node/scroll_bar.rs +845 -0
  93. package/src/node/scroll_box.rs +514 -0
  94. package/src/node/scroll_state.rs +121 -0
  95. package/src/node/scroll_view.rs +557 -0
  96. package/src/node/svg_node.rs +474 -0
  97. package/src/node/text_node.rs +633 -0
  98. package/src/node/virtual_list.rs +678 -0
  99. package/src/panic_hook.rs +36 -0
  100. package/src/persisted.rs +274 -0
  101. package/src/platform.rs +556 -0
  102. package/src/popup_presenter.rs +344 -0
  103. package/src/selection_handle_adorner.rs +838 -0
  104. package/src/signal.rs +134 -0
  105. package/src/text.rs +1065 -0
  106. package/src/theme.rs +571 -0
  107. package/src/timers.rs +106 -0
  108. package/src/tool_tip.rs +167 -0
  109. package/src/tool_tip_manager.rs +691 -0
  110. package/src/transitions.rs +41 -0
  111. package/src/typography.rs +520 -0
  112. package/src/viewport.rs +108 -0
  113. package/src/worker.rs +308 -0
  114. package/src/worker_job.rs +82 -0
  115. package/src/worker_runtime.rs +172 -0
@@ -0,0 +1,282 @@
1
+ use crate::controls::{SliderColors, SliderSizing};
2
+ use crate::ffi::{Orientation, PositionType, Unit};
3
+ use crate::node::{flex_box, FlexBox};
4
+ use crate::theme::Theme;
5
+ use std::rc::Rc;
6
+
7
+ fn clamp(value: f32, min: f32, max: f32) -> f32 {
8
+ if value < min {
9
+ return min;
10
+ }
11
+ if value > max {
12
+ return max;
13
+ }
14
+ value
15
+ }
16
+
17
+ fn resolve_thumb_size(metrics: SliderPresenterMetrics) -> f32 {
18
+ if metrics.thumb_size > 1.0 {
19
+ metrics.thumb_size
20
+ } else {
21
+ 1.0
22
+ }
23
+ }
24
+
25
+ fn resolve_track_thickness(metrics: SliderPresenterMetrics) -> f32 {
26
+ clamp(metrics.track_thickness, 1.0, resolve_thumb_size(metrics))
27
+ }
28
+
29
+ fn resolve_cross_axis_extra(metrics: SliderPresenterMetrics) -> f32 {
30
+ if metrics.cross_axis_extra > 0.0 {
31
+ metrics.cross_axis_extra
32
+ } else {
33
+ 0.0
34
+ }
35
+ }
36
+
37
+ #[derive(Clone, Copy, Debug, PartialEq)]
38
+ pub struct SliderPresenterMetrics {
39
+ pub thumb_size: f32,
40
+ pub track_thickness: f32,
41
+ pub cross_axis_extra: f32,
42
+ }
43
+
44
+ impl SliderPresenterMetrics {
45
+ pub const fn new(thumb_size: f32, track_thickness: f32) -> Self {
46
+ Self {
47
+ thumb_size,
48
+ track_thickness,
49
+ cross_axis_extra: 2.0,
50
+ }
51
+ }
52
+
53
+ pub const fn with_cross_axis_extra(mut self, cross_axis_extra: f32) -> Self {
54
+ self.cross_axis_extra = cross_axis_extra;
55
+ self
56
+ }
57
+
58
+ pub const fn with_values(thumb_size: f32, track_thickness: f32, cross_axis_extra: f32) -> Self {
59
+ Self {
60
+ thumb_size,
61
+ track_thickness,
62
+ cross_axis_extra,
63
+ }
64
+ }
65
+ }
66
+
67
+ const DEFAULT_SLIDER_METRICS: SliderPresenterMetrics = SliderPresenterMetrics::new(18.0, 6.0);
68
+
69
+ fn resolve_slider_metrics(sizing: Option<SliderSizing>) -> SliderPresenterMetrics {
70
+ let Some(sizing) = sizing else {
71
+ return DEFAULT_SLIDER_METRICS;
72
+ };
73
+ let thumb_size = if sizing.has_thumb_size() {
74
+ sizing.thumb_size_px()
75
+ } else {
76
+ DEFAULT_SLIDER_METRICS.thumb_size
77
+ };
78
+ let track_thickness = if sizing.has_track_thickness() {
79
+ sizing.track_thickness_px()
80
+ } else {
81
+ DEFAULT_SLIDER_METRICS.track_thickness
82
+ };
83
+ if (thumb_size - DEFAULT_SLIDER_METRICS.thumb_size).abs() <= f32::EPSILON
84
+ && (track_thickness - DEFAULT_SLIDER_METRICS.track_thickness).abs() <= f32::EPSILON
85
+ {
86
+ return DEFAULT_SLIDER_METRICS;
87
+ }
88
+ SliderPresenterMetrics::new(thumb_size, track_thickness)
89
+ }
90
+
91
+ #[derive(Clone, Copy, Debug, PartialEq)]
92
+ pub struct SliderVisualState {
93
+ pub value: f32,
94
+ pub min: f32,
95
+ pub max: f32,
96
+ pub normalized_value: f32,
97
+ pub orientation: Orientation,
98
+ pub hovered: bool,
99
+ pub dragging: bool,
100
+ pub focused: bool,
101
+ pub enabled: bool,
102
+ }
103
+
104
+ impl SliderVisualState {
105
+ #[allow(clippy::too_many_arguments)]
106
+ pub fn new(
107
+ value: f32,
108
+ min: f32,
109
+ max: f32,
110
+ normalized_value: f32,
111
+ orientation: Orientation,
112
+ hovered: bool,
113
+ dragging: bool,
114
+ focused: bool,
115
+ enabled: bool,
116
+ ) -> Self {
117
+ Self {
118
+ value,
119
+ min,
120
+ max,
121
+ normalized_value,
122
+ orientation,
123
+ hovered,
124
+ dragging,
125
+ focused,
126
+ enabled,
127
+ }
128
+ }
129
+ }
130
+
131
+ pub trait SliderPresenter {
132
+ fn root(&self) -> FlexBox;
133
+ fn metrics(&self) -> SliderPresenterMetrics;
134
+ fn layout(&self, state: SliderVisualState, length: f32);
135
+ fn apply(&self, theme: Theme, state: SliderVisualState, colors: Option<SliderColors>);
136
+ }
137
+
138
+ pub trait SliderTemplate {
139
+ fn create(&self, sizing: Option<SliderSizing>) -> Rc<dyn SliderPresenter>;
140
+ }
141
+
142
+ #[derive(Clone)]
143
+ pub struct DefaultSliderPresenter {
144
+ root: FlexBox,
145
+ metrics: SliderPresenterMetrics,
146
+ track_node: FlexBox,
147
+ fill_node: FlexBox,
148
+ thumb_node: FlexBox,
149
+ }
150
+
151
+ impl DefaultSliderPresenter {
152
+ pub fn new(metrics: SliderPresenterMetrics) -> Self {
153
+ let root = flex_box();
154
+ let track_node = flex_box();
155
+ track_node.position_type(PositionType::Absolute);
156
+ let fill_node = flex_box();
157
+ fill_node.position_type(PositionType::Absolute);
158
+ let thumb_node = flex_box();
159
+ thumb_node
160
+ .position_type(PositionType::Absolute)
161
+ .width(metrics.thumb_size, Unit::Pixel)
162
+ .height(metrics.thumb_size, Unit::Pixel);
163
+ root.child(&track_node).child(&fill_node).child(&thumb_node);
164
+ Self {
165
+ root,
166
+ metrics,
167
+ track_node,
168
+ fill_node,
169
+ thumb_node,
170
+ }
171
+ }
172
+ }
173
+
174
+ impl SliderPresenter for DefaultSliderPresenter {
175
+ fn root(&self) -> FlexBox {
176
+ self.root.clone()
177
+ }
178
+
179
+ fn metrics(&self) -> SliderPresenterMetrics {
180
+ self.metrics
181
+ }
182
+
183
+ fn layout(&self, state: SliderVisualState, length: f32) {
184
+ let metrics = self.metrics;
185
+ let thumb_size = resolve_thumb_size(metrics);
186
+ let track_thickness = resolve_track_thickness(metrics);
187
+ let cross_axis_extra = resolve_cross_axis_extra(metrics);
188
+ let available = if length > thumb_size {
189
+ length - thumb_size
190
+ } else {
191
+ 0.0
192
+ };
193
+ let fraction = clamp(state.normalized_value, 0.0, 1.0);
194
+ let cross_axis_inset = cross_axis_extra * 0.5;
195
+ let track_offset = cross_axis_inset + ((thumb_size - track_thickness) * 0.5);
196
+ if state.orientation == Orientation::Vertical {
197
+ self.root
198
+ .width(thumb_size + cross_axis_extra, Unit::Pixel)
199
+ .height(length, Unit::Pixel);
200
+ self.track_node
201
+ .width(track_thickness, Unit::Pixel)
202
+ .height(available, Unit::Pixel)
203
+ .position(track_offset, thumb_size * 0.5);
204
+ self.fill_node
205
+ .width(track_thickness, Unit::Pixel)
206
+ .height(available * fraction, Unit::Pixel)
207
+ .position(
208
+ track_offset,
209
+ thumb_size * 0.5 + (available * (1.0 - fraction)),
210
+ );
211
+ self.thumb_node
212
+ .position(cross_axis_inset, available - (available * fraction));
213
+ return;
214
+ }
215
+
216
+ self.root
217
+ .width(length, Unit::Pixel)
218
+ .height(thumb_size + cross_axis_extra, Unit::Pixel);
219
+ self.track_node
220
+ .width(available, Unit::Pixel)
221
+ .height(track_thickness, Unit::Pixel)
222
+ .position(thumb_size * 0.5, track_offset);
223
+ self.fill_node
224
+ .width(available * fraction, Unit::Pixel)
225
+ .height(track_thickness, Unit::Pixel)
226
+ .position(thumb_size * 0.5, track_offset);
227
+ self.thumb_node
228
+ .position(available * fraction, cross_axis_inset);
229
+ }
230
+
231
+ fn apply(&self, theme: Theme, state: SliderVisualState, colors: Option<SliderColors>) {
232
+ let accent = if state.dragging {
233
+ theme.colors.accent_pressed
234
+ } else if state.hovered {
235
+ theme.colors.accent_hovered
236
+ } else {
237
+ theme.colors.accent
238
+ };
239
+ let metrics = self.metrics;
240
+ let thumb_size = resolve_thumb_size(metrics);
241
+ let track_thickness = resolve_track_thickness(metrics);
242
+ let track_radius = track_thickness * 0.5;
243
+ self.track_node.corner_radius(track_radius);
244
+ let track_color = colors
245
+ .filter(|colors| colors.has_track())
246
+ .map(|colors| colors.track_color())
247
+ .unwrap_or(theme.colors.scrollbar_track);
248
+ self.track_node.bg_color(track_color);
249
+ self.fill_node.corner_radius(track_radius);
250
+ let fill_color = colors
251
+ .filter(|colors| colors.has_fill())
252
+ .map(|colors| colors.fill_color())
253
+ .unwrap_or(accent);
254
+ self.fill_node.bg_color(fill_color);
255
+ self.thumb_node
256
+ .width(thumb_size, Unit::Pixel)
257
+ .height(thumb_size, Unit::Pixel)
258
+ .corner_radius(thumb_size * 0.5);
259
+ let thumb_color = colors
260
+ .filter(|colors| colors.has_thumb())
261
+ .map(|colors| colors.thumb_color())
262
+ .unwrap_or(accent);
263
+ self.thumb_node
264
+ .bg_color(thumb_color)
265
+ .border(1.0, theme.colors.surface);
266
+ }
267
+ }
268
+
269
+ #[derive(Clone, Copy, Debug, Default)]
270
+ pub struct DefaultSliderTemplate;
271
+
272
+ impl SliderTemplate for DefaultSliderTemplate {
273
+ fn create(&self, sizing: Option<SliderSizing>) -> Rc<dyn SliderPresenter> {
274
+ create_default_slider_presenter(sizing)
275
+ }
276
+ }
277
+
278
+ pub const DEFAULT_SLIDER_TEMPLATE: DefaultSliderTemplate = DefaultSliderTemplate;
279
+
280
+ pub fn create_default_slider_presenter(sizing: Option<SliderSizing>) -> Rc<dyn SliderPresenter> {
281
+ Rc::new(DefaultSliderPresenter::new(resolve_slider_metrics(sizing)))
282
+ }
@@ -0,0 +1,235 @@
1
+ use super::pressable_indicator_presenter::{
2
+ PressableIndicatorMetrics, PressableIndicatorPresenter, PressableIndicatorVisualState,
3
+ };
4
+ use crate::controls::{LabeledControlColors, LabeledControlSizing};
5
+ use crate::ffi::{AlignItems, PositionType, Unit};
6
+ use crate::node::{flex_box, FlexBox};
7
+ use crate::theme::Theme;
8
+ use std::rc::Rc;
9
+
10
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
11
+ pub struct SwitchIndicatorVisualState {
12
+ pub checked: bool,
13
+ pub hovered: bool,
14
+ pub pressed: bool,
15
+ pub focused: bool,
16
+ pub enabled: bool,
17
+ }
18
+
19
+ impl SwitchIndicatorVisualState {
20
+ pub fn new(checked: bool, hovered: bool, pressed: bool, focused: bool, enabled: bool) -> Self {
21
+ Self {
22
+ checked,
23
+ hovered,
24
+ pressed,
25
+ focused,
26
+ enabled,
27
+ }
28
+ }
29
+
30
+ pub fn pressable_state(&self) -> PressableIndicatorVisualState {
31
+ PressableIndicatorVisualState {
32
+ hovered: self.hovered,
33
+ pressed: self.pressed,
34
+ focused: self.focused,
35
+ enabled: self.enabled,
36
+ }
37
+ }
38
+ }
39
+
40
+ pub trait SwitchIndicatorPresenter: PressableIndicatorPresenter {
41
+ fn apply(
42
+ &self,
43
+ theme: Theme,
44
+ state: SwitchIndicatorVisualState,
45
+ colors: Option<LabeledControlColors>,
46
+ );
47
+ }
48
+
49
+ pub trait SwitchIndicatorTemplate {
50
+ fn create(&self, sizing: Option<LabeledControlSizing>) -> Rc<dyn SwitchIndicatorPresenter>;
51
+ }
52
+
53
+ #[derive(Clone, Copy, Debug, PartialEq)]
54
+ pub struct SwitchIndicatorMetrics {
55
+ pub track_width: f32,
56
+ pub track_height: f32,
57
+ pub thumb_size: f32,
58
+ pub thumb_x: f32,
59
+ pub thumb_checked_x: f32,
60
+ pub thumb_y: f32,
61
+ }
62
+
63
+ impl SwitchIndicatorMetrics {
64
+ pub const fn new(
65
+ track_width: f32,
66
+ track_height: f32,
67
+ thumb_size: f32,
68
+ thumb_x: f32,
69
+ thumb_checked_x: f32,
70
+ thumb_y: f32,
71
+ ) -> Self {
72
+ Self {
73
+ track_width,
74
+ track_height,
75
+ thumb_size,
76
+ thumb_x,
77
+ thumb_checked_x,
78
+ thumb_y,
79
+ }
80
+ }
81
+ }
82
+
83
+ const DEFAULT_SWITCH_METRICS: SwitchIndicatorMetrics =
84
+ SwitchIndicatorMetrics::new(44.0, 26.0, 20.0, 3.0, 21.0, 2.0);
85
+
86
+ pub fn resolve_switch_metrics(sizing: Option<LabeledControlSizing>) -> SwitchIndicatorMetrics {
87
+ let Some(sizing) = sizing else {
88
+ return DEFAULT_SWITCH_METRICS;
89
+ };
90
+ if !sizing.has_indicator_size() {
91
+ return DEFAULT_SWITCH_METRICS;
92
+ }
93
+ let scale = sizing.indicator_size_px() / DEFAULT_SWITCH_METRICS.track_height;
94
+ let track_width = DEFAULT_SWITCH_METRICS.track_width * scale;
95
+ let track_height = DEFAULT_SWITCH_METRICS.track_height * scale;
96
+ let thumb_size = DEFAULT_SWITCH_METRICS.thumb_size * scale;
97
+ let thumb_x = DEFAULT_SWITCH_METRICS.thumb_x * scale;
98
+ let thumb_y = DEFAULT_SWITCH_METRICS.thumb_y * scale;
99
+ SwitchIndicatorMetrics::new(
100
+ track_width,
101
+ track_height,
102
+ thumb_size,
103
+ thumb_x,
104
+ track_width - thumb_size - thumb_x,
105
+ thumb_y,
106
+ )
107
+ }
108
+
109
+ #[derive(Clone)]
110
+ pub struct DefaultSwitchIndicatorPresenter {
111
+ root: FlexBox,
112
+ metrics: SwitchIndicatorMetrics,
113
+ thumb_node: FlexBox,
114
+ }
115
+
116
+ impl DefaultSwitchIndicatorPresenter {
117
+ pub fn new(metrics: SwitchIndicatorMetrics) -> Self {
118
+ let root = flex_box();
119
+ root.width(metrics.track_width, Unit::Pixel)
120
+ .height(metrics.track_height, Unit::Pixel)
121
+ .clip_to_bounds(true);
122
+ let thumb_node = flex_box();
123
+ thumb_node
124
+ .position_type(PositionType::Absolute)
125
+ .position(metrics.thumb_x, metrics.thumb_y)
126
+ .width(metrics.thumb_size, Unit::Pixel)
127
+ .height(metrics.thumb_size, Unit::Pixel);
128
+ root.align_items(AlignItems::Center).child(&thumb_node);
129
+ Self {
130
+ root,
131
+ metrics,
132
+ thumb_node,
133
+ }
134
+ }
135
+ }
136
+
137
+ impl PressableIndicatorPresenter for DefaultSwitchIndicatorPresenter {
138
+ fn root(&self) -> FlexBox {
139
+ self.root.clone()
140
+ }
141
+
142
+ fn metrics(&self) -> PressableIndicatorMetrics {
143
+ PressableIndicatorMetrics::new(self.metrics.track_width, self.metrics.track_height)
144
+ }
145
+ }
146
+
147
+ impl SwitchIndicatorPresenter for DefaultSwitchIndicatorPresenter {
148
+ fn apply(
149
+ &self,
150
+ theme: Theme,
151
+ state: SwitchIndicatorVisualState,
152
+ colors: Option<LabeledControlColors>,
153
+ ) {
154
+ let accent = colors
155
+ .filter(|colors| colors.has_accent())
156
+ .map(|colors| colors.accent_color())
157
+ .unwrap_or_else(|| {
158
+ if state.pressed {
159
+ theme.colors.accent_pressed
160
+ } else if state.hovered {
161
+ theme.colors.accent_hovered
162
+ } else {
163
+ theme.colors.accent
164
+ }
165
+ });
166
+ let track_color = if state.checked {
167
+ accent
168
+ } else {
169
+ colors
170
+ .filter(|colors| colors.has_background())
171
+ .map(|colors| colors.background_color())
172
+ .unwrap_or_else(|| {
173
+ if state.hovered {
174
+ theme.colors.background
175
+ } else {
176
+ theme.colors.surface
177
+ }
178
+ })
179
+ };
180
+ let border_color = colors
181
+ .filter(|colors| colors.has_border())
182
+ .map(|colors| colors.border_color())
183
+ .unwrap_or_else(|| {
184
+ if state.checked {
185
+ track_color
186
+ } else {
187
+ theme.colors.border
188
+ }
189
+ });
190
+ let metrics = self.metrics;
191
+ self.root
192
+ .corner_radius(metrics.track_height * 0.5)
193
+ .border(1.0, border_color)
194
+ .bg_color(track_color);
195
+ self.thumb_node
196
+ .position(
197
+ if state.checked {
198
+ metrics.thumb_checked_x
199
+ } else {
200
+ metrics.thumb_x
201
+ },
202
+ metrics.thumb_y,
203
+ )
204
+ .width(metrics.thumb_size, Unit::Pixel)
205
+ .height(metrics.thumb_size, Unit::Pixel)
206
+ .corner_radius(metrics.thumb_size * 0.5)
207
+ .bg_color(
208
+ colors
209
+ .filter(|colors| colors.has_background())
210
+ .map(|colors| colors.background_color())
211
+ .unwrap_or(theme.colors.surface),
212
+ )
213
+ .border(1.0, border_color);
214
+ }
215
+ }
216
+
217
+ #[derive(Clone, Copy, Debug, Default)]
218
+ pub struct DefaultSwitchIndicatorTemplate;
219
+
220
+ impl SwitchIndicatorTemplate for DefaultSwitchIndicatorTemplate {
221
+ fn create(&self, sizing: Option<LabeledControlSizing>) -> Rc<dyn SwitchIndicatorPresenter> {
222
+ create_default_switch_indicator_presenter(sizing)
223
+ }
224
+ }
225
+
226
+ pub const DEFAULT_SWITCH_INDICATOR_TEMPLATE: DefaultSwitchIndicatorTemplate =
227
+ DefaultSwitchIndicatorTemplate;
228
+
229
+ pub fn create_default_switch_indicator_presenter(
230
+ sizing: Option<LabeledControlSizing>,
231
+ ) -> Rc<dyn SwitchIndicatorPresenter> {
232
+ Rc::new(DefaultSwitchIndicatorPresenter::new(
233
+ resolve_switch_metrics(sizing),
234
+ ))
235
+ }