1use crate::{Theme, UiHost};
2use fret_core::{
3 AppWindowId, Corners, Event, NodeId, Point, Rect, Scene, SemanticsCheckedState, SemanticsFlags,
4 SemanticsInvalid, SemanticsLive, SemanticsOrientation, SemanticsPressedState, SemanticsRole,
5 Size, Transform2D, UiServices,
6};
7use fret_runtime::{
8 CommandId, DefaultAction, DefaultActionSet, Effect, InputContext, Model, ModelId,
9};
10use std::any::{Any, TypeId};
11use std::collections::HashMap;
12
13use crate::layout_constraints::LayoutConstraints;
14use crate::layout_pass::LayoutPassKind;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Invalidation {
18 Layout,
19 Paint,
20 HitTest,
21 HitTestOnly,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct UiSourceLocation {
30 pub file: &'static str,
31 pub line: u32,
32 pub column: u32,
33}
34
35pub struct EventCx<'a, H: UiHost> {
36 pub app: &'a mut H,
37 pub services: &'a mut dyn UiServices,
38 pub node: NodeId,
39 pub layer_root: Option<NodeId>,
40 pub window: Option<AppWindowId>,
41 pub pointer_id: Option<fret_core::PointerId>,
42 pub scale_factor: f32,
47 pub event_window_position: Option<Point>,
52 pub event_window_wheel_delta: Option<Point>,
57 pub input_ctx: InputContext,
58 pub pointer_hit_is_text_input: bool,
65 pub pointer_hit_is_pressable: bool,
72 pub pointer_hit_pressable_target: Option<crate::GlobalElementId>,
78 pub pointer_hit_pressable_target_in_descendant_subtree: bool,
84 pub prevented_default_actions: &'a mut DefaultActionSet,
85 pub children: &'a [NodeId],
86 pub focus: Option<NodeId>,
87 pub captured: Option<NodeId>,
88 pub bounds: Rect,
89 pub invalidations: Vec<(NodeId, Invalidation)>,
90 pub(crate) scroll_handle_invalidations: Vec<ScrollHandleInvalidationRequest>,
91 pub(crate) scroll_target_invalidations: Vec<crate::GlobalElementId>,
92 pub requested_focus: Option<NodeId>,
93 pub requested_focus_target: Option<crate::GlobalElementId>,
94 pub requested_capture: Option<Option<NodeId>>,
95 pub requested_cursor: Option<fret_core::CursorIcon>,
96 pub notify_requested: bool,
97 pub notify_requested_location: Option<UiSourceLocation>,
98 pub stop_propagation: bool,
99}
100
101impl<'a, H: UiHost> EventCx<'a, H> {
102 #[allow(clippy::too_many_arguments)]
103 pub fn new(
104 app: &'a mut H,
105 services: &'a mut dyn UiServices,
106 node: NodeId,
107 layer_root: Option<NodeId>,
108 window: Option<AppWindowId>,
109 input_ctx: InputContext,
110 pointer_id: Option<fret_core::PointerId>,
111 scale_factor: f32,
112 event_window_position: Option<Point>,
113 event_window_wheel_delta: Option<Point>,
114 pointer_hit_is_text_input: bool,
115 pointer_hit_is_pressable: bool,
116 pointer_hit_pressable_target: Option<crate::GlobalElementId>,
117 pointer_hit_pressable_target_in_descendant_subtree: bool,
118 prevented_default_actions: &'a mut DefaultActionSet,
119 children: &'a [NodeId],
120 focus: Option<NodeId>,
121 captured: Option<NodeId>,
122 bounds: Rect,
123 ) -> Self {
124 Self {
125 app,
126 services,
127 node,
128 layer_root,
129 window,
130 pointer_id,
131 scale_factor,
132 event_window_position,
133 event_window_wheel_delta,
134 input_ctx,
135 pointer_hit_is_text_input,
136 pointer_hit_is_pressable,
137 pointer_hit_pressable_target,
138 pointer_hit_pressable_target_in_descendant_subtree,
139 prevented_default_actions,
140 children,
141 focus,
142 captured,
143 bounds,
144 invalidations: Vec::new(),
145 scroll_handle_invalidations: Vec::new(),
146 scroll_target_invalidations: Vec::new(),
147 requested_focus: None,
148 requested_focus_target: None,
149 requested_capture: None,
150 requested_cursor: None,
151 notify_requested: false,
152 notify_requested_location: None,
153 stop_propagation: false,
154 }
155 }
156
157 pub fn theme(&self) -> &Theme {
158 Theme::global(&*self.app)
159 }
160
161 pub fn pointer_position_local(&self, event: &Event) -> Option<Point> {
169 let pos = Self::pointer_position_mapped(event)?;
170 Some(Point::new(
171 fret_core::Px(pos.x.0 - self.bounds.origin.x.0),
172 fret_core::Px(pos.y.0 - self.bounds.origin.y.0),
173 ))
174 }
175
176 pub fn pointer_position_window(&self, event: &Event) -> Option<Point> {
178 Self::pointer_position_mapped(event).and(self.event_window_position)
179 }
180
181 pub fn pointer_delta_local(&self, event: &Event) -> Option<Point> {
184 match event {
185 Event::Pointer(fret_core::PointerEvent::Wheel { delta, .. }) => Some(*delta),
186 _ => None,
187 }
188 }
189
190 pub fn pointer_delta_window(&self, event: &Event) -> Option<Point> {
192 self.pointer_delta_local(event)
193 .and(self.event_window_wheel_delta)
194 }
195
196 fn pointer_position_mapped(event: &Event) -> Option<Point> {
197 match event {
198 Event::Pointer(e) => match e {
199 fret_core::PointerEvent::Move { position, .. }
200 | fret_core::PointerEvent::Down { position, .. }
201 | fret_core::PointerEvent::Up { position, .. }
202 | fret_core::PointerEvent::Wheel { position, .. }
203 | fret_core::PointerEvent::PinchGesture { position, .. } => Some(*position),
204 },
205 Event::PointerCancel(e) => e.position,
206 Event::ExternalDrag(e) => Some(e.position),
207 Event::InternalDrag(e) => Some(e.position),
208 _ => None,
209 }
210 }
211
212 pub fn frame_clock(&self) -> Option<fret_core::WindowFrameClockSnapshot> {
217 let window = self.window?;
218 self.app
219 .global::<fret_core::WindowFrameClockService>()
220 .and_then(|svc| svc.snapshot(window))
221 }
222
223 pub fn prefers_reduced_motion(&self) -> Option<bool> {
225 let window = self.window?;
226 self.app
227 .global::<fret_core::WindowMetricsService>()
228 .and_then(|svc| {
229 svc.prefers_reduced_motion_is_known(window)
230 .then(|| svc.prefers_reduced_motion(window))
231 .flatten()
232 })
233 }
234
235 pub fn pointer_position_window_snapshot(
237 &self,
238 pointer_id: fret_core::PointerId,
239 ) -> Option<Point> {
240 let window = self.window?;
241 self.app
242 .global::<crate::pointer_motion::WindowPointerMotionService>()
243 .and_then(|svc| svc.position_window(window, pointer_id))
244 }
245
246 pub fn pointer_velocity_window_snapshot(
248 &self,
249 pointer_id: fret_core::PointerId,
250 ) -> Option<Point> {
251 let window = self.window?;
252 self.app
253 .global::<crate::pointer_motion::WindowPointerMotionService>()
254 .and_then(|svc| svc.velocity_window(window, pointer_id))
255 }
256
257 pub fn invalidate(&mut self, node: NodeId, kind: Invalidation) {
258 self.invalidations.push((node, kind));
259 }
260
261 pub fn invalidate_scroll_handle_bindings(&mut self, handle_key: usize, kind: Invalidation) {
266 self.scroll_handle_invalidations
267 .push(ScrollHandleInvalidationRequest { handle_key, kind });
268 }
269
270 pub(crate) fn invalidate_scroll_target(&mut self, element: crate::GlobalElementId) {
276 self.scroll_target_invalidations.push(element);
277 }
278
279 pub fn invalidate_self(&mut self, kind: Invalidation) {
280 self.invalidate(self.node, kind);
281 }
282
283 pub fn dispatch_command(&mut self, command: CommandId) {
284 self.app.push_effect(Effect::Command {
285 window: self.window,
286 command,
287 });
288 }
289
290 pub fn request_focus(&mut self, node: NodeId) {
291 self.requested_focus = Some(node);
292 }
293
294 pub fn capture_pointer(&mut self, node: NodeId) {
295 if self.pointer_id.is_none() {
296 return;
297 }
298 self.requested_capture = Some(Some(node));
299 }
300
301 pub fn release_pointer_capture(&mut self) {
302 if self.pointer_id.is_none() {
303 return;
304 }
305 self.requested_capture = Some(None);
306 }
307
308 pub fn stop_propagation(&mut self) {
309 self.stop_propagation = true;
310 }
311
312 pub fn prevent_default(&mut self, action: DefaultAction) {
313 self.prevented_default_actions.insert(action);
314 }
315
316 pub fn default_prevented(&self, action: DefaultAction) -> bool {
317 self.prevented_default_actions.contains(action)
318 }
319
320 pub fn request_redraw(&mut self) {
335 let Some(window) = self.window else {
336 return;
337 };
338 self.app.request_redraw(window);
339 }
340
341 #[track_caller]
346 pub fn notify(&mut self) {
347 self.notify_requested = true;
348 if self.notify_requested_location.is_none() {
349 let caller = std::panic::Location::caller();
350 self.notify_requested_location = Some(UiSourceLocation {
351 file: caller.file(),
352 line: caller.line(),
353 column: caller.column(),
354 });
355 }
356 }
357
358 pub fn set_cursor_icon(&mut self, icon: fret_core::CursorIcon) {
359 if !self.input_ctx.caps.ui.cursor_icons {
360 return;
361 }
362 self.requested_cursor = Some(icon);
363 }
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub(crate) struct ScrollHandleInvalidationRequest {
368 pub(crate) handle_key: usize,
369 pub(crate) kind: Invalidation,
370}
371
372pub struct ObserverCx<'a, H: UiHost> {
377 pub app: &'a mut H,
378 pub services: &'a mut dyn UiServices,
379 pub node: NodeId,
380 pub window: Option<AppWindowId>,
381 pub pointer_id: Option<fret_core::PointerId>,
382 pub input_ctx: InputContext,
383 pub children: &'a [NodeId],
384 pub focus: Option<NodeId>,
385 pub captured: Option<NodeId>,
386 pub bounds: Rect,
387 pub invalidations: Vec<(NodeId, Invalidation)>,
388 pub notify_requested: bool,
389 pub notify_requested_location: Option<UiSourceLocation>,
390}
391
392impl<'a, H: UiHost> ObserverCx<'a, H> {
393 pub fn theme(&self) -> &Theme {
394 Theme::global(&*self.app)
395 }
396
397 pub fn invalidate(&mut self, node: NodeId, kind: Invalidation) {
398 self.invalidations.push((node, kind));
399 }
400
401 pub fn invalidate_self(&mut self, kind: Invalidation) {
402 self.invalidate(self.node, kind);
403 }
404
405 pub fn dispatch_command(&mut self, command: CommandId) {
406 self.app.push_effect(Effect::Command {
407 window: self.window,
408 command,
409 });
410 }
411
412 pub fn request_redraw(&mut self) {
414 let Some(window) = self.window else {
415 return;
416 };
417 self.app.request_redraw(window);
418 }
419
420 #[track_caller]
425 pub fn notify(&mut self) {
426 self.notify_requested = true;
427 if self.notify_requested_location.is_none() {
428 let caller = std::panic::Location::caller();
429 self.notify_requested_location = Some(UiSourceLocation {
430 file: caller.file(),
431 line: caller.line(),
432 column: caller.column(),
433 });
434 }
435 }
436}
437
438pub struct CommandCx<'a, H: UiHost> {
439 pub app: &'a mut H,
440 pub services: &'a mut dyn UiServices,
441 pub tree: &'a mut crate::tree::UiTree<H>,
442 pub node: NodeId,
443 pub window: Option<AppWindowId>,
444 pub input_ctx: InputContext,
445 pub focus: Option<NodeId>,
446 pub invalidations: Vec<(NodeId, Invalidation)>,
447 pub requested_focus: Option<NodeId>,
448 pub notify_requested: bool,
449 pub notify_requested_location: Option<UiSourceLocation>,
450 pub stop_propagation: bool,
451}
452
453impl<'a, H: UiHost> CommandCx<'a, H> {
454 pub fn theme(&self) -> &Theme {
455 Theme::global(&*self.app)
456 }
457
458 pub fn invalidate(&mut self, node: NodeId, kind: Invalidation) {
459 self.invalidations.push((node, kind));
460 }
461
462 pub fn invalidate_self(&mut self, kind: Invalidation) {
463 self.invalidate(self.node, kind);
464 }
465
466 pub fn request_focus(&mut self, node: NodeId) {
467 self.requested_focus = Some(node);
468 }
469
470 pub fn stop_propagation(&mut self) {
471 self.stop_propagation = true;
472 }
473
474 pub fn request_redraw(&mut self) {
479 let Some(window) = self.window else {
480 return;
481 };
482 self.app.request_redraw(window);
483 }
484
485 #[track_caller]
490 pub fn notify(&mut self) {
491 self.notify_requested = true;
492 if self.notify_requested_location.is_none() {
493 let caller = std::panic::Location::caller();
494 self.notify_requested_location = Some(UiSourceLocation {
495 file: caller.file(),
496 line: caller.line(),
497 column: caller.column(),
498 });
499 }
500 }
501}
502
503#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
510pub enum CommandAvailability {
511 #[default]
512 NotHandled,
513 Available,
514 Blocked,
515}
516
517pub struct CommandAvailabilityCx<'a, H: UiHost> {
522 pub app: &'a mut H,
523 pub tree: &'a crate::tree::UiTree<H>,
524 pub node: NodeId,
525 pub window: Option<AppWindowId>,
526 pub input_ctx: InputContext,
527 pub focus: Option<NodeId>,
528}
529
530pub struct LayoutCx<'a, H: UiHost> {
531 pub app: &'a mut H,
532 pub tree: &'a mut crate::tree::UiTree<H>,
533 pub node: NodeId,
534 pub window: Option<AppWindowId>,
535 pub focus: Option<NodeId>,
536 pub children: &'a [NodeId],
537 pub bounds: Rect,
538 pub available: Size,
539 pub pass_kind: LayoutPassKind,
540 pub overflow_ctx: crate::layout::overflow::LayoutOverflowContext,
541 pub scale_factor: f32,
542 pub services: &'a mut dyn UiServices,
543 pub observe_model: &'a mut dyn FnMut(ModelId, Invalidation),
544 pub observe_global: &'a mut dyn FnMut(TypeId, Invalidation),
545}
546
547impl<'a, H: UiHost> LayoutCx<'a, H> {
548 pub fn probe_constraints_for_size(&self, size: Size) -> LayoutConstraints {
549 self.overflow_ctx.probe_constraints_for_size(size)
550 }
551
552 pub fn with_overflow_context<R>(
553 &mut self,
554 overflow_ctx: crate::layout::overflow::LayoutOverflowContext,
555 f: impl FnOnce(&mut Self) -> R,
556 ) -> R {
557 let prev = self.overflow_ctx;
558 self.overflow_ctx = overflow_ctx;
559 let out = f(self);
560 self.overflow_ctx = prev;
561 out
562 }
563
564 pub fn theme(&mut self) -> &Theme {
565 self.observe_global::<Theme>(Invalidation::Layout);
566 Theme::global(&*self.app)
567 }
568
569 pub fn request_redraw(&mut self) {
574 let Some(window) = self.window else {
575 return;
576 };
577 self.app.request_redraw(window);
578 }
579
580 pub fn request_animation_frame(&mut self) {
591 self.tree.invalidate_with_source_and_detail(
593 self.node,
594 Invalidation::Paint,
595 crate::tree::UiDebugInvalidationSource::Notify,
596 crate::tree::UiDebugInvalidationDetail::AnimationFrameRequest,
597 );
598 let Some(window) = self.window else {
599 return;
600 };
601 self.app.push_effect(Effect::RequestAnimationFrame(window));
602 }
603
604 pub fn frame_clock(&self) -> Option<fret_core::WindowFrameClockSnapshot> {
609 let window = self.window?;
610 self.app
611 .global::<fret_core::WindowFrameClockService>()
612 .and_then(|svc| svc.snapshot(window))
613 }
614
615 pub fn pointer_position_window_snapshot(
617 &self,
618 pointer_id: fret_core::PointerId,
619 ) -> Option<Point> {
620 let window = self.window?;
621 self.app
622 .global::<crate::pointer_motion::WindowPointerMotionService>()
623 .and_then(|svc| svc.position_window(window, pointer_id))
624 }
625
626 pub fn pointer_velocity_window_snapshot(
628 &self,
629 pointer_id: fret_core::PointerId,
630 ) -> Option<Point> {
631 let window = self.window?;
632 self.app
633 .global::<crate::pointer_motion::WindowPointerMotionService>()
634 .and_then(|svc| svc.velocity_window(window, pointer_id))
635 }
636
637 pub fn pointer_position_local_snapshot(
640 &self,
641 pointer_id: fret_core::PointerId,
642 ) -> Option<Point> {
643 let window_pos = self.pointer_position_window_snapshot(pointer_id)?;
644 let mapped = self
645 .tree
646 .map_window_point_to_node_layout_space(self.node, window_pos)?;
647 Some(Point::new(
648 fret_core::Px(mapped.x.0 - self.bounds.origin.x.0),
649 fret_core::Px(mapped.y.0 - self.bounds.origin.y.0),
650 ))
651 }
652
653 pub fn pointer_velocity_local_snapshot(
655 &self,
656 pointer_id: fret_core::PointerId,
657 ) -> Option<Point> {
658 let window_vec = self.pointer_velocity_window_snapshot(pointer_id)?;
659 self.tree
660 .map_window_vector_to_node_layout_space(self.node, window_vec)
661 }
662
663 pub fn observe_model<T>(&mut self, model: &Model<T>, invalidation: Invalidation) {
664 (self.observe_model)(model.id(), invalidation);
665 }
666
667 pub fn observe_global<T: Any>(&mut self, invalidation: Invalidation) {
668 (self.observe_global)(TypeId::of::<T>(), invalidation);
669 }
670
671 pub fn layout(&mut self, child: NodeId, available: Size) -> Size {
672 let rect = Rect::new(self.bounds.origin, available);
673 self.layout_in(child, rect)
674 }
675
676 pub fn layout_in(&mut self, child: NodeId, bounds: Rect) -> Size {
677 self.tree.layout_in_with_pass_kind(
678 self.app,
679 self.services,
680 child,
681 bounds,
682 self.scale_factor,
683 self.pass_kind,
684 self.overflow_ctx,
685 )
686 }
687
688 pub fn layout_in_probe(&mut self, child: NodeId, bounds: Rect) -> Size {
689 self.tree.layout_in_with_pass_kind(
690 self.app,
691 self.services,
692 child,
693 bounds,
694 self.scale_factor,
695 LayoutPassKind::Probe,
696 self.overflow_ctx,
697 )
698 }
699
700 pub fn layout_engine_child_bounds(&mut self, child: NodeId) -> Option<Rect> {
701 let local = self
702 .tree
703 .layout_engine_child_local_rect_profiled(self.node, child)?;
704 Some(Rect::new(
705 Point::new(
706 fret_core::Px(self.bounds.origin.x.0 + local.origin.x.0),
707 fret_core::Px(self.bounds.origin.y.0 + local.origin.y.0),
708 ),
709 local.size,
710 ))
711 }
712
713 pub fn layout_viewport_root(&mut self, child: NodeId, bounds: Rect) -> Size {
714 if self.pass_kind == LayoutPassKind::Probe {
715 return bounds.size;
716 }
717 self.tree.register_viewport_root(child, bounds);
718 bounds.size
719 }
720
721 pub fn solve_barrier_child_root(&mut self, child: NodeId, bounds: Rect) {
722 if self.pass_kind != LayoutPassKind::Final {
723 return;
724 }
725 self.tree.solve_barrier_flow_root(
726 self.app,
727 self.services,
728 child,
729 bounds,
730 self.scale_factor,
731 );
732 }
733
734 pub fn solve_barrier_child_root_if_needed(&mut self, child: NodeId, bounds: Rect) {
735 if self.pass_kind != LayoutPassKind::Final {
736 return;
737 }
738 self.tree.solve_barrier_flow_root_if_needed(
739 self.app,
740 self.services,
741 child,
742 bounds,
743 self.scale_factor,
744 );
745 }
746
747 pub fn solve_barrier_child_roots_if_needed(&mut self, roots: &[(NodeId, Rect)]) {
748 if self.pass_kind != LayoutPassKind::Final {
749 return;
750 }
751 self.tree.solve_barrier_flow_roots_if_needed(
752 self.app,
753 self.services,
754 roots,
755 self.scale_factor,
756 );
757 }
758 pub fn measure_in(&mut self, child: NodeId, constraints: LayoutConstraints) -> Size {
759 self.tree.measure_in(
760 self.app,
761 self.services,
762 child,
763 constraints,
764 self.scale_factor,
765 )
766 }
767}
768
769pub struct MeasureCx<'a, H: UiHost> {
770 pub app: &'a mut H,
771 pub tree: &'a mut crate::tree::UiTree<H>,
772 pub node: NodeId,
773 pub window: Option<AppWindowId>,
774 pub focus: Option<NodeId>,
775 pub children: &'a [NodeId],
776 pub constraints: LayoutConstraints,
777 pub scale_factor: f32,
778 pub services: &'a mut dyn UiServices,
779 pub observe_model: &'a mut dyn FnMut(ModelId, Invalidation),
780 pub observe_global: &'a mut dyn FnMut(TypeId, Invalidation),
781}
782
783impl<'a, H: UiHost> MeasureCx<'a, H> {
784 pub fn theme(&mut self) -> &Theme {
785 self.observe_global::<Theme>(Invalidation::Layout);
786 Theme::global(&*self.app)
787 }
788
789 pub fn request_redraw(&mut self) {
794 let Some(window) = self.window else {
795 return;
796 };
797 self.app.request_redraw(window);
798 }
799
800 pub fn request_animation_frame(&mut self) {
809 self.tree.invalidate_with_source_and_detail(
811 self.node,
812 Invalidation::Paint,
813 crate::tree::UiDebugInvalidationSource::Notify,
814 crate::tree::UiDebugInvalidationDetail::AnimationFrameRequest,
815 );
816 let Some(window) = self.window else {
817 return;
818 };
819 self.app.push_effect(Effect::RequestAnimationFrame(window));
820 }
821
822 pub fn frame_clock(&self) -> Option<fret_core::WindowFrameClockSnapshot> {
827 let window = self.window?;
828 self.app
829 .global::<fret_core::WindowFrameClockService>()
830 .and_then(|svc| svc.snapshot(window))
831 }
832
833 pub fn pointer_position_window_snapshot(
835 &self,
836 pointer_id: fret_core::PointerId,
837 ) -> Option<Point> {
838 let window = self.window?;
839 self.app
840 .global::<crate::pointer_motion::WindowPointerMotionService>()
841 .and_then(|svc| svc.position_window(window, pointer_id))
842 }
843
844 pub fn pointer_velocity_window_snapshot(
846 &self,
847 pointer_id: fret_core::PointerId,
848 ) -> Option<Point> {
849 let window = self.window?;
850 self.app
851 .global::<crate::pointer_motion::WindowPointerMotionService>()
852 .and_then(|svc| svc.velocity_window(window, pointer_id))
853 }
854
855 pub fn pointer_position_local_snapshot(
858 &self,
859 pointer_id: fret_core::PointerId,
860 ) -> Option<Point> {
861 let window_pos = self.pointer_position_window_snapshot(pointer_id)?;
862 let bounds = self.tree.node_bounds(self.node)?;
863 let mapped = self
864 .tree
865 .map_window_point_to_node_layout_space(self.node, window_pos)?;
866 Some(Point::new(
867 fret_core::Px(mapped.x.0 - bounds.origin.x.0),
868 fret_core::Px(mapped.y.0 - bounds.origin.y.0),
869 ))
870 }
871
872 pub fn pointer_velocity_local_snapshot(
874 &self,
875 pointer_id: fret_core::PointerId,
876 ) -> Option<Point> {
877 let window_vec = self.pointer_velocity_window_snapshot(pointer_id)?;
878 self.tree
879 .map_window_vector_to_node_layout_space(self.node, window_vec)
880 }
881
882 pub fn observe_model<T>(&mut self, model: &Model<T>, invalidation: Invalidation) {
883 (self.observe_model)(model.id(), invalidation);
884 }
885
886 pub fn observe_global<T: Any>(&mut self, invalidation: Invalidation) {
887 (self.observe_global)(TypeId::of::<T>(), invalidation);
888 }
889
890 pub fn measure_in(&mut self, child: NodeId, constraints: LayoutConstraints) -> Size {
891 if !self.tree.debug_enabled() {
892 return self.tree.measure_in(
893 self.app,
894 self.services,
895 child,
896 constraints,
897 self.scale_factor,
898 );
899 }
900
901 let started = fret_core::time::Instant::now();
902 let size = self.tree.measure_in(
903 self.app,
904 self.services,
905 child,
906 constraints,
907 self.scale_factor,
908 );
909 let elapsed = started.elapsed();
910 self.tree
911 .debug_record_measure_child(self.node, child, elapsed);
912 size
913 }
914}
915
916pub struct PrepaintCx<'a, H: UiHost> {
925 pub app: &'a mut H,
926 pub tree: &'a mut crate::tree::UiTree<H>,
927 pub node: NodeId,
928 pub window: Option<AppWindowId>,
929 pub bounds: Rect,
930 pub scale_factor: f32,
931}
932
933impl<'a, H: UiHost> PrepaintCx<'a, H> {
934 pub fn set_output<T: std::any::Any>(&mut self, value: T) {
935 self.tree.set_prepaint_output(self.node, value);
936 }
937
938 pub fn output<T: std::any::Any>(&mut self) -> Option<&T> {
939 self.tree.prepaint_output(self.node)
940 }
941
942 pub fn output_mut<T: std::any::Any>(&mut self) -> Option<&mut T> {
943 self.tree.prepaint_output_mut(self.node)
944 }
945
946 pub fn invalidate(&mut self, node: NodeId, kind: Invalidation) {
951 self.tree
952 .debug_record_prepaint_action(crate::tree::UiDebugPrepaintAction {
953 node: self.node,
954 target: Some(node),
955 kind: crate::tree::UiDebugPrepaintActionKind::Invalidate,
956 invalidation: Some(kind),
957 element: None,
958 virtual_list_window_shift_kind: None,
959 virtual_list_window_shift_reason: None,
960 chart_sampling_window_key: None,
961 node_graph_cull_window_key: None,
962 frame_id: self.app.frame_id(),
963 });
964 self.tree.invalidate_with_detail(
965 node,
966 kind,
967 crate::tree::UiDebugInvalidationDetail::Unknown,
968 );
969 }
970
971 pub fn invalidate_self(&mut self, kind: Invalidation) {
973 self.invalidate(self.node, kind);
974 }
975
976 pub fn request_redraw(&mut self) {
980 self.tree
981 .debug_record_prepaint_action(crate::tree::UiDebugPrepaintAction {
982 node: self.node,
983 target: None,
984 kind: crate::tree::UiDebugPrepaintActionKind::RequestRedraw,
985 invalidation: None,
986 element: None,
987 virtual_list_window_shift_kind: None,
988 virtual_list_window_shift_reason: None,
989 chart_sampling_window_key: None,
990 node_graph_cull_window_key: None,
991 frame_id: self.app.frame_id(),
992 });
993 let Some(window) = self.window else {
994 return;
995 };
996 self.app.request_redraw(window);
997 }
998
999 pub fn request_animation_frame(&mut self) {
1005 self.tree
1006 .debug_record_prepaint_action(crate::tree::UiDebugPrepaintAction {
1007 node: self.node,
1008 target: Some(self.node),
1009 kind: crate::tree::UiDebugPrepaintActionKind::RequestAnimationFrame,
1010 invalidation: Some(Invalidation::Paint),
1011 element: None,
1012 virtual_list_window_shift_kind: None,
1013 virtual_list_window_shift_reason: None,
1014 chart_sampling_window_key: None,
1015 node_graph_cull_window_key: None,
1016 frame_id: self.app.frame_id(),
1017 });
1018 self.tree.invalidate_with_source_and_detail(
1020 self.node,
1021 Invalidation::Paint,
1022 crate::tree::UiDebugInvalidationSource::Notify,
1023 crate::tree::UiDebugInvalidationDetail::AnimationFrameRequest,
1024 );
1025 let Some(window) = self.window else {
1026 return;
1027 };
1028 self.app.push_effect(Effect::RequestAnimationFrame(window));
1029 }
1030
1031 pub fn debug_record_chart_sampling_window_shift(&mut self, sampling_window_key: u64) {
1036 self.tree
1037 .debug_record_prepaint_action(crate::tree::UiDebugPrepaintAction {
1038 node: self.node,
1039 target: None,
1040 kind: crate::tree::UiDebugPrepaintActionKind::ChartSamplingWindowShift,
1041 invalidation: None,
1042 element: None,
1043 virtual_list_window_shift_kind: None,
1044 virtual_list_window_shift_reason: None,
1045 chart_sampling_window_key: Some(sampling_window_key),
1046 node_graph_cull_window_key: None,
1047 frame_id: self.app.frame_id(),
1048 });
1049 }
1050
1051 pub fn debug_record_node_graph_cull_window_shift(&mut self, cull_window_key: u64) {
1056 self.tree
1057 .debug_record_prepaint_action(crate::tree::UiDebugPrepaintAction {
1058 node: self.node,
1059 target: None,
1060 kind: crate::tree::UiDebugPrepaintActionKind::NodeGraphCullWindowShift,
1061 invalidation: None,
1062 element: None,
1063 virtual_list_window_shift_kind: None,
1064 virtual_list_window_shift_reason: None,
1065 chart_sampling_window_key: None,
1066 node_graph_cull_window_key: Some(cull_window_key),
1067 frame_id: self.app.frame_id(),
1068 });
1069 }
1070}
1071
1072pub struct PaintCx<'a, H: UiHost> {
1073 pub app: &'a mut H,
1074 pub tree: &'a mut crate::tree::UiTree<H>,
1075 pub node: NodeId,
1076 pub window: Option<AppWindowId>,
1077 pub focus: Option<NodeId>,
1078 pub children: &'a [NodeId],
1079 pub bounds: Rect,
1080 pub scale_factor: f32,
1081 pub(crate) paint_style: crate::tree::paint_style::PaintStyleState,
1082 pub accumulated_transform: Transform2D,
1083 pub children_render_transform: Option<Transform2D>,
1084 pub services: &'a mut dyn UiServices,
1085 pub observe_model: &'a mut dyn FnMut(ModelId, Invalidation),
1086 pub observe_global: &'a mut dyn FnMut(TypeId, Invalidation),
1087 pub scene: &'a mut Scene,
1088}
1089
1090impl<'a, H: UiHost> PaintCx<'a, H> {
1091 #[allow(clippy::too_many_arguments)]
1092 pub fn new(
1093 app: &'a mut H,
1094 tree: &'a mut crate::tree::UiTree<H>,
1095 node: NodeId,
1096 window: Option<AppWindowId>,
1097 focus: Option<NodeId>,
1098 children: &'a [NodeId],
1099 bounds: Rect,
1100 scale_factor: f32,
1101 accumulated_transform: Transform2D,
1102 children_render_transform: Option<Transform2D>,
1103 services: &'a mut dyn UiServices,
1104 observe_model: &'a mut dyn FnMut(ModelId, Invalidation),
1105 observe_global: &'a mut dyn FnMut(TypeId, Invalidation),
1106 scene: &'a mut Scene,
1107 ) -> Self {
1108 Self {
1109 app,
1110 tree,
1111 node,
1112 window,
1113 focus,
1114 children,
1115 bounds,
1116 scale_factor,
1117 paint_style: Default::default(),
1118 accumulated_transform,
1119 children_render_transform,
1120 services,
1121 observe_model,
1122 observe_global,
1123 scene,
1124 }
1125 }
1126
1127 pub fn inherited_foreground(&self) -> Option<fret_core::Color> {
1129 self.paint_style.foreground
1130 }
1131
1132 pub fn prepaint_output<T: std::any::Any>(&mut self) -> Option<&T> {
1133 self.tree.prepaint_output(self.node)
1134 }
1135
1136 pub fn prepaint_output_mut<T: std::any::Any>(&mut self) -> Option<&mut T> {
1137 self.tree.prepaint_output_mut(self.node)
1138 }
1139
1140 pub fn theme(&mut self) -> &Theme {
1141 self.observe_global::<Theme>(Invalidation::Paint);
1142 Theme::global(&*self.app)
1143 }
1144
1145 pub fn frame_clock(&self) -> Option<fret_core::WindowFrameClockSnapshot> {
1150 let window = self.window?;
1151 self.app
1152 .global::<fret_core::WindowFrameClockService>()
1153 .and_then(|svc| svc.snapshot(window))
1154 }
1155
1156 pub fn pointer_position_window_snapshot(
1158 &self,
1159 pointer_id: fret_core::PointerId,
1160 ) -> Option<Point> {
1161 let window = self.window?;
1162 self.app
1163 .global::<crate::pointer_motion::WindowPointerMotionService>()
1164 .and_then(|svc| svc.position_window(window, pointer_id))
1165 }
1166
1167 pub fn pointer_velocity_window_snapshot(
1169 &self,
1170 pointer_id: fret_core::PointerId,
1171 ) -> Option<Point> {
1172 let window = self.window?;
1173 self.app
1174 .global::<crate::pointer_motion::WindowPointerMotionService>()
1175 .and_then(|svc| svc.velocity_window(window, pointer_id))
1176 }
1177
1178 pub fn pointer_position_local_snapshot(
1181 &self,
1182 pointer_id: fret_core::PointerId,
1183 ) -> Option<Point> {
1184 let window_pos = self.pointer_position_window_snapshot(pointer_id)?;
1185 let mapped = self
1186 .tree
1187 .map_window_point_to_node_layout_space(self.node, window_pos)?;
1188 Some(Point::new(
1189 fret_core::Px(mapped.x.0 - self.bounds.origin.x.0),
1190 fret_core::Px(mapped.y.0 - self.bounds.origin.y.0),
1191 ))
1192 }
1193
1194 pub fn pointer_velocity_local_snapshot(
1196 &self,
1197 pointer_id: fret_core::PointerId,
1198 ) -> Option<Point> {
1199 let window_vec = self.pointer_velocity_window_snapshot(pointer_id)?;
1200 self.tree
1201 .map_window_vector_to_node_layout_space(self.node, window_vec)
1202 }
1203
1204 pub fn visual_rect_aabb(&self, rect: Rect) -> Rect {
1209 let t = self.accumulated_transform;
1210 if t == Transform2D::IDENTITY {
1211 return rect;
1212 }
1213
1214 let x0 = rect.origin.x.0;
1215 let y0 = rect.origin.y.0;
1216 let x1 = x0 + rect.size.width.0;
1217 let y1 = y0 + rect.size.height.0;
1218
1219 let p00 = t.apply_point(Point::new(fret_core::Px(x0), fret_core::Px(y0)));
1220 let p10 = t.apply_point(Point::new(fret_core::Px(x1), fret_core::Px(y0)));
1221 let p01 = t.apply_point(Point::new(fret_core::Px(x0), fret_core::Px(y1)));
1222 let p11 = t.apply_point(Point::new(fret_core::Px(x1), fret_core::Px(y1)));
1223
1224 let min_x = p00.x.0.min(p10.x.0).min(p01.x.0).min(p11.x.0);
1225 let max_x = p00.x.0.max(p10.x.0).max(p01.x.0).max(p11.x.0);
1226 let min_y = p00.y.0.min(p10.y.0).min(p01.y.0).min(p11.y.0);
1227 let max_y = p00.y.0.max(p10.y.0).max(p01.y.0).max(p11.y.0);
1228
1229 if !min_x.is_finite() || !max_x.is_finite() || !min_y.is_finite() || !max_y.is_finite() {
1230 return rect;
1231 }
1232
1233 Rect::new(
1234 Point::new(fret_core::Px(min_x), fret_core::Px(min_y)),
1235 Size::new(
1236 fret_core::Px((max_x - min_x).max(0.0)),
1237 fret_core::Px((max_y - min_y).max(0.0)),
1238 ),
1239 )
1240 }
1241
1242 pub fn request_redraw(&mut self) {
1247 let Some(window) = self.window else {
1248 return;
1249 };
1250 self.app.request_redraw(window);
1251 }
1252
1253 pub fn request_animation_frame(&mut self) {
1262 self.tree.invalidate_with_source_and_detail(
1264 self.node,
1265 Invalidation::Paint,
1266 crate::tree::UiDebugInvalidationSource::Notify,
1267 crate::tree::UiDebugInvalidationDetail::AnimationFrameRequest,
1268 );
1269 let Some(window) = self.window else {
1270 return;
1271 };
1272 self.app.push_effect(Effect::RequestAnimationFrame(window));
1273 }
1274
1275 pub fn request_animation_frame_paint_only(&mut self) {
1281 self.tree.invalidate_with_source_and_detail(
1282 self.node,
1283 Invalidation::Paint,
1284 crate::tree::UiDebugInvalidationSource::Other,
1285 crate::tree::UiDebugInvalidationDetail::AnimationFrameRequest,
1286 );
1287 let Some(window) = self.window else {
1288 return;
1289 };
1290 self.app.push_effect(Effect::RequestAnimationFrame(window));
1291 }
1292
1293 pub fn observe_model<T>(&mut self, model: &Model<T>, invalidation: Invalidation) {
1294 (self.observe_model)(model.id(), invalidation);
1295 }
1296
1297 pub fn observe_global<T: Any>(&mut self, invalidation: Invalidation) {
1298 (self.observe_global)(TypeId::of::<T>(), invalidation);
1299 }
1300
1301 pub fn paint(&mut self, child: NodeId, bounds: Rect) {
1302 let was_widget_timer_running = self.tree.debug_paint_widget_exclusive_pause();
1303 let child_transform = self.children_render_transform;
1304 if let Some(transform) = child_transform {
1305 self.scene
1306 .push(fret_core::SceneOp::PushTransform { transform });
1307 }
1308
1309 let accumulated = child_transform
1310 .map(|t| self.accumulated_transform.compose(t))
1311 .unwrap_or(self.accumulated_transform);
1312
1313 self.tree.paint_node(
1314 self.app,
1315 self.services,
1316 child,
1317 bounds,
1318 self.scene,
1319 self.scale_factor,
1320 self.paint_style,
1321 accumulated,
1322 );
1323
1324 if child_transform.is_some() {
1325 self.scene.push(fret_core::SceneOp::PopTransform);
1326 }
1327 if was_widget_timer_running {
1328 self.tree.debug_paint_widget_exclusive_resume();
1329 }
1330 }
1331
1332 pub fn paint_children(&mut self) {
1336 for &child in self.children {
1337 if let Some(bounds) = self.child_bounds(child) {
1338 self.paint(child, bounds);
1339 } else {
1340 self.paint(child, self.bounds);
1341 }
1342 }
1343 }
1344
1345 pub fn child_bounds(&self, child: NodeId) -> Option<Rect> {
1346 self.tree.node_bounds(child)
1347 }
1348}
1349
1350pub struct SemanticsCx<'a, H: UiHost> {
1351 pub app: &'a mut H,
1352 pub node: NodeId,
1353 pub window: Option<AppWindowId>,
1354 pub element_id_map: Option<&'a HashMap<u64, NodeId>>,
1355 pub bounds: Rect,
1356 pub children: &'a [NodeId],
1357 pub focus: Option<NodeId>,
1358 pub captured: Option<NodeId>,
1359 pub(crate) role: &'a mut SemanticsRole,
1360 pub(crate) flags: &'a mut SemanticsFlags,
1361 pub(crate) label: &'a mut Option<String>,
1362 pub(crate) value: &'a mut Option<String>,
1363 pub(crate) test_id: &'a mut Option<String>,
1364 pub(crate) extra: &'a mut fret_core::SemanticsNodeExtra,
1365 pub(crate) text_selection: &'a mut Option<(u32, u32)>,
1366 pub(crate) text_composition: &'a mut Option<(u32, u32)>,
1367 pub(crate) actions: &'a mut fret_core::SemanticsActions,
1368 pub(crate) active_descendant: &'a mut Option<NodeId>,
1369 pub(crate) pos_in_set: &'a mut Option<u32>,
1370 pub(crate) set_size: &'a mut Option<u32>,
1371 pub(crate) labelled_by: &'a mut Vec<NodeId>,
1372 pub(crate) described_by: &'a mut Vec<NodeId>,
1373 pub(crate) controls: &'a mut Vec<NodeId>,
1374 pub(crate) inline_spans: &'a mut Vec<fret_core::SemanticsInlineSpan>,
1375}
1376
1377impl<'a, H: UiHost> SemanticsCx<'a, H> {
1378 pub fn resolve_declarative_element(&mut self, element: u64) -> Option<NodeId> {
1379 if let Some(node) = self.element_id_map.and_then(|m| m.get(&element).copied()) {
1380 return Some(node);
1381 }
1382
1383 let window = self.window?;
1384 crate::elements::live_node_for_element(
1385 self.app,
1386 window,
1387 crate::elements::GlobalElementId(element),
1388 )
1389 }
1390
1391 pub fn set_role(&mut self, role: SemanticsRole) {
1392 *self.role = role;
1393 }
1394
1395 pub fn set_label(&mut self, label: impl Into<String>) {
1396 *self.label = Some(label.into());
1397 }
1398
1399 pub fn set_test_id(&mut self, id: impl Into<String>) {
1400 *self.test_id = Some(id.into());
1401 }
1402
1403 pub fn clear_test_id(&mut self) {
1404 *self.test_id = None;
1405 }
1406
1407 pub fn set_value(&mut self, value: impl Into<String>) {
1408 *self.value = Some(value.into());
1409 }
1410
1411 pub fn set_placeholder<T: Into<String>>(&mut self, placeholder: Option<T>) {
1412 self.extra.placeholder = placeholder.map(Into::into);
1413 }
1414
1415 pub fn set_url<T: Into<String>>(&mut self, url: Option<T>) {
1416 self.extra.url = url.map(Into::into);
1417 }
1418
1419 pub fn set_role_description<T: Into<String>>(&mut self, role_description: Option<T>) {
1420 self.extra.role_description = role_description.map(Into::into);
1421 }
1422
1423 pub fn clear_role_description(&mut self) {
1424 self.extra.role_description = None;
1425 }
1426
1427 pub fn set_level(&mut self, level: Option<u32>) {
1428 self.extra.level = level;
1429 }
1430
1431 pub fn set_orientation(&mut self, orientation: Option<SemanticsOrientation>) {
1432 self.extra.orientation = orientation;
1433 }
1434
1435 pub fn clear_orientation(&mut self) {
1436 self.extra.orientation = None;
1437 }
1438
1439 pub fn set_numeric_value(&mut self, value: Option<f64>) {
1440 self.extra.numeric.value = value;
1441 }
1442
1443 pub fn set_numeric_range(&mut self, min: Option<f64>, max: Option<f64>) {
1444 self.extra.numeric.min = min;
1445 self.extra.numeric.max = max;
1446 }
1447
1448 pub fn set_numeric_step(&mut self, step: Option<f64>) {
1449 self.extra.numeric.step = step;
1450 }
1451
1452 pub fn set_numeric_jump(&mut self, jump: Option<f64>) {
1453 self.extra.numeric.jump = jump;
1454 }
1455
1456 pub fn set_scroll_x(&mut self, x: Option<f64>, min: Option<f64>, max: Option<f64>) {
1457 self.extra.scroll.x = x;
1458 self.extra.scroll.x_min = min;
1459 self.extra.scroll.x_max = max;
1460 }
1461
1462 pub fn set_scroll_y(&mut self, y: Option<f64>, min: Option<f64>, max: Option<f64>) {
1463 self.extra.scroll.y = y;
1464 self.extra.scroll.y_min = min;
1465 self.extra.scroll.y_max = max;
1466 }
1467
1468 pub fn set_text_selection(&mut self, anchor: u32, focus: u32) {
1469 *self.text_selection = Some((anchor, focus));
1470 }
1471
1472 pub fn clear_text_selection(&mut self) {
1473 *self.text_selection = None;
1474 }
1475
1476 pub fn set_text_composition(&mut self, start: u32, end: u32) {
1477 *self.text_composition = Some((start, end));
1478 }
1479
1480 pub fn clear_text_composition(&mut self) {
1481 *self.text_composition = None;
1482 }
1483
1484 pub fn set_focusable(&mut self, focusable: bool) {
1485 self.actions.focus = focusable;
1486 }
1487
1488 pub fn set_invokable(&mut self, invokable: bool) {
1489 self.actions.invoke = invokable;
1490 }
1491
1492 pub fn set_value_editable(&mut self, editable: bool) {
1493 match *self.role {
1494 SemanticsRole::TextField => {
1496 self.actions.set_value = editable;
1497 }
1498 SemanticsRole::Slider | SemanticsRole::SpinButton | SemanticsRole::Splitter => {
1501 self.actions.increment = editable;
1502 self.actions.decrement = editable;
1503 }
1504 _ => {
1505 self.actions.set_value = editable;
1506 }
1507 }
1508 }
1509
1510 pub fn set_increment_supported(&mut self, supported: bool) {
1511 self.actions.increment = supported;
1512 }
1513
1514 pub fn set_decrement_supported(&mut self, supported: bool) {
1515 self.actions.decrement = supported;
1516 }
1517
1518 pub fn set_scroll_by_supported(&mut self, supported: bool) {
1519 self.actions.scroll_by = supported;
1520 }
1521
1522 pub fn set_text_selection_supported(&mut self, supported: bool) {
1523 self.actions.set_text_selection = supported;
1524 }
1525
1526 pub fn set_disabled(&mut self, disabled: bool) {
1527 self.flags.disabled = disabled;
1528 }
1529
1530 pub fn set_read_only(&mut self, read_only: bool) {
1531 self.flags.read_only = read_only;
1532 }
1533
1534 pub fn set_hidden(&mut self, hidden: bool) {
1535 self.flags.hidden = hidden;
1536 }
1537
1538 pub fn set_visited(&mut self, visited: bool) {
1539 self.flags.visited = visited;
1540 }
1541
1542 pub fn set_multiselectable(&mut self, multiselectable: bool) {
1543 self.flags.multiselectable = multiselectable;
1544 }
1545
1546 pub fn set_selected(&mut self, selected: bool) {
1547 self.flags.selected = selected;
1548 }
1549
1550 pub fn set_expanded(&mut self, expanded: bool) {
1551 self.flags.expanded = expanded;
1552 }
1553
1554 pub fn set_checked(&mut self, checked: Option<bool>) {
1555 self.flags.checked = checked;
1556 }
1557
1558 pub fn set_checked_state(&mut self, checked: Option<SemanticsCheckedState>) {
1559 self.flags.checked_state = checked;
1560 match checked {
1561 Some(SemanticsCheckedState::True) => self.flags.checked = Some(true),
1562 Some(SemanticsCheckedState::False) => self.flags.checked = Some(false),
1563 Some(SemanticsCheckedState::Mixed) => self.flags.checked = None,
1564 None => {}
1565 _ => {}
1566 }
1567 }
1568
1569 pub fn clear_checked_state(&mut self) {
1570 self.flags.checked_state = None;
1571 }
1572
1573 pub fn set_pressed_state(&mut self, pressed: Option<SemanticsPressedState>) {
1574 self.flags.pressed_state = pressed;
1575 }
1576
1577 pub fn clear_pressed_state(&mut self) {
1578 self.flags.pressed_state = None;
1579 }
1580
1581 pub fn set_required(&mut self, required: bool) {
1582 self.flags.required = required;
1583 }
1584
1585 pub fn set_invalid(&mut self, invalid: Option<SemanticsInvalid>) {
1586 self.flags.invalid = invalid;
1587 }
1588
1589 pub fn clear_invalid(&mut self) {
1590 self.flags.invalid = None;
1591 }
1592
1593 pub fn set_busy(&mut self, busy: bool) {
1594 self.flags.busy = busy;
1595 }
1596
1597 pub fn set_live(&mut self, live: Option<SemanticsLive>) {
1598 self.flags.live = live;
1599 }
1600
1601 pub fn clear_live(&mut self) {
1602 self.flags.live = None;
1603 }
1604
1605 pub fn set_live_atomic(&mut self, live_atomic: bool) {
1606 self.flags.live_atomic = live_atomic;
1607 }
1608
1609 pub fn set_active_descendant(&mut self, node: Option<NodeId>) {
1610 *self.active_descendant = node;
1611 }
1612
1613 pub fn set_pos_in_set(&mut self, pos_in_set: Option<u32>) {
1614 *self.pos_in_set = pos_in_set;
1615 }
1616
1617 pub fn set_set_size(&mut self, set_size: Option<u32>) {
1618 *self.set_size = set_size;
1619 }
1620
1621 pub fn set_collection_position(&mut self, pos_in_set: Option<u32>, set_size: Option<u32>) {
1622 *self.pos_in_set = pos_in_set;
1623 *self.set_size = set_size;
1624 }
1625
1626 pub fn push_labelled_by(&mut self, node: NodeId) {
1627 if self.labelled_by.contains(&node) {
1628 return;
1629 }
1630 self.labelled_by.push(node);
1631 }
1632
1633 pub fn clear_labelled_by(&mut self) {
1634 self.labelled_by.clear();
1635 }
1636
1637 pub fn push_described_by(&mut self, node: NodeId) {
1638 if self.described_by.contains(&node) {
1639 return;
1640 }
1641 self.described_by.push(node);
1642 }
1643
1644 pub fn clear_described_by(&mut self) {
1645 self.described_by.clear();
1646 }
1647
1648 pub fn push_controlled(&mut self, node: NodeId) {
1649 if self.controls.contains(&node) {
1650 return;
1651 }
1652 self.controls.push(node);
1653 }
1654
1655 pub fn push_inline_span(&mut self, span: fret_core::SemanticsInlineSpan) {
1656 self.inline_spans.push(span);
1657 }
1658
1659 pub fn push_inline_link_span(&mut self, start_utf8: u32, end_utf8: u32, tag: Option<String>) {
1660 self.push_inline_span(fret_core::SemanticsInlineSpan {
1661 range_utf8: (start_utf8, end_utf8),
1662 role: SemanticsRole::Link,
1663 tag,
1664 });
1665 }
1666
1667 pub fn clear_controls(&mut self) {
1668 self.controls.clear();
1669 }
1670}
1671
1672pub trait Widget<H: UiHost> {
1673 fn event_capture(&mut self, _cx: &mut EventCx<'_, H>, _event: &Event) {}
1677
1678 fn event_observer(&mut self, _cx: &mut ObserverCx<'_, H>, _event: &Event) {}
1684
1685 fn debug_type_name(&self) -> &'static str {
1686 std::any::type_name::<Self>()
1687 }
1688
1689 fn event(&mut self, _cx: &mut EventCx<'_, H>, _event: &Event) {}
1690 fn command(&mut self, _cx: &mut CommandCx<'_, H>, _command: &CommandId) -> bool {
1691 false
1692 }
1693
1694 fn command_availability(
1696 &self,
1697 _cx: &mut CommandAvailabilityCx<'_, H>,
1698 _command: &CommandId,
1699 ) -> CommandAvailability {
1700 CommandAvailability::NotHandled
1701 }
1702 fn cleanup_resources(&mut self, _services: &mut dyn UiServices) {}
1703 fn render_transform(&self, _bounds: Rect) -> Option<Transform2D> {
1716 None
1717 }
1718 fn children_render_transform(&self, _bounds: Rect) -> Option<Transform2D> {
1726 None
1727 }
1728 fn cursor_icon_at(
1738 &self,
1739 _bounds: Rect,
1740 _position: Point,
1741 _input_ctx: &fret_runtime::InputContext,
1742 ) -> Option<fret_core::CursorIcon> {
1743 None
1744 }
1745 fn clips_hit_test(&self, _bounds: Rect) -> bool {
1752 true
1753 }
1754 fn clip_hit_test_corner_radii(&self, _bounds: Rect) -> Option<Corners> {
1762 None
1763 }
1764 fn hit_test(&self, _bounds: Rect, _position: Point) -> bool {
1771 true
1772 }
1773 fn hit_test_children(&self, _bounds: Rect, _position: Point) -> bool {
1780 true
1781 }
1782 fn semantics_present(&self) -> bool {
1790 true
1791 }
1792 fn semantics_children(&self) -> bool {
1796 true
1797 }
1798 fn sync_interactivity_gate(&mut self, _present: bool, _interactive: bool) {}
1806
1807 fn sync_hit_test_gate(&mut self, _hit_test: bool) {}
1813
1814 fn sync_focus_traversal_gate(&mut self, _traverse: bool) {}
1820 fn focus_traversal_children(&self) -> bool {
1828 true
1829 }
1830 fn is_focusable(&self) -> bool {
1831 false
1832 }
1833 fn is_text_input(&self) -> bool {
1834 false
1835 }
1836
1837 fn platform_text_input_snapshot(&self) -> Option<fret_runtime::WindowTextInputSnapshot> {
1845 None
1846 }
1847
1848 fn platform_text_input_selected_range_utf16(&self) -> Option<fret_runtime::Utf16Range> {
1850 None
1851 }
1852
1853 fn platform_text_input_marked_range_utf16(&self) -> Option<fret_runtime::Utf16Range> {
1855 None
1856 }
1857
1858 fn platform_text_input_text_for_range_utf16(
1859 &self,
1860 _range: fret_runtime::Utf16Range,
1861 ) -> Option<String> {
1862 None
1863 }
1864
1865 fn platform_text_input_bounds_for_range_utf16(
1866 &mut self,
1867 _cx: &mut PlatformTextInputCx<'_, H>,
1868 _range: fret_runtime::Utf16Range,
1869 ) -> Option<Rect> {
1870 None
1871 }
1872
1873 fn platform_text_input_character_index_for_point_utf16(
1874 &mut self,
1875 _cx: &mut PlatformTextInputCx<'_, H>,
1876 _point: Point,
1877 ) -> Option<u32> {
1878 None
1879 }
1880
1881 fn platform_text_input_replace_text_in_range_utf16(
1882 &mut self,
1883 _cx: &mut PlatformTextInputCx<'_, H>,
1884 _range: fret_runtime::Utf16Range,
1885 _text: &str,
1886 ) -> bool {
1887 false
1888 }
1889
1890 fn platform_text_input_replace_and_mark_text_in_range_utf16(
1891 &mut self,
1892 _cx: &mut PlatformTextInputCx<'_, H>,
1893 _range: fret_runtime::Utf16Range,
1894 _text: &str,
1895 _marked: Option<fret_runtime::Utf16Range>,
1896 _selected: Option<fret_runtime::Utf16Range>,
1897 ) -> bool {
1898 false
1899 }
1900 fn can_scroll_by(&self) -> bool {
1902 false
1903 }
1904 fn scroll_by(&mut self, _cx: &mut ScrollByCx<'_, H>, _delta: Point) -> ScrollByResult {
1905 ScrollByResult::NotHandled
1906 }
1907 fn can_scroll_descendant_into_view(&self) -> bool {
1913 false
1914 }
1915 fn scroll_descendant_into_view(
1916 &mut self,
1917 _cx: &mut ScrollIntoViewCx<'_, H>,
1918 _descendant_bounds: Rect,
1919 ) -> ScrollIntoViewResult {
1920 ScrollIntoViewResult::NotHandled
1921 }
1922 fn measure(&mut self, _cx: &mut MeasureCx<'_, H>) -> Size {
1923 Size::default()
1924 }
1925 fn layout(&mut self, _cx: &mut LayoutCx<'_, H>) -> Size {
1926 Size::default()
1927 }
1928 fn prepaint(&mut self, _cx: &mut PrepaintCx<'_, H>) {}
1932 fn paint(&mut self, cx: &mut PaintCx<'_, H>) {
1933 cx.paint_children();
1934 }
1935 fn semantics(&mut self, _cx: &mut SemanticsCx<'_, H>) {}
1936}
1937
1938#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
1939pub enum ScrollByResult {
1940 #[default]
1941 NotHandled,
1942 Handled {
1943 did_scroll: bool,
1944 },
1945}
1946
1947pub struct ScrollByCx<'a, H: UiHost> {
1948 pub app: &'a mut H,
1949 pub node: NodeId,
1950 pub window: Option<AppWindowId>,
1951 pub bounds: Rect,
1952}
1953
1954#[derive(Debug, Default, Clone, Copy)]
1955pub enum ScrollIntoViewResult {
1956 #[default]
1957 NotHandled,
1958 Handled {
1959 did_scroll: bool,
1960 propagated_bounds: Option<Rect>,
1964 },
1965}
1966
1967pub struct ScrollIntoViewCx<'a, H: UiHost> {
1968 pub app: &'a mut H,
1969 pub node: NodeId,
1970 pub window: Option<AppWindowId>,
1971 pub bounds: Rect,
1972}
1973
1974pub struct PlatformTextInputCx<'a, H: UiHost> {
1975 pub app: &'a mut H,
1976 pub services: &'a mut dyn UiServices,
1977 pub window: Option<AppWindowId>,
1978 pub node: NodeId,
1979 pub bounds: Rect,
1980 pub scale_factor: f32,
1981}
1982
1983impl<'a, H: UiHost> PlatformTextInputCx<'a, H> {
1984 pub fn theme(&self) -> &Theme {
1985 Theme::global(&*self.app)
1986 }
1987}