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