@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,424 @@
1
+ use crate::ffi::CursorStyle;
2
+ use crate::ffi::PointerEventType;
3
+ use crate::node::NodeRef;
4
+ use std::cell::RefCell;
5
+ use std::collections::HashMap;
6
+ use std::rc::Rc;
7
+
8
+ const DRAG_DROP_TEXT_FORMAT: &str = "text/plain";
9
+
10
+ #[repr(u32)]
11
+ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12
+ pub enum DragDropEffects {
13
+ #[default]
14
+ None = 0,
15
+ Copy = 1,
16
+ Move = 2,
17
+ Link = 4,
18
+ }
19
+
20
+ fn normalize_effect(candidate: DragDropEffects, allowed: DragDropEffects) -> DragDropEffects {
21
+ let masked = (candidate as u32) & (allowed as u32);
22
+ if masked == DragDropEffects::None as u32 {
23
+ return DragDropEffects::None;
24
+ }
25
+ if (masked & DragDropEffects::Move as u32) != 0 {
26
+ return DragDropEffects::Move;
27
+ }
28
+ if (masked & DragDropEffects::Copy as u32) != 0 {
29
+ return DragDropEffects::Copy;
30
+ }
31
+ if (masked & DragDropEffects::Link as u32) != 0 {
32
+ return DragDropEffects::Link;
33
+ }
34
+ DragDropEffects::None
35
+ }
36
+
37
+ #[derive(Clone, Debug, Default, PartialEq, Eq)]
38
+ pub struct DragDataObject {
39
+ formats: HashMap<String, String>,
40
+ }
41
+
42
+ impl DragDataObject {
43
+ pub fn new() -> Self {
44
+ Self::default()
45
+ }
46
+
47
+ pub fn set_text(mut self, value: impl Into<String>) -> Self {
48
+ self.formats
49
+ .insert(String::from(DRAG_DROP_TEXT_FORMAT), value.into());
50
+ self
51
+ }
52
+
53
+ pub fn set_format(mut self, format: impl Into<String>, value: impl Into<String>) -> Self {
54
+ self.formats.insert(format.into(), value.into());
55
+ self
56
+ }
57
+
58
+ pub fn has_format(&self, format: &str) -> bool {
59
+ self.formats.contains_key(format)
60
+ }
61
+
62
+ pub fn get_text(&self) -> Option<String> {
63
+ self.get_format(DRAG_DROP_TEXT_FORMAT)
64
+ }
65
+
66
+ pub fn get_format(&self, format: &str) -> Option<String> {
67
+ self.formats.get(format).cloned()
68
+ }
69
+ }
70
+
71
+ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
72
+ pub struct DragCompletedEventArgs {
73
+ pub effect: DragDropEffects,
74
+ }
75
+
76
+ #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
77
+ pub struct DropProposal {
78
+ pub effect: DragDropEffects,
79
+ pub show_insertion_marker: bool,
80
+ }
81
+
82
+ impl DropProposal {
83
+ pub fn new(effect: DragDropEffects, show_insertion_marker: bool) -> Self {
84
+ Self {
85
+ effect,
86
+ show_insertion_marker,
87
+ }
88
+ }
89
+
90
+ pub fn none() -> Self {
91
+ Self::default()
92
+ }
93
+ }
94
+
95
+ type DragCompletedCallback = Rc<dyn Fn(DragCompletedEventArgs)>;
96
+
97
+ struct DragSessionState {
98
+ source: NodeRef,
99
+ current_effect: DragDropEffects,
100
+ active: bool,
101
+ completed_callback: Option<DragCompletedCallback>,
102
+ }
103
+
104
+ #[derive(Clone)]
105
+ pub struct DragSession {
106
+ pub data: DragDataObject,
107
+ pub allowed_effects: DragDropEffects,
108
+ inner: Rc<RefCell<DragSessionState>>,
109
+ }
110
+
111
+ impl DragSession {
112
+ pub(crate) fn new(
113
+ source: NodeRef,
114
+ data: DragDataObject,
115
+ allowed_effects: DragDropEffects,
116
+ ) -> Self {
117
+ Self {
118
+ data,
119
+ allowed_effects,
120
+ inner: Rc::new(RefCell::new(DragSessionState {
121
+ source,
122
+ current_effect: DragDropEffects::None,
123
+ active: true,
124
+ completed_callback: None,
125
+ })),
126
+ }
127
+ }
128
+
129
+ pub fn current_effect(&self) -> DragDropEffects {
130
+ self.inner.borrow().current_effect
131
+ }
132
+
133
+ pub fn is_active(&self) -> bool {
134
+ self.inner.borrow().active
135
+ }
136
+
137
+ pub fn on_completed(&self, callback: impl Fn(DragCompletedEventArgs) + 'static) -> &Self {
138
+ self.inner.borrow_mut().completed_callback = Some(Rc::new(callback));
139
+ self
140
+ }
141
+
142
+ pub fn cancel(&self) {
143
+ if !self.is_active() {
144
+ return;
145
+ }
146
+ crate::event::cancel_drag_session(self.clone());
147
+ }
148
+
149
+ pub(crate) fn source(&self) -> NodeRef {
150
+ self.inner.borrow().source.clone()
151
+ }
152
+
153
+ pub(crate) fn set_current_effect(&self, effect: DragDropEffects) {
154
+ self.inner.borrow_mut().current_effect = effect;
155
+ }
156
+
157
+ pub(crate) fn complete(&self, effect: DragDropEffects) {
158
+ let callback = {
159
+ let mut state = self.inner.borrow_mut();
160
+ if !state.active {
161
+ return;
162
+ }
163
+ state.active = false;
164
+ state.current_effect = effect;
165
+ state.completed_callback.clone()
166
+ };
167
+ if let Some(callback) = callback {
168
+ callback(DragCompletedEventArgs { effect });
169
+ }
170
+ }
171
+ }
172
+
173
+ #[derive(Clone)]
174
+ pub struct DragEventArgs {
175
+ pub session: DragSession,
176
+ pub x: f32,
177
+ pub y: f32,
178
+ pub modifiers: u32,
179
+ }
180
+
181
+ impl DragEventArgs {
182
+ pub fn new(session: DragSession, x: f32, y: f32, modifiers: u32) -> Self {
183
+ Self {
184
+ session,
185
+ x,
186
+ y,
187
+ modifiers,
188
+ }
189
+ }
190
+ }
191
+
192
+ #[derive(Default)]
193
+ struct DragDropState {
194
+ active_session: Option<DragSession>,
195
+ active_target: Option<NodeRef>,
196
+ }
197
+
198
+ thread_local! {
199
+ static STATE: RefCell<DragDropState> = RefCell::new(DragDropState::default());
200
+ }
201
+
202
+ fn is_default_proposal(proposal: DropProposal) -> bool {
203
+ proposal.effect == DragDropEffects::None && !proposal.show_insertion_marker
204
+ }
205
+
206
+ fn with_state<T>(callback: impl FnOnce(&mut DragDropState) -> T) -> T {
207
+ STATE.with(|slot| callback(&mut slot.borrow_mut()))
208
+ }
209
+
210
+ pub(crate) fn cursor_override_style() -> CursorStyle {
211
+ STATE.with(|slot| {
212
+ let state = slot.borrow();
213
+ let Some(session) = state.active_session.as_ref() else {
214
+ return CursorStyle::Default;
215
+ };
216
+ if !session.is_active() {
217
+ return CursorStyle::Default;
218
+ }
219
+ if session.current_effect() == DragDropEffects::None {
220
+ CursorStyle::Grabbing
221
+ } else {
222
+ CursorStyle::Move
223
+ }
224
+ })
225
+ }
226
+
227
+ pub(crate) fn begin_session(source: NodeRef) -> bool {
228
+ let existing = STATE.with(|slot| slot.borrow().active_session.clone());
229
+ if let Some(existing) = existing {
230
+ finish_session(existing, DragDropEffects::None, 0.0, 0.0, 0, true);
231
+ }
232
+ if !source.has_drag_source() {
233
+ return false;
234
+ }
235
+ let Some(data) = source.create_drag_data_object() else {
236
+ return false;
237
+ };
238
+ let allowed = source.get_drag_allowed_effects();
239
+ if allowed == DragDropEffects::None {
240
+ return false;
241
+ }
242
+ with_state(|state| {
243
+ state.active_target = None;
244
+ state.active_session = Some(DragSession::new(source, data, allowed));
245
+ });
246
+ true
247
+ }
248
+
249
+ pub(crate) fn cancel_session(session: DragSession) {
250
+ let is_active = STATE.with(|slot| {
251
+ slot.borrow()
252
+ .active_session
253
+ .as_ref()
254
+ .map(|active| Rc::ptr_eq(&active.inner, &session.inner))
255
+ .unwrap_or(false)
256
+ });
257
+ if !is_active {
258
+ return;
259
+ }
260
+ finish_session(session, DragDropEffects::None, 0.0, 0.0, 0, true);
261
+ }
262
+
263
+ pub(crate) fn cancel_session_for_source(source: &NodeRef) {
264
+ let active = STATE.with(|slot| slot.borrow().active_session.clone());
265
+ let Some(session) = active else {
266
+ return;
267
+ };
268
+ if !session.source().ptr_eq(source) {
269
+ return;
270
+ }
271
+ finish_session(session, DragDropEffects::None, 0.0, 0.0, 0, true);
272
+ }
273
+
274
+ pub(crate) fn handle_node_destroyed(node: NodeRef) {
275
+ let active = STATE.with(|slot| {
276
+ let state = slot.borrow();
277
+ (state.active_session.clone(), state.active_target.clone())
278
+ });
279
+ if let Some(session) = active.0 {
280
+ if session.source().ptr_eq(&node) {
281
+ finish_session(session, DragDropEffects::None, 0.0, 0.0, 0, true);
282
+ return;
283
+ }
284
+ }
285
+ if let Some(target) = active.1 {
286
+ if target.ptr_eq(&node) {
287
+ with_state(|state| {
288
+ state.active_target = None;
289
+ if let Some(session) = state.active_session.as_ref() {
290
+ session.set_current_effect(DragDropEffects::None);
291
+ }
292
+ });
293
+ }
294
+ }
295
+ }
296
+
297
+ pub(crate) fn handle_pointer_event(
298
+ pointed_node: Option<NodeRef>,
299
+ event_type: PointerEventType,
300
+ x: f32,
301
+ y: f32,
302
+ modifiers: u32,
303
+ ) {
304
+ let active = STATE.with(|slot| slot.borrow().active_session.clone());
305
+ let Some(session) = active else {
306
+ return;
307
+ };
308
+ if !session.is_active() {
309
+ return;
310
+ }
311
+ match event_type {
312
+ PointerEventType::Down
313
+ | PointerEventType::Enter
314
+ | PointerEventType::Move
315
+ | PointerEventType::Leave => {
316
+ update_target(pointed_node, session, x, y, modifiers);
317
+ }
318
+ PointerEventType::Up => {
319
+ let effect = update_target(pointed_node, session.clone(), x, y, modifiers);
320
+ let target = STATE.with(|slot| slot.borrow().active_target.clone());
321
+ if let Some(target) = target {
322
+ if effect != DragDropEffects::None {
323
+ target.handle_drop_event(DragEventArgs::new(session.clone(), x, y, modifiers));
324
+ }
325
+ }
326
+ finish_session(session, effect, x, y, modifiers, true);
327
+ }
328
+ PointerEventType::Cancel => {}
329
+ }
330
+ }
331
+
332
+ pub(crate) fn reset() {
333
+ with_state(|state| {
334
+ state.active_session = None;
335
+ state.active_target = None;
336
+ });
337
+ }
338
+
339
+ fn update_target(
340
+ pointed_node: Option<NodeRef>,
341
+ session: DragSession,
342
+ x: f32,
343
+ y: f32,
344
+ modifiers: u32,
345
+ ) -> DragDropEffects {
346
+ let target = resolve_drop_target(pointed_node);
347
+ let args = DragEventArgs::new(session.clone(), x, y, modifiers);
348
+ let mut proposal = DropProposal::none();
349
+ let previous_target = STATE.with(|slot| slot.borrow().active_target.clone());
350
+ let target_changed = match (&target, &previous_target) {
351
+ (Some(left), Some(right)) => !left.ptr_eq(right),
352
+ (None, None) => false,
353
+ _ => true,
354
+ };
355
+ if target_changed {
356
+ if let Some(previous_target) = previous_target {
357
+ previous_target.handle_drag_leave(args.clone());
358
+ }
359
+ with_state(|state| {
360
+ state.active_target = target.clone();
361
+ });
362
+ if let Some(target) = target.as_ref() {
363
+ if target.has_drag_enter_handler() {
364
+ proposal = target.handle_drag_enter(args.clone());
365
+ }
366
+ }
367
+ }
368
+ let Some(target) = target else {
369
+ session.set_current_effect(DragDropEffects::None);
370
+ return DragDropEffects::None;
371
+ };
372
+ if target.has_drag_over_handler() {
373
+ proposal = target.handle_drag_over(args);
374
+ } else if !target_changed && is_default_proposal(proposal) {
375
+ return session.current_effect();
376
+ }
377
+ let effect = normalize_effect(proposal.effect, session.allowed_effects);
378
+ session.set_current_effect(effect);
379
+ effect
380
+ }
381
+
382
+ fn resolve_drop_target(pointed_node: Option<NodeRef>) -> Option<NodeRef> {
383
+ let mut current = pointed_node;
384
+ while let Some(node) = current {
385
+ if node.allows_drop() {
386
+ return Some(node);
387
+ }
388
+ current = node.parent();
389
+ }
390
+ None
391
+ }
392
+
393
+ fn finish_session(
394
+ session: DragSession,
395
+ effect: DragDropEffects,
396
+ x: f32,
397
+ y: f32,
398
+ modifiers: u32,
399
+ notify_target_leave: bool,
400
+ ) {
401
+ let target = with_state(|state| {
402
+ let Some(active) = state.active_session.as_ref() else {
403
+ return None;
404
+ };
405
+ if !Rc::ptr_eq(&active.inner, &session.inner) {
406
+ return None;
407
+ }
408
+ let target = state.active_target.clone();
409
+ state.active_session = None;
410
+ state.active_target = None;
411
+ Some(target)
412
+ });
413
+ let Some(target) = target else {
414
+ return;
415
+ };
416
+ session.set_current_effect(effect);
417
+ if notify_target_leave {
418
+ if let Some(target) = target {
419
+ target.handle_drag_leave(DragEventArgs::new(session.clone(), x, y, modifiers));
420
+ }
421
+ }
422
+ session.complete(effect);
423
+ session.source().notify_drag_completed(effect);
424
+ }
@@ -0,0 +1,258 @@
1
+ use crate::event;
2
+ use crate::node::{NodeHandle, NodeRef, WeakNodeRef};
3
+
4
+ const DEFAULT_DRAG_THRESHOLD: f32 = 4.0;
5
+
6
+ #[allow(dead_code)]
7
+ #[derive(Clone, Copy, Debug, Default)]
8
+ pub(crate) struct DragStartedEvent {
9
+ pub(crate) x: f32,
10
+ pub(crate) y: f32,
11
+ pub(crate) modifiers: u32,
12
+ }
13
+
14
+ #[allow(dead_code)]
15
+ #[derive(Clone, Copy, Debug, Default)]
16
+ pub(crate) struct DragCompletedEvent {
17
+ pub(crate) x: f32,
18
+ pub(crate) y: f32,
19
+ pub(crate) total_delta_x: f32,
20
+ pub(crate) total_delta_y: f32,
21
+ pub(crate) modifiers: u32,
22
+ pub(crate) cancelled: bool,
23
+ }
24
+
25
+ type DragStartedCallback = Box<dyn Fn(DragStartedEvent)>;
26
+ type DragCompletedCallback = Box<dyn Fn(DragCompletedEvent)>;
27
+
28
+ pub(crate) struct DragGesture {
29
+ host: WeakNodeRef,
30
+ threshold_value: f32,
31
+ pointer_down_value: bool,
32
+ drag_started_value: bool,
33
+ awaiting_long_press_value: bool,
34
+ pointer_captured_value: bool,
35
+ start_x: f32,
36
+ start_y: f32,
37
+ last_pointer_x: f32,
38
+ last_pointer_y: f32,
39
+ last_dispatched_x: f32,
40
+ last_dispatched_y: f32,
41
+ last_modifiers: u32,
42
+ started_callback: Option<DragStartedCallback>,
43
+ completed_callback: Option<DragCompletedCallback>,
44
+ }
45
+
46
+ impl DragGesture {
47
+ pub(crate) fn new(host: &NodeRef) -> Self {
48
+ Self {
49
+ host: host.downgrade(),
50
+ threshold_value: DEFAULT_DRAG_THRESHOLD,
51
+ pointer_down_value: false,
52
+ drag_started_value: false,
53
+ awaiting_long_press_value: false,
54
+ pointer_captured_value: false,
55
+ start_x: 0.0,
56
+ start_y: 0.0,
57
+ last_pointer_x: 0.0,
58
+ last_pointer_y: 0.0,
59
+ last_dispatched_x: 0.0,
60
+ last_dispatched_y: 0.0,
61
+ last_modifiers: 0,
62
+ started_callback: None,
63
+ completed_callback: None,
64
+ }
65
+ }
66
+
67
+ pub(crate) fn threshold(&mut self, value: f32) -> &mut Self {
68
+ self.threshold_value = if value > 0.0 { value } else { 0.0 };
69
+ self
70
+ }
71
+
72
+ pub(crate) fn set_started(&mut self, callback: impl Fn(DragStartedEvent) + 'static) {
73
+ self.started_callback = Some(Box::new(callback));
74
+ }
75
+
76
+ pub(crate) fn set_completed(&mut self, callback: impl Fn(DragCompletedEvent) + 'static) {
77
+ self.completed_callback = Some(Box::new(callback));
78
+ }
79
+
80
+ pub(crate) fn is_dragging(&self) -> bool {
81
+ self.drag_started_value
82
+ }
83
+
84
+ pub(crate) fn handle_pointer_down(
85
+ &mut self,
86
+ x: f32,
87
+ y: f32,
88
+ modifiers: u32,
89
+ wait_for_long_press: bool,
90
+ ) {
91
+ if self.pointer_down_value {
92
+ self.cancel();
93
+ }
94
+ self.pointer_down_value = true;
95
+ self.drag_started_value = false;
96
+ self.awaiting_long_press_value = wait_for_long_press;
97
+ self.start_x = x;
98
+ self.start_y = y;
99
+ self.last_pointer_x = x;
100
+ self.last_pointer_y = y;
101
+ self.last_dispatched_x = x;
102
+ self.last_dispatched_y = y;
103
+ self.last_modifiers = modifiers;
104
+ if !wait_for_long_press {
105
+ self.capture_drag_pointer();
106
+ }
107
+ if !wait_for_long_press && self.threshold_value <= 0.0 {
108
+ self.begin_drag(x, y, modifiers);
109
+ }
110
+ }
111
+
112
+ pub(crate) fn handle_pointer_move(&mut self, x: f32, y: f32, modifiers: u32) {
113
+ if !self.pointer_down_value {
114
+ return;
115
+ }
116
+ self.last_pointer_x = x;
117
+ self.last_pointer_y = y;
118
+ self.last_modifiers = modifiers;
119
+ if self.awaiting_long_press_value {
120
+ return;
121
+ }
122
+ if !self.drag_started_value
123
+ && !self.has_exceeded_threshold(x - self.start_x, y - self.start_y)
124
+ {
125
+ return;
126
+ }
127
+ if !self.drag_started_value {
128
+ self.begin_drag(x, y, modifiers);
129
+ }
130
+ self.emit_delta(x, y);
131
+ }
132
+
133
+ pub(crate) fn handle_long_press(&mut self, x: f32, y: f32, modifiers: u32) -> bool {
134
+ if !self.pointer_down_value {
135
+ self.pointer_down_value = true;
136
+ self.drag_started_value = false;
137
+ self.start_x = x;
138
+ self.start_y = y;
139
+ self.last_dispatched_x = x;
140
+ self.last_dispatched_y = y;
141
+ }
142
+ self.awaiting_long_press_value = false;
143
+ self.last_pointer_x = x;
144
+ self.last_pointer_y = y;
145
+ self.last_modifiers = modifiers;
146
+ self.capture_drag_pointer();
147
+ self.begin_drag(x, y, modifiers);
148
+ true
149
+ }
150
+
151
+ pub(crate) fn handle_pointer_up(&mut self, x: f32, y: f32, modifiers: u32) {
152
+ if !self.pointer_down_value {
153
+ return;
154
+ }
155
+ self.last_pointer_x = x;
156
+ self.last_pointer_y = y;
157
+ self.last_modifiers = modifiers;
158
+ if self.drag_started_value {
159
+ self.emit_delta(x, y);
160
+ if let Some(callback) = self.completed_callback.as_ref() {
161
+ callback(DragCompletedEvent {
162
+ x,
163
+ y,
164
+ total_delta_x: x - self.start_x,
165
+ total_delta_y: y - self.start_y,
166
+ modifiers,
167
+ cancelled: false,
168
+ });
169
+ }
170
+ }
171
+ self.pointer_down_value = false;
172
+ self.drag_started_value = false;
173
+ self.awaiting_long_press_value = false;
174
+ self.release_drag_pointer();
175
+ }
176
+
177
+ pub(crate) fn cancel(&mut self) {
178
+ if !self.pointer_down_value {
179
+ return;
180
+ }
181
+ if self.drag_started_value {
182
+ if let Some(callback) = self.completed_callback.as_ref() {
183
+ callback(DragCompletedEvent {
184
+ x: self.last_pointer_x,
185
+ y: self.last_pointer_y,
186
+ total_delta_x: self.last_pointer_x - self.start_x,
187
+ total_delta_y: self.last_pointer_y - self.start_y,
188
+ modifiers: self.last_modifiers,
189
+ cancelled: true,
190
+ });
191
+ }
192
+ }
193
+ self.pointer_down_value = false;
194
+ self.drag_started_value = false;
195
+ self.awaiting_long_press_value = false;
196
+ self.release_drag_pointer();
197
+ }
198
+
199
+ fn begin_drag(&mut self, x: f32, y: f32, modifiers: u32) {
200
+ if self.drag_started_value {
201
+ return;
202
+ }
203
+ self.drag_started_value = true;
204
+ self.last_dispatched_x = self.start_x;
205
+ self.last_dispatched_y = self.start_y;
206
+ if let Some(callback) = self.started_callback.as_ref() {
207
+ callback(DragStartedEvent { x, y, modifiers });
208
+ }
209
+ }
210
+
211
+ fn emit_delta(&mut self, x: f32, y: f32) {
212
+ if x == self.last_dispatched_x && y == self.last_dispatched_y {
213
+ return;
214
+ }
215
+ self.last_dispatched_x = x;
216
+ self.last_dispatched_y = y;
217
+ }
218
+
219
+ fn has_exceeded_threshold(&self, total_delta_x: f32, total_delta_y: f32) -> bool {
220
+ if self.threshold_value <= 0.0 {
221
+ return true;
222
+ }
223
+ ((total_delta_x * total_delta_x) + (total_delta_y * total_delta_y))
224
+ >= (self.threshold_value * self.threshold_value)
225
+ }
226
+
227
+ fn capture_drag_pointer(&mut self) {
228
+ if self.pointer_captured_value {
229
+ return;
230
+ }
231
+ let Some(host) = self.host.upgrade() else {
232
+ return;
233
+ };
234
+ let handle = host.handle();
235
+ if handle == NodeHandle::INVALID {
236
+ return;
237
+ }
238
+ self.pointer_captured_value = true;
239
+ event::capture_pointer(handle);
240
+ unsafe { crate::ffi::fui_set_pointer_capture(handle.raw()) };
241
+ }
242
+
243
+ fn release_drag_pointer(&mut self) {
244
+ if !self.pointer_captured_value {
245
+ return;
246
+ }
247
+ self.pointer_captured_value = false;
248
+ let Some(host) = self.host.upgrade() else {
249
+ return;
250
+ };
251
+ let handle = host.handle();
252
+ if handle == NodeHandle::INVALID {
253
+ return;
254
+ }
255
+ event::release_pointer(handle);
256
+ unsafe { crate::ffi::fui_release_pointer_capture() };
257
+ }
258
+ }