Skip to main content

cranpose_ui/modifier/
mod.rs

1//! Modifier system for Cranpose
2//!
3//! This module now acts as a thin builder around modifier elements. Each
4//! [`Modifier`] stores the element chain required by the modifier node system
5//! together with inspector metadata while resolved state is computed directly
6//! from the modifier nodes.
7
8#![allow(non_snake_case)]
9
10use std::{
11    fmt,
12    hash::{Hash, Hasher},
13    rc::Rc,
14};
15
16use cranpose_core::hash::default;
17
18mod alignment;
19mod background;
20mod blur;
21mod chain;
22mod clickable;
23mod draw_cache;
24mod fill;
25mod focus;
26mod graphics_layer;
27mod local;
28mod offset;
29mod padding;
30pub(crate) mod pointer_input;
31mod rotary_input;
32mod scroll;
33mod semantics;
34mod shadow;
35mod size;
36mod slices;
37mod toggleable;
38mod weight;
39
40#[allow(unused_imports)]
41pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
42pub use cranpose_foundation::{
43    AnyModifierElement, DynModifierElement, FocusState, PointerEvent, PointerEventKind,
44    PointerSource, RotaryScrollEvent, SemanticsConfiguration, modifier_element,
45};
46use cranpose_foundation::{ModifierNodeElement, NodeCapabilities};
47#[allow(unused_imports)]
48pub use cranpose_ui_graphics::{
49    BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
50    CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec, GradientFadeMaskSpec,
51    GraphicsLayer, LayerShape, Point, Rect, RenderEffect, RoundedCornerShape, RuntimeShader,
52    Shadow, ShadowScope, Size, TransformOrigin,
53};
54use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
55use focus::FocusTargetElement;
56#[allow(unused_imports)]
57pub use focus::{FocusDirection, FocusRequestError, FocusRequester, FocusRequesterElement};
58pub use graphics_layer::GlassMaterial;
59pub(crate) use local::{
60    ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
61};
62use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
63#[allow(unused_imports)]
64pub use local::{ModifierLocalKey, ModifierLocalReadScope};
65#[allow(unused_imports)]
66pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
67pub use rotary_input::RotaryInputModifierNode;
68#[cfg(feature = "test-helpers")]
69pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
70use semantics::SemanticsElement;
71pub use semantics::{
72    SemanticsRequester, SemanticsRequesterElement, collect_semantics_from_chain,
73    collect_semantics_from_modifier,
74};
75pub use slices::{
76    ModifierNodeSlices, ModifierNodeSlicesDebugStats, collect_modifier_slices,
77    collect_modifier_slices_into, collect_slices_from_modifier,
78};
79
80pub use crate::draw::{DrawCacheBuilder, DrawCommand};
81use crate::modifier_nodes::ClipToBoundsElement;
82
83#[derive(Clone, Debug, Default)]
84pub struct InspectorInfo {
85    properties: Vec<InspectorProperty>,
86}
87
88impl InspectorInfo {
89    pub fn new() -> Self {
90        Self::default()
91    }
92
93    pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
94        self.properties.push(InspectorProperty {
95            name,
96            value: value.into(),
97        });
98    }
99
100    pub fn properties(&self) -> &[InspectorProperty] {
101        &self.properties
102    }
103
104    pub fn is_empty(&self) -> bool {
105        self.properties.is_empty()
106    }
107
108    pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
109        self.add_property(name, describe_dimension(constraint));
110    }
111
112    pub fn add_offset_components(
113        &mut self,
114        x_name: &'static str,
115        y_name: &'static str,
116        offset: Point,
117    ) {
118        self.add_property(x_name, offset.x.to_string());
119        self.add_property(y_name, offset.y.to_string());
120    }
121
122    pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
123    where
124        A: fmt::Debug,
125    {
126        self.add_property(name, format!("{alignment:?}"));
127    }
128}
129
130#[derive(Clone, Debug, PartialEq)]
131pub struct InspectorProperty {
132    pub name: &'static str,
133    pub value: String,
134}
135
136#[derive(Clone, Debug, PartialEq)]
137pub struct ModifierInspectorRecord {
138    pub name: &'static str,
139    pub properties: Vec<InspectorProperty>,
140}
141
142#[derive(Clone, Debug)]
143pub(crate) struct InspectorMetadata {
144    name: &'static str,
145    info: InspectorInfo,
146}
147
148impl InspectorMetadata {
149    pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
150    where
151        F: FnOnce(&mut InspectorInfo),
152    {
153        let mut info = InspectorInfo::new();
154        recorder(&mut info);
155        Self { name, info }
156    }
157
158    fn is_empty(&self) -> bool {
159        self.info.is_empty()
160    }
161
162    fn to_record(&self) -> ModifierInspectorRecord {
163        ModifierInspectorRecord {
164            name: self.name,
165            properties: self.info.properties().to_vec(),
166        }
167    }
168}
169
170fn describe_dimension(constraint: DimensionConstraint) -> String {
171    match constraint {
172        DimensionConstraint::Unspecified => "unspecified".to_string(),
173        DimensionConstraint::Points(value) => value.to_string(),
174        DimensionConstraint::Fraction(value) => format!("fraction({value})"),
175        DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
176    }
177}
178
179pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
180where
181    F: FnOnce(&mut InspectorInfo),
182{
183    if !inspector_metadata_enabled() {
184        return InspectorMetadata::new(name, |_| {});
185    }
186    InspectorMetadata::new(name, recorder)
187}
188
189pub(crate) fn modifier_debug_enabled() -> bool {
190    #[cfg(not(target_arch = "wasm32"))]
191    {
192        cranpose_core::env_flag!("COMPOSE_DEBUG_MODIFIERS")
193    }
194    #[cfg(target_arch = "wasm32")]
195    {
196        false
197    }
198}
199
200fn inspector_metadata_enabled() -> bool {
201    cfg!(test) || modifier_debug_enabled()
202}
203
204#[derive(Clone)]
205enum ModifierKind {
206    Empty,
207    Single {
208        elements: Rc<Vec<DynModifierElement>>,
209        inspector: Rc<Vec<InspectorMetadata>>,
210    },
211}
212
213const FINGERPRINT_KIND_EMPTY: u8 = 0;
214const FINGERPRINT_KIND_SINGLE: u8 = 1;
215
216const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
217const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
218const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
219const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
220const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
221const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
222const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
223
224#[derive(Clone, Copy, Debug, PartialEq, Eq)]
225struct ModifierFingerprints {
226    strict: u64,
227    structural: u64,
228}
229
230#[inline]
231fn mix_fingerprint_bits(mut value: u64) -> u64 {
232    value ^= value >> 33;
233    value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
234    value ^= value >> 33;
235    value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
236    value ^ (value >> 33)
237}
238
239#[inline]
240fn fold_fingerprint(state: u64, value: u64) -> u64 {
241    mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
242        .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
243}
244
245#[inline]
246fn empty_fingerprints() -> ModifierFingerprints {
247    ModifierFingerprints {
248        strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
249        structural: fold_fingerprint(
250            FINGERPRINT_EMPTY_STRUCTURAL_SEED,
251            FINGERPRINT_KIND_EMPTY as u64,
252        ),
253    }
254}
255
256#[inline]
257fn single_fingerprint_seed() -> ModifierFingerprints {
258    let strict = fold_fingerprint(
259        FINGERPRINT_SINGLE_STRICT_SEED,
260        FINGERPRINT_KIND_SINGLE as u64,
261    );
262    let structural = fold_fingerprint(
263        FINGERPRINT_SINGLE_STRUCTURAL_SEED,
264        FINGERPRINT_KIND_SINGLE as u64,
265    );
266    ModifierFingerprints { strict, structural }
267}
268
269#[inline]
270fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
271    let mut hasher = default::new();
272    element.element_type().hash(&mut hasher);
273    element.capabilities().bits().hash(&mut hasher);
274    hasher.finish()
275}
276
277#[inline]
278fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
279    let common = element_common_fingerprint(element);
280    let requires_update = element.requires_update();
281    let strict_payload = if requires_update {
282        let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
283        element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
284    } else {
285        element.hash_code()
286    };
287    let strict = mix_fingerprint_bits(common ^ strict_payload);
288
289    let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
290    let structural_payload = if is_draw_only {
291        FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
292    } else {
293        element.hash_code()
294    };
295    let structural = mix_fingerprint_bits(common ^ structural_payload);
296
297    ModifierFingerprints { strict, structural }
298}
299
300#[inline]
301fn append_fingerprints(
302    mut fingerprints: ModifierFingerprints,
303    elements: &[DynModifierElement],
304) -> ModifierFingerprints {
305    for element in elements {
306        let element_fingerprints = element_fingerprints(element);
307        fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
308        fingerprints.structural =
309            fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
310    }
311    fingerprints
312}
313
314fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
315    append_fingerprints(single_fingerprint_seed(), elements)
316}
317
318pub struct ModifierElementIterator<'a> {
319    inner: std::slice::Iter<'a, DynModifierElement>,
320}
321
322impl<'a> Iterator for ModifierElementIterator<'a> {
323    type Item = &'a DynModifierElement;
324
325    #[inline]
326    fn next(&mut self) -> Option<Self::Item> {
327        self.inner.next()
328    }
329
330    #[inline]
331    fn size_hint(&self) -> (usize, Option<usize>) {
332        self.inner.size_hint()
333    }
334}
335
336impl ExactSizeIterator for ModifierElementIterator<'_> {}
337
338pub(crate) struct ModifierInspectorIterator<'a> {
339    inner: std::slice::Iter<'a, InspectorMetadata>,
340}
341
342impl<'a> Iterator for ModifierInspectorIterator<'a> {
343    type Item = &'a InspectorMetadata;
344
345    #[inline]
346    fn next(&mut self) -> Option<Self::Item> {
347        self.inner.next()
348    }
349
350    #[inline]
351    fn size_hint(&self) -> (usize, Option<usize>) {
352        self.inner.size_hint()
353    }
354}
355
356impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
357
358/// A modifier chain that can be applied to composable elements.
359///
360/// Modifiers allow you to decorate or augment a composable. Common operations include:
361/// - Adjusting layout (e.g., `padding`, `fill_max_size`)
362/// - Adding behavior (e.g., `clickable`, `scrollable`)
363/// - Drawing (e.g., `background`, `border`)
364///
365/// Modifiers are immutable and form a chain using the builder pattern.
366/// The order of modifiers matters: previous modifiers wrap subsequent ones.
367///
368/// # Example
369///
370/// ```rust,ignore
371/// Modifier::padding(16.0)     // Applied first (outer)
372///     .background(Color::Red) // Applied second
373///     .clickable(|| println!("Clicked")) // Applied last (inner)
374/// ```
375#[derive(Clone)]
376pub struct Modifier {
377    kind: ModifierKind,
378    strict_fingerprint: u64,
379    structural_fingerprint: u64,
380    element_count: usize,
381}
382
383impl Default for Modifier {
384    fn default() -> Self {
385        let fingerprints = empty_fingerprints();
386        Self {
387            kind: ModifierKind::Empty,
388            strict_fingerprint: fingerprints.strict,
389            structural_fingerprint: fingerprints.structural,
390            element_count: 0,
391        }
392    }
393}
394
395impl Modifier {
396    pub fn empty() -> Self {
397        Self::default()
398    }
399
400    /// Creates a modifier from a custom modifier node element.
401    pub fn from_element<E>(element: E) -> Self
402    where
403        E: ModifierNodeElement,
404    {
405        Self::with_element(element)
406    }
407
408    /// Clip the content to the bounds of this modifier.
409    ///
410    /// Example: `Modifier::empty().clip_to_bounds()`
411    pub fn clip_to_bounds(self) -> Self {
412        let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
413            inspector_metadata("clipToBounds", |info| {
414                info.add_property("clipToBounds", "true");
415            }),
416        );
417        self.then(modifier)
418    }
419
420    pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
421    where
422        T: 'static,
423        F: Fn() -> T + 'static,
424    {
425        let element = ModifierLocalProviderElement::new(key, value);
426        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
427        self.then(modifier)
428    }
429
430    pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
431    where
432        F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
433    {
434        let element = ModifierLocalConsumerElement::new(consumer);
435        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
436        self.then(modifier)
437    }
438
439    pub fn semantics<F>(self, recorder: F) -> Self
440    where
441        F: Fn(&mut SemanticsConfiguration) + 'static,
442    {
443        let recorder: std::rc::Rc<dyn Fn(&mut SemanticsConfiguration)> = std::rc::Rc::new(recorder);
444        let metadata = if inspector_metadata_enabled() {
445            let mut preview = SemanticsConfiguration::default();
446            recorder(&mut preview);
447            let description = preview.content_description.clone();
448            let state_description = preview.state_description.clone();
449            let role = preview.role;
450            let is_clickable = preview.is_activatable();
451            let canvas_children = preview.canvas_children.len();
452            inspector_metadata("semantics", move |info| {
453                if let Some(desc) = &description {
454                    info.add_property("contentDescription", desc.clone());
455                }
456                if let Some(state) = &state_description {
457                    info.add_property("stateDescription", state.clone());
458                }
459                if let Some(role) = role {
460                    info.add_property("role", format!("{role:?}"));
461                }
462                if is_clickable {
463                    info.add_property("isClickable", "true");
464                }
465                if canvas_children > 0 {
466                    info.add_property("canvasSemanticsChildren", canvas_children.to_string());
467                }
468            })
469        } else {
470            inspector_metadata("semantics", |_| {})
471        };
472        let element = SemanticsElement::new(recorder);
473        let modifier =
474            Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
475        self.then(modifier)
476    }
477
478    /// Makes this component focusable.
479    ///
480    /// This adds a focus target node that can receive focus and participate
481    /// in focus traversal. The component will be included in tab order and
482    /// can be focused programmatically.
483    pub fn focus_target(self) -> Self {
484        let element = FocusTargetElement::new();
485        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
486        self.then(modifier)
487    }
488
489    /// Makes this component focusable with a callback for focus changes.
490    ///
491    /// The callback is invoked whenever the focus state changes, allowing
492    /// components to react to gaining or losing focus.
493    pub fn on_focus_changed<F>(self, callback: F) -> Self
494    where
495        F: Fn(FocusState) + 'static,
496    {
497        let element = FocusTargetElement::with_callback(callback);
498        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
499        self.then(modifier)
500    }
501
502    /// Binds a [`FocusRequester`] to this node, so an app can move focus onto
503    /// it imperatively with [`FocusRequester::request_focus`].
504    ///
505    /// Pair it with [`focus_target`](Self::focus_target) (or
506    /// [`on_focus_changed`](Self::on_focus_changed)) on the same node —
507    /// `request_focus` moves whichever focus targets are attached there.
508    pub fn focus_requester(self, requester: &FocusRequester) -> Self {
509        let element = FocusRequesterElement::new(requester.clone());
510        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
511        self.then(modifier)
512    }
513
514    /// The Compose `Modifier.focusable()` convenience.
515    ///
516    /// Compose's version also wires an optional `MutableInteractionSource` so
517    /// a `focusable` can drive its own visual indication. Cranpose's
518    /// [`MutableInteractionSource`](crate::MutableInteractionSource) only
519    /// emits press interactions — there is no focus interaction or indication
520    /// concept to plug in yet — so `focusable` here is honestly just
521    /// [`focus_target`](Self::focus_target), nothing more.
522    pub fn focusable(self) -> Self {
523        self.focus_target()
524    }
525
526    /// Binds a [`SemanticsRequester`] to this node, so an app can mark the
527    /// node's semantics for re-collection without recomposing or laying out.
528    ///
529    /// Pair it with [`semantics`](Self::semantics) on the same node when the
530    /// recorder reads state the composition does not observe — app state behind
531    /// a `RefCell`, a game's own model — which is the case a recorder cannot
532    /// signal for itself.
533    pub fn semantics_requester(self, requester: &SemanticsRequester) -> Self {
534        let element = SemanticsRequesterElement::new(requester.clone());
535        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
536        self.then(modifier)
537    }
538
539    /// Enables debug logging for this modifier chain.
540    ///
541    /// When enabled, logs the entire modifier chain structure including:
542    /// - Element types and their properties
543    /// - Inspector metadata
544    /// - Capability flags
545    ///
546    /// This is useful for debugging modifier composition issues and understanding
547    /// how the modifier chain is structured at runtime.
548    ///
549    /// Example:
550    /// ```text
551    /// Modifier::empty()
552    ///     .padding(8.0)
553    ///     .background(Color(1.0, 0.0, 0.0, 1.0))
554    ///     .debug_chain("MyWidget")
555    /// ```
556    pub fn debug_chain(self, tag: &'static str) -> Self {
557        use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
558
559        #[derive(Clone)]
560        struct DebugChainElement {
561            tag: &'static str,
562        }
563
564        impl fmt::Debug for DebugChainElement {
565            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
566                f.debug_struct("DebugChainElement")
567                    .field("tag", &self.tag)
568                    .finish()
569            }
570        }
571
572        impl PartialEq for DebugChainElement {
573            fn eq(&self, other: &Self) -> bool {
574                self.tag == other.tag
575            }
576        }
577
578        impl Eq for DebugChainElement {}
579
580        impl std::hash::Hash for DebugChainElement {
581            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
582                self.tag.hash(state);
583            }
584        }
585
586        impl ModifierNodeElement for DebugChainElement {
587            type Node = DebugChainNode;
588
589            fn create(&self) -> Self::Node {
590                DebugChainNode::new(self.tag)
591            }
592
593            fn update(&self, node: &mut Self::Node) {
594                node.tag = self.tag;
595            }
596
597            fn capabilities(&self) -> NodeCapabilities {
598                NodeCapabilities::empty()
599            }
600        }
601
602        struct DebugChainNode {
603            tag: &'static str,
604            state: NodeState,
605        }
606
607        impl DebugChainNode {
608            fn new(tag: &'static str) -> Self {
609                Self {
610                    tag,
611                    state: NodeState::new(),
612                }
613            }
614        }
615
616        impl ModifierNode for DebugChainNode {
617            fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
618                eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
619            }
620
621            fn on_detach(&mut self) {
622                eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
623            }
624
625            fn on_reset(&mut self) {
626                eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
627            }
628        }
629
630        impl cranpose_foundation::DelegatableNode for DebugChainNode {
631            fn node_state(&self) -> &NodeState {
632                &self.state
633            }
634        }
635
636        let element = DebugChainElement { tag };
637        let modifier = Modifier::from_parts(vec![modifier_element(element)]);
638        self.then(modifier)
639            .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
640                info.add_property("tag", tag);
641            }))
642    }
643
644    /// Concatenates this modifier with another.
645    ///
646    /// Eagerly concatenates both element vectors into a single flat `Single`
647    /// variant, avoiding recursive Rc tree overhead on drop and comparison.
648    pub fn then(&self, next: Modifier) -> Modifier {
649        if self.is_trivially_empty() {
650            return next;
651        }
652        if next.is_trivially_empty() {
653            return self.clone();
654        }
655
656        let Some((self_elements, self_inspector)) = self.single_parts() else {
657            return next;
658        };
659        let Some((next_elements, next_inspector)) = next.single_parts() else {
660            return self.clone();
661        };
662
663        let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
664        merged_elements.extend_from_slice(self_elements);
665        merged_elements.extend_from_slice(next_elements);
666
667        let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
668        merged_inspector.extend_from_slice(self_inspector);
669        merged_inspector.extend_from_slice(next_inspector);
670
671        let fingerprints = append_fingerprints(
672            ModifierFingerprints {
673                strict: self.strict_fingerprint,
674                structural: self.structural_fingerprint,
675            },
676            next_elements,
677        );
678        Modifier {
679            kind: ModifierKind::Single {
680                elements: Rc::new(merged_elements),
681                inspector: Rc::new(merged_inspector),
682            },
683            strict_fingerprint: fingerprints.strict,
684            structural_fingerprint: fingerprints.structural,
685            element_count: self.element_count + next.element_count,
686        }
687    }
688
689    pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
690        match &self.kind {
691            ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
692            ModifierKind::Single { elements, .. } => ModifierElementIterator {
693                inner: elements.iter(),
694            },
695        }
696    }
697
698    pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
699        match &self.kind {
700            ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
701            ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
702                inner: inspector.iter(),
703            },
704        }
705    }
706
707    #[cfg(test)]
708    pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
709        match &self.kind {
710            ModifierKind::Empty => Vec::new(),
711            ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
712        }
713    }
714
715    pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
716        match &self.kind {
717            ModifierKind::Empty => Vec::new(),
718            ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
719        }
720    }
721
722    pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
723        match &self.kind {
724            ModifierKind::Empty => Self::default(),
725            ModifierKind::Single {
726                elements,
727                inspector,
728            } => Self {
729                kind: ModifierKind::Single {
730                    elements: Rc::new(elements.iter().cloned().collect()),
731                    inspector: Rc::new(inspector.as_ref().clone()),
732                },
733                strict_fingerprint: self.strict_fingerprint,
734                structural_fingerprint: self.structural_fingerprint,
735                element_count: self.element_count,
736            },
737        }
738    }
739
740    pub fn total_padding(&self) -> f32 {
741        let padding = self.padding_values();
742        padding
743            .left
744            .max(padding.right)
745            .max(padding.top)
746            .max(padding.bottom)
747    }
748
749    pub fn explicit_size(&self) -> Option<Size> {
750        let props = self.layout_properties();
751        match (props.width, props.height) {
752            (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
753                Some(Size { width, height })
754            }
755            _ => None,
756        }
757    }
758
759    pub fn padding_values(&self) -> EdgeInsets {
760        self.resolved_modifiers().padding()
761    }
762
763    pub(crate) fn layout_properties(&self) -> LayoutProperties {
764        self.resolved_modifiers().layout_properties()
765    }
766
767    pub fn box_alignment(&self) -> Option<Alignment> {
768        self.layout_properties().box_alignment()
769    }
770
771    pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
772        self.layout_properties().column_alignment()
773    }
774
775    pub fn row_alignment(&self) -> Option<VerticalAlignment> {
776        self.layout_properties().row_alignment()
777    }
778
779    pub fn draw_commands(&self) -> Vec<DrawCommand> {
780        collect_slices_from_modifier(self).draw_commands().to_vec()
781    }
782
783    pub fn clips_to_bounds(&self) -> bool {
784        collect_slices_from_modifier(self).clip_to_bounds()
785    }
786
787    /// Returns structured inspector records for each modifier element.
788    pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
789        self.inspector_metadata()
790            .iter()
791            .map(|metadata| metadata.to_record())
792            .collect()
793    }
794
795    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
796        let mut handle = ModifierChainHandle::new();
797        let _ = handle.update(self);
798        handle.resolved_modifiers()
799    }
800
801    pub(crate) fn with_element<E>(element: E) -> Self
802    where
803        E: ModifierNodeElement,
804    {
805        let dyn_element = modifier_element(element);
806        Self::from_parts(vec![dyn_element])
807    }
808
809    pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
810        if elements.is_empty() {
811            Self::default()
812        } else {
813            let element_count = elements.len();
814            let fingerprints = single_fingerprints(elements.as_slice());
815            Self {
816                kind: ModifierKind::Single {
817                    elements: Rc::new(elements),
818                    inspector: Rc::new(Vec::new()),
819                },
820                strict_fingerprint: fingerprints.strict,
821                structural_fingerprint: fingerprints.structural,
822                element_count,
823            }
824        }
825    }
826
827    fn is_trivially_empty(&self) -> bool {
828        matches!(self.kind, ModifierKind::Empty)
829    }
830
831    fn single_parts(&self) -> Option<(&[DynModifierElement], &[InspectorMetadata])> {
832        match &self.kind {
833            ModifierKind::Empty => None,
834            ModifierKind::Single {
835                elements,
836                inspector,
837            } => Some((elements.as_slice(), inspector.as_slice())),
838        }
839    }
840
841    pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
842        if metadata.is_empty() {
843            return self;
844        }
845        match self.kind {
846            ModifierKind::Empty => self,
847            ModifierKind::Single {
848                elements,
849                inspector,
850            } => {
851                let mut new_inspector = inspector.as_ref().clone();
852                new_inspector.push(metadata);
853                Self {
854                    kind: ModifierKind::Single {
855                        elements,
856                        inspector: Rc::new(new_inspector),
857                    },
858                    strict_fingerprint: self.strict_fingerprint,
859                    structural_fingerprint: self.structural_fingerprint,
860                    element_count: self.element_count,
861                }
862            }
863        }
864    }
865
866    /// Checks whether two modifiers are structurally equivalent for layout decisions.
867    ///
868    /// This ignores identity-sensitive modifier elements (e.g., draw closures) so
869    /// draw-only updates do not force measure/layout invalidation.
870    pub fn structural_eq(&self, other: &Self) -> bool {
871        self.eq_internal(other, false)
872    }
873
874    fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
875        if self.element_count != other.element_count {
876            return false;
877        }
878        if consider_always_update {
879            if self.strict_fingerprint != other.strict_fingerprint {
880                return false;
881            }
882        } else if self.structural_fingerprint != other.structural_fingerprint {
883            return false;
884        }
885
886        match (&self.kind, &other.kind) {
887            (ModifierKind::Empty, ModifierKind::Empty) => true,
888            (
889                ModifierKind::Single {
890                    elements: e1,
891                    inspector: _,
892                },
893                ModifierKind::Single {
894                    elements: e2,
895                    inspector: _,
896                },
897            ) => {
898                if Rc::ptr_eq(e1, e2) {
899                    return true;
900                }
901
902                if e1.len() != e2.len() {
903                    return false;
904                }
905
906                for (a, b) in e1.iter().zip(e2.iter()) {
907                    if !consider_always_update
908                        && a.element_type() == b.element_type()
909                        && a.capabilities() == NodeCapabilities::DRAW
910                        && b.capabilities() == NodeCapabilities::DRAW
911                    {
912                        continue;
913                    }
914
915                    if consider_always_update && (a.requires_update() || b.requires_update()) {
916                        if !Rc::ptr_eq(a, b) {
917                            return false;
918                        }
919                        continue;
920                    }
921
922                    if !a.equals_element(&**b) {
923                        return false;
924                    }
925                }
926
927                true
928            }
929            _ => false,
930        }
931    }
932}
933
934impl PartialEq for Modifier {
935    fn eq(&self, other: &Self) -> bool {
936        self.eq_internal(other, true)
937    }
938}
939
940impl Eq for Modifier {}
941
942impl fmt::Display for Modifier {
943    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
944        match &self.kind {
945            ModifierKind::Empty => write!(f, "Modifier.empty"),
946            ModifierKind::Single { elements, .. } => {
947                if elements.is_empty() {
948                    return write!(f, "Modifier.empty");
949                }
950                write!(f, "Modifier[")?;
951                for (index, element) in elements.iter().enumerate() {
952                    if index > 0 {
953                        write!(f, ", ")?;
954                    }
955                    let name = element.inspector_name();
956                    let mut properties = Vec::new();
957                    element.record_inspector_properties(&mut |prop, value| {
958                        properties.push(format!("{prop}={value}"));
959                    });
960                    if properties.is_empty() {
961                        write!(f, "{name}")?;
962                    } else {
963                        write!(f, "{name}({})", properties.join(", "))?;
964                    }
965                }
966                write!(f, "]")
967            }
968        }
969    }
970}
971
972impl fmt::Debug for Modifier {
973    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
974        fmt::Display::fmt(self, f)
975    }
976}
977
978#[derive(Clone, Copy, Debug, PartialEq)]
979pub struct ResolvedBackground {
980    color: Color,
981    shape: Option<RoundedCornerShape>,
982}
983
984impl ResolvedBackground {
985    pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
986        Self { color, shape }
987    }
988
989    pub fn color(&self) -> Color {
990        self.color
991    }
992
993    pub fn shape(&self) -> Option<RoundedCornerShape> {
994        self.shape
995    }
996
997    pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
998        self.shape = shape;
999    }
1000}
1001
1002#[derive(Clone, Copy, Debug, PartialEq, Default)]
1003pub struct ResolvedModifiers {
1004    padding: EdgeInsets,
1005    layout: LayoutProperties,
1006    offset: Point,
1007}
1008
1009impl ResolvedModifiers {
1010    pub fn padding(&self) -> EdgeInsets {
1011        self.padding
1012    }
1013
1014    pub fn layout_properties(&self) -> LayoutProperties {
1015        self.layout
1016    }
1017
1018    pub fn offset(&self) -> Point {
1019        self.offset
1020    }
1021
1022    pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
1023        self.padding = padding;
1024    }
1025
1026    pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1027        self.layout = layout;
1028    }
1029
1030    pub(crate) fn set_offset(&mut self, offset: Point) {
1031        self.offset = offset;
1032    }
1033}
1034
1035#[derive(Clone, Copy, Debug, Default, PartialEq)]
1036pub enum DimensionConstraint {
1037    #[default]
1038    Unspecified,
1039    Points(f32),
1040    Fraction(f32),
1041    Intrinsic(IntrinsicSize),
1042}
1043
1044#[derive(Clone, Copy, Debug, Default, PartialEq)]
1045pub struct LayoutWeight {
1046    pub weight: f32,
1047    pub fill: bool,
1048}
1049
1050#[derive(Clone, Copy, Debug, Default, PartialEq)]
1051pub struct LayoutProperties {
1052    padding: EdgeInsets,
1053    width: DimensionConstraint,
1054    height: DimensionConstraint,
1055    min_width: Option<f32>,
1056    min_height: Option<f32>,
1057    max_width: Option<f32>,
1058    max_height: Option<f32>,
1059    weight: Option<LayoutWeight>,
1060    box_alignment: Option<Alignment>,
1061    column_alignment: Option<HorizontalAlignment>,
1062    row_alignment: Option<VerticalAlignment>,
1063}
1064
1065impl LayoutProperties {
1066    pub fn padding(&self) -> EdgeInsets {
1067        self.padding
1068    }
1069
1070    pub fn width(&self) -> DimensionConstraint {
1071        self.width
1072    }
1073
1074    pub fn height(&self) -> DimensionConstraint {
1075        self.height
1076    }
1077
1078    pub fn min_width(&self) -> Option<f32> {
1079        self.min_width
1080    }
1081
1082    pub fn min_height(&self) -> Option<f32> {
1083        self.min_height
1084    }
1085
1086    pub fn max_width(&self) -> Option<f32> {
1087        self.max_width
1088    }
1089
1090    pub fn max_height(&self) -> Option<f32> {
1091        self.max_height
1092    }
1093
1094    pub fn weight(&self) -> Option<LayoutWeight> {
1095        self.weight
1096    }
1097
1098    pub fn box_alignment(&self) -> Option<Alignment> {
1099        self.box_alignment
1100    }
1101
1102    pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1103        self.column_alignment
1104    }
1105
1106    pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1107        self.row_alignment
1108    }
1109}
1110
1111#[cfg(test)]
1112#[path = "tests/modifier_tests.rs"]
1113mod tests;