@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,283 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
use crate::logger;
|
|
3
|
+
use std::cell::{Cell, RefCell};
|
|
4
|
+
use std::rc::{Rc, Weak};
|
|
5
|
+
|
|
6
|
+
#[derive(Clone)]
|
|
7
|
+
pub(crate) struct RadioGroupEventTarget {
|
|
8
|
+
radios: Rc<RefCell<Vec<RadioButton>>>,
|
|
9
|
+
selected_index: Rc<Cell<i32>>,
|
|
10
|
+
changed: Rc<RefCell<Option<RadioGroupChangedCallback>>>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl RadioGroupEventTarget {
|
|
14
|
+
pub(crate) fn select_radio_handle(&self, radio_handle: u64, focus: bool) {
|
|
15
|
+
let index = self.index_of_radio_handle(radio_handle);
|
|
16
|
+
if index >= 0 {
|
|
17
|
+
self.select_index_internal(index, focus, true, true);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub(crate) fn move_selection_from_handle(&self, radio_handle: u64, delta: i32) {
|
|
22
|
+
let start_index = self.index_of_radio_handle(radio_handle);
|
|
23
|
+
if start_index < 0 || self.radios.borrow().is_empty() {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let next_index = self.find_enabled_index(start_index, delta);
|
|
27
|
+
if next_index >= 0 {
|
|
28
|
+
self.select_index_internal(next_index, true, true, true);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub(crate) fn select_first_enabled(&self, focus: bool) {
|
|
33
|
+
let next_index = self.find_boundary_index(true);
|
|
34
|
+
if next_index >= 0 {
|
|
35
|
+
self.select_index_internal(next_index, focus, true, true);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pub(crate) fn select_last_enabled(&self, focus: bool) {
|
|
40
|
+
let next_index = self.find_boundary_index(false);
|
|
41
|
+
if next_index >= 0 {
|
|
42
|
+
self.select_index_internal(next_index, focus, true, true);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub(crate) fn select_index(&self, index: i32) {
|
|
47
|
+
if index == -1 {
|
|
48
|
+
let previous_index = self.selected_index.get();
|
|
49
|
+
let changed = previous_index != -1;
|
|
50
|
+
if let Some(previous) = self.radio_at(previous_index) {
|
|
51
|
+
previous.update_checked(false, true, false);
|
|
52
|
+
}
|
|
53
|
+
self.selected_index.set(-1);
|
|
54
|
+
if changed {
|
|
55
|
+
self.emit_changed(String::new());
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let radio_count = self.radios.borrow().len() as i32;
|
|
60
|
+
if radio_count == 0 {
|
|
61
|
+
if index != -1 {
|
|
62
|
+
logger::warn(
|
|
63
|
+
"Layout",
|
|
64
|
+
&format!(
|
|
65
|
+
"RadioGroup.select_index() received {index} before any radios were added."
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let clamped_index = if index < 0 {
|
|
72
|
+
0
|
|
73
|
+
} else if index >= radio_count {
|
|
74
|
+
radio_count - 1
|
|
75
|
+
} else {
|
|
76
|
+
index
|
|
77
|
+
};
|
|
78
|
+
if clamped_index != index {
|
|
79
|
+
logger::warn(
|
|
80
|
+
"Layout",
|
|
81
|
+
&format!(
|
|
82
|
+
"RadioGroup.select_index() received {index}; clamping to {clamped_index}."
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
self.select_index_internal(clamped_index, false, true, false);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fn select_index_internal(&self, index: i32, focus: bool, emit: bool, announce: bool) {
|
|
90
|
+
let Some(radio) = self.radio_at(index) else {
|
|
91
|
+
return;
|
|
92
|
+
};
|
|
93
|
+
if !radio.is_enabled() {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
let previous_index = self.selected_index.get();
|
|
97
|
+
if previous_index == index {
|
|
98
|
+
if focus {
|
|
99
|
+
radio.focus_now();
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if let Some(previous) = self.radio_at(previous_index) {
|
|
104
|
+
previous.update_checked(false, emit, false);
|
|
105
|
+
}
|
|
106
|
+
self.selected_index.set(index);
|
|
107
|
+
radio.update_checked(true, emit, announce);
|
|
108
|
+
if focus {
|
|
109
|
+
radio.focus_now();
|
|
110
|
+
}
|
|
111
|
+
if emit {
|
|
112
|
+
self.emit_changed(radio.value().to_string());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn emit_changed(&self, value: String) {
|
|
117
|
+
if let Some(callback) = self.changed.borrow().clone() {
|
|
118
|
+
callback(RadioGroupChangedEventArgs { value });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn index_of_radio_handle(&self, target_handle: u64) -> i32 {
|
|
123
|
+
self.radios
|
|
124
|
+
.borrow()
|
|
125
|
+
.iter()
|
|
126
|
+
.position(|radio| radio.retained_node_ref().handle().raw() == target_handle)
|
|
127
|
+
.map(|index| index as i32)
|
|
128
|
+
.unwrap_or(-1)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn find_enabled_index(&self, start_index: i32, delta: i32) -> i32 {
|
|
132
|
+
let radios = self.radios.borrow();
|
|
133
|
+
if radios.is_empty() {
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
let len = radios.len() as i32;
|
|
137
|
+
let mut cursor = start_index;
|
|
138
|
+
for _ in 0..len {
|
|
139
|
+
cursor += delta;
|
|
140
|
+
if cursor < 0 {
|
|
141
|
+
cursor = len - 1;
|
|
142
|
+
} else if cursor >= len {
|
|
143
|
+
cursor = 0;
|
|
144
|
+
}
|
|
145
|
+
if radios[cursor as usize].is_enabled() {
|
|
146
|
+
return cursor;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
-1
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn find_boundary_index(&self, first: bool) -> i32 {
|
|
153
|
+
let radios = self.radios.borrow();
|
|
154
|
+
if first {
|
|
155
|
+
radios
|
|
156
|
+
.iter()
|
|
157
|
+
.position(|radio| radio.is_enabled())
|
|
158
|
+
.map(|index| index as i32)
|
|
159
|
+
.unwrap_or(-1)
|
|
160
|
+
} else {
|
|
161
|
+
radios
|
|
162
|
+
.iter()
|
|
163
|
+
.rposition(|radio| radio.is_enabled())
|
|
164
|
+
.map(|index| index as i32)
|
|
165
|
+
.unwrap_or(-1)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fn radio_at(&self, index: i32) -> Option<RadioButton> {
|
|
170
|
+
if index < 0 {
|
|
171
|
+
return None;
|
|
172
|
+
}
|
|
173
|
+
self.radios.borrow().get(index as usize).cloned()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
#[derive(Clone)]
|
|
178
|
+
pub struct RadioGroup {
|
|
179
|
+
root: FlexBox,
|
|
180
|
+
event_target: Rc<RadioGroupEventTarget>,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
impl RadioGroup {
|
|
184
|
+
pub fn new() -> Self {
|
|
185
|
+
let root = flex_box();
|
|
186
|
+
root.semantic_role(SemanticRole::RadioGroup)
|
|
187
|
+
.flex_direction(FlexDirection::Column);
|
|
188
|
+
let event_target = Rc::new(RadioGroupEventTarget {
|
|
189
|
+
radios: Rc::new(RefCell::new(Vec::new())),
|
|
190
|
+
selected_index: Rc::new(Cell::new(-1)),
|
|
191
|
+
changed: Rc::new(RefCell::new(None)),
|
|
192
|
+
});
|
|
193
|
+
root.retained_node_ref()
|
|
194
|
+
.retain_attachment(event_target.clone());
|
|
195
|
+
let control = Self { root, event_target };
|
|
196
|
+
let selected_index = control.event_target.selected_index.clone();
|
|
197
|
+
let restore_target = control.event_target.clone();
|
|
198
|
+
control.persist_state(crate::persisted::persisted_value_adapter(
|
|
199
|
+
"radio-group-selected-index",
|
|
200
|
+
crate::persisted::PersistedInt32Codec,
|
|
201
|
+
1,
|
|
202
|
+
move || {
|
|
203
|
+
let index = selected_index.get();
|
|
204
|
+
if index >= 0 {
|
|
205
|
+
Some(index)
|
|
206
|
+
} else {
|
|
207
|
+
None
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
move |index| {
|
|
211
|
+
restore_target.select_index(index);
|
|
212
|
+
},
|
|
213
|
+
));
|
|
214
|
+
control
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub fn add_radio(&self, radio: RadioButton) -> &Self {
|
|
218
|
+
radio.bind_group(Rc::downgrade(&self.event_target));
|
|
219
|
+
if self.selected_index() < 0 && radio.is_checked() {
|
|
220
|
+
self.event_target
|
|
221
|
+
.selected_index
|
|
222
|
+
.set(self.event_target.radios.borrow().len() as i32);
|
|
223
|
+
}
|
|
224
|
+
self.root.child(&radio);
|
|
225
|
+
self.event_target.radios.borrow_mut().push(radio);
|
|
226
|
+
self
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
pub fn add_option(&self, value: impl Into<String>, label: impl Into<String>) -> RadioButton {
|
|
230
|
+
let radio = RadioButton::with_label(value, label);
|
|
231
|
+
self.add_radio(radio.clone());
|
|
232
|
+
radio
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
pub fn add_options<I>(&self, radios: I) -> &Self
|
|
236
|
+
where
|
|
237
|
+
I: IntoIterator<Item = RadioButton>,
|
|
238
|
+
{
|
|
239
|
+
for radio in radios {
|
|
240
|
+
self.add_radio(radio);
|
|
241
|
+
}
|
|
242
|
+
self
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
pub fn on_changed(&self, handler: impl Fn(RadioGroupChangedEventArgs) + 'static) -> &Self {
|
|
246
|
+
*self.event_target.changed.borrow_mut() = Some(Rc::new(handler));
|
|
247
|
+
self
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
pub fn selected_index(&self) -> i32 {
|
|
251
|
+
self.event_target.selected_index.get()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
pub fn selected_value(&self) -> String {
|
|
255
|
+
self.event_target
|
|
256
|
+
.radio_at(self.selected_index())
|
|
257
|
+
.map(|radio| radio.value().to_string())
|
|
258
|
+
.unwrap_or_default()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
pub fn select_index(&self, index: i32) -> &Self {
|
|
262
|
+
self.event_target.select_index(index);
|
|
263
|
+
self
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
impl Default for RadioGroup {
|
|
268
|
+
fn default() -> Self {
|
|
269
|
+
Self::new()
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
impl Node for RadioGroup {
|
|
274
|
+
fn retained_node_ref(&self) -> NodeRef {
|
|
275
|
+
self.root.retained_node_ref()
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fn build_self(&self) {
|
|
279
|
+
self.root.build_self();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub(crate) type WeakRadioGroupEventTarget = Weak<RadioGroupEventTarget>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
use crate::signal::{Callback, Signal, Subscription};
|
|
3
|
+
|
|
4
|
+
#[derive(Clone)]
|
|
5
|
+
pub struct SelectionArea {
|
|
6
|
+
root: FlexBox,
|
|
7
|
+
selected_text_signal: Rc<RefCell<Signal<String>>>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl SelectionArea {
|
|
11
|
+
pub fn new() -> Self {
|
|
12
|
+
let root = flex_box();
|
|
13
|
+
root.selection_area(true);
|
|
14
|
+
let selected_text_signal = Rc::new(RefCell::new(Signal::new(String::new())));
|
|
15
|
+
let signal = selected_text_signal.clone();
|
|
16
|
+
root.core.borrow_mut().handlers.cross_selection_changed = Some(Rc::new(move |text| {
|
|
17
|
+
let callbacks = signal.borrow_mut().set(text);
|
|
18
|
+
if let Some(callbacks) = callbacks {
|
|
19
|
+
for callback in callbacks {
|
|
20
|
+
callback();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}));
|
|
24
|
+
Self {
|
|
25
|
+
root,
|
|
26
|
+
selected_text_signal,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn child<T: Node>(&self, node: &T) -> &Self {
|
|
31
|
+
self.root.child(node);
|
|
32
|
+
self
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub fn children<I, C>(&self, nodes: I) -> &Self
|
|
36
|
+
where
|
|
37
|
+
I: IntoIterator<Item = C>,
|
|
38
|
+
C: Into<Child>,
|
|
39
|
+
{
|
|
40
|
+
for node in nodes {
|
|
41
|
+
self.root
|
|
42
|
+
.retained_node_ref()
|
|
43
|
+
.append_child_ref(&node.into().node_ref);
|
|
44
|
+
}
|
|
45
|
+
self
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn selected_text(&self) -> String {
|
|
49
|
+
self.selected_text_signal.borrow().get()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn subscribe_selected_text(&self, handler: impl Fn(String) + 'static) -> Subscription {
|
|
53
|
+
let signal = self.selected_text_signal.clone();
|
|
54
|
+
let callback: Callback = Rc::new(move || handler(signal.borrow().get()));
|
|
55
|
+
self.selected_text_signal.borrow_mut().subscribe(callback)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
impl Default for SelectionArea {
|
|
60
|
+
fn default() -> Self {
|
|
61
|
+
Self::new()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
impl Node for SelectionArea {
|
|
66
|
+
fn retained_node_ref(&self) -> NodeRef {
|
|
67
|
+
let selection_area = self.clone();
|
|
68
|
+
self.root
|
|
69
|
+
.retained_node_ref()
|
|
70
|
+
.with_build_callback(move || selection_area.build())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn build_self(&self) {
|
|
74
|
+
self.root.build_self();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl HasFlexBoxRoot for SelectionArea {
|
|
79
|
+
fn flex_box_root(&self) -> &FlexBox {
|
|
80
|
+
&self.root
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
3
|
+
pub(crate) const PROGRESS_LENGTH: f32 = 220.0;
|
|
4
|
+
pub(crate) const PROGRESS_THICKNESS: f32 = 14.0;
|
|
5
|
+
pub(crate) const SLIDER_FOCUS_BORDER_WIDTH: f32 = 2.0;
|
|
6
|
+
pub(crate) const SLIDER_PADDING: f32 = 2.0;
|
|
7
|
+
pub(crate) const SLIDER_CONTENT_INSET: f32 = 1.0;
|
|
8
|
+
pub(crate) const SLIDER_OUTER_INSET: f32 =
|
|
9
|
+
SLIDER_FOCUS_BORDER_WIDTH + SLIDER_PADDING + SLIDER_CONTENT_INSET;
|
|
10
|
+
pub(crate) const SLIDER_CHILD_INSET: f32 = SLIDER_PADDING + SLIDER_CONTENT_INSET;
|
|
11
|
+
|
|
12
|
+
pub(crate) type ClickCallback = Rc<dyn Fn(ClickEventArgs)>;
|
|
13
|
+
pub(crate) type CheckboxChangedCallback = Rc<dyn Fn(CheckboxChangedEventArgs)>;
|
|
14
|
+
pub(crate) type RadioButtonChangedCallback = Rc<dyn Fn(RadioButtonChangedEventArgs)>;
|
|
15
|
+
pub(crate) type RadioGroupChangedCallback = Rc<dyn Fn(RadioGroupChangedEventArgs)>;
|
|
16
|
+
pub(crate) type SwitchChangedCallback = Rc<dyn Fn(SwitchChangedEventArgs)>;
|
|
17
|
+
pub(crate) type SliderChangedCallback = Rc<dyn Fn(SliderChangedEventArgs)>;
|
|
18
|
+
|
|
19
|
+
pub(crate) fn is_activation_key(event: &KeyEventArgs) -> bool {
|
|
20
|
+
event.key == "Enter" || event.key == " " || event.key == "Space" || event.key == "Spacebar"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pub(crate) fn fire_click_callbacks(
|
|
24
|
+
click: &Rc<RefCell<Option<ClickCallback>>>,
|
|
25
|
+
double_click: &Rc<RefCell<Option<ClickCallback>>>,
|
|
26
|
+
triple_click: &Rc<RefCell<Option<ClickCallback>>>,
|
|
27
|
+
click_count: i32,
|
|
28
|
+
) {
|
|
29
|
+
let args = ClickEventArgs { click_count };
|
|
30
|
+
if let Some(callback) = click.borrow().clone() {
|
|
31
|
+
callback(args);
|
|
32
|
+
}
|
|
33
|
+
if click_count == 3 {
|
|
34
|
+
if let Some(callback) = triple_click.borrow().clone() {
|
|
35
|
+
callback(args);
|
|
36
|
+
}
|
|
37
|
+
} else if click_count == 2 {
|
|
38
|
+
if let Some(callback) = double_click.borrow().clone() {
|
|
39
|
+
callback(args);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pub(crate) fn normalize_slider_value(value: f32, min: f32, max: f32, step: f32) -> f32 {
|
|
45
|
+
let clamped = value.clamp(min.min(max), min.max(max));
|
|
46
|
+
if step <= 0.0 {
|
|
47
|
+
return clamped;
|
|
48
|
+
}
|
|
49
|
+
let snapped = ((clamped - min) / step).round() * step;
|
|
50
|
+
(min + snapped).clamp(min.min(max), min.max(max))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pub(crate) fn upgraded_handle(weak_root: &Rc<WeakNodeRef>) -> Option<crate::node::NodeHandle> {
|
|
54
|
+
weak_root.upgrade().map(|node| node.handle())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pub(crate) fn update_semantic_checked(
|
|
58
|
+
weak_root: &Rc<WeakNodeRef>,
|
|
59
|
+
state: SemanticCheckedState,
|
|
60
|
+
announce: bool,
|
|
61
|
+
) {
|
|
62
|
+
if let Some(handle) = upgraded_handle(weak_root) {
|
|
63
|
+
ui::set_semantic_checked(handle.raw(), state as u32);
|
|
64
|
+
if announce {
|
|
65
|
+
ui::request_semantic_announcement(handle.raw());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|