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 use graphics_layer::GlassMaterial;
57pub(crate) use local::{
58 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
59};
60#[allow(unused_imports)]
61pub use local::{ModifierLocalKey, ModifierLocalReadScope};
62#[allow(unused_imports)]
63pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
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#[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#[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#[derive(Clone, Debug, PartialEq)]
128pub struct InspectorProperty {
129 pub name: &'static str,
130 pub value: String,
131}
132
133#[derive(Clone, Debug, PartialEq)]
135pub struct ModifierInspectorRecord {
136 pub name: &'static str,
137 pub properties: Vec<InspectorProperty>,
138}
139
140#[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 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 static ENV_DEBUG: OnceLock<bool> = OnceLock::new();
194 *ENV_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG_MODIFIERS").is_some())
195 }
196 #[cfg(target_arch = "wasm32")]
197 {
198 false
199 }
200}
201
202fn inspector_metadata_enabled() -> bool {
203 cfg!(test) || modifier_debug_enabled()
204}
205
206#[derive(Clone)]
212enum ModifierKind {
213 Empty,
215 Single {
217 elements: Rc<Vec<DynModifierElement>>,
218 inspector: Rc<Vec<InspectorMetadata>>,
219 },
220}
221
222const FINGERPRINT_KIND_EMPTY: u8 = 0;
223const FINGERPRINT_KIND_SINGLE: u8 = 1;
224
225const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
226const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
227const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
228const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
229const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
230const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
231const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
232
233#[derive(Clone, Copy, Debug, PartialEq, Eq)]
234struct ModifierFingerprints {
235 strict: u64,
236 structural: u64,
237}
238
239#[inline]
240fn mix_fingerprint_bits(mut value: u64) -> u64 {
241 value ^= value >> 33;
242 value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
243 value ^= value >> 33;
244 value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
245 value ^ (value >> 33)
246}
247
248#[inline]
249fn fold_fingerprint(state: u64, value: u64) -> u64 {
250 mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
251 .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
252}
253
254#[inline]
255fn empty_fingerprints() -> ModifierFingerprints {
256 ModifierFingerprints {
257 strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
258 structural: fold_fingerprint(
259 FINGERPRINT_EMPTY_STRUCTURAL_SEED,
260 FINGERPRINT_KIND_EMPTY as u64,
261 ),
262 }
263}
264
265#[inline]
266fn single_fingerprint_seed() -> ModifierFingerprints {
267 let strict = fold_fingerprint(
268 FINGERPRINT_SINGLE_STRICT_SEED,
269 FINGERPRINT_KIND_SINGLE as u64,
270 );
271 let structural = fold_fingerprint(
272 FINGERPRINT_SINGLE_STRUCTURAL_SEED,
273 FINGERPRINT_KIND_SINGLE as u64,
274 );
275 ModifierFingerprints { strict, structural }
276}
277
278#[inline]
279fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
280 let mut hasher = default::new();
281 element.element_type().hash(&mut hasher);
282 element.capabilities().bits().hash(&mut hasher);
283 hasher.finish()
284}
285
286#[inline]
287fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
288 let common = element_common_fingerprint(element);
289 let requires_update = element.requires_update();
290 let strict_payload = if requires_update {
291 let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
292 element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
293 } else {
294 element.hash_code()
295 };
296 let strict = mix_fingerprint_bits(common ^ strict_payload);
297
298 let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
299 let structural_payload = if is_draw_only {
300 FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
301 } else {
302 element.hash_code()
303 };
304 let structural = mix_fingerprint_bits(common ^ structural_payload);
305
306 ModifierFingerprints { strict, structural }
307}
308
309#[inline]
310fn append_fingerprints(
311 mut fingerprints: ModifierFingerprints,
312 elements: &[DynModifierElement],
313) -> ModifierFingerprints {
314 for element in elements {
315 let element_fingerprints = element_fingerprints(element);
316 fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
317 fingerprints.structural =
318 fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
319 }
320 fingerprints
321}
322
323fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
324 append_fingerprints(single_fingerprint_seed(), elements)
325}
326
327pub struct ModifierElementIterator<'a> {
330 inner: std::slice::Iter<'a, DynModifierElement>,
331}
332
333impl<'a> Iterator for ModifierElementIterator<'a> {
334 type Item = &'a DynModifierElement;
335
336 #[inline]
337 fn next(&mut self) -> Option<Self::Item> {
338 self.inner.next()
339 }
340
341 #[inline]
342 fn size_hint(&self) -> (usize, Option<usize>) {
343 self.inner.size_hint()
344 }
345}
346
347impl ExactSizeIterator for ModifierElementIterator<'_> {}
348
349pub(crate) struct ModifierInspectorIterator<'a> {
351 inner: std::slice::Iter<'a, InspectorMetadata>,
352}
353
354impl<'a> Iterator for ModifierInspectorIterator<'a> {
355 type Item = &'a InspectorMetadata;
356
357 #[inline]
358 fn next(&mut self) -> Option<Self::Item> {
359 self.inner.next()
360 }
361
362 #[inline]
363 fn size_hint(&self) -> (usize, Option<usize>) {
364 self.inner.size_hint()
365 }
366}
367
368impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
369
370#[derive(Clone)]
388pub struct Modifier {
389 kind: ModifierKind,
390 strict_fingerprint: u64,
391 structural_fingerprint: u64,
392 element_count: usize,
393}
394
395impl Default for Modifier {
396 fn default() -> Self {
397 let fingerprints = empty_fingerprints();
398 Self {
399 kind: ModifierKind::Empty,
400 strict_fingerprint: fingerprints.strict,
401 structural_fingerprint: fingerprints.structural,
402 element_count: 0,
403 }
404 }
405}
406
407impl Modifier {
408 pub fn empty() -> Self {
409 Self::default()
410 }
411
412 pub fn clip_to_bounds(self) -> Self {
416 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
417 inspector_metadata("clipToBounds", |info| {
418 info.add_property("clipToBounds", "true");
419 }),
420 );
421 self.then(modifier)
422 }
423
424 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
425 where
426 T: 'static,
427 F: Fn() -> T + 'static,
428 {
429 let element = ModifierLocalProviderElement::new(key, value);
430 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
431 self.then(modifier)
432 }
433
434 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
435 where
436 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
437 {
438 let element = ModifierLocalConsumerElement::new(consumer);
439 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
440 self.then(modifier)
441 }
442
443 pub fn semantics<F>(self, recorder: F) -> Self
444 where
445 F: Fn(&mut SemanticsConfiguration) + 'static,
446 {
447 let mut preview = SemanticsConfiguration::default();
448 recorder(&mut preview);
449 let description = preview.content_description.clone();
450 let is_button = preview.is_button;
451 let is_clickable = preview.is_clickable;
452 let metadata = inspector_metadata("semantics", move |info| {
453 if let Some(desc) = &description {
454 info.add_property("contentDescription", desc.clone());
455 }
456 if is_button {
457 info.add_property("isButton", "true");
458 }
459 if is_clickable {
460 info.add_property("isClickable", "true");
461 }
462 });
463 let element = SemanticsElement::new(recorder);
464 let modifier =
465 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
466 self.then(modifier)
467 }
468
469 pub fn focus_target(self) -> Self {
475 let element = FocusTargetElement::new();
476 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
477 self.then(modifier)
478 }
479
480 pub fn on_focus_changed<F>(self, callback: F) -> Self
485 where
486 F: Fn(FocusState) + 'static,
487 {
488 let element = FocusTargetElement::with_callback(callback);
489 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
490 self.then(modifier)
491 }
492
493 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
498 let element = FocusRequesterElement::new(requester.id());
499 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
500 self.then(modifier)
501 }
502
503 pub fn debug_chain(self, tag: &'static str) -> Self {
521 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
522
523 #[derive(Clone)]
524 struct DebugChainElement {
525 tag: &'static str,
526 }
527
528 impl fmt::Debug for DebugChainElement {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 f.debug_struct("DebugChainElement")
531 .field("tag", &self.tag)
532 .finish()
533 }
534 }
535
536 impl PartialEq for DebugChainElement {
537 fn eq(&self, other: &Self) -> bool {
538 self.tag == other.tag
539 }
540 }
541
542 impl Eq for DebugChainElement {}
543
544 impl std::hash::Hash for DebugChainElement {
545 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
546 self.tag.hash(state);
547 }
548 }
549
550 impl ModifierNodeElement for DebugChainElement {
551 type Node = DebugChainNode;
552
553 fn create(&self) -> Self::Node {
554 DebugChainNode::new(self.tag)
555 }
556
557 fn update(&self, node: &mut Self::Node) {
558 node.tag = self.tag;
559 }
560
561 fn capabilities(&self) -> NodeCapabilities {
562 NodeCapabilities::empty()
563 }
564 }
565
566 struct DebugChainNode {
567 tag: &'static str,
568 state: NodeState,
569 }
570
571 impl DebugChainNode {
572 fn new(tag: &'static str) -> Self {
573 Self {
574 tag,
575 state: NodeState::new(),
576 }
577 }
578 }
579
580 impl ModifierNode for DebugChainNode {
581 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
582 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
583 }
584
585 fn on_detach(&mut self) {
586 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
587 }
588
589 fn on_reset(&mut self) {
590 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
591 }
592 }
593
594 impl cranpose_foundation::DelegatableNode for DebugChainNode {
595 fn node_state(&self) -> &NodeState {
596 &self.state
597 }
598 }
599
600 let element = DebugChainElement { tag };
601 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
602 self.then(modifier)
603 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
604 info.add_property("tag", tag);
605 }))
606 }
607
608 pub fn then(&self, next: Modifier) -> Modifier {
613 if self.is_trivially_empty() {
614 return next;
615 }
616 if next.is_trivially_empty() {
617 return self.clone();
618 }
619
620 let (self_elements, self_inspector) = match &self.kind {
622 ModifierKind::Empty => unreachable!(),
623 ModifierKind::Single {
624 elements,
625 inspector,
626 ..
627 } => (elements.as_ref(), inspector.as_ref()),
628 };
629 let (next_elements, next_inspector) = match &next.kind {
630 ModifierKind::Empty => unreachable!(),
631 ModifierKind::Single {
632 elements,
633 inspector,
634 ..
635 } => (elements.as_ref(), inspector.as_ref()),
636 };
637
638 let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
639 merged_elements.extend_from_slice(self_elements);
640 merged_elements.extend_from_slice(next_elements);
641
642 let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
643 merged_inspector.extend_from_slice(self_inspector);
644 merged_inspector.extend_from_slice(next_inspector);
645
646 let fingerprints = append_fingerprints(
647 ModifierFingerprints {
648 strict: self.strict_fingerprint,
649 structural: self.structural_fingerprint,
650 },
651 next_elements,
652 );
653 Modifier {
654 kind: ModifierKind::Single {
655 elements: Rc::new(merged_elements),
656 inspector: Rc::new(merged_inspector),
657 },
658 strict_fingerprint: fingerprints.strict,
659 structural_fingerprint: fingerprints.structural,
660 element_count: self.element_count + next.element_count,
661 }
662 }
663
664 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
666 match &self.kind {
667 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
668 ModifierKind::Single { elements, .. } => ModifierElementIterator {
669 inner: elements.iter(),
670 },
671 }
672 }
673
674 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
675 match &self.kind {
676 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
677 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
678 inner: inspector.iter(),
679 },
680 }
681 }
682
683 #[cfg(test)]
687 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
688 match &self.kind {
689 ModifierKind::Empty => Vec::new(),
690 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
691 }
692 }
693
694 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
696 match &self.kind {
697 ModifierKind::Empty => Vec::new(),
698 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
699 }
700 }
701
702 pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
703 match &self.kind {
704 ModifierKind::Empty => Self::default(),
705 ModifierKind::Single {
706 elements,
707 inspector,
708 } => Self {
709 kind: ModifierKind::Single {
710 elements: Rc::new(elements.iter().cloned().collect()),
711 inspector: Rc::new(inspector.as_ref().clone()),
712 },
713 strict_fingerprint: self.strict_fingerprint,
714 structural_fingerprint: self.structural_fingerprint,
715 element_count: self.element_count,
716 },
717 }
718 }
719
720 pub fn total_padding(&self) -> f32 {
721 let padding = self.padding_values();
722 padding
723 .left
724 .max(padding.right)
725 .max(padding.top)
726 .max(padding.bottom)
727 }
728
729 pub fn explicit_size(&self) -> Option<Size> {
730 let props = self.layout_properties();
731 match (props.width, props.height) {
732 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
733 Some(Size { width, height })
734 }
735 _ => None,
736 }
737 }
738
739 pub fn padding_values(&self) -> EdgeInsets {
740 self.resolved_modifiers().padding()
741 }
742
743 pub(crate) fn layout_properties(&self) -> LayoutProperties {
744 self.resolved_modifiers().layout_properties()
745 }
746
747 pub fn box_alignment(&self) -> Option<Alignment> {
748 self.layout_properties().box_alignment()
749 }
750
751 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
752 self.layout_properties().column_alignment()
753 }
754
755 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
756 self.layout_properties().row_alignment()
757 }
758
759 pub fn draw_commands(&self) -> Vec<DrawCommand> {
760 collect_slices_from_modifier(self).draw_commands().to_vec()
761 }
762
763 pub fn clips_to_bounds(&self) -> bool {
764 collect_slices_from_modifier(self).clip_to_bounds()
765 }
766
767 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
769 self.inspector_metadata()
770 .iter()
771 .map(|metadata| metadata.to_record())
772 .collect()
773 }
774
775 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
776 let mut handle = ModifierChainHandle::new();
777 let _ = handle.update(self);
778 handle.resolved_modifiers()
779 }
780
781 pub(crate) fn with_element<E>(element: E) -> Self
782 where
783 E: ModifierNodeElement,
784 {
785 let dyn_element = modifier_element(element);
786 Self::from_parts(vec![dyn_element])
787 }
788
789 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
790 if elements.is_empty() {
791 Self::default()
792 } else {
793 let element_count = elements.len();
794 let fingerprints = single_fingerprints(elements.as_slice());
795 Self {
796 kind: ModifierKind::Single {
797 elements: Rc::new(elements),
798 inspector: Rc::new(Vec::new()),
799 },
800 strict_fingerprint: fingerprints.strict,
801 structural_fingerprint: fingerprints.structural,
802 element_count,
803 }
804 }
805 }
806
807 fn is_trivially_empty(&self) -> bool {
808 matches!(self.kind, ModifierKind::Empty)
809 }
810
811 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
812 if metadata.is_empty() {
813 return self;
814 }
815 match self.kind {
816 ModifierKind::Empty => self,
817 ModifierKind::Single {
818 elements,
819 inspector,
820 } => {
821 let mut new_inspector = inspector.as_ref().clone();
822 new_inspector.push(metadata);
823 Self {
824 kind: ModifierKind::Single {
825 elements,
826 inspector: Rc::new(new_inspector),
827 },
828 strict_fingerprint: self.strict_fingerprint,
829 structural_fingerprint: self.structural_fingerprint,
830 element_count: self.element_count,
831 }
832 }
833 }
834 }
835
836 pub fn structural_eq(&self, other: &Self) -> bool {
841 self.eq_internal(other, false)
842 }
843
844 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
845 if self.element_count != other.element_count {
846 return false;
847 }
848 if consider_always_update {
849 if self.strict_fingerprint != other.strict_fingerprint {
850 return false;
851 }
852 } else if self.structural_fingerprint != other.structural_fingerprint {
853 return false;
854 }
855
856 match (&self.kind, &other.kind) {
857 (ModifierKind::Empty, ModifierKind::Empty) => true,
858 (
859 ModifierKind::Single {
860 elements: e1,
861 inspector: _,
862 },
863 ModifierKind::Single {
864 elements: e2,
865 inspector: _,
866 },
867 ) => {
868 if Rc::ptr_eq(e1, e2) {
869 return true;
870 }
871
872 if e1.len() != e2.len() {
873 return false;
874 }
875
876 for (a, b) in e1.iter().zip(e2.iter()) {
877 if !consider_always_update
881 && a.element_type() == b.element_type()
882 && a.capabilities() == NodeCapabilities::DRAW
883 && b.capabilities() == NodeCapabilities::DRAW
884 {
885 continue;
886 }
887
888 if consider_always_update && (a.requires_update() || b.requires_update()) {
889 if !Rc::ptr_eq(a, b) {
890 return false;
891 }
892 continue;
893 }
894
895 if !a.equals_element(&**b) {
896 return false;
897 }
898 }
899
900 true
901 }
902 _ => false,
903 }
904 }
905}
906
907impl PartialEq for Modifier {
908 fn eq(&self, other: &Self) -> bool {
909 self.eq_internal(other, true)
910 }
911}
912
913impl Eq for Modifier {}
914
915impl fmt::Display for Modifier {
916 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
917 match &self.kind {
918 ModifierKind::Empty => write!(f, "Modifier.empty"),
919 ModifierKind::Single { elements, .. } => {
920 if elements.is_empty() {
921 return write!(f, "Modifier.empty");
922 }
923 write!(f, "Modifier[")?;
924 for (index, element) in elements.iter().enumerate() {
925 if index > 0 {
926 write!(f, ", ")?;
927 }
928 let name = element.inspector_name();
929 let mut properties = Vec::new();
930 element.record_inspector_properties(&mut |prop, value| {
931 properties.push(format!("{prop}={value}"));
932 });
933 if properties.is_empty() {
934 write!(f, "{name}")?;
935 } else {
936 write!(f, "{name}({})", properties.join(", "))?;
937 }
938 }
939 write!(f, "]")
940 }
941 }
942 }
943}
944
945impl fmt::Debug for Modifier {
946 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
947 fmt::Display::fmt(self, f)
948 }
949}
950
951#[derive(Clone, Copy, Debug, PartialEq)]
952pub struct ResolvedBackground {
953 color: Color,
954 shape: Option<RoundedCornerShape>,
955}
956
957impl ResolvedBackground {
958 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
959 Self { color, shape }
960 }
961
962 pub fn color(&self) -> Color {
963 self.color
964 }
965
966 pub fn shape(&self) -> Option<RoundedCornerShape> {
967 self.shape
968 }
969
970 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
971 self.shape = shape;
972 }
973}
974
975#[derive(Clone, Copy, Debug, PartialEq, Default)]
976pub struct ResolvedModifiers {
977 padding: EdgeInsets,
978 layout: LayoutProperties,
979 offset: Point,
980}
981
982impl ResolvedModifiers {
983 pub fn padding(&self) -> EdgeInsets {
984 self.padding
985 }
986
987 pub fn layout_properties(&self) -> LayoutProperties {
988 self.layout
989 }
990
991 pub fn offset(&self) -> Point {
992 self.offset
993 }
994
995 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
996 self.padding = padding;
997 }
998
999 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1000 self.layout = layout;
1001 }
1002
1003 pub(crate) fn set_offset(&mut self, offset: Point) {
1004 self.offset = offset;
1005 }
1006}
1007
1008#[derive(Clone, Copy, Debug, Default, PartialEq)]
1009pub enum DimensionConstraint {
1010 #[default]
1011 Unspecified,
1012 Points(f32),
1013 Fraction(f32),
1014 Intrinsic(IntrinsicSize),
1015}
1016
1017#[derive(Clone, Copy, Debug, Default, PartialEq)]
1018pub struct LayoutWeight {
1019 pub weight: f32,
1020 pub fill: bool,
1021}
1022
1023#[derive(Clone, Copy, Debug, Default, PartialEq)]
1024pub struct LayoutProperties {
1025 padding: EdgeInsets,
1026 width: DimensionConstraint,
1027 height: DimensionConstraint,
1028 min_width: Option<f32>,
1029 min_height: Option<f32>,
1030 max_width: Option<f32>,
1031 max_height: Option<f32>,
1032 weight: Option<LayoutWeight>,
1033 box_alignment: Option<Alignment>,
1034 column_alignment: Option<HorizontalAlignment>,
1035 row_alignment: Option<VerticalAlignment>,
1036}
1037
1038impl LayoutProperties {
1039 pub fn padding(&self) -> EdgeInsets {
1040 self.padding
1041 }
1042
1043 pub fn width(&self) -> DimensionConstraint {
1044 self.width
1045 }
1046
1047 pub fn height(&self) -> DimensionConstraint {
1048 self.height
1049 }
1050
1051 pub fn min_width(&self) -> Option<f32> {
1052 self.min_width
1053 }
1054
1055 pub fn min_height(&self) -> Option<f32> {
1056 self.min_height
1057 }
1058
1059 pub fn max_width(&self) -> Option<f32> {
1060 self.max_width
1061 }
1062
1063 pub fn max_height(&self) -> Option<f32> {
1064 self.max_height
1065 }
1066
1067 pub fn weight(&self) -> Option<LayoutWeight> {
1068 self.weight
1069 }
1070
1071 pub fn box_alignment(&self) -> Option<Alignment> {
1072 self.box_alignment
1073 }
1074
1075 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1076 self.column_alignment
1077 }
1078
1079 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1080 self.row_alignment
1081 }
1082}
1083
1084#[cfg(test)]
1085#[path = "tests/modifier_tests.rs"]
1086mod tests;