1#![allow(non_snake_case)]
9
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::rc::Rc;
13#[cfg(not(target_arch = "wasm32"))]
14use std::sync::OnceLock;
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;
30mod pointer_input;
31mod scroll;
32mod semantics;
33mod shadow;
34mod size;
35mod slices;
36mod weight;
37
38pub use crate::draw::{DrawCacheBuilder, DrawCommand};
39#[allow(unused_imports)]
40pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
41pub use cranpose_foundation::{
42 modifier_element, AnyModifierElement, DynModifierElement, FocusState, PointerEvent,
43 PointerEventKind, SemanticsConfiguration,
44};
45use cranpose_foundation::{ModifierNodeElement, NodeCapabilities};
46#[allow(unused_imports)]
47pub use cranpose_ui_graphics::{
48 BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
49 CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec, GradientFadeMaskSpec,
50 GraphicsLayer, LayerShape, Point, Rect, RenderEffect, RoundedCornerShape, RuntimeShader,
51 Shadow, ShadowScope, Size, TransformOrigin,
52};
53use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
54#[allow(unused_imports)]
55pub use focus::{FocusDirection, FocusRequester};
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 semantics::{collect_semantics_from_chain, collect_semantics_from_modifier};
64pub use slices::{
65 collect_modifier_slices, collect_modifier_slices_into, collect_slices_from_modifier,
66 ModifierNodeSlices,
67};
68#[cfg(feature = "test-helpers")]
70pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
71
72use crate::modifier_nodes::ClipToBoundsElement;
73use focus::{FocusRequesterElement, FocusTargetElement};
74use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
75use semantics::SemanticsElement;
76
77#[derive(Clone, Debug, Default)]
79pub struct InspectorInfo {
80 properties: Vec<InspectorProperty>,
81}
82
83impl InspectorInfo {
84 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
89 self.properties.push(InspectorProperty {
90 name,
91 value: value.into(),
92 });
93 }
94
95 pub fn properties(&self) -> &[InspectorProperty] {
96 &self.properties
97 }
98
99 pub fn is_empty(&self) -> bool {
100 self.properties.is_empty()
101 }
102
103 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
104 self.add_property(name, describe_dimension(constraint));
105 }
106
107 pub fn add_offset_components(
108 &mut self,
109 x_name: &'static str,
110 y_name: &'static str,
111 offset: Point,
112 ) {
113 self.add_property(x_name, offset.x.to_string());
114 self.add_property(y_name, offset.y.to_string());
115 }
116
117 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
118 where
119 A: fmt::Debug,
120 {
121 self.add_property(name, format!("{alignment:?}"));
122 }
123
124 #[allow(dead_code)] pub fn debug_properties(&self) -> Vec<(&'static str, String)> {
126 self.properties
127 .iter()
128 .map(|property| (property.name, property.value.clone()))
129 .collect()
130 }
131
132 #[allow(dead_code)] pub fn describe(&self) -> String {
134 self.properties
135 .iter()
136 .map(|property| format!("{}={}", property.name, property.value))
137 .collect::<Vec<_>>()
138 .join(", ")
139 }
140}
141
142#[derive(Clone, Debug, PartialEq)]
144pub struct InspectorProperty {
145 pub name: &'static str,
146 pub value: String,
147}
148
149#[derive(Clone, Debug, PartialEq)]
151pub struct ModifierInspectorRecord {
152 pub name: &'static str,
153 pub properties: Vec<InspectorProperty>,
154}
155
156#[derive(Clone, Debug)]
158pub(crate) struct InspectorMetadata {
159 name: &'static str,
160 info: InspectorInfo,
161}
162
163impl InspectorMetadata {
164 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
165 where
166 F: FnOnce(&mut InspectorInfo),
167 {
168 let mut info = InspectorInfo::new();
169 recorder(&mut info);
170 Self { name, info }
171 }
172
173 fn is_empty(&self) -> bool {
174 self.info.is_empty()
175 }
176
177 fn to_record(&self) -> ModifierInspectorRecord {
178 ModifierInspectorRecord {
179 name: self.name,
180 properties: self.info.properties().to_vec(),
181 }
182 }
183}
184
185fn describe_dimension(constraint: DimensionConstraint) -> String {
186 match constraint {
187 DimensionConstraint::Unspecified => "unspecified".to_string(),
188 DimensionConstraint::Points(value) => value.to_string(),
189 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
190 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
191 }
192}
193
194pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
195where
196 F: FnOnce(&mut InspectorInfo),
197{
198 if !inspector_metadata_enabled() {
201 return InspectorMetadata::new(name, |_| {});
202 }
203 InspectorMetadata::new(name, recorder)
204}
205
206pub(crate) fn modifier_debug_enabled() -> bool {
207 #[cfg(not(target_arch = "wasm32"))]
208 {
209 static ENV_DEBUG: OnceLock<bool> = OnceLock::new();
210 *ENV_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG_MODIFIERS").is_some())
211 }
212 #[cfg(target_arch = "wasm32")]
213 {
214 false
215 }
216}
217
218fn inspector_metadata_enabled() -> bool {
219 cfg!(debug_assertions) || cfg!(test) || modifier_debug_enabled()
220}
221
222#[derive(Clone)]
228enum ModifierKind {
229 Empty,
231 Single {
233 elements: Rc<Vec<DynModifierElement>>,
234 inspector: Rc<Vec<InspectorMetadata>>,
235 },
236}
237
238const FINGERPRINT_KIND_EMPTY: u8 = 0;
239const FINGERPRINT_KIND_SINGLE: u8 = 1;
240
241const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
242const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
243const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
244const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
245const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
246const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
247const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
248
249#[derive(Clone, Copy, Debug, PartialEq, Eq)]
250struct ModifierFingerprints {
251 strict: u64,
252 structural: u64,
253}
254
255#[inline]
256fn mix_fingerprint_bits(mut value: u64) -> u64 {
257 value ^= value >> 33;
258 value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
259 value ^= value >> 33;
260 value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
261 value ^ (value >> 33)
262}
263
264#[inline]
265fn fold_fingerprint(state: u64, value: u64) -> u64 {
266 mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
267 .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
268}
269
270#[inline]
271fn empty_fingerprints() -> ModifierFingerprints {
272 ModifierFingerprints {
273 strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
274 structural: fold_fingerprint(
275 FINGERPRINT_EMPTY_STRUCTURAL_SEED,
276 FINGERPRINT_KIND_EMPTY as u64,
277 ),
278 }
279}
280
281#[inline]
282fn single_fingerprint_seed() -> ModifierFingerprints {
283 let strict = fold_fingerprint(
284 FINGERPRINT_SINGLE_STRICT_SEED,
285 FINGERPRINT_KIND_SINGLE as u64,
286 );
287 let structural = fold_fingerprint(
288 FINGERPRINT_SINGLE_STRUCTURAL_SEED,
289 FINGERPRINT_KIND_SINGLE as u64,
290 );
291 ModifierFingerprints { strict, structural }
292}
293
294#[inline]
295fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
296 let mut hasher = default::new();
297 element.element_type().hash(&mut hasher);
298 element.capabilities().bits().hash(&mut hasher);
299 hasher.finish()
300}
301
302#[inline]
303fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
304 let common = element_common_fingerprint(element);
305 let requires_update = element.requires_update();
306 let strict_payload = if requires_update {
307 let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
308 element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
309 } else {
310 element.hash_code()
311 };
312 let strict = mix_fingerprint_bits(common ^ strict_payload);
313
314 let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
315 let structural_payload = if is_draw_only {
316 FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
317 } else {
318 element.hash_code()
319 };
320 let structural = mix_fingerprint_bits(common ^ structural_payload);
321
322 ModifierFingerprints { strict, structural }
323}
324
325#[inline]
326fn append_fingerprints(
327 mut fingerprints: ModifierFingerprints,
328 elements: &[DynModifierElement],
329) -> ModifierFingerprints {
330 for element in elements {
331 let element_fingerprints = element_fingerprints(element);
332 fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
333 fingerprints.structural =
334 fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
335 }
336 fingerprints
337}
338
339fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
340 append_fingerprints(single_fingerprint_seed(), elements)
341}
342
343pub struct ModifierElementIterator<'a> {
346 inner: std::slice::Iter<'a, DynModifierElement>,
347}
348
349impl<'a> Iterator for ModifierElementIterator<'a> {
350 type Item = &'a DynModifierElement;
351
352 #[inline]
353 fn next(&mut self) -> Option<Self::Item> {
354 self.inner.next()
355 }
356
357 #[inline]
358 fn size_hint(&self) -> (usize, Option<usize>) {
359 self.inner.size_hint()
360 }
361}
362
363impl ExactSizeIterator for ModifierElementIterator<'_> {}
364
365pub(crate) struct ModifierInspectorIterator<'a> {
367 inner: std::slice::Iter<'a, InspectorMetadata>,
368}
369
370impl<'a> Iterator for ModifierInspectorIterator<'a> {
371 type Item = &'a InspectorMetadata;
372
373 #[inline]
374 fn next(&mut self) -> Option<Self::Item> {
375 self.inner.next()
376 }
377
378 #[inline]
379 fn size_hint(&self) -> (usize, Option<usize>) {
380 self.inner.size_hint()
381 }
382}
383
384impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
385
386#[derive(Clone)]
404pub struct Modifier {
405 kind: ModifierKind,
406 strict_fingerprint: u64,
407 structural_fingerprint: u64,
408 element_count: usize,
409}
410
411impl Default for Modifier {
412 fn default() -> Self {
413 let fingerprints = empty_fingerprints();
414 Self {
415 kind: ModifierKind::Empty,
416 strict_fingerprint: fingerprints.strict,
417 structural_fingerprint: fingerprints.structural,
418 element_count: 0,
419 }
420 }
421}
422
423impl Modifier {
424 pub fn empty() -> Self {
425 Self::default()
426 }
427
428 pub fn clip_to_bounds(self) -> Self {
432 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
433 inspector_metadata("clipToBounds", |info| {
434 info.add_property("clipToBounds", "true");
435 }),
436 );
437 self.then(modifier)
438 }
439
440 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
441 where
442 T: 'static,
443 F: Fn() -> T + 'static,
444 {
445 let element = ModifierLocalProviderElement::new(key, value);
446 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
447 self.then(modifier)
448 }
449
450 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
451 where
452 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
453 {
454 let element = ModifierLocalConsumerElement::new(consumer);
455 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
456 self.then(modifier)
457 }
458
459 pub fn semantics<F>(self, recorder: F) -> Self
460 where
461 F: Fn(&mut SemanticsConfiguration) + 'static,
462 {
463 let mut preview = SemanticsConfiguration::default();
464 recorder(&mut preview);
465 let description = preview.content_description.clone();
466 let is_button = preview.is_button;
467 let is_clickable = preview.is_clickable;
468 let metadata = inspector_metadata("semantics", move |info| {
469 if let Some(desc) = &description {
470 info.add_property("contentDescription", desc.clone());
471 }
472 if is_button {
473 info.add_property("isButton", "true");
474 }
475 if is_clickable {
476 info.add_property("isClickable", "true");
477 }
478 });
479 let element = SemanticsElement::new(recorder);
480 let modifier =
481 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
482 self.then(modifier)
483 }
484
485 pub fn focus_target(self) -> Self {
491 let element = FocusTargetElement::new();
492 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
493 self.then(modifier)
494 }
495
496 pub fn on_focus_changed<F>(self, callback: F) -> Self
501 where
502 F: Fn(FocusState) + 'static,
503 {
504 let element = FocusTargetElement::with_callback(callback);
505 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
506 self.then(modifier)
507 }
508
509 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
514 let element = FocusRequesterElement::new(requester.id());
515 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
516 self.then(modifier)
517 }
518
519 pub fn debug_chain(self, tag: &'static str) -> Self {
537 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
538
539 #[derive(Clone)]
540 struct DebugChainElement {
541 tag: &'static str,
542 }
543
544 impl fmt::Debug for DebugChainElement {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546 f.debug_struct("DebugChainElement")
547 .field("tag", &self.tag)
548 .finish()
549 }
550 }
551
552 impl PartialEq for DebugChainElement {
553 fn eq(&self, other: &Self) -> bool {
554 self.tag == other.tag
555 }
556 }
557
558 impl Eq for DebugChainElement {}
559
560 impl std::hash::Hash for DebugChainElement {
561 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
562 self.tag.hash(state);
563 }
564 }
565
566 impl ModifierNodeElement for DebugChainElement {
567 type Node = DebugChainNode;
568
569 fn create(&self) -> Self::Node {
570 DebugChainNode::new(self.tag)
571 }
572
573 fn update(&self, node: &mut Self::Node) {
574 node.tag = self.tag;
575 }
576
577 fn capabilities(&self) -> NodeCapabilities {
578 NodeCapabilities::empty()
579 }
580 }
581
582 struct DebugChainNode {
583 tag: &'static str,
584 state: NodeState,
585 }
586
587 impl DebugChainNode {
588 fn new(tag: &'static str) -> Self {
589 Self {
590 tag,
591 state: NodeState::new(),
592 }
593 }
594 }
595
596 impl ModifierNode for DebugChainNode {
597 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
598 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
599 }
600
601 fn on_detach(&mut self) {
602 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
603 }
604
605 fn on_reset(&mut self) {
606 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
607 }
608 }
609
610 impl cranpose_foundation::DelegatableNode for DebugChainNode {
611 fn node_state(&self) -> &NodeState {
612 &self.state
613 }
614 }
615
616 let element = DebugChainElement { tag };
617 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
618 self.then(modifier)
619 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
620 info.add_property("tag", tag);
621 }))
622 }
623
624 pub fn then(&self, next: Modifier) -> Modifier {
629 if self.is_trivially_empty() {
630 return next;
631 }
632 if next.is_trivially_empty() {
633 return self.clone();
634 }
635
636 let (self_elements, self_inspector) = match &self.kind {
638 ModifierKind::Empty => unreachable!(),
639 ModifierKind::Single {
640 elements,
641 inspector,
642 ..
643 } => (elements.as_ref(), inspector.as_ref()),
644 };
645 let (next_elements, next_inspector) = match &next.kind {
646 ModifierKind::Empty => unreachable!(),
647 ModifierKind::Single {
648 elements,
649 inspector,
650 ..
651 } => (elements.as_ref(), inspector.as_ref()),
652 };
653
654 let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
655 merged_elements.extend_from_slice(self_elements);
656 merged_elements.extend_from_slice(next_elements);
657
658 let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
659 merged_inspector.extend_from_slice(self_inspector);
660 merged_inspector.extend_from_slice(next_inspector);
661
662 let fingerprints = append_fingerprints(
663 ModifierFingerprints {
664 strict: self.strict_fingerprint,
665 structural: self.structural_fingerprint,
666 },
667 next_elements,
668 );
669 Modifier {
670 kind: ModifierKind::Single {
671 elements: Rc::new(merged_elements),
672 inspector: Rc::new(merged_inspector),
673 },
674 strict_fingerprint: fingerprints.strict,
675 structural_fingerprint: fingerprints.structural,
676 element_count: self.element_count + next.element_count,
677 }
678 }
679
680 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
682 match &self.kind {
683 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
684 ModifierKind::Single { elements, .. } => ModifierElementIterator {
685 inner: elements.iter(),
686 },
687 }
688 }
689
690 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
691 match &self.kind {
692 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
693 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
694 inner: inspector.iter(),
695 },
696 }
697 }
698
699 #[cfg(test)]
703 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
704 match &self.kind {
705 ModifierKind::Empty => Vec::new(),
706 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
707 }
708 }
709
710 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
712 match &self.kind {
713 ModifierKind::Empty => Vec::new(),
714 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
715 }
716 }
717
718 pub fn total_padding(&self) -> f32 {
719 let padding = self.padding_values();
720 padding
721 .left
722 .max(padding.right)
723 .max(padding.top)
724 .max(padding.bottom)
725 }
726
727 pub fn explicit_size(&self) -> Option<Size> {
728 let props = self.layout_properties();
729 match (props.width, props.height) {
730 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
731 Some(Size { width, height })
732 }
733 _ => None,
734 }
735 }
736
737 pub fn padding_values(&self) -> EdgeInsets {
738 self.resolved_modifiers().padding()
739 }
740
741 pub(crate) fn layout_properties(&self) -> LayoutProperties {
742 self.resolved_modifiers().layout_properties()
743 }
744
745 pub fn box_alignment(&self) -> Option<Alignment> {
746 self.layout_properties().box_alignment()
747 }
748
749 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
750 self.layout_properties().column_alignment()
751 }
752
753 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
754 self.layout_properties().row_alignment()
755 }
756
757 pub fn draw_commands(&self) -> Vec<DrawCommand> {
758 collect_slices_from_modifier(self).draw_commands().to_vec()
759 }
760
761 pub fn clips_to_bounds(&self) -> bool {
762 collect_slices_from_modifier(self).clip_to_bounds()
763 }
764
765 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
767 self.inspector_metadata()
768 .iter()
769 .map(|metadata| metadata.to_record())
770 .collect()
771 }
772
773 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
774 let mut handle = ModifierChainHandle::new();
775 let _ = handle.update(self);
776 handle.resolved_modifiers()
777 }
778
779 fn with_element<E>(element: E) -> Self
780 where
781 E: ModifierNodeElement,
782 {
783 let dyn_element = modifier_element(element);
784 Self::from_parts(vec![dyn_element])
785 }
786
787 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
788 if elements.is_empty() {
789 Self::default()
790 } else {
791 let element_count = elements.len();
792 let fingerprints = single_fingerprints(elements.as_slice());
793 Self {
794 kind: ModifierKind::Single {
795 elements: Rc::new(elements),
796 inspector: Rc::new(Vec::new()),
797 },
798 strict_fingerprint: fingerprints.strict,
799 structural_fingerprint: fingerprints.structural,
800 element_count,
801 }
802 }
803 }
804
805 fn is_trivially_empty(&self) -> bool {
806 matches!(self.kind, ModifierKind::Empty)
807 }
808
809 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
810 if metadata.is_empty() {
811 return self;
812 }
813 match self.kind {
814 ModifierKind::Empty => self,
815 ModifierKind::Single {
816 elements,
817 inspector,
818 } => {
819 let mut new_inspector = inspector.as_ref().clone();
820 new_inspector.push(metadata);
821 Self {
822 kind: ModifierKind::Single {
823 elements,
824 inspector: Rc::new(new_inspector),
825 },
826 strict_fingerprint: self.strict_fingerprint,
827 structural_fingerprint: self.structural_fingerprint,
828 element_count: self.element_count,
829 }
830 }
831 }
832 }
833
834 pub fn structural_eq(&self, other: &Self) -> bool {
839 self.eq_internal(other, false)
840 }
841
842 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
843 if self.element_count != other.element_count {
844 return false;
845 }
846 if consider_always_update {
847 if self.strict_fingerprint != other.strict_fingerprint {
848 return false;
849 }
850 } else if self.structural_fingerprint != other.structural_fingerprint {
851 return false;
852 }
853
854 match (&self.kind, &other.kind) {
855 (ModifierKind::Empty, ModifierKind::Empty) => true,
856 (
857 ModifierKind::Single {
858 elements: e1,
859 inspector: _,
860 },
861 ModifierKind::Single {
862 elements: e2,
863 inspector: _,
864 },
865 ) => {
866 if Rc::ptr_eq(e1, e2) {
867 return true;
868 }
869
870 if e1.len() != e2.len() {
871 return false;
872 }
873
874 for (a, b) in e1.iter().zip(e2.iter()) {
875 if !consider_always_update
879 && a.element_type() == b.element_type()
880 && a.capabilities() == NodeCapabilities::DRAW
881 && b.capabilities() == NodeCapabilities::DRAW
882 {
883 continue;
884 }
885
886 if consider_always_update && (a.requires_update() || b.requires_update()) {
887 if !Rc::ptr_eq(a, b) {
888 return false;
889 }
890 continue;
891 }
892
893 if !a.equals_element(&**b) {
894 return false;
895 }
896 }
897
898 true
899 }
900 _ => false,
901 }
902 }
903}
904
905impl PartialEq for Modifier {
906 fn eq(&self, other: &Self) -> bool {
907 self.eq_internal(other, true)
908 }
909}
910
911impl Eq for Modifier {}
912
913impl fmt::Display for Modifier {
914 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
915 match &self.kind {
916 ModifierKind::Empty => write!(f, "Modifier.empty"),
917 ModifierKind::Single { elements, .. } => {
918 if elements.is_empty() {
919 return write!(f, "Modifier.empty");
920 }
921 write!(f, "Modifier[")?;
922 for (index, element) in elements.iter().enumerate() {
923 if index > 0 {
924 write!(f, ", ")?;
925 }
926 let name = element.inspector_name();
927 let mut properties = Vec::new();
928 element.record_inspector_properties(&mut |prop, value| {
929 properties.push(format!("{prop}={value}"));
930 });
931 if properties.is_empty() {
932 write!(f, "{name}")?;
933 } else {
934 write!(f, "{name}({})", properties.join(", "))?;
935 }
936 }
937 write!(f, "]")
938 }
939 }
940 }
941}
942
943impl fmt::Debug for Modifier {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 fmt::Display::fmt(self, f)
946 }
947}
948
949#[derive(Clone, Copy, Debug, PartialEq)]
950pub struct ResolvedBackground {
951 color: Color,
952 shape: Option<RoundedCornerShape>,
953}
954
955impl ResolvedBackground {
956 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
957 Self { color, shape }
958 }
959
960 pub fn color(&self) -> Color {
961 self.color
962 }
963
964 pub fn shape(&self) -> Option<RoundedCornerShape> {
965 self.shape
966 }
967
968 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
969 self.shape = shape;
970 }
971}
972
973#[derive(Clone, Copy, Debug, PartialEq, Default)]
974pub struct ResolvedModifiers {
975 padding: EdgeInsets,
976 layout: LayoutProperties,
977 offset: Point,
978}
979
980impl ResolvedModifiers {
981 pub fn padding(&self) -> EdgeInsets {
982 self.padding
983 }
984
985 pub fn layout_properties(&self) -> LayoutProperties {
986 self.layout
987 }
988
989 pub fn offset(&self) -> Point {
990 self.offset
991 }
992
993 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
994 self.padding = padding;
995 }
996
997 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
998 self.layout = layout;
999 }
1000
1001 pub(crate) fn set_offset(&mut self, offset: Point) {
1002 self.offset = offset;
1003 }
1004}
1005
1006#[derive(Clone, Copy, Debug, Default, PartialEq)]
1007pub enum DimensionConstraint {
1008 #[default]
1009 Unspecified,
1010 Points(f32),
1011 Fraction(f32),
1012 Intrinsic(IntrinsicSize),
1013}
1014
1015#[derive(Clone, Copy, Debug, Default, PartialEq)]
1016pub struct LayoutWeight {
1017 pub weight: f32,
1018 pub fill: bool,
1019}
1020
1021#[derive(Clone, Copy, Debug, Default, PartialEq)]
1022pub struct LayoutProperties {
1023 padding: EdgeInsets,
1024 width: DimensionConstraint,
1025 height: DimensionConstraint,
1026 min_width: Option<f32>,
1027 min_height: Option<f32>,
1028 max_width: Option<f32>,
1029 max_height: Option<f32>,
1030 weight: Option<LayoutWeight>,
1031 box_alignment: Option<Alignment>,
1032 column_alignment: Option<HorizontalAlignment>,
1033 row_alignment: Option<VerticalAlignment>,
1034}
1035
1036impl LayoutProperties {
1037 pub fn padding(&self) -> EdgeInsets {
1038 self.padding
1039 }
1040
1041 pub fn width(&self) -> DimensionConstraint {
1042 self.width
1043 }
1044
1045 pub fn height(&self) -> DimensionConstraint {
1046 self.height
1047 }
1048
1049 pub fn min_width(&self) -> Option<f32> {
1050 self.min_width
1051 }
1052
1053 pub fn min_height(&self) -> Option<f32> {
1054 self.min_height
1055 }
1056
1057 pub fn max_width(&self) -> Option<f32> {
1058 self.max_width
1059 }
1060
1061 pub fn max_height(&self) -> Option<f32> {
1062 self.max_height
1063 }
1064
1065 pub fn weight(&self) -> Option<LayoutWeight> {
1066 self.weight
1067 }
1068
1069 pub fn box_alignment(&self) -> Option<Alignment> {
1070 self.box_alignment
1071 }
1072
1073 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1074 self.column_alignment
1075 }
1076
1077 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1078 self.row_alignment
1079 }
1080}
1081
1082#[cfg(test)]
1083#[path = "tests/modifier_tests.rs"]
1084mod tests;