@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,385 @@
1
+ use crate::bindings::ui;
2
+ use crate::event;
3
+ use crate::node::{NodeHandle, NodeRef};
4
+ use std::cell::RefCell;
5
+
6
+ const BOUNDS_TOLERANCE: f32 = 0.5;
7
+
8
+ thread_local! {
9
+ static SCROLL_VIEWS: RefCell<Vec<NodeHandle>> = const { RefCell::new(Vec::new()) };
10
+ static SELECTED_SCROLL_VIEW: RefCell<Option<NodeHandle>> = const { RefCell::new(None) };
11
+ static SELECTED_BRANCH_ROOT: RefCell<Option<NodeHandle>> = const { RefCell::new(None) };
12
+ }
13
+
14
+ fn get_bounds(handle: NodeHandle) -> Option<[f32; 4]> {
15
+ ui::get_bounds(handle.raw())
16
+ }
17
+
18
+ fn is_visible(bounds: [f32; 4]) -> bool {
19
+ let viewport_width = ui::get_viewport_width();
20
+ let viewport_height = ui::get_viewport_height();
21
+ bounds[0] + bounds[2] > 0.0
22
+ && bounds[1] + bounds[3] > 0.0
23
+ && bounds[0] < viewport_width
24
+ && bounds[1] < viewport_height
25
+ }
26
+
27
+ fn get_usable_visible_bounds(handle: NodeHandle) -> Option<[f32; 4]> {
28
+ let bounds = get_bounds(handle)?;
29
+ if bounds[2] <= BOUNDS_TOLERANCE || bounds[3] <= BOUNDS_TOLERANCE || !is_visible(bounds) {
30
+ return None;
31
+ }
32
+ Some(bounds)
33
+ }
34
+
35
+ fn contains_point(bounds: [f32; 4], x: f32, y: f32) -> bool {
36
+ x >= bounds[0] && x <= bounds[0] + bounds[2] && y >= bounds[1] && y <= bounds[1] + bounds[3]
37
+ }
38
+
39
+ fn area(bounds: [f32; 4]) -> f32 {
40
+ bounds[2] * bounds[3]
41
+ }
42
+
43
+ fn distance_squared(bounds: [f32; 4], x: f32, y: f32) -> f32 {
44
+ let delta_x = if x < bounds[0] {
45
+ bounds[0] - x
46
+ } else {
47
+ let right = bounds[0] + bounds[2];
48
+ if x > right {
49
+ x - right
50
+ } else {
51
+ 0.0
52
+ }
53
+ };
54
+ let delta_y = if y < bounds[1] {
55
+ bounds[1] - y
56
+ } else {
57
+ let bottom = bounds[1] + bounds[3];
58
+ if y > bottom {
59
+ y - bottom
60
+ } else {
61
+ 0.0
62
+ }
63
+ };
64
+ (delta_x * delta_x) + (delta_y * delta_y)
65
+ }
66
+
67
+ fn is_better_point_candidate(
68
+ candidate_bounds: [f32; 4],
69
+ candidate_contains_point: bool,
70
+ best_bounds: [f32; 4],
71
+ best_contains_point: bool,
72
+ point_x: f32,
73
+ point_y: f32,
74
+ ) -> bool {
75
+ if candidate_contains_point != best_contains_point {
76
+ return candidate_contains_point;
77
+ }
78
+ if candidate_contains_point {
79
+ let candidate_area = area(candidate_bounds);
80
+ let best_area = area(best_bounds);
81
+ if candidate_area + BOUNDS_TOLERANCE < best_area {
82
+ return true;
83
+ }
84
+ if best_area + BOUNDS_TOLERANCE < candidate_area {
85
+ return false;
86
+ }
87
+ } else {
88
+ let candidate_distance = distance_squared(candidate_bounds, point_x, point_y);
89
+ let best_distance = distance_squared(best_bounds, point_x, point_y);
90
+ if candidate_distance + BOUNDS_TOLERANCE < best_distance {
91
+ return true;
92
+ }
93
+ if best_distance + BOUNDS_TOLERANCE < candidate_distance {
94
+ return false;
95
+ }
96
+ }
97
+ if candidate_bounds[1] + BOUNDS_TOLERANCE < best_bounds[1] {
98
+ return true;
99
+ }
100
+ if best_bounds[1] + BOUNDS_TOLERANCE < candidate_bounds[1] {
101
+ return false;
102
+ }
103
+ candidate_bounds[0] < best_bounds[0]
104
+ }
105
+
106
+ fn is_better_default_candidate(candidate_bounds: [f32; 4], best_bounds: [f32; 4]) -> bool {
107
+ if candidate_bounds[1] + BOUNDS_TOLERANCE < best_bounds[1] {
108
+ return true;
109
+ }
110
+ if best_bounds[1] + BOUNDS_TOLERANCE < candidate_bounds[1] {
111
+ return false;
112
+ }
113
+ candidate_bounds[0] < best_bounds[0]
114
+ }
115
+
116
+ fn is_descendant_of(node: NodeRef, ancestor: NodeRef) -> bool {
117
+ let mut current = Some(node);
118
+ while let Some(node) = current {
119
+ if node.handle() == ancestor.handle() {
120
+ return true;
121
+ }
122
+ current = node.parent();
123
+ }
124
+ false
125
+ }
126
+
127
+ fn default_ordering_anchor(scroll_view: NodeRef) -> NodeRef {
128
+ let mut anchor = scroll_view.clone();
129
+ let mut cursor = scroll_view.parent();
130
+ while let Some(current) = cursor {
131
+ if current.is_scroll_view_for_routing() {
132
+ break;
133
+ }
134
+ anchor = current.clone();
135
+ cursor = current.parent();
136
+ }
137
+ anchor
138
+ }
139
+
140
+ fn append_unique_scroll_view(target: &mut Vec<NodeHandle>, handle: NodeHandle) {
141
+ if target.contains(&handle) || get_usable_visible_bounds(handle).is_none() {
142
+ return;
143
+ }
144
+ SCROLL_VIEWS.with(|scroll_views| {
145
+ if scroll_views.borrow().contains(&handle) {
146
+ target.push(handle);
147
+ }
148
+ });
149
+ }
150
+
151
+ fn append_default_ordered_candidate(target: &mut Vec<NodeHandle>, candidate: NodeHandle) {
152
+ let candidate_bounds = match get_usable_visible_bounds(candidate) {
153
+ Some(bounds) => bounds,
154
+ None => return,
155
+ };
156
+ let Some(candidate_node) = event::resolve_node(candidate) else {
157
+ return;
158
+ };
159
+ let candidate_anchor_bounds =
160
+ match get_usable_visible_bounds(default_ordering_anchor(candidate_node).handle()) {
161
+ Some(bounds) => bounds,
162
+ None => return,
163
+ };
164
+ let mut insert_index = target.len();
165
+ while insert_index > 0 {
166
+ let current = target[insert_index - 1];
167
+ let Some(current_node) = event::resolve_node(current) else {
168
+ insert_index -= 1;
169
+ continue;
170
+ };
171
+ let Some(current_anchor_bounds) =
172
+ get_usable_visible_bounds(default_ordering_anchor(current_node).handle())
173
+ else {
174
+ insert_index -= 1;
175
+ continue;
176
+ };
177
+ let Some(current_candidate_bounds) = get_usable_visible_bounds(current) else {
178
+ insert_index -= 1;
179
+ continue;
180
+ };
181
+ if is_better_default_candidate(candidate_anchor_bounds, current_anchor_bounds) {
182
+ insert_index -= 1;
183
+ continue;
184
+ }
185
+ if candidate_anchor_bounds == current_anchor_bounds
186
+ && is_better_default_candidate(candidate_bounds, current_candidate_bounds)
187
+ {
188
+ insert_index -= 1;
189
+ continue;
190
+ }
191
+ break;
192
+ }
193
+ target.push(candidate);
194
+ for cursor in (insert_index + 1..target.len()).rev() {
195
+ target[cursor] = target[cursor - 1];
196
+ }
197
+ target[insert_index] = candidate;
198
+ }
199
+
200
+ fn select_default_candidates_within(root: Option<NodeRef>) -> Vec<NodeHandle> {
201
+ let mut ordered = Vec::new();
202
+ let Some(root) = root else {
203
+ return ordered;
204
+ };
205
+ SCROLL_VIEWS.with(|scroll_views| {
206
+ for handle in scroll_views.borrow().iter().copied() {
207
+ let Some(candidate) = event::resolve_node(handle) else {
208
+ continue;
209
+ };
210
+ if !is_descendant_of(candidate, root.clone()) {
211
+ continue;
212
+ }
213
+ append_default_ordered_candidate(&mut ordered, handle);
214
+ }
215
+ });
216
+ ordered
217
+ }
218
+
219
+ fn find_nearest_descendant_scroll_branch(node: Option<NodeRef>) -> Option<NodeHandle> {
220
+ let mut current = node;
221
+ while let Some(node) = current {
222
+ if node.is_scroll_view_for_routing() {
223
+ return None;
224
+ }
225
+ if !select_default_candidates_within(Some(node.clone())).is_empty() {
226
+ return Some(node.handle());
227
+ }
228
+ current = node.parent();
229
+ }
230
+ None
231
+ }
232
+
233
+ fn select_scroll_view_by_point(point_x: f32, point_y: f32) -> Option<NodeHandle> {
234
+ let mut best_view = None;
235
+ let mut best_bounds = None;
236
+ let mut best_contains_point = false;
237
+ SCROLL_VIEWS.with(|scroll_views| {
238
+ for handle in scroll_views.borrow().iter().copied() {
239
+ let Some(candidate_bounds) = get_usable_visible_bounds(handle) else {
240
+ continue;
241
+ };
242
+ let candidate_contains_point = contains_point(candidate_bounds, point_x, point_y);
243
+ if best_bounds.is_none()
244
+ || is_better_point_candidate(
245
+ candidate_bounds,
246
+ candidate_contains_point,
247
+ best_bounds.unwrap_or([0.0; 4]),
248
+ best_contains_point,
249
+ point_x,
250
+ point_y,
251
+ )
252
+ {
253
+ best_view = Some(handle);
254
+ best_bounds = Some(candidate_bounds);
255
+ best_contains_point = candidate_contains_point;
256
+ }
257
+ }
258
+ });
259
+ best_view
260
+ }
261
+
262
+ fn resolve_ancestor_scroll_view(node: Option<NodeRef>) -> Option<NodeHandle> {
263
+ let mut current = node;
264
+ while let Some(node) = current {
265
+ if node.is_scroll_view_for_routing() {
266
+ return Some(node.handle());
267
+ }
268
+ current = node.parent();
269
+ }
270
+ None
271
+ }
272
+
273
+ fn append_ancestor_scroll_view_fallbacks(view: Option<NodeHandle>, target: &mut Vec<NodeHandle>) {
274
+ let Some(view) = view else {
275
+ return;
276
+ };
277
+ let Some(view_node) = event::resolve_node(view) else {
278
+ return;
279
+ };
280
+ let mut current = view_node.parent();
281
+ while let Some(node) = current {
282
+ if node.is_scroll_view_for_routing() {
283
+ append_unique_scroll_view(target, node.handle());
284
+ }
285
+ current = node.parent();
286
+ }
287
+ }
288
+
289
+ fn select_default_candidates() -> Vec<NodeHandle> {
290
+ let mut ordered = Vec::new();
291
+ SCROLL_VIEWS.with(|scroll_views| {
292
+ for handle in scroll_views.borrow().iter().copied() {
293
+ append_default_ordered_candidate(&mut ordered, handle);
294
+ }
295
+ });
296
+ ordered
297
+ }
298
+
299
+ pub(crate) fn register_keyboard_scroll_node(node: &NodeRef) {
300
+ if !node.is_scroll_view_for_routing() {
301
+ return;
302
+ }
303
+ SCROLL_VIEWS.with(|scroll_views| {
304
+ let mut scroll_views = scroll_views.borrow_mut();
305
+ if !scroll_views.contains(&node.handle()) {
306
+ scroll_views.push(node.handle());
307
+ }
308
+ });
309
+ }
310
+
311
+ pub(crate) fn unregister_keyboard_scroll_node(handle: NodeHandle) {
312
+ SCROLL_VIEWS.with(|scroll_views| {
313
+ scroll_views
314
+ .borrow_mut()
315
+ .retain(|candidate| *candidate != handle);
316
+ });
317
+ SELECTED_SCROLL_VIEW.with(|selected| {
318
+ if *selected.borrow() == Some(handle) {
319
+ selected.replace(None);
320
+ }
321
+ });
322
+ }
323
+
324
+ pub(crate) fn track_keyboard_scroll_pointer_up(target_node: Option<NodeRef>, x: f32, y: f32) {
325
+ let selected_branch_root = target_node.clone().and_then(|node| {
326
+ if node.is_scroll_view_for_routing() {
327
+ Some(node.handle())
328
+ } else {
329
+ find_nearest_descendant_scroll_branch(Some(node))
330
+ }
331
+ });
332
+ SELECTED_BRANCH_ROOT.with(|branch| branch.replace(selected_branch_root));
333
+
334
+ if let Some(ancestor_scroll_view) = resolve_ancestor_scroll_view(target_node.clone()) {
335
+ SELECTED_BRANCH_ROOT.with(|branch| {
336
+ if branch.borrow().is_none() {
337
+ branch.replace(Some(ancestor_scroll_view));
338
+ }
339
+ });
340
+ SELECTED_SCROLL_VIEW.with(|selected| selected.replace(Some(ancestor_scroll_view)));
341
+ return;
342
+ }
343
+
344
+ if let Some(branch_root) = SELECTED_BRANCH_ROOT.with(|branch| *branch.borrow()) {
345
+ let branch_candidates = select_default_candidates_within(event::resolve_node(branch_root));
346
+ if let Some(candidate) = branch_candidates.first().copied() {
347
+ SELECTED_SCROLL_VIEW.with(|selected| selected.replace(Some(candidate)));
348
+ return;
349
+ }
350
+ }
351
+
352
+ let selected = select_scroll_view_by_point(x, y);
353
+ SELECTED_SCROLL_VIEW.with(|slot| slot.replace(selected));
354
+ if selected.is_none() {
355
+ SELECTED_BRANCH_ROOT.with(|branch| branch.replace(None));
356
+ }
357
+ }
358
+
359
+ pub(crate) fn get_keyboard_scroll_selected_candidate() -> Option<NodeHandle> {
360
+ SELECTED_SCROLL_VIEW.with(|selected| *selected.borrow())
361
+ }
362
+
363
+ pub(crate) fn get_keyboard_scroll_fallback_candidates() -> Vec<NodeHandle> {
364
+ let mut ordered = Vec::new();
365
+ append_ancestor_scroll_view_fallbacks(get_keyboard_scroll_selected_candidate(), &mut ordered);
366
+ if let Some(branch_root) = SELECTED_BRANCH_ROOT.with(|branch| *branch.borrow()) {
367
+ for candidate in select_default_candidates_within(event::resolve_node(branch_root)) {
368
+ if Some(candidate) != get_keyboard_scroll_selected_candidate() {
369
+ append_unique_scroll_view(&mut ordered, candidate);
370
+ }
371
+ }
372
+ }
373
+ for candidate in select_default_candidates() {
374
+ if Some(candidate) != get_keyboard_scroll_selected_candidate() {
375
+ append_unique_scroll_view(&mut ordered, candidate);
376
+ }
377
+ }
378
+ ordered
379
+ }
380
+
381
+ pub(crate) fn reset_keyboard_scroll_tracking() {
382
+ SCROLL_VIEWS.with(|scroll_views| scroll_views.borrow_mut().clear());
383
+ SELECTED_SCROLL_VIEW.with(|selected| selected.replace(None));
384
+ SELECTED_BRANCH_ROOT.with(|branch| branch.replace(None));
385
+ }