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
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;
28mod pointer_input;
29mod scroll;
30mod semantics;
31mod shadow;
32mod size;
33mod slices;
34mod weight;
35
36pub use crate::draw::{DrawCacheBuilder, DrawCommand};
37#[allow(unused_imports)]
38pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
39pub use cranpose_foundation::{
40 modifier_element, AnyModifierElement, DynModifierElement, FocusState, PointerEvent,
41 PointerEventKind, SemanticsConfiguration,
42};
43use cranpose_foundation::{ModifierNodeElement, NodeCapabilities};
44#[allow(unused_imports)]
45pub use cranpose_ui_graphics::{
46 BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
47 CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec, GradientFadeMaskSpec,
48 GraphicsLayer, LayerShape, Point, Rect, RenderEffect, RoundedCornerShape, RuntimeShader,
49 Shadow, ShadowScope, Size, TransformOrigin,
50};
51use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
52#[allow(unused_imports)]
53pub use focus::{FocusDirection, FocusRequester};
54pub(crate) use local::{
55 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
56};
57#[allow(unused_imports)]
58pub use local::{ModifierLocalKey, ModifierLocalReadScope};
59#[allow(unused_imports)]
60pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
61pub use semantics::{collect_semantics_from_chain, collect_semantics_from_modifier};
62pub use slices::{
63 collect_modifier_slices, collect_modifier_slices_into, collect_slices_from_modifier,
64 ModifierNodeSlices,
65};
66#[cfg(feature = "test-helpers")]
68pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
69
70use crate::modifier_nodes::ClipToBoundsElement;
71use focus::{FocusRequesterElement, FocusTargetElement};
72use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
73use semantics::SemanticsElement;
74
75#[derive(Clone, Debug, Default)]
77pub struct InspectorInfo {
78 properties: Vec<InspectorProperty>,
79}
80
81impl InspectorInfo {
82 pub fn new() -> Self {
83 Self::default()
84 }
85
86 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
87 self.properties.push(InspectorProperty {
88 name,
89 value: value.into(),
90 });
91 }
92
93 pub fn properties(&self) -> &[InspectorProperty] {
94 &self.properties
95 }
96
97 pub fn is_empty(&self) -> bool {
98 self.properties.is_empty()
99 }
100
101 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
102 self.add_property(name, describe_dimension(constraint));
103 }
104
105 pub fn add_offset_components(
106 &mut self,
107 x_name: &'static str,
108 y_name: &'static str,
109 offset: Point,
110 ) {
111 self.add_property(x_name, offset.x.to_string());
112 self.add_property(y_name, offset.y.to_string());
113 }
114
115 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
116 where
117 A: fmt::Debug,
118 {
119 self.add_property(name, format!("{alignment:?}"));
120 }
121
122 #[allow(dead_code)] pub fn debug_properties(&self) -> Vec<(&'static str, String)> {
124 self.properties
125 .iter()
126 .map(|property| (property.name, property.value.clone()))
127 .collect()
128 }
129
130 #[allow(dead_code)] pub fn describe(&self) -> String {
132 self.properties
133 .iter()
134 .map(|property| format!("{}={}", property.name, property.value))
135 .collect::<Vec<_>>()
136 .join(", ")
137 }
138}
139
140#[derive(Clone, Debug, PartialEq)]
142pub struct InspectorProperty {
143 pub name: &'static str,
144 pub value: String,
145}
146
147#[derive(Clone, Debug, PartialEq)]
149pub struct ModifierInspectorRecord {
150 pub name: &'static str,
151 pub properties: Vec<InspectorProperty>,
152}
153
154#[derive(Clone, Debug)]
156pub(crate) struct InspectorMetadata {
157 name: &'static str,
158 info: InspectorInfo,
159}
160
161impl InspectorMetadata {
162 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
163 where
164 F: FnOnce(&mut InspectorInfo),
165 {
166 let mut info = InspectorInfo::new();
167 recorder(&mut info);
168 Self { name, info }
169 }
170
171 fn is_empty(&self) -> bool {
172 self.info.is_empty()
173 }
174
175 fn to_record(&self) -> ModifierInspectorRecord {
176 ModifierInspectorRecord {
177 name: self.name,
178 properties: self.info.properties().to_vec(),
179 }
180 }
181}
182
183fn describe_dimension(constraint: DimensionConstraint) -> String {
184 match constraint {
185 DimensionConstraint::Unspecified => "unspecified".to_string(),
186 DimensionConstraint::Points(value) => value.to_string(),
187 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
188 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
189 }
190}
191
192pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
193where
194 F: FnOnce(&mut InspectorInfo),
195{
196 if !inspector_metadata_enabled() {
199 return InspectorMetadata::new(name, |_| {});
200 }
201 InspectorMetadata::new(name, recorder)
202}
203
204pub(crate) fn modifier_debug_enabled() -> bool {
205 #[cfg(not(target_arch = "wasm32"))]
206 {
207 static ENV_DEBUG: OnceLock<bool> = OnceLock::new();
208 *ENV_DEBUG.get_or_init(|| std::env::var_os("COMPOSE_DEBUG_MODIFIERS").is_some())
209 }
210 #[cfg(target_arch = "wasm32")]
211 {
212 false
213 }
214}
215
216fn inspector_metadata_enabled() -> bool {
217 cfg!(debug_assertions) || cfg!(test) || modifier_debug_enabled()
218}
219
220#[derive(Clone)]
224enum ModifierKind {
225 Empty,
227 Single {
229 elements: Rc<Vec<DynModifierElement>>,
230 inspector: Rc<Vec<InspectorMetadata>>,
231 },
232 Combined {
234 outer: Rc<Modifier>,
235 inner: Rc<Modifier>,
236 },
237}
238
239const FINGERPRINT_KIND_EMPTY: u8 = 0;
240const FINGERPRINT_KIND_SINGLE: u8 = 1;
241const FINGERPRINT_KIND_COMBINED: u8 = 2;
242
243fn empty_fingerprints() -> (u64, u64) {
244 let mut strict_hasher = std::collections::hash_map::DefaultHasher::new();
245 let mut structural_hasher = std::collections::hash_map::DefaultHasher::new();
246 FINGERPRINT_KIND_EMPTY.hash(&mut strict_hasher);
247 FINGERPRINT_KIND_EMPTY.hash(&mut structural_hasher);
248 (strict_hasher.finish(), structural_hasher.finish())
249}
250
251fn single_fingerprints(elements: &[DynModifierElement]) -> (u64, u64) {
252 let mut strict_hasher = std::collections::hash_map::DefaultHasher::new();
253 let mut structural_hasher = std::collections::hash_map::DefaultHasher::new();
254
255 FINGERPRINT_KIND_SINGLE.hash(&mut strict_hasher);
256 FINGERPRINT_KIND_SINGLE.hash(&mut structural_hasher);
257 elements.len().hash(&mut strict_hasher);
258 elements.len().hash(&mut structural_hasher);
259
260 for element in elements {
261 let element_type = element.element_type();
262 let capabilities = element.capabilities();
263 let element_hash = element.hash_code();
264
265 element_type.hash(&mut strict_hasher);
266 capabilities.hash(&mut strict_hasher);
267
268 element_type.hash(&mut structural_hasher);
269 capabilities.hash(&mut structural_hasher);
270
271 let requires_update = element.requires_update();
272 requires_update.hash(&mut strict_hasher);
273 if requires_update {
274 let element_ptr = Rc::as_ptr(element) as *const ();
275 element_ptr.hash(&mut strict_hasher);
276 } else {
277 element_hash.hash(&mut strict_hasher);
278 }
279
280 let is_draw_only = capabilities == NodeCapabilities::DRAW;
281 is_draw_only.hash(&mut structural_hasher);
282 if !is_draw_only {
283 element_hash.hash(&mut structural_hasher);
284 }
285 }
286
287 (strict_hasher.finish(), structural_hasher.finish())
288}
289
290fn combined_fingerprints(outer: &Modifier, inner: &Modifier) -> (u64, u64) {
291 let mut strict_hasher = std::collections::hash_map::DefaultHasher::new();
292 let mut structural_hasher = std::collections::hash_map::DefaultHasher::new();
293
294 FINGERPRINT_KIND_COMBINED.hash(&mut strict_hasher);
295 outer.strict_fingerprint.hash(&mut strict_hasher);
296 inner.strict_fingerprint.hash(&mut strict_hasher);
297
298 FINGERPRINT_KIND_COMBINED.hash(&mut structural_hasher);
299 outer.structural_fingerprint.hash(&mut structural_hasher);
300 inner.structural_fingerprint.hash(&mut structural_hasher);
301
302 (strict_hasher.finish(), structural_hasher.finish())
303}
304
305pub struct ModifierElementIterator<'a> {
310 stack: Vec<&'a Modifier>,
312 current_elements: Option<(&'a [DynModifierElement], usize)>,
314}
315
316impl<'a> ModifierElementIterator<'a> {
317 fn new(modifier: &'a Modifier) -> Self {
318 let mut iter = Self {
319 stack: Vec::new(),
320 current_elements: None,
321 };
322 iter.push_modifier(modifier);
323 iter
324 }
325
326 fn push_modifier(&mut self, modifier: &'a Modifier) {
327 match &modifier.kind {
328 ModifierKind::Empty => {}
329 ModifierKind::Single { elements, .. } => {
330 if !elements.is_empty() {
331 self.current_elements = Some((elements.as_slice(), 0));
332 }
333 }
334 ModifierKind::Combined { outer, inner } => {
335 self.stack.push(inner.as_ref());
337 self.push_modifier(outer.as_ref());
338 }
339 }
340 }
341}
342
343impl<'a> Iterator for ModifierElementIterator<'a> {
344 type Item = &'a DynModifierElement;
345
346 fn next(&mut self) -> Option<Self::Item> {
347 loop {
348 if let Some((elements, index)) = &mut self.current_elements {
350 if *index < elements.len() {
351 let element = &elements[*index];
352 *index += 1;
353 return Some(element);
354 }
355 self.current_elements = None;
356 }
357
358 let next_modifier = self.stack.pop()?;
360 self.push_modifier(next_modifier);
361 }
362 }
363}
364
365pub(crate) struct ModifierInspectorIterator<'a> {
367 stack: Vec<&'a Modifier>,
368 current_inspector: Option<(&'a [InspectorMetadata], usize)>,
369}
370
371impl<'a> ModifierInspectorIterator<'a> {
372 fn new(modifier: &'a Modifier) -> Self {
373 let mut iter = Self {
374 stack: Vec::new(),
375 current_inspector: None,
376 };
377 iter.push_modifier(modifier);
378 iter
379 }
380
381 fn push_modifier(&mut self, modifier: &'a Modifier) {
382 match &modifier.kind {
383 ModifierKind::Empty => {}
384 ModifierKind::Single { inspector, .. } => {
385 if !inspector.is_empty() {
386 self.current_inspector = Some((inspector.as_slice(), 0));
387 }
388 }
389 ModifierKind::Combined { outer, inner } => {
390 self.stack.push(inner.as_ref());
392 self.push_modifier(outer.as_ref());
393 }
394 }
395 }
396}
397
398impl<'a> Iterator for ModifierInspectorIterator<'a> {
399 type Item = &'a InspectorMetadata;
400
401 fn next(&mut self) -> Option<Self::Item> {
402 loop {
403 if let Some((inspector, index)) = &mut self.current_inspector {
404 if *index < inspector.len() {
405 let metadata = &inspector[*index];
406 *index += 1;
407 return Some(metadata);
408 }
409 self.current_inspector = None;
410 }
411
412 let next_modifier = self.stack.pop()?;
413 self.push_modifier(next_modifier);
414 }
415 }
416}
417
418#[derive(Clone)]
436pub struct Modifier {
437 kind: ModifierKind,
438 strict_fingerprint: u64,
439 structural_fingerprint: u64,
440}
441
442impl Default for Modifier {
443 fn default() -> Self {
444 let (strict_fingerprint, structural_fingerprint) = empty_fingerprints();
445 Self {
446 kind: ModifierKind::Empty,
447 strict_fingerprint,
448 structural_fingerprint,
449 }
450 }
451}
452
453impl Modifier {
454 pub fn empty() -> Self {
455 Self::default()
456 }
457
458 pub fn clip_to_bounds(self) -> Self {
462 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
463 inspector_metadata("clipToBounds", |info| {
464 info.add_property("clipToBounds", "true");
465 }),
466 );
467 self.then(modifier)
468 }
469
470 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
471 where
472 T: 'static,
473 F: Fn() -> T + 'static,
474 {
475 let element = ModifierLocalProviderElement::new(key, value);
476 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
477 self.then(modifier)
478 }
479
480 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
481 where
482 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
483 {
484 let element = ModifierLocalConsumerElement::new(consumer);
485 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
486 self.then(modifier)
487 }
488
489 pub fn semantics<F>(self, recorder: F) -> Self
490 where
491 F: Fn(&mut SemanticsConfiguration) + 'static,
492 {
493 let mut preview = SemanticsConfiguration::default();
494 recorder(&mut preview);
495 let description = preview.content_description.clone();
496 let is_button = preview.is_button;
497 let is_clickable = preview.is_clickable;
498 let metadata = inspector_metadata("semantics", move |info| {
499 if let Some(desc) = &description {
500 info.add_property("contentDescription", desc.clone());
501 }
502 if is_button {
503 info.add_property("isButton", "true");
504 }
505 if is_clickable {
506 info.add_property("isClickable", "true");
507 }
508 });
509 let element = SemanticsElement::new(recorder);
510 let modifier =
511 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
512 self.then(modifier)
513 }
514
515 pub fn focus_target(self) -> Self {
521 let element = FocusTargetElement::new();
522 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
523 self.then(modifier)
524 }
525
526 pub fn on_focus_changed<F>(self, callback: F) -> Self
531 where
532 F: Fn(FocusState) + 'static,
533 {
534 let element = FocusTargetElement::with_callback(callback);
535 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
536 self.then(modifier)
537 }
538
539 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
544 let element = FocusRequesterElement::new(requester.id());
545 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
546 self.then(modifier)
547 }
548
549 pub fn debug_chain(self, tag: &'static str) -> Self {
567 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
568
569 #[derive(Clone)]
570 struct DebugChainElement {
571 tag: &'static str,
572 }
573
574 impl fmt::Debug for DebugChainElement {
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 f.debug_struct("DebugChainElement")
577 .field("tag", &self.tag)
578 .finish()
579 }
580 }
581
582 impl PartialEq for DebugChainElement {
583 fn eq(&self, other: &Self) -> bool {
584 self.tag == other.tag
585 }
586 }
587
588 impl Eq for DebugChainElement {}
589
590 impl std::hash::Hash for DebugChainElement {
591 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
592 self.tag.hash(state);
593 }
594 }
595
596 impl ModifierNodeElement for DebugChainElement {
597 type Node = DebugChainNode;
598
599 fn create(&self) -> Self::Node {
600 DebugChainNode::new(self.tag)
601 }
602
603 fn update(&self, node: &mut Self::Node) {
604 node.tag = self.tag;
605 }
606
607 fn capabilities(&self) -> NodeCapabilities {
608 NodeCapabilities::empty()
609 }
610 }
611
612 struct DebugChainNode {
613 tag: &'static str,
614 state: NodeState,
615 }
616
617 impl DebugChainNode {
618 fn new(tag: &'static str) -> Self {
619 Self {
620 tag,
621 state: NodeState::new(),
622 }
623 }
624 }
625
626 impl ModifierNode for DebugChainNode {
627 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
628 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
629 }
630
631 fn on_detach(&mut self) {
632 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
633 }
634
635 fn on_reset(&mut self) {
636 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
637 }
638 }
639
640 impl cranpose_foundation::DelegatableNode for DebugChainNode {
641 fn node_state(&self) -> &NodeState {
642 &self.state
643 }
644 }
645
646 let element = DebugChainElement { tag };
647 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
648 self.then(modifier)
649 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
650 info.add_property("tag", tag);
651 }))
652 }
653
654 pub fn then(&self, next: Modifier) -> Modifier {
662 if self.is_trivially_empty() {
663 return next;
664 }
665 if next.is_trivially_empty() {
666 return self.clone();
667 }
668 let (strict_fingerprint, structural_fingerprint) = combined_fingerprints(self, &next);
669 Modifier {
670 kind: ModifierKind::Combined {
671 outer: Rc::new(self.clone()),
672 inner: Rc::new(next),
673 },
674 strict_fingerprint,
675 structural_fingerprint,
676 }
677 }
678
679 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
685 ModifierElementIterator::new(self)
686 }
687
688 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
689 ModifierInspectorIterator::new(self)
690 }
691
692 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
697 match &self.kind {
698 ModifierKind::Empty => Vec::new(),
699 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
700 ModifierKind::Combined { outer, inner } => {
701 let mut result = outer.elements();
702 result.extend(inner.elements());
703 result
704 }
705 }
706 }
707
708 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 ModifierKind::Combined { outer, inner } => {
716 let mut result = outer.inspector_metadata();
717 result.extend(inner.inspector_metadata());
718 result
719 }
720 }
721 }
722
723 pub fn total_padding(&self) -> f32 {
724 let padding = self.padding_values();
725 padding
726 .left
727 .max(padding.right)
728 .max(padding.top)
729 .max(padding.bottom)
730 }
731
732 pub fn explicit_size(&self) -> Option<Size> {
733 let props = self.layout_properties();
734 match (props.width, props.height) {
735 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
736 Some(Size { width, height })
737 }
738 _ => None,
739 }
740 }
741
742 pub fn padding_values(&self) -> EdgeInsets {
743 self.resolved_modifiers().padding()
744 }
745
746 pub(crate) fn layout_properties(&self) -> LayoutProperties {
747 self.resolved_modifiers().layout_properties()
748 }
749
750 pub fn box_alignment(&self) -> Option<Alignment> {
751 self.layout_properties().box_alignment()
752 }
753
754 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
755 self.layout_properties().column_alignment()
756 }
757
758 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
759 self.layout_properties().row_alignment()
760 }
761
762 pub fn draw_commands(&self) -> Vec<DrawCommand> {
763 collect_slices_from_modifier(self).draw_commands().to_vec()
764 }
765
766 pub fn clips_to_bounds(&self) -> bool {
767 collect_slices_from_modifier(self).clip_to_bounds()
768 }
769
770 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
772 self.inspector_metadata()
773 .iter()
774 .map(|metadata| metadata.to_record())
775 .collect()
776 }
777
778 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
779 let mut handle = ModifierChainHandle::new();
780 let _ = handle.update(self);
781 handle.resolved_modifiers()
782 }
783
784 fn with_element<E>(element: E) -> Self
785 where
786 E: ModifierNodeElement,
787 {
788 let dyn_element = modifier_element(element);
789 Self::from_parts(vec![dyn_element])
790 }
791
792 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
793 if elements.is_empty() {
794 Self::default()
795 } else {
796 let (strict_fingerprint, structural_fingerprint) =
797 single_fingerprints(elements.as_slice());
798 Self {
799 kind: ModifierKind::Single {
800 elements: Rc::new(elements),
801 inspector: Rc::new(Vec::new()),
802 },
803 strict_fingerprint,
804 structural_fingerprint,
805 }
806 }
807 }
808
809 fn is_trivially_empty(&self) -> bool {
810 matches!(self.kind, ModifierKind::Empty)
811 }
812
813 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
814 if metadata.is_empty() {
815 return self;
816 }
817 match self.kind {
818 ModifierKind::Empty => self,
819 ModifierKind::Single {
820 elements,
821 inspector,
822 } => {
823 let mut new_inspector = inspector.as_ref().clone();
824 new_inspector.push(metadata);
825 Self {
826 kind: ModifierKind::Single {
827 elements,
828 inspector: Rc::new(new_inspector),
829 },
830 strict_fingerprint: self.strict_fingerprint,
831 structural_fingerprint: self.structural_fingerprint,
832 }
833 }
834 ModifierKind::Combined { .. } => {
835 panic!("Cannot add inspector metadata to a combined modifier")
838 }
839 }
840 }
841
842 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 consider_always_update {
852 if self.strict_fingerprint != other.strict_fingerprint {
853 return false;
854 }
855 } else if self.structural_fingerprint != other.structural_fingerprint {
856 return false;
857 }
858
859 match (&self.kind, &other.kind) {
860 (ModifierKind::Empty, ModifierKind::Empty) => true,
861 (
862 ModifierKind::Single {
863 elements: e1,
864 inspector: _,
865 },
866 ModifierKind::Single {
867 elements: e2,
868 inspector: _,
869 },
870 ) => {
871 if Rc::ptr_eq(e1, e2) {
872 return true;
873 }
874
875 if e1.len() != e2.len() {
876 return false;
877 }
878
879 for (a, b) in e1.iter().zip(e2.iter()) {
880 if !consider_always_update
884 && a.element_type() == b.element_type()
885 && a.capabilities() == NodeCapabilities::DRAW
886 && b.capabilities() == NodeCapabilities::DRAW
887 {
888 continue;
889 }
890
891 if consider_always_update && (a.requires_update() || b.requires_update()) {
892 if !Rc::ptr_eq(a, b) {
893 return false;
894 }
895 continue;
896 }
897
898 if !a.equals_element(&**b) {
899 return false;
900 }
901 }
902
903 true
904 }
905 (
906 ModifierKind::Combined {
907 outer: o1,
908 inner: i1,
909 },
910 ModifierKind::Combined {
911 outer: o2,
912 inner: i2,
913 },
914 ) => {
915 if Rc::ptr_eq(o1, o2) && Rc::ptr_eq(i1, i2) {
916 return true;
917 }
918 o1.as_ref().eq_internal(o2.as_ref(), consider_always_update)
919 && i1.as_ref().eq_internal(i2.as_ref(), consider_always_update)
920 }
921 _ => false,
922 }
923 }
924}
925
926impl PartialEq for Modifier {
927 fn eq(&self, other: &Self) -> bool {
928 self.eq_internal(other, true)
929 }
930}
931
932impl Eq for Modifier {}
933
934impl fmt::Display for Modifier {
935 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
936 match &self.kind {
937 ModifierKind::Empty => write!(f, "Modifier.empty"),
938 ModifierKind::Single { elements, .. } => {
939 if elements.is_empty() {
940 return write!(f, "Modifier.empty");
941 }
942 write!(f, "Modifier[")?;
943 for (index, element) in elements.iter().enumerate() {
944 if index > 0 {
945 write!(f, ", ")?;
946 }
947 let name = element.inspector_name();
948 let mut properties = Vec::new();
949 element.record_inspector_properties(&mut |prop, value| {
950 properties.push(format!("{prop}={value}"));
951 });
952 if properties.is_empty() {
953 write!(f, "{name}")?;
954 } else {
955 write!(f, "{name}({})", properties.join(", "))?;
956 }
957 }
958 write!(f, "]")
959 }
960 ModifierKind::Combined { outer: _, inner: _ } => {
961 write!(f, "[")?;
964 let elements = self.elements();
965 for (index, element) in elements.iter().enumerate() {
966 if index > 0 {
967 write!(f, ", ")?;
968 }
969 let name = element.inspector_name();
970 let mut properties = Vec::new();
971 element.record_inspector_properties(&mut |prop, value| {
972 properties.push(format!("{prop}={value}"));
973 });
974 if properties.is_empty() {
975 write!(f, "{name}")?;
976 } else {
977 write!(f, "{name}({})", properties.join(", "))?;
978 }
979 }
980 write!(f, "]")
981 }
982 }
983 }
984}
985
986impl fmt::Debug for Modifier {
987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 fmt::Display::fmt(self, f)
989 }
990}
991
992#[derive(Clone, Copy, Debug, PartialEq)]
993pub struct ResolvedBackground {
994 color: Color,
995 shape: Option<RoundedCornerShape>,
996}
997
998impl ResolvedBackground {
999 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
1000 Self { color, shape }
1001 }
1002
1003 pub fn color(&self) -> Color {
1004 self.color
1005 }
1006
1007 pub fn shape(&self) -> Option<RoundedCornerShape> {
1008 self.shape
1009 }
1010
1011 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
1012 self.shape = shape;
1013 }
1014}
1015
1016#[derive(Clone, Copy, Debug, PartialEq, Default)]
1017pub struct ResolvedModifiers {
1018 padding: EdgeInsets,
1019 layout: LayoutProperties,
1020 offset: Point,
1021}
1022
1023impl ResolvedModifiers {
1024 pub fn padding(&self) -> EdgeInsets {
1025 self.padding
1026 }
1027
1028 pub fn layout_properties(&self) -> LayoutProperties {
1029 self.layout
1030 }
1031
1032 pub fn offset(&self) -> Point {
1033 self.offset
1034 }
1035
1036 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
1037 self.padding = padding;
1038 }
1039
1040 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1041 self.layout = layout;
1042 }
1043
1044 pub(crate) fn set_offset(&mut self, offset: Point) {
1045 self.offset = offset;
1046 }
1047}
1048
1049#[derive(Clone, Copy, Debug, Default, PartialEq)]
1050pub enum DimensionConstraint {
1051 #[default]
1052 Unspecified,
1053 Points(f32),
1054 Fraction(f32),
1055 Intrinsic(IntrinsicSize),
1056}
1057
1058#[derive(Clone, Copy, Debug, Default, PartialEq)]
1059pub struct LayoutWeight {
1060 pub weight: f32,
1061 pub fill: bool,
1062}
1063
1064#[derive(Clone, Copy, Debug, Default, PartialEq)]
1065pub struct LayoutProperties {
1066 padding: EdgeInsets,
1067 width: DimensionConstraint,
1068 height: DimensionConstraint,
1069 min_width: Option<f32>,
1070 min_height: Option<f32>,
1071 max_width: Option<f32>,
1072 max_height: Option<f32>,
1073 weight: Option<LayoutWeight>,
1074 box_alignment: Option<Alignment>,
1075 column_alignment: Option<HorizontalAlignment>,
1076 row_alignment: Option<VerticalAlignment>,
1077}
1078
1079impl LayoutProperties {
1080 pub fn padding(&self) -> EdgeInsets {
1081 self.padding
1082 }
1083
1084 pub fn width(&self) -> DimensionConstraint {
1085 self.width
1086 }
1087
1088 pub fn height(&self) -> DimensionConstraint {
1089 self.height
1090 }
1091
1092 pub fn min_width(&self) -> Option<f32> {
1093 self.min_width
1094 }
1095
1096 pub fn min_height(&self) -> Option<f32> {
1097 self.min_height
1098 }
1099
1100 pub fn max_width(&self) -> Option<f32> {
1101 self.max_width
1102 }
1103
1104 pub fn max_height(&self) -> Option<f32> {
1105 self.max_height
1106 }
1107
1108 pub fn weight(&self) -> Option<LayoutWeight> {
1109 self.weight
1110 }
1111
1112 pub fn box_alignment(&self) -> Option<Alignment> {
1113 self.box_alignment
1114 }
1115
1116 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1117 self.column_alignment
1118 }
1119
1120 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1121 self.row_alignment
1122 }
1123}
1124
1125#[cfg(test)]
1126#[path = "tests/modifier_tests.rs"]
1127mod tests;