Skip to main content

tessera_ui/
modifier.rs

1//! Modifier chains for node-local layout, drawing, focus, semantics, and other
2//! node-scoped behavior.
3//!
4//! ## Usage
5//!
6//! Build reusable modifier chains that attach behavior directly to the current
7//! component node.
8
9use std::{
10    any::{Any, TypeId},
11    collections::HashMap,
12    fmt,
13    hash::{Hash, Hasher},
14    sync::Arc,
15};
16
17use rustc_hash::FxHasher;
18use smallvec::SmallVec;
19
20use crate::{
21    AccessibilityActionHandler, AccessibilityNode, ComputedData, Constraint, FocusGroupNode,
22    FocusProperties, FocusRequester, FocusScopeNode, FocusState, FocusTraversalPolicy, ImeInput,
23    KeyboardInput, MeasurementError, PointerInput, PxPosition,
24    clipboard::ClipboardInputModifierNode,
25    focus::{FocusDirection, FocusNode, FocusRevealRequest},
26    layout::{
27        LayoutInput, LayoutResult, MeasureScope, PlacementResult, PlacementScope, RenderInput,
28    },
29    prop::CallbackWith,
30    runtime::{TesseraRuntime, ensure_build_phase},
31    winit::window::CursorIcon,
32};
33
34/// Parent-data payloads collected from modifier nodes.
35pub type ParentDataMap = HashMap<TypeId, Arc<dyn Any + Send + Sync>>;
36
37/// Child measurement entry used by layout modifier nodes.
38pub trait LayoutModifierChild {
39    /// Measures the wrapped content under the given constraint.
40    fn measure(&mut self, constraint: &Constraint) -> Result<ComputedData, MeasurementError>;
41
42    /// Places the wrapped content at the provided relative position.
43    fn place(&mut self, position: PxPosition);
44}
45
46/// Input passed to layout modifier nodes.
47pub struct LayoutModifierInput<'a> {
48    /// The original layout input for the current node.
49    pub layout_input: &'a LayoutInput<'a>,
50}
51
52impl LayoutModifierInput<'_> {
53    /// Calculates positions and placement bookkeeping independent of measured
54    /// size.
55    ///
56    /// State reads invalidate placement. Size and constraint calculations must
57    /// remain outside this closure.
58    pub fn placement<R>(&self, calculate: impl FnOnce() -> R) -> R {
59        let _phase = crate::runtime::push_phase(crate::runtime::RuntimePhase::Place);
60        calculate()
61    }
62}
63
64/// Draw continuation used by draw modifier nodes.
65pub trait DrawModifierContent {
66    /// Records the wrapped content.
67    fn draw(&mut self, input: &mut RenderInput<'_>);
68}
69
70/// Draw input passed to draw modifier nodes.
71pub struct DrawModifierContext<'a, 'b> {
72    /// The render input for the current node.
73    pub render_input: &'a mut RenderInput<'b>,
74}
75
76/// A node-local layout modifier.
77///
78/// This is the single layout measurement contract shared by the innermost
79/// layout node (the measure policy applied at the innermost layer) and by
80/// wrapping layout modifiers. A node overrides either [`Self::measure`] (pure
81/// measure, applied at the innermost layer) or [`Self::measure_content`]
82/// (wrapping measure). An innermost node may override [`Self::place_children`]
83/// to update placements against cached child sizes. Wrapping modifiers can be
84/// measured again around that cached result to update their content bounds.
85pub trait LayoutModifierNode: Send + Sync + 'static {
86    /// Computes layout for this node when it is applied at the innermost layer.
87    fn measure(&self, _scope: &mut MeasureScope<'_>) -> Result<LayoutResult, MeasurementError> {
88        Err(MeasurementError::MeasureFnFailed(
89            "layout node does not implement pure measure".to_string(),
90        ))
91    }
92
93    /// Measures and places the wrapped content.
94    fn measure_content(
95        &self,
96        _input: &LayoutModifierInput<'_>,
97        _child: &mut dyn LayoutModifierChild,
98    ) -> Result<LayoutResult, MeasurementError> {
99        Err(MeasurementError::MeasureFnFailed(
100            "layout node does not implement wrapped measure".to_string(),
101        ))
102    }
103
104    /// Compares measurement-relevant state for layout dirty tracking.
105    fn measure_eq(&self, other: &Self) -> bool
106    where
107        Self: Sized + PartialEq,
108    {
109        self == other
110    }
111
112    /// Compares placement-relevant state for layout dirty tracking.
113    fn placement_eq(&self, other: &Self) -> bool
114    where
115        Self: Sized + PartialEq,
116    {
117        self == other
118    }
119
120    /// Recomputes child placements from cached child sizes.
121    ///
122    /// An innermost layout node derives placements from the cached child sizes
123    /// in `scope`. Returning `None` means it cannot place without re-measuring.
124    /// Wrapping modifiers use [`Self::measure_content`] around the cached
125    /// result so drawing receives the same content bounds as child
126    /// placement.
127    fn place_children(
128        &self,
129        _scope: &PlacementScope<'_>,
130        _inner: &PlacementResult,
131    ) -> Option<PlacementResult> {
132        None
133    }
134}
135
136/// A node-local placement modifier that transforms the current node position
137/// without affecting measured size.
138pub trait PlacementModifierNode: Send + Sync + 'static {
139    /// Returns the concrete modifier name used by layout inspection.
140    fn diagnostic_name(&self) -> &'static str {
141        std::any::type_name::<Self>()
142    }
143    /// Returns the transformed node position relative to the parent.
144    fn transform_position(&self, position: PxPosition) -> PxPosition;
145}
146
147/// A node-local draw modifier.
148pub trait DrawModifierNode: Send + Sync + 'static {
149    /// Returns the concrete modifier name used by paint inspection.
150    fn diagnostic_name(&self) -> &'static str {
151        std::any::type_name::<Self>()
152    }
153
154    /// Records drawing behavior around the wrapped content.
155    ///
156    /// Drawing uses the content bounds at this position in the modifier chain.
157    /// For example, a background before padding includes that padding, while a
158    /// background after padding covers only the inner content.
159    fn draw(&self, ctx: &mut DrawModifierContext<'_, '_>, content: &mut dyn DrawModifierContent);
160}
161
162/// A node-local parent-data modifier.
163pub trait ParentDataModifierNode: Any + Send + Sync + 'static {
164    /// Applies parent data visible to the parent layout.
165    fn apply_parent_data(&self, data: &mut ParentDataMap);
166
167    /// Returns whether this node supplies the same layout inputs as another.
168    ///
169    /// The default conservatively invalidates the parent layout when the node
170    /// is replaced. Override this for value-based parent data such as weights.
171    fn equivalent(&self, _other: &dyn ParentDataModifierNode) -> bool {
172        false
173    }
174}
175
176/// A node-local build modifier.
177pub trait BuildModifierNode: Any + Send + Sync {
178    /// Applies build-time effects to the current component node.
179    fn apply(&self, _runtime: &mut TesseraRuntime) {}
180
181    /// Expands a stateful modifier at attachment time.
182    ///
183    /// Construct ordinary modifier data here. The context binds node state and
184    /// frame subscriptions to this capability in its owning node.
185    fn materialize(&self, _context: &mut ModifierBuildContext) -> Option<Modifier> {
186        None
187    }
188
189    /// Returns this node's nested scroll attachment, when present.
190    fn nested_scroll_connection(&self) -> Option<crate::nested_scroll::NestedScrollConnection> {
191        None
192    }
193}
194
195/// Node-owned services available while attaching a stateful modifier.
196///
197/// Modifier constructors remain reusable values. Their state is allocated when
198/// the chain attaches. State survives rebuilds of the same node and capability;
199/// inserting unrelated modifier types does not change that ownership.
200pub struct ModifierBuildContext {
201    instance: u64,
202    owner: u64,
203    path: Vec<u64>,
204    state_positions: SmallVec<[(TypeId, usize); 4]>,
205    clock_positions: SmallVec<[(TypeId, usize); 4]>,
206}
207
208fn next_type_position(positions: &mut SmallVec<[(TypeId, usize); 4]>, id: TypeId) -> usize {
209    if let Some((_, next)) = positions.iter_mut().find(|(kind, _)| *kind == id) {
210        let position = *next;
211        *next = next.checked_add(1).expect("modifier position exhausted");
212        position
213    } else {
214        positions.push((id, 1));
215        0
216    }
217}
218
219fn modifier_type_key(id: TypeId) -> u64 {
220    let mut hasher = FxHasher::default();
221    id.hash(&mut hasher);
222    hasher.finish()
223}
224
225impl ModifierBuildContext {
226    /// Gets persistent state by value type and occurrence within this capability.
227    ///
228    /// Calls of a different value type do not shift existing state. Multiple
229    /// values of the same type follow their declaration order.
230    pub fn state<T: Send + Sync + 'static>(&mut self, init: impl FnOnce() -> T) -> crate::State<T> {
231        let ordinal = next_type_position(&mut self.state_positions, TypeId::of::<T>());
232        crate::runtime::modifier_state(self.instance, &self.path, ordinal, init)
233    }
234
235    /// Registers a callback on this node's frame clock.
236    pub fn receive_frame_nanos<F>(&mut self, callback: F)
237    where
238        F: FnMut(u64) -> crate::FrameNanosControl + Send + 'static,
239    {
240        let callback_type = TypeId::of::<F>();
241        let ordinal = next_type_position(&mut self.clock_positions, callback_type);
242        let mut path = self.path.clone();
243        path.push(u64::MAX);
244        path.push(modifier_type_key(callback_type));
245        crate::runtime::modifier_frame_receiver(
246            self.instance,
247            self.owner,
248            &path,
249            ordinal,
250            callback,
251        );
252    }
253
254    /// Gets the nearest nested scroll connection above the attached node.
255    pub fn nested_scroll_parent(&self) -> Option<crate::nested_scroll::NestedScrollConnection> {
256        crate::nested_scroll::NestedScrollConnection::nearest()
257    }
258}
259
260/// A node-local semantics modifier.
261pub trait SemanticsModifierNode: Send + Sync + 'static {
262    /// Applies accessibility metadata and optional action handling.
263    fn apply(
264        &self,
265        accessibility: &mut AccessibilityNode,
266        action_handler: &mut Option<AccessibilityActionHandler>,
267    );
268}
269
270/// A node-local pointer input modifier.
271pub trait PointerInputModifierNode: Send + Sync + 'static {
272    /// Handles pointer input for the current node.
273    ///
274    /// Pointer modifiers should operate on pointer-specific event flow and
275    /// local interaction state. Cross-cutting concerns such as semantics,
276    /// hover cursors, and IME publication should use their dedicated modifier
277    /// or session APIs.
278    fn on_pointer_input(&self, input: PointerInput<'_>);
279}
280
281/// A node-local hover cursor modifier.
282pub trait CursorModifierNode: Send + Sync + 'static {
283    /// Returns the cursor icon that should be used when the pointer hovers this
284    /// node.
285    fn cursor_icon(&self) -> CursorIcon;
286}
287
288/// A node-local keyboard input modifier.
289pub trait KeyboardInputModifierNode: Send + Sync + 'static {
290    /// Handles keyboard input for the current node.
291    fn on_keyboard_input(&self, input: KeyboardInput<'_>);
292}
293
294/// A node-local IME input modifier.
295pub trait ImeInputModifierNode: Send + Sync + 'static {
296    /// Handles IME input for the current node.
297    fn on_ime_input(&self, input: ImeInput<'_>);
298}
299
300/// Low-level modifier primitive API for framework crates that build semantic
301/// modifier extensions.
302///
303/// This trait is intended for crates such as `tessera-foundation` and
304/// `tessera-components` that implement higher-level `Modifier` extensions. Most
305/// application code should prefer those semantic extensions instead of calling
306/// these methods directly.
307pub trait ModifierCapabilityExt {
308    /// Appends a layout modifier node to the current modifier chain.
309    fn push_layout<N>(self, node: N) -> Self
310    where
311        N: LayoutModifierNode + PartialEq;
312
313    /// Appends a placement modifier node to the current modifier chain.
314    fn push_placement<N>(self, node: N) -> Self
315    where
316        N: PlacementModifierNode + PartialEq;
317
318    /// Appends a draw modifier node to the current modifier chain.
319    fn push_draw<N>(self, node: N) -> Self
320    where
321        N: DrawModifierNode;
322
323    /// Appends a parent-data modifier node to the current modifier chain.
324    fn push_parent_data<N>(self, node: N) -> Self
325    where
326        N: ParentDataModifierNode;
327
328    /// Appends a build modifier node to the current modifier chain.
329    fn push_build<N>(self, node: N) -> Self
330    where
331        N: BuildModifierNode;
332
333    /// Appends a semantics modifier node to the current modifier chain.
334    fn push_semantics<N>(self, node: N) -> Self
335    where
336        N: SemanticsModifierNode;
337
338    /// Appends a preview pointer-input modifier node to the current modifier
339    /// chain.
340    fn push_pointer_preview_input<N>(self, node: N) -> Self
341    where
342        N: PointerInputModifierNode;
343
344    /// Appends a pointer-input modifier node to the current modifier chain.
345    fn push_pointer_input<N>(self, node: N) -> Self
346    where
347        N: PointerInputModifierNode;
348
349    /// Appends a final pointer-input modifier node to the current modifier
350    /// chain.
351    fn push_pointer_final_input<N>(self, node: N) -> Self
352    where
353        N: PointerInputModifierNode;
354
355    /// Appends a preview keyboard-input modifier node to the current modifier
356    /// chain.
357    fn push_keyboard_preview_input<N>(self, node: N) -> Self
358    where
359        N: KeyboardInputModifierNode;
360
361    /// Appends a keyboard-input modifier node to the current modifier chain.
362    fn push_keyboard_input<N>(self, node: N) -> Self
363    where
364        N: KeyboardInputModifierNode;
365
366    /// Appends a clipboard-input modifier for focused content.
367    fn push_clipboard_input<N>(self, node: N) -> Self
368    where
369        N: ClipboardInputModifierNode;
370
371    /// Appends a preview IME-input modifier node to the current modifier chain.
372    fn push_ime_preview_input<N>(self, node: N) -> Self
373    where
374        N: ImeInputModifierNode;
375
376    /// Appends an IME-input modifier node to the current modifier chain.
377    fn push_ime_input<N>(self, node: N) -> Self
378    where
379        N: ImeInputModifierNode;
380}
381
382#[derive(Clone, PartialEq, Eq)]
383pub(crate) enum FocusModifierRegistration {
384    Target(Option<FocusNode>),
385    Scope(Option<FocusScopeNode>),
386    Group(Option<FocusGroupNode>),
387    Restorer {
388        scope: Option<FocusScopeNode>,
389        fallback: Option<FocusRequester>,
390    },
391}
392
393#[derive(Clone, PartialEq, Eq)]
394enum FocusModifierOp {
395    Requester(FocusRequester),
396    Registration(FocusModifierRegistration),
397    Properties(Arc<FocusProperties>),
398    PreferredTabStop(bool),
399    TraversalPolicy(FocusTraversalPolicy),
400    ChangedHandler(CallbackWith<FocusState>),
401    EventHandler(CallbackWith<FocusState>),
402    BeyondBoundsHandler(CallbackWith<FocusDirection, bool>),
403    RevealHandler(CallbackWith<FocusRevealRequest, bool>),
404}
405
406pub(crate) trait ErasedLayoutModifierNode: Send + Sync + 'static {
407    fn node(&self) -> Arc<dyn LayoutModifierNode>;
408
409    fn eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool;
410
411    fn measure_eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool;
412
413    fn placement_eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool;
414
415    fn hash_node(&self, state: &mut dyn Hasher);
416
417    fn as_any(&self) -> &dyn Any;
418}
419
420pub(crate) trait ErasedPlacementModifierNode: Send + Sync + 'static {
421    fn node(&self) -> Arc<dyn PlacementModifierNode>;
422
423    fn eq(&self, other: &dyn ErasedPlacementModifierNode) -> bool;
424
425    fn hash_node(&self, state: &mut dyn Hasher);
426
427    fn as_any(&self) -> &dyn Any;
428}
429
430struct ComparableLayoutModifierNode<N>
431where
432    N: LayoutModifierNode + PartialEq,
433{
434    node: Arc<N>,
435}
436
437impl<N> ErasedLayoutModifierNode for ComparableLayoutModifierNode<N>
438where
439    N: LayoutModifierNode + PartialEq,
440{
441    fn node(&self) -> Arc<dyn LayoutModifierNode> {
442        self.node.clone()
443    }
444
445    fn eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool {
446        other
447            .as_any()
448            .downcast_ref::<Self>()
449            .is_some_and(|other| self.node.as_ref() == other.node.as_ref())
450    }
451
452    fn measure_eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool {
453        other
454            .as_any()
455            .downcast_ref::<Self>()
456            .is_some_and(|other| self.node.measure_eq(other.node.as_ref()))
457    }
458
459    fn placement_eq(&self, other: &dyn ErasedLayoutModifierNode) -> bool {
460        other
461            .as_any()
462            .downcast_ref::<Self>()
463            .is_some_and(|other| self.node.placement_eq(other.node.as_ref()))
464    }
465
466    fn hash_node(&self, state: &mut dyn Hasher) {
467        // Hash by concrete node type. Equal nodes share a type and therefore a
468        // hash, which is all the hash/equality contract requires.
469        let mut concrete = std::collections::hash_map::DefaultHasher::new();
470        TypeId::of::<N>().hash(&mut concrete);
471        state.write_u64(concrete.finish());
472    }
473
474    fn as_any(&self) -> &dyn Any {
475        self
476    }
477}
478
479struct ComparablePlacementModifierNode<N>
480where
481    N: PlacementModifierNode + PartialEq,
482{
483    node: Arc<N>,
484}
485
486impl<N> ErasedPlacementModifierNode for ComparablePlacementModifierNode<N>
487where
488    N: PlacementModifierNode + PartialEq,
489{
490    fn node(&self) -> Arc<dyn PlacementModifierNode> {
491        self.node.clone()
492    }
493
494    fn eq(&self, other: &dyn ErasedPlacementModifierNode) -> bool {
495        other
496            .as_any()
497            .downcast_ref::<Self>()
498            .is_some_and(|other| self.node.as_ref() == other.node.as_ref())
499    }
500
501    fn hash_node(&self, state: &mut dyn Hasher) {
502        // Hash by concrete node type. Equal nodes share a type and therefore a
503        // hash, which is all the hash/equality contract requires.
504        let mut concrete = std::collections::hash_map::DefaultHasher::new();
505        TypeId::of::<N>().hash(&mut concrete);
506        state.write_u64(concrete.finish());
507    }
508
509    fn as_any(&self) -> &dyn Any {
510        self
511    }
512}
513
514#[derive(Clone)]
515enum ModifierAction {
516    Layout(Arc<dyn ErasedLayoutModifierNode>),
517    Placement(Arc<dyn ErasedPlacementModifierNode>),
518    Draw(Arc<dyn DrawModifierNode>),
519    ParentData(Arc<dyn ParentDataModifierNode>),
520    Build(Arc<dyn BuildModifierNode>),
521    Semantics(Arc<dyn SemanticsModifierNode>),
522    Cursor(Arc<dyn CursorModifierNode>),
523    PointerPreviewInput(Arc<dyn PointerInputModifierNode>),
524    PointerInput(Arc<dyn PointerInputModifierNode>),
525    PointerFinalInput(Arc<dyn PointerInputModifierNode>),
526    KeyboardPreviewInput(Arc<dyn KeyboardInputModifierNode>),
527    KeyboardInput(Arc<dyn KeyboardInputModifierNode>),
528    ClipboardInput(Arc<dyn ClipboardInputModifierNode>),
529    ImePreviewInput(Arc<dyn ImeInputModifierNode>),
530    ImeInput(Arc<dyn ImeInputModifierNode>),
531    Focus(FocusModifierOp),
532    GloballyPositioned(crate::component_tree::GloballyPositionedHandler),
533}
534
535impl ModifierAction {
536    fn layout_node(&self) -> Option<&dyn ErasedLayoutModifierNode> {
537        match self {
538            Self::Layout(node) => Some(node.as_ref()),
539            _ => None,
540        }
541    }
542}
543
544#[derive(Clone)]
545pub(crate) enum OrderedModifierAction {
546    Layout,
547    Placement(Arc<dyn ErasedPlacementModifierNode>),
548    Draw(Arc<dyn DrawModifierNode>),
549    ParentData,
550    Cursor(Arc<dyn CursorModifierNode>),
551    PointerPreviewInput(Arc<dyn PointerInputModifierNode>),
552    PointerInput(Arc<dyn PointerInputModifierNode>),
553    PointerFinalInput(Arc<dyn PointerInputModifierNode>),
554    KeyboardPreviewInput(Arc<dyn KeyboardInputModifierNode>),
555    KeyboardInput(Arc<dyn KeyboardInputModifierNode>),
556    ClipboardInput(Arc<dyn ClipboardInputModifierNode>),
557    ImePreviewInput(Arc<dyn ImeInputModifierNode>),
558    ImeInput(Arc<dyn ImeInputModifierNode>),
559}
560
561/// Compose-style modifier element.
562///
563/// `Combined` keeps the outer chain and the inner chain. `then` does not
564/// flatten or rebuild individual links. Equality and hashing walk the folded
565/// action sequence, so an empty side disappears: `a.then(empty) == a`.
566#[derive(Clone)]
567enum ModifierElement {
568    Empty,
569    Action(ModifierAction),
570    Combined(Arc<ModifierCombination>),
571}
572
573struct ModifierCombination {
574    outer: ModifierElement,
575    inner: ModifierElement,
576}
577
578impl Default for ModifierElement {
579    fn default() -> Self {
580        Self::Empty
581    }
582}
583
584impl ModifierElement {
585    fn is_empty(&self) -> bool {
586        matches!(self, Self::Empty)
587    }
588
589    fn then(self, inner: Self) -> Self {
590        match (self, inner) {
591            (lhs, Self::Empty) => lhs,
592            (Self::Empty, rhs) => rhs,
593            (outer, inner) => Self::Combined(Arc::new(ModifierCombination { outer, inner })),
594        }
595    }
596
597    fn fold_actions<'a, F>(&'a self, visit: &mut F)
598    where
599        F: FnMut(&'a ModifierAction),
600    {
601        match self {
602            Self::Empty => {}
603            Self::Action(action) => visit(action),
604            Self::Combined(combination) => {
605                combination.outer.fold_actions(visit);
606                combination.inner.fold_actions(visit);
607            }
608        }
609    }
610
611    fn actions(&self) -> impl Iterator<Item = &ModifierAction> {
612        let mut pending = SmallVec::<[&Self; 8]>::new();
613        pending.push(self);
614        std::iter::from_fn(move || {
615            while let Some(element) = pending.pop() {
616                match element {
617                    Self::Empty => {}
618                    Self::Action(action) => return Some(action),
619                    Self::Combined(combination) => {
620                        pending.push(&combination.inner);
621                        pending.push(&combination.outer);
622                    }
623                }
624            }
625            None
626        })
627    }
628
629    fn for_each_action_outer_first<F>(&self, visit: &mut F)
630    where
631        F: FnMut(ModifierAction),
632    {
633        match self {
634            Self::Empty => {}
635            Self::Action(action) => visit(action.clone()),
636            Self::Combined(combination) => {
637                combination.inner.for_each_action_outer_first(visit);
638                combination.outer.for_each_action_outer_first(visit);
639            }
640        }
641    }
642}
643
644/// A persistent handle to a modifier chain.
645#[derive(Clone, Default)]
646pub struct Modifier {
647    element: ModifierElement,
648}
649
650/// Focus-specific modifier extensions for [`Modifier`].
651pub trait FocusModifierExt {
652    /// Registers a focus target for this subtree.
653    fn focusable(self) -> Modifier;
654
655    /// Binds an explicit focus requester to this subtree.
656    fn focus_requester(self, requester: FocusRequester) -> Modifier;
657
658    /// Applies explicit focus properties to this subtree.
659    fn focus_properties(self, properties: FocusProperties) -> Modifier;
660
661    /// Prefers this focus target when Tab enters a group with a single Tab
662    /// stop, preserving its other focus properties.
663    fn preferred_tab_stop(self, preferred: bool) -> Modifier;
664
665    /// Registers a traversal-only focus group for this subtree.
666    fn focus_group(self) -> Modifier;
667
668    /// Registers an explicit focus scope handle for this subtree.
669    fn focus_scope_with(self, scope: FocusScopeNode) -> Modifier;
670
671    /// Registers an explicit focus group handle for this subtree.
672    fn focus_group_with(self, group: FocusGroupNode) -> Modifier;
673
674    /// Registers a focus scope with restore behavior for this subtree.
675    fn focus_restorer(self, fallback: Option<FocusRequester>) -> Modifier;
676
677    /// Registers an explicit focus restorer scope for this subtree.
678    fn focus_restorer_with(
679        self,
680        scope: FocusScopeNode,
681        fallback: Option<FocusRequester>,
682    ) -> Modifier;
683
684    /// Applies a traversal policy to the current focus group or scope.
685    fn focus_traversal_policy(self, policy: FocusTraversalPolicy) -> Modifier;
686
687    /// Registers a callback that runs when the subtree focus state changes.
688    fn on_focus_changed<F>(self, handler: F) -> Modifier
689    where
690        F: Into<CallbackWith<FocusState>>;
691
692    /// Registers a callback that observes focus events for this subtree.
693    fn on_focus_event<F>(self, handler: F) -> Modifier
694    where
695        F: Into<CallbackWith<FocusState>>;
696
697    /// Registers a callback that moves focus beyond the current viewport.
698    fn focus_beyond_bounds_handler<F>(self, handler: F) -> Modifier
699    where
700        F: Into<CallbackWith<FocusDirection, bool>>;
701
702    /// Registers a callback that reveals the focused target within the
703    /// viewport.
704    fn focus_reveal_handler<F>(self, handler: F) -> Modifier
705    where
706        F: Into<CallbackWith<FocusRevealRequest, bool>>;
707
708    /// Registers a callback invoked with this node's window rectangle after
709    /// attachment and whenever the rectangle changes.
710    fn on_globally_positioned<F>(self, handler: F) -> Modifier
711    where
712        F: Into<CallbackWith<crate::PxRect>>;
713
714    /// Observes this node's exact fractional rectangle in window coordinates.
715    fn on_globally_positioned_precise<F>(self, handler: F) -> Modifier
716    where
717        F: Into<CallbackWith<crate::FloatRect>>;
718
719    /// Observes the transformation from this node's local units to the window.
720    fn on_local_to_window<F>(self, handler: F) -> Modifier
721    where
722        F: Into<CallbackWith<crate::LocalToWindow>>;
723
724    /// Observes local layout dimensions and their exact transformation to the
725    /// window.
726    fn on_layout_coordinates<F>(self, handler: F) -> Modifier
727    where
728        F: Into<CallbackWith<crate::LayoutCoordinates>>;
729}
730
731/// Cursor-specific modifier extensions for [`Modifier`].
732pub trait CursorModifierExt {
733    /// Sets the cursor icon used while the pointer hovers this node.
734    fn hover_cursor_icon(self, icon: CursorIcon) -> Modifier;
735}
736
737impl Modifier {
738    /// Observes measured dimensions in local layout units after initial
739    /// placement and size changes. Moving or scaling an ancestor does not
740    /// trigger a callback unless the local measured size changes. Hidden
741    /// layouts resume observation when placed again.
742    pub fn on_layout_size_changed<F>(self, handler: F) -> Self
743    where
744        F: Into<CallbackWith<crate::PxSize>>,
745    {
746        self.push_action(ModifierAction::GloballyPositioned(
747            crate::component_tree::GloballyPositionedHandler::Size(handler.into()),
748        ))
749    }
750
751    /// Observes the window-space bounds remaining after ancestor rectangular
752    /// clipping and window boundaries. Reports `None` when fully clipped or
753    /// when a previously placed layout becomes unplaced. Shape bounds are
754    /// included; occlusion by other content is not considered.
755    pub fn on_visible_window_bounds<F>(self, handler: F) -> Self
756    where
757        F: Into<CallbackWith<Option<crate::FloatRect>>>,
758    {
759        self.push_action(ModifierAction::GloballyPositioned(
760            crate::component_tree::GloballyPositionedHandler::Visible(handler.into()),
761        ))
762    }
763
764    /// Uses this layout's local units as a coordinate space for descendant
765    /// [`Self::on_layout_rect_in_space`] observers, including this layout.
766    /// Nested spaces take precedence over their ancestors.
767    pub fn layout_coordinate_space(self) -> Self {
768        self.push_build(LayoutCoordinateSpace)
769    }
770
771    /// Observes this layout's fractional bounds in the nearest declared
772    /// [`Self::layout_coordinate_space`]. Without a declared space, observes
773    /// window bounds. Calls after initial placement and when these bounds
774    /// change; moving or scaling the entire space does not trigger a callback.
775    /// Hidden layouts resume observation when placed again.
776    pub fn on_layout_rect_in_space<F>(self, handler: F) -> Self
777    where
778        F: Into<CallbackWith<crate::FloatRect>>,
779    {
780        self.push_action(ModifierAction::GloballyPositioned(
781            crate::component_tree::GloballyPositionedHandler::InSpace(handler.into()),
782        ))
783    }
784
785    /// Creates an empty modifier chain.
786    pub fn new() -> Self {
787        Self::default()
788    }
789
790    fn push_action(self, action: ModifierAction) -> Self {
791        Self {
792            element: self.element.then(ModifierElement::Action(action)),
793        }
794    }
795
796    /// Appends another modifier chain after this one without flattening it.
797    pub fn then(self, other: Modifier) -> Self {
798        Self {
799            element: self.element.then(other.element),
800        }
801    }
802
803    pub(crate) fn push_layout<N>(self, node: N) -> Self
804    where
805        N: LayoutModifierNode + PartialEq,
806    {
807        self.push_action(ModifierAction::Layout(Arc::new(
808            ComparableLayoutModifierNode {
809                node: Arc::new(node),
810            },
811        )))
812    }
813
814    pub(crate) fn push_placement<N>(self, node: N) -> Self
815    where
816        N: PlacementModifierNode + PartialEq,
817    {
818        self.push_action(ModifierAction::Placement(Arc::new(
819            ComparablePlacementModifierNode {
820                node: Arc::new(node),
821            },
822        )))
823    }
824
825    pub(crate) fn push_draw<N>(self, node: N) -> Self
826    where
827        N: DrawModifierNode,
828    {
829        self.push_action(ModifierAction::Draw(Arc::new(node)))
830    }
831
832    pub(crate) fn push_parent_data<N>(self, node: N) -> Self
833    where
834        N: ParentDataModifierNode,
835    {
836        self.push_action(ModifierAction::ParentData(Arc::new(node)))
837    }
838
839    pub(crate) fn push_build<N>(self, node: N) -> Self
840    where
841        N: BuildModifierNode,
842    {
843        self.push_action(ModifierAction::Build(Arc::new(node)))
844    }
845
846    pub(crate) fn push_semantics<N>(self, node: N) -> Self
847    where
848        N: SemanticsModifierNode,
849    {
850        self.push_action(ModifierAction::Semantics(Arc::new(node)))
851    }
852
853    fn push_cursor<N>(self, node: N) -> Self
854    where
855        N: CursorModifierNode,
856    {
857        self.push_action(ModifierAction::Cursor(Arc::new(node)))
858    }
859
860    pub(crate) fn push_pointer_preview_input<N>(self, node: N) -> Self
861    where
862        N: PointerInputModifierNode,
863    {
864        self.push_action(ModifierAction::PointerPreviewInput(Arc::new(node)))
865    }
866
867    pub(crate) fn push_pointer_input<N>(self, node: N) -> Self
868    where
869        N: PointerInputModifierNode,
870    {
871        self.push_action(ModifierAction::PointerInput(Arc::new(node)))
872    }
873
874    pub(crate) fn push_pointer_final_input<N>(self, node: N) -> Self
875    where
876        N: PointerInputModifierNode,
877    {
878        self.push_action(ModifierAction::PointerFinalInput(Arc::new(node)))
879    }
880
881    pub(crate) fn push_keyboard_preview_input<N>(self, node: N) -> Self
882    where
883        N: KeyboardInputModifierNode,
884    {
885        self.push_action(ModifierAction::KeyboardPreviewInput(Arc::new(node)))
886    }
887
888    pub(crate) fn push_keyboard_input<N>(self, node: N) -> Self
889    where
890        N: KeyboardInputModifierNode,
891    {
892        self.push_action(ModifierAction::KeyboardInput(Arc::new(node)))
893    }
894
895    pub(crate) fn push_clipboard_input<N>(self, node: N) -> Self
896    where
897        N: ClipboardInputModifierNode,
898    {
899        self.push_action(ModifierAction::ClipboardInput(Arc::new(node)))
900    }
901
902    pub(crate) fn push_ime_preview_input<N>(self, node: N) -> Self
903    where
904        N: ImeInputModifierNode,
905    {
906        self.push_action(ModifierAction::ImePreviewInput(Arc::new(node)))
907    }
908
909    pub(crate) fn push_ime_input<N>(self, node: N) -> Self
910    where
911        N: ImeInputModifierNode,
912    {
913        self.push_action(ModifierAction::ImeInput(Arc::new(node)))
914    }
915
916    fn push_focus_requester(self, requester: FocusRequester) -> Self {
917        self.push_focus_op(FocusModifierOp::Requester(requester))
918    }
919
920    fn push_focus_target(self) -> Self {
921        self.push_focus_op(FocusModifierOp::Registration(
922            FocusModifierRegistration::Target(None),
923        ))
924    }
925
926    fn push_focus_scope_with(self, scope: FocusScopeNode) -> Self {
927        self.push_focus_op(FocusModifierOp::Registration(
928            FocusModifierRegistration::Scope(Some(scope)),
929        ))
930    }
931
932    fn push_focus_group(self) -> Self {
933        self.push_focus_op(FocusModifierOp::Registration(
934            FocusModifierRegistration::Group(None),
935        ))
936    }
937
938    fn push_focus_group_with(self, group: FocusGroupNode) -> Self {
939        self.push_focus_op(FocusModifierOp::Registration(
940            FocusModifierRegistration::Group(Some(group)),
941        ))
942    }
943
944    fn push_focus_restorer(self, fallback: Option<FocusRequester>) -> Self {
945        self.push_focus_op(FocusModifierOp::Registration(
946            FocusModifierRegistration::Restorer {
947                scope: None,
948                fallback,
949            },
950        ))
951    }
952
953    fn push_focus_restorer_with(
954        self,
955        scope: FocusScopeNode,
956        fallback: Option<FocusRequester>,
957    ) -> Self {
958        self.push_focus_op(FocusModifierOp::Registration(
959            FocusModifierRegistration::Restorer {
960                scope: Some(scope),
961                fallback,
962            },
963        ))
964    }
965
966    fn push_focus_properties(self, properties: FocusProperties) -> Self {
967        self.push_focus_op(FocusModifierOp::Properties(Arc::new(properties)))
968    }
969
970    fn push_focus_traversal_policy(self, policy: FocusTraversalPolicy) -> Self {
971        self.push_focus_op(FocusModifierOp::TraversalPolicy(policy))
972    }
973
974    fn push_focus_changed_handler(self, handler: CallbackWith<FocusState>) -> Self {
975        self.push_focus_op(FocusModifierOp::ChangedHandler(handler))
976    }
977
978    fn push_focus_event_handler(self, handler: CallbackWith<FocusState>) -> Self {
979        self.push_focus_op(FocusModifierOp::EventHandler(handler))
980    }
981
982    fn push_focus_beyond_bounds_handler(self, handler: CallbackWith<FocusDirection, bool>) -> Self {
983        self.push_focus_op(FocusModifierOp::BeyondBoundsHandler(handler))
984    }
985
986    fn push_focus_reveal_handler(self, handler: CallbackWith<FocusRevealRequest, bool>) -> Self {
987        self.push_focus_op(FocusModifierOp::RevealHandler(handler))
988    }
989
990    fn push_globally_positioned(self, handler: CallbackWith<crate::PxRect>) -> Self {
991        self.push_action(ModifierAction::GloballyPositioned(
992            crate::component_tree::GloballyPositionedHandler::Pixels(handler),
993        ))
994    }
995
996    fn push_focus_op(self, op: FocusModifierOp) -> Self {
997        self.push_action(ModifierAction::Focus(op))
998    }
999
1000    /// Attaches a nested scroll connection to this node.
1001    pub fn nested_scroll(self, connection: crate::nested_scroll::NestedScrollConnection) -> Self {
1002        self.push_build(connection)
1003    }
1004
1005    pub(crate) fn nested_scroll_connection(
1006        &self,
1007    ) -> Option<crate::nested_scroll::NestedScrollConnection> {
1008        let mut connection = None;
1009        self.element.for_each_action_outer_first(&mut |action| {
1010            if let ModifierAction::Build(node) = action {
1011                if let Some(attached) = node.nested_scroll_connection() {
1012                    connection = Some(attached);
1013                }
1014            }
1015        });
1016        connection
1017    }
1018
1019    fn materialize(self, instance: u64, owner: u64, path: &[u64]) -> Self {
1020        fn expand(
1021            element: &ModifierElement,
1022            instance: u64,
1023            owner: u64,
1024            path: &[u64],
1025            positions: &mut SmallVec<[(TypeId, usize); 4]>,
1026        ) -> Option<ModifierElement> {
1027            match element {
1028                ModifierElement::Empty => None,
1029                ModifierElement::Action(action) => {
1030                    let ModifierAction::Build(node) = action else {
1031                        return None;
1032                    };
1033                    let kind = node.as_ref().type_id();
1034                    let position = next_type_position(positions, kind);
1035                    let mut path = path.to_vec();
1036                    path.push(modifier_type_key(kind));
1037                    path.push(position as u64);
1038                    let mut context = ModifierBuildContext {
1039                        instance,
1040                        owner,
1041                        path: path.clone(),
1042                        state_positions: SmallVec::new(),
1043                        clock_positions: SmallVec::new(),
1044                    };
1045                    node.materialize(&mut context)
1046                        .map(|modifier| modifier.materialize(instance, owner, &path).element)
1047                }
1048                ModifierElement::Combined(combination) => {
1049                    let ModifierCombination { outer, inner } = combination.as_ref();
1050                    let first = expand(outer, instance, owner, path, positions);
1051                    let second = expand(inner, instance, owner, path, positions);
1052                    if first.is_none() && second.is_none() {
1053                        return None;
1054                    }
1055                    Some(
1056                        first
1057                            .unwrap_or_else(|| outer.clone())
1058                            .then(second.unwrap_or_else(|| inner.clone())),
1059                    )
1060                }
1061            }
1062        }
1063        assert!(path.len() < 64, "recursive modifier materialization");
1064        match expand(&self.element, instance, owner, path, &mut SmallVec::new()) {
1065            Some(element) => Self { element },
1066            None => self,
1067        }
1068    }
1069
1070    /// Attaches this modifier chain to the current component node.
1071    pub fn attach(self) {
1072        ensure_build_phase();
1073
1074        let (instance, owner) = TesseraRuntime::with(|runtime| {
1075            let node = runtime
1076                .component_tree
1077                .current_node()
1078                .expect("modifier attachment requires a node");
1079            (node.instance_logic_id, node.instance_key)
1080        });
1081        let materialized =
1082            self.materialize(instance, owner, &crate::runtime::modifier_attachment_path());
1083
1084        TesseraRuntime::with_mut(|runtime| {
1085            runtime.append_current_modifier(materialized.clone());
1086        });
1087        crate::nested_scroll::attachment_changed();
1088
1089        let mut accessibility = AccessibilityNode::new();
1090        let mut action_handler = None;
1091        let mut has_semantics = false;
1092        materialized
1093            .element
1094            .fold_actions(&mut |action| match action {
1095                ModifierAction::Build(node) => {
1096                    TesseraRuntime::with_mut(|runtime| node.apply(runtime));
1097                }
1098                ModifierAction::Semantics(node) => {
1099                    has_semantics = true;
1100                    node.apply(&mut accessibility, &mut action_handler);
1101                }
1102                ModifierAction::Focus(op) => {
1103                    TesseraRuntime::with_mut(|runtime| {
1104                        apply_focus_op(runtime::FocusModifierRuntime::new(runtime), op.clone());
1105                    });
1106                }
1107                ModifierAction::GloballyPositioned(handler) => {
1108                    TesseraRuntime::with_mut(|runtime| {
1109                        runtime.push_current_globally_positioned_handler(handler.clone());
1110                    });
1111                }
1112                _ => {}
1113            });
1114
1115        TesseraRuntime::with_mut(|runtime| {
1116            runtime
1117                .set_current_accessibility(has_semantics.then_some(accessibility), action_handler);
1118        });
1119    }
1120
1121    /// Attaches this modifier chain to the current node and then runs `child`.
1122    pub fn run<F>(self, child: F)
1123    where
1124        F: Fn() + Send + Sync + 'static,
1125    {
1126        self.attach();
1127        child();
1128    }
1129
1130    /// Returns true when the modifier has no actions.
1131    pub fn is_empty(&self) -> bool {
1132        self.element.is_empty()
1133    }
1134
1135    pub(crate) fn ordered_actions(&self) -> Vec<OrderedModifierAction> {
1136        let mut actions = Vec::new();
1137        self.element.fold_actions(&mut |action| {
1138            let action = match action {
1139                ModifierAction::Layout(_) => OrderedModifierAction::Layout,
1140                ModifierAction::Placement(node) => OrderedModifierAction::Placement(node.clone()),
1141                ModifierAction::Draw(node) => OrderedModifierAction::Draw(node.clone()),
1142                ModifierAction::ParentData(_) => OrderedModifierAction::ParentData,
1143                ModifierAction::Cursor(node) => OrderedModifierAction::Cursor(node.clone()),
1144                ModifierAction::PointerPreviewInput(node) => {
1145                    OrderedModifierAction::PointerPreviewInput(node.clone())
1146                }
1147                ModifierAction::PointerInput(node) => {
1148                    OrderedModifierAction::PointerInput(node.clone())
1149                }
1150                ModifierAction::PointerFinalInput(node) => {
1151                    OrderedModifierAction::PointerFinalInput(node.clone())
1152                }
1153                ModifierAction::KeyboardPreviewInput(node) => {
1154                    OrderedModifierAction::KeyboardPreviewInput(node.clone())
1155                }
1156                ModifierAction::KeyboardInput(node) => {
1157                    OrderedModifierAction::KeyboardInput(node.clone())
1158                }
1159                ModifierAction::ClipboardInput(node) => {
1160                    OrderedModifierAction::ClipboardInput(node.clone())
1161                }
1162                ModifierAction::ImePreviewInput(node) => {
1163                    OrderedModifierAction::ImePreviewInput(node.clone())
1164                }
1165                ModifierAction::ImeInput(node) => OrderedModifierAction::ImeInput(node.clone()),
1166                ModifierAction::Build(_)
1167                | ModifierAction::Semantics(_)
1168                | ModifierAction::Focus(_)
1169                | ModifierAction::GloballyPositioned(_) => return,
1170            };
1171            actions.push(action);
1172        });
1173        actions
1174    }
1175
1176    pub(crate) fn input_geometry_eq(&self, other: &Self) -> bool {
1177        let shape = |modifier: &Self| {
1178            let mut pointer = false;
1179            let mut actions = SmallVec::<[_; 4]>::new();
1180            modifier.element.fold_actions(&mut |action| {
1181                pointer |= matches!(
1182                    action,
1183                    ModifierAction::Cursor(_)
1184                        | ModifierAction::PointerPreviewInput(_)
1185                        | ModifierAction::PointerInput(_)
1186                        | ModifierAction::PointerFinalInput(_)
1187                );
1188                if !matches!(
1189                    action,
1190                    ModifierAction::Build(_)
1191                        | ModifierAction::Semantics(_)
1192                        | ModifierAction::Focus(_)
1193                        | ModifierAction::GloballyPositioned(_)
1194                ) {
1195                    actions.push(std::mem::discriminant(action));
1196                }
1197            });
1198            (pointer, actions)
1199        };
1200        let (left_pointer, left) = shape(self);
1201        let (right_pointer, right) = shape(other);
1202        (!left_pointer && !right_pointer) || (left_pointer == right_pointer && left == right)
1203    }
1204
1205    pub(crate) fn has_pointer_input(&self) -> bool {
1206        let mut pointer = false;
1207        self.element.fold_actions(&mut |action| {
1208            pointer |= matches!(
1209                action,
1210                ModifierAction::Cursor(_)
1211                    | ModifierAction::PointerPreviewInput(_)
1212                    | ModifierAction::PointerInput(_)
1213                    | ModifierAction::PointerFinalInput(_)
1214            );
1215        });
1216        pointer
1217    }
1218
1219    pub(crate) fn has_input_geometry(&self) -> bool {
1220        let mut input = false;
1221        self.element.fold_actions(&mut |action| {
1222            input |= matches!(
1223                action,
1224                ModifierAction::Cursor(_)
1225                    | ModifierAction::PointerPreviewInput(_)
1226                    | ModifierAction::PointerInput(_)
1227                    | ModifierAction::PointerFinalInput(_)
1228                    | ModifierAction::KeyboardPreviewInput(_)
1229                    | ModifierAction::KeyboardInput(_)
1230                    | ModifierAction::ImePreviewInput(_)
1231                    | ModifierAction::ImeInput(_)
1232            );
1233        });
1234        input
1235    }
1236
1237    pub(crate) fn resolve_placement(
1238        &self,
1239        mut position: PxPosition,
1240        instance: u64,
1241    ) -> (PxPosition, Vec<PxPosition>) {
1242        let pointer = self.has_pointer_input();
1243        let mut positions = Vec::new();
1244        self.element.fold_actions(&mut |action| {
1245            if let ModifierAction::Placement(node) = action {
1246                crate::work::record(
1247                    instance,
1248                    crate::WorkPhase::Transform,
1249                    node.node().diagnostic_name(),
1250                );
1251                position = node.node().transform_position(position);
1252            }
1253            if pointer
1254                && !matches!(
1255                    action,
1256                    ModifierAction::Build(_)
1257                        | ModifierAction::Semantics(_)
1258                        | ModifierAction::Focus(_)
1259                        | ModifierAction::GloballyPositioned(_)
1260                )
1261            {
1262                positions.push(position);
1263            }
1264        });
1265        (position, positions)
1266    }
1267
1268    fn select_actions<'a, T>(
1269        &'a self,
1270        mut select: impl FnMut(&'a ModifierAction) -> Option<T>,
1271    ) -> SmallVec<[T; 4]> {
1272        let mut selected = SmallVec::new();
1273        self.element.fold_actions(&mut |action| {
1274            if let Some(value) = select(action) {
1275                selected.push(value);
1276            }
1277        });
1278        selected
1279    }
1280
1281    fn selected_actions_eq<'a, T>(
1282        &'a self,
1283        other: &'a Self,
1284        select: impl Fn(&'a ModifierAction) -> Option<T>,
1285        mut eq: impl FnMut(T, T) -> bool,
1286    ) -> bool {
1287        let lhs = self.select_actions(&select);
1288        let rhs = other.select_actions(select);
1289        lhs.len() == rhs.len() && lhs.into_iter().zip(rhs).all(|(lhs, rhs)| eq(lhs, rhs))
1290    }
1291
1292    pub(crate) fn layout_modifiers(&self) -> SmallVec<[Arc<dyn LayoutModifierNode>; 4]> {
1293        self.select_actions(|action| action.layout_node().map(|node| node.node()))
1294    }
1295
1296    pub(crate) fn layout_measure_eq(&self, other: &Self) -> bool {
1297        self.selected_actions_eq(other, ModifierAction::layout_node, |lhs, rhs| {
1298            lhs.measure_eq(rhs)
1299        })
1300    }
1301
1302    pub(crate) fn layout_placement_eq(&self, other: &Self) -> bool {
1303        self.selected_actions_eq(other, ModifierAction::layout_node, |lhs, rhs| {
1304            lhs.placement_eq(rhs)
1305        })
1306    }
1307
1308    pub(crate) fn position_eq(&self, other: &Self) -> bool {
1309        self.selected_actions_eq(
1310            other,
1311            |action| match action {
1312                ModifierAction::Placement(node) => Some(node.as_ref()),
1313                _ => None,
1314            },
1315            |lhs, rhs| lhs.eq(rhs),
1316        )
1317    }
1318
1319    pub(crate) fn parent_data<T: Clone + Send + Sync + 'static>(&self) -> Option<T> {
1320        let mut data = ParentDataMap::default();
1321        self.element.fold_actions(&mut |action| {
1322            if let ModifierAction::ParentData(node) = action {
1323                node.apply_parent_data(&mut data);
1324            }
1325        });
1326        data.get(&TypeId::of::<T>())?.downcast_ref::<T>().cloned()
1327    }
1328
1329    pub(crate) fn parent_data_eq(&self, other: &Self) -> bool {
1330        self.selected_actions_eq(
1331            other,
1332            |action| match action {
1333                ModifierAction::ParentData(node) => Some(node),
1334                _ => None,
1335            },
1336            |lhs, rhs| Arc::ptr_eq(lhs, rhs) || lhs.equivalent(rhs.as_ref()),
1337        )
1338    }
1339}
1340
1341impl ModifierCapabilityExt for Modifier {
1342    fn push_clipboard_input<N>(self, node: N) -> Self
1343    where
1344        N: ClipboardInputModifierNode,
1345    {
1346        Modifier::push_clipboard_input(self, node)
1347    }
1348    fn push_layout<N>(self, node: N) -> Self
1349    where
1350        N: LayoutModifierNode + PartialEq,
1351    {
1352        Modifier::push_layout(self, node)
1353    }
1354
1355    fn push_placement<N>(self, node: N) -> Self
1356    where
1357        N: PlacementModifierNode + PartialEq,
1358    {
1359        Modifier::push_placement(self, node)
1360    }
1361
1362    fn push_draw<N>(self, node: N) -> Self
1363    where
1364        N: DrawModifierNode,
1365    {
1366        Modifier::push_draw(self, node)
1367    }
1368
1369    fn push_parent_data<N>(self, node: N) -> Self
1370    where
1371        N: ParentDataModifierNode,
1372    {
1373        Modifier::push_parent_data(self, node)
1374    }
1375
1376    fn push_build<N>(self, node: N) -> Self
1377    where
1378        N: BuildModifierNode,
1379    {
1380        Modifier::push_build(self, node)
1381    }
1382
1383    fn push_semantics<N>(self, node: N) -> Self
1384    where
1385        N: SemanticsModifierNode,
1386    {
1387        Modifier::push_semantics(self, node)
1388    }
1389
1390    fn push_pointer_preview_input<N>(self, node: N) -> Self
1391    where
1392        N: PointerInputModifierNode,
1393    {
1394        Modifier::push_pointer_preview_input(self, node)
1395    }
1396
1397    fn push_pointer_input<N>(self, node: N) -> Self
1398    where
1399        N: PointerInputModifierNode,
1400    {
1401        Modifier::push_pointer_input(self, node)
1402    }
1403
1404    fn push_pointer_final_input<N>(self, node: N) -> Self
1405    where
1406        N: PointerInputModifierNode,
1407    {
1408        Modifier::push_pointer_final_input(self, node)
1409    }
1410
1411    fn push_keyboard_preview_input<N>(self, node: N) -> Self
1412    where
1413        N: KeyboardInputModifierNode,
1414    {
1415        Modifier::push_keyboard_preview_input(self, node)
1416    }
1417
1418    fn push_keyboard_input<N>(self, node: N) -> Self
1419    where
1420        N: KeyboardInputModifierNode,
1421    {
1422        Modifier::push_keyboard_input(self, node)
1423    }
1424
1425    fn push_ime_preview_input<N>(self, node: N) -> Self
1426    where
1427        N: ImeInputModifierNode,
1428    {
1429        Modifier::push_ime_preview_input(self, node)
1430    }
1431
1432    fn push_ime_input<N>(self, node: N) -> Self
1433    where
1434        N: ImeInputModifierNode,
1435    {
1436        Modifier::push_ime_input(self, node)
1437    }
1438}
1439
1440#[derive(Clone, Copy)]
1441struct StaticCursorModifierNode {
1442    icon: CursorIcon,
1443}
1444
1445impl CursorModifierNode for StaticCursorModifierNode {
1446    fn cursor_icon(&self) -> CursorIcon {
1447        self.icon
1448    }
1449}
1450
1451impl CursorModifierExt for Modifier {
1452    fn hover_cursor_icon(self, icon: CursorIcon) -> Modifier {
1453        self.push_cursor(StaticCursorModifierNode { icon })
1454    }
1455}
1456
1457impl FocusModifierExt for Modifier {
1458    fn focusable(self) -> Modifier {
1459        self.push_focus_target()
1460    }
1461
1462    fn focus_requester(self, requester: FocusRequester) -> Modifier {
1463        self.push_focus_requester(requester)
1464    }
1465
1466    fn focus_properties(self, properties: FocusProperties) -> Modifier {
1467        self.push_focus_properties(properties)
1468    }
1469
1470    fn preferred_tab_stop(self, preferred: bool) -> Modifier {
1471        self.push_focus_op(FocusModifierOp::PreferredTabStop(preferred))
1472    }
1473
1474    fn focus_group(self) -> Modifier {
1475        self.push_focus_group()
1476    }
1477
1478    fn focus_scope_with(self, scope: FocusScopeNode) -> Modifier {
1479        self.push_focus_scope_with(scope)
1480    }
1481
1482    fn focus_group_with(self, group: FocusGroupNode) -> Modifier {
1483        self.push_focus_group_with(group)
1484    }
1485
1486    fn focus_restorer(self, fallback: Option<FocusRequester>) -> Modifier {
1487        self.push_focus_restorer(fallback)
1488    }
1489
1490    fn focus_restorer_with(
1491        self,
1492        scope: FocusScopeNode,
1493        fallback: Option<FocusRequester>,
1494    ) -> Modifier {
1495        self.push_focus_restorer_with(scope, fallback)
1496    }
1497
1498    fn focus_traversal_policy(self, policy: FocusTraversalPolicy) -> Modifier {
1499        self.push_focus_traversal_policy(policy)
1500    }
1501
1502    fn on_focus_changed<F>(self, handler: F) -> Modifier
1503    where
1504        F: Into<CallbackWith<FocusState>>,
1505    {
1506        self.push_focus_changed_handler(handler.into())
1507    }
1508
1509    fn on_focus_event<F>(self, handler: F) -> Modifier
1510    where
1511        F: Into<CallbackWith<FocusState>>,
1512    {
1513        self.push_focus_event_handler(handler.into())
1514    }
1515
1516    fn focus_beyond_bounds_handler<F>(self, handler: F) -> Modifier
1517    where
1518        F: Into<CallbackWith<FocusDirection, bool>>,
1519    {
1520        self.push_focus_beyond_bounds_handler(handler.into())
1521    }
1522
1523    fn focus_reveal_handler<F>(self, handler: F) -> Modifier
1524    where
1525        F: Into<CallbackWith<FocusRevealRequest, bool>>,
1526    {
1527        self.push_focus_reveal_handler(handler.into())
1528    }
1529
1530    fn on_globally_positioned<F>(self, handler: F) -> Modifier
1531    where
1532        F: Into<CallbackWith<crate::PxRect>>,
1533    {
1534        self.push_globally_positioned(handler.into())
1535    }
1536
1537    fn on_globally_positioned_precise<F>(self, handler: F) -> Modifier
1538    where
1539        F: Into<CallbackWith<crate::FloatRect>>,
1540    {
1541        self.push_action(ModifierAction::GloballyPositioned(
1542            crate::component_tree::GloballyPositionedHandler::Precise(handler.into()),
1543        ))
1544    }
1545
1546    fn on_local_to_window<F>(self, handler: F) -> Modifier
1547    where
1548        F: Into<CallbackWith<crate::LocalToWindow>>,
1549    {
1550        self.push_action(ModifierAction::GloballyPositioned(
1551            crate::component_tree::GloballyPositionedHandler::Transform(handler.into()),
1552        ))
1553    }
1554
1555    fn on_layout_coordinates<F>(self, handler: F) -> Modifier
1556    where
1557        F: Into<CallbackWith<crate::LayoutCoordinates>>,
1558    {
1559        self.push_action(ModifierAction::GloballyPositioned(
1560            crate::component_tree::GloballyPositionedHandler::Coordinates(handler.into()),
1561        ))
1562    }
1563}
1564
1565impl fmt::Debug for Modifier {
1566    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1567        f.debug_struct("Modifier")
1568            .field("is_empty", &self.element.is_empty())
1569            .finish()
1570    }
1571}
1572
1573fn modifier_action_eq(lhs: &ModifierAction, rhs: &ModifierAction) -> bool {
1574    match (lhs, rhs) {
1575        (ModifierAction::Layout(lhs), ModifierAction::Layout(rhs)) => lhs.eq(rhs.as_ref()),
1576        (ModifierAction::Placement(lhs), ModifierAction::Placement(rhs)) => lhs.eq(rhs.as_ref()),
1577        (ModifierAction::Draw(lhs), ModifierAction::Draw(rhs)) => Arc::ptr_eq(lhs, rhs),
1578        (ModifierAction::ParentData(lhs), ModifierAction::ParentData(rhs)) => Arc::ptr_eq(lhs, rhs),
1579        (ModifierAction::Build(lhs), ModifierAction::Build(rhs)) => Arc::ptr_eq(lhs, rhs),
1580        (ModifierAction::Semantics(lhs), ModifierAction::Semantics(rhs)) => Arc::ptr_eq(lhs, rhs),
1581        (ModifierAction::Cursor(lhs), ModifierAction::Cursor(rhs)) => Arc::ptr_eq(lhs, rhs),
1582        (ModifierAction::PointerPreviewInput(lhs), ModifierAction::PointerPreviewInput(rhs)) => {
1583            Arc::ptr_eq(lhs, rhs)
1584        }
1585        (ModifierAction::PointerInput(lhs), ModifierAction::PointerInput(rhs)) => {
1586            Arc::ptr_eq(lhs, rhs)
1587        }
1588        (ModifierAction::PointerFinalInput(lhs), ModifierAction::PointerFinalInput(rhs)) => {
1589            Arc::ptr_eq(lhs, rhs)
1590        }
1591        (ModifierAction::KeyboardPreviewInput(lhs), ModifierAction::KeyboardPreviewInput(rhs)) => {
1592            Arc::ptr_eq(lhs, rhs)
1593        }
1594        (ModifierAction::KeyboardInput(lhs), ModifierAction::KeyboardInput(rhs)) => {
1595            Arc::ptr_eq(lhs, rhs)
1596        }
1597        (ModifierAction::ClipboardInput(lhs), ModifierAction::ClipboardInput(rhs)) => {
1598            Arc::ptr_eq(lhs, rhs)
1599        }
1600        (ModifierAction::ImePreviewInput(lhs), ModifierAction::ImePreviewInput(rhs)) => {
1601            Arc::ptr_eq(lhs, rhs)
1602        }
1603        (ModifierAction::ImeInput(lhs), ModifierAction::ImeInput(rhs)) => Arc::ptr_eq(lhs, rhs),
1604        (ModifierAction::Focus(lhs), ModifierAction::Focus(rhs)) => lhs == rhs,
1605        (ModifierAction::GloballyPositioned(lhs), ModifierAction::GloballyPositioned(rhs)) => {
1606            lhs == rhs
1607        }
1608        _ => false,
1609    }
1610}
1611
1612struct LayoutCoordinateSpace;
1613
1614impl BuildModifierNode for LayoutCoordinateSpace {
1615    fn apply(&self, runtime: &mut TesseraRuntime) {
1616        if let Some(node) = runtime.component_tree.current_node_mut() {
1617            node.layout_coordinate_space = true;
1618        }
1619    }
1620}
1621
1622fn hash_arc<T: ?Sized, H: Hasher>(value: &Arc<T>, state: &mut H) {
1623    std::ptr::hash(Arc::as_ptr(value), state);
1624}
1625
1626fn hash_modifier_action<H: Hasher>(action: &ModifierAction, state: &mut H) {
1627    std::mem::discriminant(action).hash(state);
1628    match action {
1629        ModifierAction::Layout(node) => node.hash_node(state),
1630        ModifierAction::Placement(node) => node.hash_node(state),
1631        ModifierAction::Draw(node) => hash_arc(node, state),
1632        ModifierAction::ParentData(node) => hash_arc(node, state),
1633        ModifierAction::Build(node) => hash_arc(node, state),
1634        ModifierAction::Semantics(node) => hash_arc(node, state),
1635        ModifierAction::Cursor(node) => hash_arc(node, state),
1636        ModifierAction::PointerPreviewInput(node) => hash_arc(node, state),
1637        ModifierAction::PointerInput(node) => hash_arc(node, state),
1638        ModifierAction::PointerFinalInput(node) => hash_arc(node, state),
1639        ModifierAction::KeyboardPreviewInput(node) => hash_arc(node, state),
1640        ModifierAction::KeyboardInput(node) => hash_arc(node, state),
1641        ModifierAction::ClipboardInput(node) => hash_arc(node, state),
1642        ModifierAction::ImePreviewInput(node) => hash_arc(node, state),
1643        ModifierAction::ImeInput(node) => hash_arc(node, state),
1644        ModifierAction::Focus(op) => hash_focus_op(op, state),
1645        ModifierAction::GloballyPositioned(handler) => {
1646            std::ptr::hash(
1647                handler as *const crate::component_tree::GloballyPositionedHandler,
1648                state,
1649            );
1650        }
1651    }
1652}
1653
1654fn hash_focus_op<H: Hasher>(op: &FocusModifierOp, state: &mut H) {
1655    std::mem::discriminant(op).hash(state);
1656    match op {
1657        FocusModifierOp::Requester(value) => value.hash(state),
1658        FocusModifierOp::Registration(FocusModifierRegistration::Target(node)) => {
1659            0u8.hash(state);
1660            node.map(|node| node.handle_id()).hash(state);
1661        }
1662        FocusModifierOp::Registration(FocusModifierRegistration::Scope(node)) => {
1663            1u8.hash(state);
1664            node.map(|node| node.handle_id()).hash(state);
1665        }
1666        FocusModifierOp::Registration(FocusModifierRegistration::Group(node)) => {
1667            2u8.hash(state);
1668            node.map(|node| node.handle_id()).hash(state);
1669        }
1670        FocusModifierOp::Registration(FocusModifierRegistration::Restorer { scope, fallback }) => {
1671            3u8.hash(state);
1672            scope.map(|node| node.handle_id()).hash(state);
1673            fallback.hash(state);
1674        }
1675        FocusModifierOp::Properties(value) => {
1676            value.can_focus.hash(state);
1677            value.can_request_focus.hash(state);
1678            value.skip_traversal.hash(state);
1679            value.preferred_tab_stop.hash(state);
1680            value.next.hash(state);
1681            value.previous.hash(state);
1682            value.up.hash(state);
1683            value.down.hash(state);
1684            value.left.hash(state);
1685            value.right.hash(state);
1686            value.enter.hash(state);
1687            value.exit.hash(state);
1688        }
1689        FocusModifierOp::TraversalPolicy(value) => value.hash(state),
1690        FocusModifierOp::PreferredTabStop(value) => value.hash(state),
1691        FocusModifierOp::ChangedHandler(value) => hash_callback_bits(value, state),
1692        FocusModifierOp::EventHandler(value) => hash_callback_bits(value, state),
1693        FocusModifierOp::BeyondBoundsHandler(value) => hash_callback_bits(value, state),
1694        FocusModifierOp::RevealHandler(value) => hash_callback_bits(value, state),
1695    }
1696}
1697
1698fn hash_callback_bits<T, R, H: Hasher>(callback: &CallbackWith<T, R>, state: &mut H) {
1699    let bytes = unsafe {
1700        std::slice::from_raw_parts(
1701            (callback as *const CallbackWith<T, R>).cast::<u8>(),
1702            std::mem::size_of::<CallbackWith<T, R>>(),
1703        )
1704    };
1705    bytes.hash(state);
1706}
1707
1708impl PartialEq for Modifier {
1709    fn eq(&self, other: &Self) -> bool {
1710        let mut lhs = self.element.actions();
1711        let mut rhs = other.element.actions();
1712        loop {
1713            match (lhs.next(), rhs.next()) {
1714                (Some(lhs), Some(rhs)) if modifier_action_eq(lhs, rhs) => {}
1715                (None, None) => return true,
1716                _ => return false,
1717            }
1718        }
1719    }
1720}
1721
1722impl Eq for Modifier {}
1723
1724impl Hash for Modifier {
1725    fn hash<H: Hasher>(&self, state: &mut H) {
1726        let mut count = 0usize;
1727        self.element.fold_actions(&mut |action| {
1728            hash_modifier_action(action, state);
1729            count += 1;
1730        });
1731        count.hash(state);
1732    }
1733}
1734
1735mod runtime {
1736    use super::FocusModifierRegistration;
1737    use crate::{
1738        FocusProperties, FocusRequester, FocusScopeNode, FocusState, FocusTraversalPolicy,
1739        focus::{FocusDirection, FocusRevealRequest},
1740        prop::CallbackWith,
1741        runtime::TesseraRuntime,
1742    };
1743
1744    pub(super) struct FocusModifierRuntime<'a> {
1745        runtime: &'a mut TesseraRuntime,
1746    }
1747
1748    impl<'a> FocusModifierRuntime<'a> {
1749        pub(super) fn new(runtime: &'a mut TesseraRuntime) -> Self {
1750            Self { runtime }
1751        }
1752
1753        pub(super) fn bind_requester(&mut self, requester: FocusRequester) {
1754            self.runtime.bind_current_focus_requester(requester);
1755        }
1756
1757        pub(super) fn ensure_registration(&mut self, registration: FocusModifierRegistration) {
1758            match registration {
1759                FocusModifierRegistration::Target(node) => {
1760                    let node = node.unwrap_or_else(|| {
1761                        self.runtime
1762                            .current_focus_target_handle()
1763                            .unwrap_or_else(|| {
1764                                crate::runtime::persistent_focus_target_for_current_instance(
1765                                    "__tessera_focus_target",
1766                                )
1767                            })
1768                    });
1769                    self.runtime.ensure_current_focus_target(node);
1770                }
1771                FocusModifierRegistration::Scope(scope) => {
1772                    let scope = scope.unwrap_or_else(|| {
1773                        self.runtime
1774                            .current_focus_scope_handle()
1775                            .unwrap_or_else(|| {
1776                                crate::runtime::persistent_focus_scope_for_current_instance(
1777                                    "__tessera_focus_scope",
1778                                )
1779                            })
1780                    });
1781                    self.runtime.ensure_current_focus_scope(scope);
1782                }
1783                FocusModifierRegistration::Group(group) => {
1784                    let group = group.unwrap_or_else(|| {
1785                        self.runtime
1786                            .current_focus_group_handle()
1787                            .unwrap_or_else(|| {
1788                                crate::runtime::persistent_focus_group_for_current_instance(
1789                                    "__tessera_focus_group",
1790                                )
1791                            })
1792                    });
1793                    self.runtime.ensure_current_focus_group(group);
1794                }
1795                FocusModifierRegistration::Restorer { scope, fallback } => {
1796                    let scope: FocusScopeNode = scope.unwrap_or_else(|| {
1797                        self.runtime
1798                            .current_focus_scope_handle()
1799                            .unwrap_or_else(|| {
1800                                crate::runtime::persistent_focus_scope_for_current_instance(
1801                                    "__tessera_focus_scope",
1802                                )
1803                            })
1804                    });
1805                    self.runtime.ensure_current_focus_scope(scope);
1806                    if let Some(fallback) = fallback {
1807                        self.runtime.set_current_focus_restorer_fallback(fallback);
1808                    }
1809                }
1810            }
1811        }
1812
1813        pub(super) fn set_properties(&mut self, properties: FocusProperties) {
1814            self.runtime.set_current_focus_properties(properties);
1815        }
1816
1817        pub(super) fn set_preferred_tab_stop(&mut self, preferred: bool) {
1818            self.runtime.set_current_preferred_tab_stop(preferred);
1819        }
1820
1821        pub(super) fn set_traversal_policy(&mut self, policy: FocusTraversalPolicy) {
1822            self.runtime.set_current_focus_traversal_policy(policy);
1823        }
1824
1825        pub(super) fn set_changed_handler(&mut self, handler: CallbackWith<FocusState>) {
1826            self.runtime.set_current_focus_changed_handler(handler);
1827        }
1828
1829        pub(super) fn set_event_handler(&mut self, handler: CallbackWith<FocusState>) {
1830            self.runtime.set_current_focus_event_handler(handler);
1831        }
1832
1833        pub(super) fn set_beyond_bounds_handler(
1834            &mut self,
1835            handler: CallbackWith<FocusDirection, bool>,
1836        ) {
1837            self.runtime
1838                .set_current_focus_beyond_bounds_handler(handler);
1839        }
1840
1841        pub(super) fn set_reveal_handler(
1842            &mut self,
1843            handler: CallbackWith<FocusRevealRequest, bool>,
1844        ) {
1845            self.runtime.set_current_focus_reveal_handler(handler);
1846        }
1847    }
1848}
1849
1850fn apply_focus_op(runtime: runtime::FocusModifierRuntime<'_>, op: FocusModifierOp) {
1851    let mut runtime = runtime;
1852    match op {
1853        FocusModifierOp::Requester(requester) => runtime.bind_requester(requester),
1854        FocusModifierOp::Registration(registration) => runtime.ensure_registration(registration),
1855        FocusModifierOp::Properties(properties) => runtime.set_properties(*properties),
1856        FocusModifierOp::PreferredTabStop(preferred) => runtime.set_preferred_tab_stop(preferred),
1857        FocusModifierOp::TraversalPolicy(policy) => runtime.set_traversal_policy(policy),
1858        FocusModifierOp::ChangedHandler(handler) => runtime.set_changed_handler(handler),
1859        FocusModifierOp::EventHandler(handler) => runtime.set_event_handler(handler),
1860        FocusModifierOp::BeyondBoundsHandler(handler) => runtime.set_beyond_bounds_handler(handler),
1861        FocusModifierOp::RevealHandler(handler) => runtime.set_reveal_handler(handler),
1862    }
1863}
1864
1865#[cfg(test)]
1866mod tests {
1867    use std::{
1868        collections::hash_map::DefaultHasher,
1869        hash::{Hash, Hasher},
1870    };
1871
1872    use super::{FocusModifierOp, Modifier, ModifierAction};
1873
1874    fn flag(value: bool) -> Modifier {
1875        Modifier::new().push_action(ModifierAction::Focus(FocusModifierOp::PreferredTabStop(
1876            value,
1877        )))
1878    }
1879
1880    #[test]
1881    fn modifier_equality_flattens_different_groupings_and_checks_all_actions() {
1882        let left = flag(true).then(flag(false)).then(flag(true));
1883        let right = flag(true).then(flag(false).then(flag(true)));
1884        assert_eq!(left, right);
1885
1886        let mut left_hash = DefaultHasher::new();
1887        let mut right_hash = DefaultHasher::new();
1888        left.hash(&mut left_hash);
1889        right.hash(&mut right_hash);
1890        assert_eq!(left_hash.finish(), right_hash.finish());
1891
1892        assert_ne!(left, flag(true).then(flag(true)).then(flag(true)));
1893        assert_ne!(left, right.then(flag(false)));
1894    }
1895}