@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.
- package/COMMERCIAL.md +7 -0
- package/Cargo.toml +34 -0
- package/LICENSE.md +9 -0
- package/README.md +88 -0
- package/package.json +54 -0
- package/scripts/build.sh +227 -0
- package/scripts/check-runtime-dependency.sh +41 -0
- package/scripts/framework-host-services.ts +40 -0
- package/scripts/generate-host-events.ts +17 -0
- package/scripts/generate-host-services.ts +28 -0
- package/scripts/hostgen/common.ts +73 -0
- package/scripts/hostgen/registry.ts +159 -0
- package/scripts/hostgen/rust-host-events.ts +171 -0
- package/scripts/hostgen/rust-host-services.ts +257 -0
- package/src/animation.rs +625 -0
- package/src/app.rs +324 -0
- package/src/assets.rs +572 -0
- package/src/bindings/mod.rs +1 -0
- package/src/bindings/ui.rs +705 -0
- package/src/bitmap.rs +317 -0
- package/src/bridge_callbacks.rs +242 -0
- package/src/context_menu_manager.rs +332 -0
- package/src/controls/anti_selection_area.rs +54 -0
- package/src/controls/button.rs +542 -0
- package/src/controls/checkbox.rs +372 -0
- package/src/controls/combobox.rs +1574 -0
- package/src/controls/context_menu.rs +1367 -0
- package/src/controls/control_template_set.rs +46 -0
- package/src/controls/control_tokens.rs +596 -0
- package/src/controls/dialog.rs +590 -0
- package/src/controls/dropdown.rs +1010 -0
- package/src/controls/form.rs +229 -0
- package/src/controls/internal/button_presenter.rs +142 -0
- package/src/controls/internal/checkbox_indicator_presenter.rs +210 -0
- package/src/controls/internal/dropdown_chevron_presenter.rs +134 -0
- package/src/controls/internal/dropdown_field_presenter.rs +269 -0
- package/src/controls/internal/dropdown_option_row_presenter.rs +184 -0
- package/src/controls/internal/mod.rs +13 -0
- package/src/controls/internal/pressable_indicator_presenter.rs +26 -0
- package/src/controls/internal/pressable_labeled_control.rs +492 -0
- package/src/controls/internal/radio_indicator_presenter.rs +208 -0
- package/src/controls/internal/selectable_popup_list.rs +597 -0
- package/src/controls/internal/slider_presenter.rs +282 -0
- package/src/controls/internal/switch_indicator_presenter.rs +235 -0
- package/src/controls/internal/text_input_core.rs +1074 -0
- package/src/controls/internal/text_input_presenter.rs +108 -0
- package/src/controls/mod.rs +235 -0
- package/src/controls/nav_link.rs +395 -0
- package/src/controls/popup.rs +191 -0
- package/src/controls/progress_bar.rs +287 -0
- package/src/controls/radio_button.rs +348 -0
- package/src/controls/radio_group.rs +283 -0
- package/src/controls/selection_area.rs +82 -0
- package/src/controls/shared.rs +68 -0
- package/src/controls/slider.rs +701 -0
- package/src/controls/switch.rs +293 -0
- package/src/controls/templating.rs +49 -0
- package/src/controls/tests.rs +3905 -0
- package/src/controls/text_area.rs +207 -0
- package/src/controls/text_input.rs +192 -0
- package/src/debug.rs +146 -0
- package/src/drag_drop.rs +424 -0
- package/src/drag_gesture.rs +258 -0
- package/src/drawing.rs +385 -0
- package/src/event.rs +1603 -0
- package/src/external_drop.rs +467 -0
- package/src/fetch.rs +500 -0
- package/src/ffi.rs +3542 -0
- package/src/file.rs +1677 -0
- package/src/focus_adorner.rs +307 -0
- package/src/focus_visibility.rs +121 -0
- package/src/frame_scheduler.rs +159 -0
- package/src/frame_signal.rs +64 -0
- package/src/generated/ffi.rs +798 -0
- package/src/generated/framework_host_services.rs +74 -0
- package/src/generated/mod.rs +2 -0
- package/src/host_services.rs +162 -0
- package/src/image_sampling.rs +78 -0
- package/src/keyboard_scroll.rs +137 -0
- package/src/keyboard_scroll_tracker.rs +385 -0
- package/src/lib.rs +547 -0
- package/src/logger.rs +56 -0
- package/src/mobile_text_selection_toolbar.rs +1034 -0
- package/src/navigation.rs +48 -0
- package/src/node/core.rs +2210 -0
- package/src/node/custom_drawable.rs +149 -0
- package/src/node/flex_box.rs +874 -0
- package/src/node/grid.rs +304 -0
- package/src/node/helpers.rs +442 -0
- package/src/node/image.rs +506 -0
- package/src/node/mod.rs +315 -0
- package/src/node/scroll_bar.rs +845 -0
- package/src/node/scroll_box.rs +514 -0
- package/src/node/scroll_state.rs +121 -0
- package/src/node/scroll_view.rs +557 -0
- package/src/node/svg_node.rs +474 -0
- package/src/node/text_node.rs +633 -0
- package/src/node/virtual_list.rs +678 -0
- package/src/panic_hook.rs +36 -0
- package/src/persisted.rs +274 -0
- package/src/platform.rs +556 -0
- package/src/popup_presenter.rs +344 -0
- package/src/selection_handle_adorner.rs +838 -0
- package/src/signal.rs +134 -0
- package/src/text.rs +1065 -0
- package/src/theme.rs +571 -0
- package/src/timers.rs +106 -0
- package/src/tool_tip.rs +167 -0
- package/src/tool_tip_manager.rs +691 -0
- package/src/transitions.rs +41 -0
- package/src/typography.rs +520 -0
- package/src/viewport.rs +108 -0
- package/src/worker.rs +308 -0
- package/src/worker_job.rs +82 -0
- 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
|
+
}
|