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
125#[derive(Clone, Debug, PartialEq)]
127pub struct InspectorProperty {
128 pub name: &'static str,
129 pub value: String,
130}
131
132#[derive(Clone, Debug, PartialEq)]
134pub struct ModifierInspectorRecord {
135 pub name: &'static str,
136 pub properties: Vec<InspectorProperty>,
137}
138
139#[derive(Clone, Debug)]
141pub(crate) struct InspectorMetadata {
142 name: &'static str,
143 info: InspectorInfo,
144}
145
146impl InspectorMetadata {
147 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
148 where
149 F: FnOnce(&mut InspectorInfo),
150 {
151 let mut info = InspectorInfo::new();
152 recorder(&mut info);
153 Self { name, info }
154 }
155
156 fn is_empty(&self) -> bool {
157 self.info.is_empty()
158 }
159
160 fn to_record(&self) -> ModifierInspectorRecord {
161 ModifierInspectorRecord {
162 name: self.name,
163 properties: self.info.properties().to_vec(),
164 }
165 }
166}
167
168fn describe_dimension(constraint: DimensionConstraint) -> String {
169 match constraint {
170 DimensionConstraint::Unspecified => "unspecified".to_string(),
171 DimensionConstraint::Points(value) => value.to_string(),
172 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
173 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
174 }
175}
176
177pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
178where
179 F: FnOnce(&mut InspectorInfo),
180{
181 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 static ENV_DEBUG: OnceLock<bool> = OnceLock::new();
193 *ENV_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG_MODIFIERS").is_some())
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#[derive(Clone)]
211enum ModifierKind {
212 Empty,
214 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
326pub 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
348pub(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#[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 pub fn clip_to_bounds(self) -> Self {
415 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
416 inspector_metadata("clipToBounds", |info| {
417 info.add_property("clipToBounds", "true");
418 }),
419 );
420 self.then(modifier)
421 }
422
423 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
424 where
425 T: 'static,
426 F: Fn() -> T + 'static,
427 {
428 let element = ModifierLocalProviderElement::new(key, value);
429 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
430 self.then(modifier)
431 }
432
433 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
434 where
435 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
436 {
437 let element = ModifierLocalConsumerElement::new(consumer);
438 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
439 self.then(modifier)
440 }
441
442 pub fn semantics<F>(self, recorder: F) -> Self
443 where
444 F: Fn(&mut SemanticsConfiguration) + 'static,
445 {
446 let mut preview = SemanticsConfiguration::default();
447 recorder(&mut preview);
448 let description = preview.content_description.clone();
449 let is_button = preview.is_button;
450 let is_clickable = preview.is_clickable;
451 let metadata = inspector_metadata("semantics", move |info| {
452 if let Some(desc) = &description {
453 info.add_property("contentDescription", desc.clone());
454 }
455 if is_button {
456 info.add_property("isButton", "true");
457 }
458 if is_clickable {
459 info.add_property("isClickable", "true");
460 }
461 });
462 let element = SemanticsElement::new(recorder);
463 let modifier =
464 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
465 self.then(modifier)
466 }
467
468 pub fn focus_target(self) -> Self {
474 let element = FocusTargetElement::new();
475 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
476 self.then(modifier)
477 }
478
479 pub fn on_focus_changed<F>(self, callback: F) -> Self
484 where
485 F: Fn(FocusState) + 'static,
486 {
487 let element = FocusTargetElement::with_callback(callback);
488 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
489 self.then(modifier)
490 }
491
492 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
497 let element = FocusRequesterElement::new(requester.id());
498 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
499 self.then(modifier)
500 }
501
502 pub fn debug_chain(self, tag: &'static str) -> Self {
520 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
521
522 #[derive(Clone)]
523 struct DebugChainElement {
524 tag: &'static str,
525 }
526
527 impl fmt::Debug for DebugChainElement {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 f.debug_struct("DebugChainElement")
530 .field("tag", &self.tag)
531 .finish()
532 }
533 }
534
535 impl PartialEq for DebugChainElement {
536 fn eq(&self, other: &Self) -> bool {
537 self.tag == other.tag
538 }
539 }
540
541 impl Eq for DebugChainElement {}
542
543 impl std::hash::Hash for DebugChainElement {
544 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
545 self.tag.hash(state);
546 }
547 }
548
549 impl ModifierNodeElement for DebugChainElement {
550 type Node = DebugChainNode;
551
552 fn create(&self) -> Self::Node {
553 DebugChainNode::new(self.tag)
554 }
555
556 fn update(&self, node: &mut Self::Node) {
557 node.tag = self.tag;
558 }
559
560 fn capabilities(&self) -> NodeCapabilities {
561 NodeCapabilities::empty()
562 }
563 }
564
565 struct DebugChainNode {
566 tag: &'static str,
567 state: NodeState,
568 }
569
570 impl DebugChainNode {
571 fn new(tag: &'static str) -> Self {
572 Self {
573 tag,
574 state: NodeState::new(),
575 }
576 }
577 }
578
579 impl ModifierNode for DebugChainNode {
580 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
581 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
582 }
583
584 fn on_detach(&mut self) {
585 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
586 }
587
588 fn on_reset(&mut self) {
589 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
590 }
591 }
592
593 impl cranpose_foundation::DelegatableNode for DebugChainNode {
594 fn node_state(&self) -> &NodeState {
595 &self.state
596 }
597 }
598
599 let element = DebugChainElement { tag };
600 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
601 self.then(modifier)
602 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
603 info.add_property("tag", tag);
604 }))
605 }
606
607 pub fn then(&self, next: Modifier) -> Modifier {
612 if self.is_trivially_empty() {
613 return next;
614 }
615 if next.is_trivially_empty() {
616 return self.clone();
617 }
618
619 let (self_elements, self_inspector) = match &self.kind {
621 ModifierKind::Empty => unreachable!(),
622 ModifierKind::Single {
623 elements,
624 inspector,
625 ..
626 } => (elements.as_ref(), inspector.as_ref()),
627 };
628 let (next_elements, next_inspector) = match &next.kind {
629 ModifierKind::Empty => unreachable!(),
630 ModifierKind::Single {
631 elements,
632 inspector,
633 ..
634 } => (elements.as_ref(), inspector.as_ref()),
635 };
636
637 let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
638 merged_elements.extend_from_slice(self_elements);
639 merged_elements.extend_from_slice(next_elements);
640
641 let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
642 merged_inspector.extend_from_slice(self_inspector);
643 merged_inspector.extend_from_slice(next_inspector);
644
645 let fingerprints = append_fingerprints(
646 ModifierFingerprints {
647 strict: self.strict_fingerprint,
648 structural: self.structural_fingerprint,
649 },
650 next_elements,
651 );
652 Modifier {
653 kind: ModifierKind::Single {
654 elements: Rc::new(merged_elements),
655 inspector: Rc::new(merged_inspector),
656 },
657 strict_fingerprint: fingerprints.strict,
658 structural_fingerprint: fingerprints.structural,
659 element_count: self.element_count + next.element_count,
660 }
661 }
662
663 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
665 match &self.kind {
666 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
667 ModifierKind::Single { elements, .. } => ModifierElementIterator {
668 inner: elements.iter(),
669 },
670 }
671 }
672
673 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
674 match &self.kind {
675 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
676 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
677 inner: inspector.iter(),
678 },
679 }
680 }
681
682 #[cfg(test)]
686 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
687 match &self.kind {
688 ModifierKind::Empty => Vec::new(),
689 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
690 }
691 }
692
693 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
695 match &self.kind {
696 ModifierKind::Empty => Vec::new(),
697 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
698 }
699 }
700
701 pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
702 match &self.kind {
703 ModifierKind::Empty => Self::default(),
704 ModifierKind::Single {
705 elements,
706 inspector,
707 } => Self {
708 kind: ModifierKind::Single {
709 elements: Rc::new(elements.iter().cloned().collect()),
710 inspector: Rc::new(inspector.as_ref().clone()),
711 },
712 strict_fingerprint: self.strict_fingerprint,
713 structural_fingerprint: self.structural_fingerprint,
714 element_count: self.element_count,
715 },
716 }
717 }
718
719 pub fn total_padding(&self) -> f32 {
720 let padding = self.padding_values();
721 padding
722 .left
723 .max(padding.right)
724 .max(padding.top)
725 .max(padding.bottom)
726 }
727
728 pub fn explicit_size(&self) -> Option<Size> {
729 let props = self.layout_properties();
730 match (props.width, props.height) {
731 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
732 Some(Size { width, height })
733 }
734 _ => None,
735 }
736 }
737
738 pub fn padding_values(&self) -> EdgeInsets {
739 self.resolved_modifiers().padding()
740 }
741
742 pub(crate) fn layout_properties(&self) -> LayoutProperties {
743 self.resolved_modifiers().layout_properties()
744 }
745
746 pub fn box_alignment(&self) -> Option<Alignment> {
747 self.layout_properties().box_alignment()
748 }
749
750 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
751 self.layout_properties().column_alignment()
752 }
753
754 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
755 self.layout_properties().row_alignment()
756 }
757
758 pub fn draw_commands(&self) -> Vec<DrawCommand> {
759 collect_slices_from_modifier(self).draw_commands().to_vec()
760 }
761
762 pub fn clips_to_bounds(&self) -> bool {
763 collect_slices_from_modifier(self).clip_to_bounds()
764 }
765
766 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
768 self.inspector_metadata()
769 .iter()
770 .map(|metadata| metadata.to_record())
771 .collect()
772 }
773
774 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
775 let mut handle = ModifierChainHandle::new();
776 let _ = handle.update(self);
777 handle.resolved_modifiers()
778 }
779
780 fn with_element<E>(element: E) -> Self
781 where
782 E: ModifierNodeElement,
783 {
784 let dyn_element = modifier_element(element);
785 Self::from_parts(vec![dyn_element])
786 }
787
788 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
789 if elements.is_empty() {
790 Self::default()
791 } else {
792 let element_count = elements.len();
793 let fingerprints = single_fingerprints(elements.as_slice());
794 Self {
795 kind: ModifierKind::Single {
796 elements: Rc::new(elements),
797 inspector: Rc::new(Vec::new()),
798 },
799 strict_fingerprint: fingerprints.strict,
800 structural_fingerprint: fingerprints.structural,
801 element_count,
802 }
803 }
804 }
805
806 fn is_trivially_empty(&self) -> bool {
807 matches!(self.kind, ModifierKind::Empty)
808 }
809
810 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
811 if metadata.is_empty() {
812 return self;
813 }
814 match self.kind {
815 ModifierKind::Empty => self,
816 ModifierKind::Single {
817 elements,
818 inspector,
819 } => {
820 let mut new_inspector = inspector.as_ref().clone();
821 new_inspector.push(metadata);
822 Self {
823 kind: ModifierKind::Single {
824 elements,
825 inspector: Rc::new(new_inspector),
826 },
827 strict_fingerprint: self.strict_fingerprint,
828 structural_fingerprint: self.structural_fingerprint,
829 element_count: self.element_count,
830 }
831 }
832 }
833 }
834
835 pub fn structural_eq(&self, other: &Self) -> bool {
840 self.eq_internal(other, false)
841 }
842
843 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
844 if self.element_count != other.element_count {
845 return false;
846 }
847 if consider_always_update {
848 if self.strict_fingerprint != other.strict_fingerprint {
849 return false;
850 }
851 } else if self.structural_fingerprint != other.structural_fingerprint {
852 return false;
853 }
854
855 match (&self.kind, &other.kind) {
856 (ModifierKind::Empty, ModifierKind::Empty) => true,
857 (
858 ModifierKind::Single {
859 elements: e1,
860 inspector: _,
861 },
862 ModifierKind::Single {
863 elements: e2,
864 inspector: _,
865 },
866 ) => {
867 if Rc::ptr_eq(e1, e2) {
868 return true;
869 }
870
871 if e1.len() != e2.len() {
872 return false;
873 }
874
875 for (a, b) in e1.iter().zip(e2.iter()) {
876 if !consider_always_update
880 && a.element_type() == b.element_type()
881 && a.capabilities() == NodeCapabilities::DRAW
882 && b.capabilities() == NodeCapabilities::DRAW
883 {
884 continue;
885 }
886
887 if consider_always_update && (a.requires_update() || b.requires_update()) {
888 if !Rc::ptr_eq(a, b) {
889 return false;
890 }
891 continue;
892 }
893
894 if !a.equals_element(&**b) {
895 return false;
896 }
897 }
898
899 true
900 }
901 _ => false,
902 }
903 }
904}
905
906impl PartialEq for Modifier {
907 fn eq(&self, other: &Self) -> bool {
908 self.eq_internal(other, true)
909 }
910}
911
912impl Eq for Modifier {}
913
914impl fmt::Display for Modifier {
915 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
916 match &self.kind {
917 ModifierKind::Empty => write!(f, "Modifier.empty"),
918 ModifierKind::Single { elements, .. } => {
919 if elements.is_empty() {
920 return write!(f, "Modifier.empty");
921 }
922 write!(f, "Modifier[")?;
923 for (index, element) in elements.iter().enumerate() {
924 if index > 0 {
925 write!(f, ", ")?;
926 }
927 let name = element.inspector_name();
928 let mut properties = Vec::new();
929 element.record_inspector_properties(&mut |prop, value| {
930 properties.push(format!("{prop}={value}"));
931 });
932 if properties.is_empty() {
933 write!(f, "{name}")?;
934 } else {
935 write!(f, "{name}({})", properties.join(", "))?;
936 }
937 }
938 write!(f, "]")
939 }
940 }
941 }
942}
943
944impl fmt::Debug for Modifier {
945 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
946 fmt::Display::fmt(self, f)
947 }
948}
949
950#[derive(Clone, Copy, Debug, PartialEq)]
951pub struct ResolvedBackground {
952 color: Color,
953 shape: Option<RoundedCornerShape>,
954}
955
956impl ResolvedBackground {
957 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
958 Self { color, shape }
959 }
960
961 pub fn color(&self) -> Color {
962 self.color
963 }
964
965 pub fn shape(&self) -> Option<RoundedCornerShape> {
966 self.shape
967 }
968
969 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
970 self.shape = shape;
971 }
972}
973
974#[derive(Clone, Copy, Debug, PartialEq, Default)]
975pub struct ResolvedModifiers {
976 padding: EdgeInsets,
977 layout: LayoutProperties,
978 offset: Point,
979}
980
981impl ResolvedModifiers {
982 pub fn padding(&self) -> EdgeInsets {
983 self.padding
984 }
985
986 pub fn layout_properties(&self) -> LayoutProperties {
987 self.layout
988 }
989
990 pub fn offset(&self) -> Point {
991 self.offset
992 }
993
994 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
995 self.padding = padding;
996 }
997
998 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
999 self.layout = layout;
1000 }
1001
1002 pub(crate) fn set_offset(&mut self, offset: Point) {
1003 self.offset = offset;
1004 }
1005}
1006
1007#[derive(Clone, Copy, Debug, Default, PartialEq)]
1008pub enum DimensionConstraint {
1009 #[default]
1010 Unspecified,
1011 Points(f32),
1012 Fraction(f32),
1013 Intrinsic(IntrinsicSize),
1014}
1015
1016#[derive(Clone, Copy, Debug, Default, PartialEq)]
1017pub struct LayoutWeight {
1018 pub weight: f32,
1019 pub fill: bool,
1020}
1021
1022#[derive(Clone, Copy, Debug, Default, PartialEq)]
1023pub struct LayoutProperties {
1024 padding: EdgeInsets,
1025 width: DimensionConstraint,
1026 height: DimensionConstraint,
1027 min_width: Option<f32>,
1028 min_height: Option<f32>,
1029 max_width: Option<f32>,
1030 max_height: Option<f32>,
1031 weight: Option<LayoutWeight>,
1032 box_alignment: Option<Alignment>,
1033 column_alignment: Option<HorizontalAlignment>,
1034 row_alignment: Option<VerticalAlignment>,
1035}
1036
1037impl LayoutProperties {
1038 pub fn padding(&self) -> EdgeInsets {
1039 self.padding
1040 }
1041
1042 pub fn width(&self) -> DimensionConstraint {
1043 self.width
1044 }
1045
1046 pub fn height(&self) -> DimensionConstraint {
1047 self.height
1048 }
1049
1050 pub fn min_width(&self) -> Option<f32> {
1051 self.min_width
1052 }
1053
1054 pub fn min_height(&self) -> Option<f32> {
1055 self.min_height
1056 }
1057
1058 pub fn max_width(&self) -> Option<f32> {
1059 self.max_width
1060 }
1061
1062 pub fn max_height(&self) -> Option<f32> {
1063 self.max_height
1064 }
1065
1066 pub fn weight(&self) -> Option<LayoutWeight> {
1067 self.weight
1068 }
1069
1070 pub fn box_alignment(&self) -> Option<Alignment> {
1071 self.box_alignment
1072 }
1073
1074 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1075 self.column_alignment
1076 }
1077
1078 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1079 self.row_alignment
1080 }
1081}
1082
1083#[cfg(test)]
1084#[path = "tests/modifier_tests.rs"]
1085mod tests;