@effindomv2/fui-rs 0.1.22 → 0.1.24

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 (46) hide show
  1. package/Cargo.toml +1 -1
  2. package/README.md +12 -0
  3. package/package.json +1 -1
  4. package/src/controls/anti_selection_area.rs +0 -18
  5. package/src/controls/button.rs +14 -42
  6. package/src/controls/checkbox.rs +7 -0
  7. package/src/controls/combobox.rs +7 -6
  8. package/src/controls/context_menu.rs +38 -5
  9. package/src/controls/dialog.rs +41 -3
  10. package/src/controls/dropdown.rs +2 -2
  11. package/src/controls/form.rs +16 -20
  12. package/src/controls/internal/button_presenter.rs +5 -5
  13. package/src/controls/internal/checkbox_indicator_presenter.rs +1 -1
  14. package/src/controls/internal/pressable_labeled_control.rs +21 -7
  15. package/src/controls/internal/selectable_popup_list.rs +2 -2
  16. package/src/controls/internal/text_input_core.rs +38 -68
  17. package/src/controls/internal/text_input_presenter.rs +4 -4
  18. package/src/controls/mod.rs +11 -4
  19. package/src/controls/popup.rs +35 -20
  20. package/src/controls/radio_button.rs +7 -0
  21. package/src/controls/radio_group.rs +17 -0
  22. package/src/controls/selection_area.rs +0 -18
  23. package/src/controls/shared.rs +2 -17
  24. package/src/controls/switch.rs +7 -0
  25. package/src/controls/tests.rs +230 -6
  26. package/src/controls/text_area.rs +28 -142
  27. package/src/controls/text_editor_surface.rs +167 -0
  28. package/src/controls/text_input.rs +30 -144
  29. package/src/lib.rs +26 -20
  30. package/src/mobile_text_selection_toolbar.rs +2 -2
  31. package/src/node/core.rs +451 -60
  32. package/src/node/custom_drawable.rs +20 -0
  33. package/src/node/flex_box.rs +39 -47
  34. package/src/node/grid.rs +43 -128
  35. package/src/node/helpers.rs +34 -43
  36. package/src/node/image.rs +47 -56
  37. package/src/node/mod.rs +398 -73
  38. package/src/node/scroll_bar.rs +6 -0
  39. package/src/node/scroll_box.rs +8 -21
  40. package/src/node/scroll_view.rs +144 -13
  41. package/src/node/svg_node.rs +47 -66
  42. package/src/node/text_node.rs +358 -79
  43. package/src/node/virtual_list.rs +17 -0
  44. package/src/popup_presenter.rs +31 -0
  45. package/src/text.rs +59 -0
  46. package/src/text_indices.rs +48 -0
package/src/node/core.rs CHANGED
@@ -84,6 +84,8 @@ impl NodeKind {
84
84
  #[derive(Clone, Default)]
85
85
  pub(crate) struct EventHandlers {
86
86
  pub(crate) pointer_click: Option<PointerCallback>,
87
+ pub(crate) pointer_double_click: Option<PointerCallback>,
88
+ pub(crate) pointer_triple_click: Option<PointerCallback>,
87
89
  pub(crate) pointer_down: Option<PointerCallback>,
88
90
  pub(crate) pointer_move: Option<PointerCallback>,
89
91
  pub(crate) pointer_up: Option<PointerCallback>,
@@ -114,6 +116,29 @@ impl EventHandlers {
114
116
  ..Self::default()
115
117
  }
116
118
  }
119
+
120
+ fn has_pointer_click_handler(&self) -> bool {
121
+ self.pointer_click.is_some()
122
+ || self.pointer_double_click.is_some()
123
+ || self.pointer_triple_click.is_some()
124
+ }
125
+
126
+ fn dispatch_pointer_click(&self, event: &mut PointerEventArgs, click_count: i32) {
127
+ let click_count = click_count.max(1);
128
+ event.click_count = click_count;
129
+ if let Some(handler) = self.pointer_click.as_ref() {
130
+ handler(event);
131
+ }
132
+ if click_count == 2 {
133
+ if let Some(handler) = self.pointer_double_click.as_ref() {
134
+ handler(event);
135
+ }
136
+ } else if click_count == 3 {
137
+ if let Some(handler) = self.pointer_triple_click.as_ref() {
138
+ handler(event);
139
+ }
140
+ }
141
+ }
117
142
  }
118
143
  #[derive(Clone)]
119
144
  pub(crate) struct NodeBehavior {
@@ -130,6 +155,7 @@ pub(crate) struct NodeBehavior {
130
155
  pub(crate) semantic_disabled: Option<bool>,
131
156
  pub(crate) semantic_checked: Option<SemanticCheckedState>,
132
157
  pub(crate) semantic_selected: Option<bool>,
158
+ pub(crate) semantic_expanded: Option<bool>,
133
159
  pub(crate) semantic_value_range: Option<(f32, f32, f32)>,
134
160
  pub(crate) semantic_orientation: Option<Orientation>,
135
161
  pub(crate) selectable_text: bool,
@@ -143,7 +169,6 @@ pub(crate) struct NodeBehavior {
143
169
  pub(crate) context_menu_handler: Option<ContextMenuCallback>,
144
170
  pub(crate) tool_tip: Option<ToolTip>,
145
171
  pub(crate) track_semantic_disabled_from_enabled: bool,
146
- pub(crate) request_semantic_announcement: bool,
147
172
  pub(crate) visibility: Option<Visibility>,
148
173
  pub(crate) is_portal: bool,
149
174
  pub(crate) fill_width: bool,
@@ -188,6 +213,7 @@ impl Default for NodeBehavior {
188
213
  semantic_disabled: None,
189
214
  semantic_checked: None,
190
215
  semantic_selected: None,
216
+ semantic_expanded: None,
191
217
  semantic_value_range: None,
192
218
  semantic_orientation: None,
193
219
  selectable_text: false,
@@ -201,7 +227,6 @@ impl Default for NodeBehavior {
201
227
  context_menu_handler: None,
202
228
  tool_tip: None,
203
229
  track_semantic_disabled_from_enabled: false,
204
- request_semantic_announcement: false,
205
230
  visibility: None,
206
231
  is_portal: false,
207
232
  fill_width: false,
@@ -266,6 +291,10 @@ pub(crate) struct LinearGradient {
266
291
  pub(crate) struct FlexBoxProps {
267
292
  pub(crate) width: Option<(f32, Unit)>,
268
293
  pub(crate) height: Option<(f32, Unit)>,
294
+ pub(crate) min_width: Option<(f32, Unit)>,
295
+ pub(crate) max_width: Option<(f32, Unit)>,
296
+ pub(crate) min_height: Option<(f32, Unit)>,
297
+ pub(crate) max_height: Option<(f32, Unit)>,
269
298
  pub(crate) bg_color: Option<u32>,
270
299
  pub(crate) padding: Option<(f32, f32, f32, f32)>,
271
300
  pub(crate) flex_direction: Option<FlexDirection>,
@@ -283,6 +312,10 @@ pub(crate) struct TextProps {
283
312
  pub(crate) content: String,
284
313
  pub(crate) width: Option<(f32, Unit)>,
285
314
  pub(crate) height: Option<(f32, Unit)>,
315
+ pub(crate) min_width: Option<(f32, Unit)>,
316
+ pub(crate) max_width: Option<(f32, Unit)>,
317
+ pub(crate) min_height: Option<(f32, Unit)>,
318
+ pub(crate) max_height: Option<(f32, Unit)>,
286
319
  pub(crate) font_id: u32,
287
320
  pub(crate) font_size: f32,
288
321
  pub(crate) has_font: bool,
@@ -307,14 +340,13 @@ pub(crate) struct TextProps {
307
340
  pub(crate) editor_accepts_tab: Option<bool>,
308
341
  pub(crate) obscured: Option<bool>,
309
342
  pub(crate) caret_color: Option<u32>,
343
+ pub(crate) selection_range_bytes: Option<(u32, u32)>,
344
+ pub(crate) selection_start: u32,
345
+ pub(crate) selection_end: u32,
310
346
  }
311
347
 
312
348
  #[derive(Clone, Default)]
313
349
  pub(crate) struct GridProps {
314
- pub(crate) width: Option<(f32, Unit)>,
315
- pub(crate) height: Option<(f32, Unit)>,
316
- pub(crate) bg_color: Option<u32>,
317
- pub(crate) padding: Option<(f32, f32, f32, f32)>,
318
350
  pub(crate) columns: Vec<f32>,
319
351
  pub(crate) column_types: Vec<GridUnit>,
320
352
  pub(crate) rows: Vec<f32>,
@@ -324,8 +356,6 @@ pub(crate) struct GridProps {
324
356
  }
325
357
  #[derive(Clone)]
326
358
  pub(crate) struct ImageProps {
327
- pub(crate) width: Option<(f32, Unit)>,
328
- pub(crate) height: Option<(f32, Unit)>,
329
359
  pub(crate) texture_id: u32,
330
360
  pub(crate) source_url: Option<String>,
331
361
  pub(crate) object_fit: ObjectFit,
@@ -336,22 +366,17 @@ pub(crate) struct ImageProps {
336
366
 
337
367
  #[derive(Clone)]
338
368
  pub(crate) struct SvgProps {
339
- pub(crate) width: Option<(f32, Unit)>,
340
- pub(crate) height: Option<(f32, Unit)>,
341
369
  pub(crate) svg_id: u32,
342
370
  pub(crate) source_url: Option<String>,
343
371
  pub(crate) tint_color: u32,
344
372
  pub(crate) sampling_kind: ImageSamplingKind,
345
373
  pub(crate) max_aniso: u32,
346
- pub(crate) opacity: Option<f32>,
347
374
  }
348
375
 
349
376
  #[derive(Clone)]
350
377
  pub(crate) struct ScrollViewProps {
351
378
  pub(crate) width: Option<(f32, Unit)>,
352
379
  pub(crate) height: Option<(f32, Unit)>,
353
- pub(crate) bg_color: Option<u32>,
354
- pub(crate) padding: Option<(f32, f32, f32, f32)>,
355
380
  pub(crate) enable_scroll_x: bool,
356
381
  pub(crate) enable_scroll_y: bool,
357
382
  pub(crate) friction: Option<f32>,
@@ -490,6 +515,18 @@ impl WeakFlexBox {
490
515
  }
491
516
 
492
517
  impl NodeRef {
518
+ pub(crate) fn set_shared_size_scope(&self, enabled: bool) {
519
+ let handle = {
520
+ let mut core = self.inner.borrow_mut();
521
+ core.behavior.is_shared_size_scope = enabled;
522
+ core.handle
523
+ };
524
+ if handle != NodeHandle::INVALID {
525
+ ui::set_is_shared_size_scope(handle.raw(), enabled);
526
+ crate::frame_scheduler::mark_needs_commit();
527
+ }
528
+ }
529
+
493
530
  pub(crate) fn from_core(inner: Rc<RefCell<NodeCore>>) -> Self {
494
531
  Self {
495
532
  inner,
@@ -580,6 +617,7 @@ impl NodeRef {
580
617
  self.inner.borrow_mut().children.push(child.clone());
581
618
  self.inner.borrow_mut().children_built = false;
582
619
  child.set_inherited_enabled(self.is_enabled_for_routing());
620
+ child.refresh_effective_visibility();
583
621
  if self.handle() != NodeHandle::INVALID {
584
622
  child.build();
585
623
  ui::add_child(self.handle().raw(), child.handle().raw());
@@ -628,6 +666,7 @@ impl NodeRef {
628
666
  };
629
667
  removed.inner.borrow_mut().parent = Weak::new();
630
668
  removed.set_inherited_enabled(true);
669
+ removed.refresh_effective_visibility();
631
670
  parent.inner.borrow_mut().children_built = false;
632
671
  if parent.handle() != NodeHandle::INVALID && self.handle() != NodeHandle::INVALID {
633
672
  ui::remove_child(parent.handle().raw(), self.handle().raw());
@@ -644,14 +683,27 @@ impl NodeRef {
644
683
  }
645
684
 
646
685
  pub(crate) fn is_effectively_visible_for_routing(&self) -> bool {
686
+ self.effective_visibility_for_routing() == Visibility::Normal
687
+ }
688
+
689
+ fn effective_visibility_for_routing(&self) -> Visibility {
690
+ let mut result = Visibility::Normal;
647
691
  let mut node = Some(self.clone());
648
692
  while let Some(current) = node {
649
- if !current.is_visible_for_routing() {
650
- return false;
693
+ match current
694
+ .inner
695
+ .borrow()
696
+ .behavior
697
+ .visibility
698
+ .unwrap_or(Visibility::Normal)
699
+ {
700
+ Visibility::Collapsed => return Visibility::Collapsed,
701
+ Visibility::Hidden => result = Visibility::Hidden,
702
+ Visibility::Normal => {}
651
703
  }
652
704
  node = current.parent();
653
705
  }
654
- true
706
+ result
655
707
  }
656
708
 
657
709
  pub(crate) fn is_enabled_for_routing(&self) -> bool {
@@ -681,6 +733,20 @@ impl NodeRef {
681
733
  self.apply_enabled_changed();
682
734
  }
683
735
 
736
+ pub(crate) fn set_own_visibility(&self, visibility: Visibility) {
737
+ {
738
+ let mut core = self.inner.borrow_mut();
739
+ if core.behavior.visibility == Some(visibility) {
740
+ return;
741
+ }
742
+ core.behavior.visibility = Some(visibility);
743
+ }
744
+ if visibility != Visibility::Normal {
745
+ self.cancel_drag_state();
746
+ }
747
+ self.refresh_effective_visibility();
748
+ }
749
+
684
750
  pub(crate) fn set_inherited_enabled(&self, enabled: bool) {
685
751
  {
686
752
  let mut core = self.inner.borrow_mut();
@@ -700,6 +766,7 @@ impl NodeRef {
700
766
  }
701
767
 
702
768
  fn apply_enabled_changed(&self) {
769
+ let effectively_visible = self.is_effectively_visible_for_routing();
703
770
  let (
704
771
  effective,
705
772
  handle,
@@ -721,10 +788,7 @@ impl NodeRef {
721
788
  core.handle,
722
789
  core.behavior.interactive,
723
790
  core.behavior.focusable,
724
- !matches!(
725
- core.behavior.visibility,
726
- Some(Visibility::Hidden | Visibility::Collapsed)
727
- ),
791
+ effectively_visible,
728
792
  core.behavior.track_semantic_disabled_from_enabled,
729
793
  core.effective_enabled_changed_callbacks.clone(),
730
794
  core.children.clone(),
@@ -765,15 +829,52 @@ impl NodeRef {
765
829
  .unwrap_or(CursorStyle::Default)
766
830
  }
767
831
 
832
+ fn refresh_effective_visibility(&self) {
833
+ let effective_visibility = self.effective_visibility_for_routing();
834
+ let effective_visible = effective_visibility == Visibility::Normal;
835
+ let (handle, visibility, enabled, interactive, focusable, children) = {
836
+ let core = self.inner.borrow();
837
+ (
838
+ core.handle,
839
+ effective_visibility,
840
+ core.behavior.enabled && core.behavior.inherited_enabled,
841
+ core.behavior.interactive,
842
+ core.behavior.focusable,
843
+ core.children.clone(),
844
+ )
845
+ };
846
+ if handle != NodeHandle::INVALID {
847
+ ui::set_visibility(handle.raw(), visibility as u32);
848
+ ui::set_interactive(handle.raw(), enabled && effective_visible && interactive);
849
+ if let Some((focusable, tab_index)) = focusable {
850
+ ui::set_focusable(
851
+ handle.raw(),
852
+ enabled && effective_visible && focusable,
853
+ tab_index,
854
+ );
855
+ }
856
+ crate::frame_scheduler::mark_needs_commit();
857
+ }
858
+ for child in children {
859
+ child.refresh_effective_visibility();
860
+ }
861
+ }
862
+
768
863
  pub(crate) fn require_interactive(&self) {
769
- let (handle, enabled, changed) = {
864
+ let visible = self.is_effectively_visible_for_routing();
865
+ let (handle, enabled, visible, changed) = {
770
866
  let mut inner = self.inner.borrow_mut();
771
867
  let changed = !inner.behavior.interactive;
772
868
  inner.behavior.interactive = true;
773
- (inner.handle, inner.behavior.enabled, changed)
869
+ (
870
+ inner.handle,
871
+ inner.behavior.enabled && inner.behavior.inherited_enabled,
872
+ visible,
873
+ changed,
874
+ )
774
875
  };
775
876
  if handle != NodeHandle::INVALID {
776
- ui::set_interactive(handle.raw(), enabled);
877
+ ui::set_interactive(handle.raw(), enabled && visible);
777
878
  if changed {
778
879
  crate::frame_scheduler::mark_needs_commit();
779
880
  }
@@ -1053,7 +1154,7 @@ impl NodeRef {
1053
1154
  match event.event_type {
1054
1155
  crate::ffi::PointerEventType::Down => {
1055
1156
  crate::tool_tip_manager::ToolTipManager::handle_pointer_down(self);
1056
- if let Some(handler) = handlers.pointer_down {
1157
+ if let Some(handler) = handlers.pointer_down.as_ref() {
1057
1158
  handler(event);
1058
1159
  }
1059
1160
  if event.handled {
@@ -1074,7 +1175,7 @@ impl NodeRef {
1074
1175
  matches!(event.pointer_type, PointerType::Touch | PointerType::Pen),
1075
1176
  );
1076
1177
  let mut core = self.inner.borrow_mut();
1077
- core.drag_click_pending = handlers.pointer_click.is_some();
1178
+ core.drag_click_pending = handlers.has_pointer_click_handler();
1078
1179
  core.drag_click_pending_count = event.click_count;
1079
1180
  core.click_pending = false;
1080
1181
  core.click_pending_count = 0;
@@ -1085,7 +1186,7 @@ impl NodeRef {
1085
1186
  core.drag_click_pending = false;
1086
1187
  core.drag_click_pending_count = 0;
1087
1188
  core.click_pending =
1088
- handlers.pointer_click.is_some() && is_primary_activation_pointer(event);
1189
+ handlers.has_pointer_click_handler() && is_primary_activation_pointer(event);
1089
1190
  core.click_pending_count = event.click_count;
1090
1191
  }
1091
1192
  crate::ffi::PointerEventType::Move => {
@@ -1115,7 +1216,7 @@ impl NodeRef {
1115
1216
  }
1116
1217
  }
1117
1218
  crate::ffi::PointerEventType::Up => {
1118
- if let Some(handler) = handlers.pointer_up {
1219
+ if let Some(handler) = handlers.pointer_up.as_ref() {
1119
1220
  handler(event);
1120
1221
  }
1121
1222
  if event.handled {
@@ -1156,21 +1257,11 @@ impl NodeRef {
1156
1257
  }
1157
1258
  }
1158
1259
  if can_fire_click {
1159
- if let Some(handler) = handlers.pointer_click.clone() {
1160
- event.click_count = if click_count > 0 { click_count } else { 1 };
1161
- handler(event);
1162
- }
1260
+ handlers.dispatch_pointer_click(event, click_count);
1163
1261
  }
1164
1262
  self.clear_click_pending_state();
1165
1263
  if can_fire_pending_click {
1166
- if let Some(handler) = handlers.pointer_click {
1167
- event.click_count = if pending_click_count > 0 {
1168
- pending_click_count
1169
- } else {
1170
- 1
1171
- };
1172
- handler(event);
1173
- }
1264
+ handlers.dispatch_pointer_click(event, pending_click_count);
1174
1265
  }
1175
1266
  self.clear_drag_click_pending_state();
1176
1267
  }
@@ -1560,6 +1651,12 @@ impl NodeRef {
1560
1651
  }
1561
1652
  }
1562
1653
 
1654
+ /// Universal retained-node capabilities shared by every visual node and control.
1655
+ ///
1656
+ /// Rust controls use composition rather than FUI-AS class inheritance, but this
1657
+ /// trait preserves the common retained identity, state, semantics, focus,
1658
+ /// routed-input, gesture, geometry, and child-inspection contract. Layout and
1659
+ /// appearance are exposed separately through the cohesive surface traits.
1563
1660
  pub trait Node: Clone {
1564
1661
  #[doc(hidden)]
1565
1662
  fn node_ref(&self) -> NodeRef {
@@ -1579,15 +1676,67 @@ pub trait Node: Clone {
1579
1676
  Vec::new()
1580
1677
  }
1581
1678
 
1582
- fn node_id(&self, node_id: impl Into<String>) -> &Self
1583
- where
1584
- Self: Sized,
1585
- {
1679
+ #[doc(hidden)]
1680
+ fn apply_node_id(&self, node_id: String) {
1586
1681
  let node_ref = self.node_ref();
1587
1682
  node_ref.set_node_id(node_id);
1588
1683
  if self.has_built_handle() {
1589
1684
  self.notify_retained_mutation();
1590
1685
  }
1686
+ }
1687
+
1688
+ #[doc(hidden)]
1689
+ fn apply_semantic_label(&self, label: String) {
1690
+ let node_ref = self.node_ref();
1691
+ node_ref.inner.borrow_mut().behavior.semantic_label = Some(label.clone());
1692
+ let handle = node_ref.handle();
1693
+ if handle != NodeHandle::INVALID {
1694
+ ui::set_semantic_label(handle.raw(), &label);
1695
+ self.notify_retained_mutation();
1696
+ }
1697
+ }
1698
+
1699
+ #[doc(hidden)]
1700
+ fn apply_focusable(&self, enabled: bool, tab_index: i32) {
1701
+ let node_ref = self.node_ref();
1702
+ if enabled {
1703
+ node_ref.require_interactive();
1704
+ }
1705
+ let visible = node_ref.is_effectively_visible_for_routing();
1706
+ let mut core = node_ref.inner.borrow_mut();
1707
+ core.behavior.focusable = Some((enabled, tab_index));
1708
+ let interactive = core.behavior.enabled && core.behavior.inherited_enabled && visible;
1709
+ let handle = core.handle;
1710
+ drop(core);
1711
+ if handle != NodeHandle::INVALID {
1712
+ ui::set_focusable(handle.raw(), interactive && enabled, tab_index);
1713
+ self.notify_retained_mutation();
1714
+ }
1715
+ }
1716
+
1717
+ #[doc(hidden)]
1718
+ fn apply_focus_now(&self) {
1719
+ let handle = self.node_ref().handle();
1720
+ if handle != NodeHandle::INVALID {
1721
+ ui::request_focus(handle.raw());
1722
+ }
1723
+ }
1724
+
1725
+ #[doc(hidden)]
1726
+ fn apply_enabled(&self, enabled: bool) {
1727
+ self.node_ref().set_own_enabled(enabled);
1728
+ }
1729
+
1730
+ #[doc(hidden)]
1731
+ fn apply_focus_changed_handler(&self, handler: Rc<dyn Fn(FocusChangedEventArgs)>) {
1732
+ self.node_ref().inner.borrow_mut().handlers.focus_changed = Some(handler);
1733
+ }
1734
+
1735
+ fn node_id(&self, node_id: impl Into<String>) -> &Self
1736
+ where
1737
+ Self: Sized,
1738
+ {
1739
+ self.apply_node_id(node_id.into());
1591
1740
  self
1592
1741
  }
1593
1742
 
@@ -1608,16 +1757,199 @@ pub trait Node: Clone {
1608
1757
  where
1609
1758
  Self: Sized,
1610
1759
  {
1611
- let label = label.into();
1760
+ self.apply_semantic_label(label.into());
1761
+ self
1762
+ }
1763
+
1764
+ fn semantic_checked(&self, state: SemanticCheckedState) -> &Self
1765
+ where
1766
+ Self: Sized,
1767
+ {
1612
1768
  let node_ref = self.node_ref();
1613
- node_ref.inner.borrow_mut().behavior.semantic_label = Some(label.clone());
1769
+ node_ref.inner.borrow_mut().behavior.semantic_checked = Some(state);
1770
+ if self.has_built_handle() {
1771
+ ui::set_semantic_checked(self.handle().raw(), state as u32);
1772
+ self.notify_retained_mutation();
1773
+ }
1774
+ self
1775
+ }
1776
+
1777
+ fn semantic_disabled(&self, disabled: bool) -> &Self
1778
+ where
1779
+ Self: Sized,
1780
+ {
1781
+ let node_ref = self.node_ref();
1782
+ let mut core = node_ref.inner.borrow_mut();
1783
+ core.behavior.track_semantic_disabled_from_enabled = false;
1784
+ core.behavior.semantic_disabled = Some(disabled);
1785
+ drop(core);
1786
+ if self.has_built_handle() {
1787
+ ui::set_semantic_disabled(self.handle().raw(), true, disabled);
1788
+ self.notify_retained_mutation();
1789
+ }
1790
+ self
1791
+ }
1792
+
1793
+ fn clear_semantic_disabled(&self) -> &Self
1794
+ where
1795
+ Self: Sized,
1796
+ {
1797
+ let node_ref = self.node_ref();
1798
+ let mut core = node_ref.inner.borrow_mut();
1799
+ core.behavior.track_semantic_disabled_from_enabled = false;
1800
+ core.behavior.semantic_disabled = None;
1801
+ drop(core);
1802
+ if self.has_built_handle() {
1803
+ ui::set_semantic_disabled(self.handle().raw(), false, false);
1804
+ self.notify_retained_mutation();
1805
+ }
1806
+ self
1807
+ }
1808
+
1809
+ fn semantic_selected(&self, selected: bool) -> &Self
1810
+ where
1811
+ Self: Sized,
1812
+ {
1813
+ self.node_ref()
1814
+ .inner
1815
+ .borrow_mut()
1816
+ .behavior
1817
+ .semantic_selected = Some(selected);
1818
+ if self.has_built_handle() {
1819
+ ui::set_semantic_selected(self.handle().raw(), true, selected);
1820
+ self.notify_retained_mutation();
1821
+ }
1822
+ self
1823
+ }
1824
+
1825
+ fn clear_semantic_selected(&self) -> &Self
1826
+ where
1827
+ Self: Sized,
1828
+ {
1829
+ self.node_ref()
1830
+ .inner
1831
+ .borrow_mut()
1832
+ .behavior
1833
+ .semantic_selected = None;
1834
+ if self.has_built_handle() {
1835
+ ui::set_semantic_selected(self.handle().raw(), false, false);
1836
+ self.notify_retained_mutation();
1837
+ }
1838
+ self
1839
+ }
1840
+
1841
+ fn semantic_expanded(&self, expanded: bool) -> &Self
1842
+ where
1843
+ Self: Sized,
1844
+ {
1845
+ self.node_ref()
1846
+ .inner
1847
+ .borrow_mut()
1848
+ .behavior
1849
+ .semantic_expanded = Some(expanded);
1850
+ if self.has_built_handle() {
1851
+ ui::set_semantic_expanded(self.handle().raw(), true, expanded);
1852
+ self.notify_retained_mutation();
1853
+ }
1854
+ self
1855
+ }
1856
+
1857
+ fn clear_semantic_expanded(&self) -> &Self
1858
+ where
1859
+ Self: Sized,
1860
+ {
1861
+ self.node_ref()
1862
+ .inner
1863
+ .borrow_mut()
1864
+ .behavior
1865
+ .semantic_expanded = None;
1866
+ if self.has_built_handle() {
1867
+ ui::set_semantic_expanded(self.handle().raw(), false, false);
1868
+ self.notify_retained_mutation();
1869
+ }
1870
+ self
1871
+ }
1872
+
1873
+ fn semantic_value_range(&self, value_now: f32, value_min: f32, value_max: f32) -> &Self
1874
+ where
1875
+ Self: Sized,
1876
+ {
1877
+ self.node_ref()
1878
+ .inner
1879
+ .borrow_mut()
1880
+ .behavior
1881
+ .semantic_value_range = Some((value_now, value_min, value_max));
1882
+ if self.has_built_handle() {
1883
+ ui::set_semantic_value_range(
1884
+ self.handle().raw(),
1885
+ true,
1886
+ value_now,
1887
+ value_min,
1888
+ value_max,
1889
+ );
1890
+ self.notify_retained_mutation();
1891
+ }
1892
+ self
1893
+ }
1894
+
1895
+ fn clear_semantic_value_range(&self) -> &Self
1896
+ where
1897
+ Self: Sized,
1898
+ {
1899
+ self.node_ref()
1900
+ .inner
1901
+ .borrow_mut()
1902
+ .behavior
1903
+ .semantic_value_range = None;
1904
+ if self.has_built_handle() {
1905
+ ui::set_semantic_value_range(self.handle().raw(), false, 0.0, 0.0, 0.0);
1906
+ self.notify_retained_mutation();
1907
+ }
1908
+ self
1909
+ }
1910
+
1911
+ fn semantic_orientation(&self, orientation: Orientation) -> &Self
1912
+ where
1913
+ Self: Sized,
1914
+ {
1915
+ self.node_ref()
1916
+ .inner
1917
+ .borrow_mut()
1918
+ .behavior
1919
+ .semantic_orientation = Some(orientation);
1614
1920
  if self.has_built_handle() {
1615
- ui::set_semantic_label(self.handle().raw(), &label);
1921
+ ui::set_semantic_orientation(self.handle().raw(), orientation as u32);
1616
1922
  self.notify_retained_mutation();
1617
1923
  }
1618
1924
  self
1619
1925
  }
1620
1926
 
1927
+ fn request_semantic_announcement(&self) -> &Self
1928
+ where
1929
+ Self: Sized,
1930
+ {
1931
+ if self.has_built_handle() {
1932
+ ui::request_semantic_announcement(self.handle().raw());
1933
+ }
1934
+ self
1935
+ }
1936
+
1937
+ fn child_count(&self) -> usize {
1938
+ self.node_ref().children().len()
1939
+ }
1940
+
1941
+ fn is_enabled(&self) -> bool {
1942
+ self.node_ref().is_enabled_for_routing()
1943
+ }
1944
+
1945
+ fn is_visible(&self) -> bool {
1946
+ self.node_ref().is_effectively_visible_for_routing()
1947
+ }
1948
+
1949
+ fn cursor_style(&self) -> CursorStyle {
1950
+ self.node_ref().cursor_style_for_routing()
1951
+ }
1952
+
1621
1953
  fn persist_state(&self, adapter: Rc<dyn PersistedStateAdapter>) -> &Self
1622
1954
  where
1623
1955
  Self: Sized,
@@ -1670,7 +2002,11 @@ pub trait Node: Clone {
1670
2002
  self
1671
2003
  }
1672
2004
 
1673
- fn on_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self
2005
+ /// Handles the low-level routed pointer-click event.
2006
+ ///
2007
+ /// This is not control activation. Use [`Button::on_click`](crate::Button::on_click)
2008
+ /// for a button action that also supports keyboard activation.
2009
+ fn on_pointer_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self
1674
2010
  where
1675
2011
  Self: Sized,
1676
2012
  {
@@ -1680,6 +2016,28 @@ pub trait Node: Clone {
1680
2016
  self
1681
2017
  }
1682
2018
 
2019
+ /// Handles a low-level routed pointer double-click after the ordinary pointer-click callback.
2020
+ fn on_pointer_double_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self
2021
+ where
2022
+ Self: Sized,
2023
+ {
2024
+ let node_ref = self.node_ref();
2025
+ node_ref.inner.borrow_mut().handlers.pointer_double_click = Some(Rc::new(handler));
2026
+ node_ref.require_interactive();
2027
+ self
2028
+ }
2029
+
2030
+ /// Handles a low-level routed pointer triple-click after the ordinary pointer-click callback.
2031
+ fn on_pointer_triple_click(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self
2032
+ where
2033
+ Self: Sized,
2034
+ {
2035
+ let node_ref = self.node_ref();
2036
+ node_ref.inner.borrow_mut().handlers.pointer_triple_click = Some(Rc::new(handler));
2037
+ node_ref.require_interactive();
2038
+ self
2039
+ }
2040
+
1683
2041
  fn on_pointer_down(&self, handler: impl Fn(&mut PointerEventArgs) + 'static) -> &Self
1684
2042
  where
1685
2043
  Self: Sized,
@@ -1792,22 +2150,55 @@ pub trait Node: Clone {
1792
2150
  where
1793
2151
  Self: Sized,
1794
2152
  {
1795
- let node_ref = self.node_ref();
1796
- if enabled {
1797
- node_ref.require_interactive();
1798
- }
1799
- let mut core = node_ref.inner.borrow_mut();
1800
- core.behavior.focusable = Some((enabled, tab_index));
1801
- let interactive = core.behavior.enabled && core.behavior.inherited_enabled;
1802
- let handle = core.handle;
1803
- drop(core);
1804
- if handle != NodeHandle::INVALID {
1805
- ui::set_focusable(handle.raw(), interactive && enabled, tab_index);
1806
- self.notify_retained_mutation();
2153
+ self.apply_focusable(enabled, tab_index);
2154
+ self
2155
+ }
2156
+
2157
+ fn focus_now(&self) -> &Self
2158
+ where
2159
+ Self: Sized,
2160
+ {
2161
+ self.apply_focus_now();
2162
+ self
2163
+ }
2164
+
2165
+ fn enabled(&self, enabled: bool) -> &Self
2166
+ where
2167
+ Self: Sized,
2168
+ {
2169
+ self.apply_enabled(enabled);
2170
+ self
2171
+ }
2172
+
2173
+ fn visibility(&self, visibility: Visibility) -> &Self
2174
+ where
2175
+ Self: Sized,
2176
+ {
2177
+ self.node_ref().set_own_visibility(visibility);
2178
+ if self.has_built_handle() {
2179
+ self.notify_retained_layout_mutation();
1807
2180
  }
1808
2181
  self
1809
2182
  }
1810
2183
 
2184
+ fn cursor(&self, style: CursorStyle) -> &Self
2185
+ where
2186
+ Self: Sized,
2187
+ {
2188
+ self.node_ref().inner.borrow_mut().behavior.cursor = Some(style);
2189
+ crate::event::handle_cursor_style_changed(self.handle());
2190
+ self
2191
+ }
2192
+
2193
+ fn clear_cursor(&self) -> &Self
2194
+ where
2195
+ Self: Sized,
2196
+ {
2197
+ self.node_ref().inner.borrow_mut().behavior.cursor = None;
2198
+ crate::event::handle_cursor_style_changed(self.handle());
2199
+ self
2200
+ }
2201
+
1811
2202
  fn on_key_down(&self, handler: impl Fn(&mut KeyEventArgs) + 'static) -> &Self
1812
2203
  where
1813
2204
  Self: Sized,
@@ -1828,7 +2219,7 @@ pub trait Node: Clone {
1828
2219
  where
1829
2220
  Self: Sized,
1830
2221
  {
1831
- self.node_ref().inner.borrow_mut().handlers.focus_changed = Some(Rc::new(handler));
2222
+ self.apply_focus_changed_handler(Rc::new(handler));
1832
2223
  self
1833
2224
  }
1834
2225