1#![allow(non_snake_case)]
9
10use std::{
11 fmt,
12 hash::{Hash, Hasher},
13 rc::Rc,
14};
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 focus_ring;
27mod graphics_layer;
28mod local;
29mod minimum_interactive;
30mod offset;
31mod padding;
32mod pointer_icon;
33pub(crate) mod pointer_input;
34mod rotary_input;
35mod scroll;
36mod selectable;
37mod semantics;
38mod shadow;
39mod size;
40mod slices;
41mod toggleable;
42mod weight;
43
44#[allow(unused_imports)]
45pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
46pub use cranpose_foundation::{
47 AnyModifierElement, DynModifierElement, FocusState, PointerEvent, PointerEventKind,
48 PointerSource, RotaryScrollEvent, SemanticsConfiguration, modifier_element,
49};
50use cranpose_foundation::{ModifierNodeElement, NodeCapabilities, ProgressBarRangeInfo};
51#[allow(unused_imports)]
52pub use cranpose_ui_graphics::{
53 BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
54 CursorIcon, CustomPointerIcon, CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec,
55 GradientFadeMaskSpec, GraphicsLayer, LayerShape, Point, PointerIcon, PointerIconError, Rect,
56 RenderEffect, RoundedCornerShape, RuntimeShader, Shadow, ShadowScope, Size, TransformOrigin,
57};
58use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
59use focus::FocusTargetElement;
60#[allow(unused_imports)]
61pub use focus::{FocusDirection, FocusRequestError, FocusRequester, FocusRequesterElement};
62pub use graphics_layer::GlassMaterial;
63pub(crate) use local::{
64 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
65};
66use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
67#[allow(unused_imports)]
68pub use local::{ModifierLocalKey, ModifierLocalReadScope};
69#[allow(unused_imports)]
70pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
71pub use rotary_input::RotaryInputModifierNode;
72#[cfg(test)]
73pub(crate) use scroll::lazy_scroll_semantics;
74#[cfg(feature = "test-helpers")]
75pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
76use semantics::SemanticsElement;
77pub use semantics::{
78 SemanticsRequester, SemanticsRequesterElement, collect_semantics_from_chain,
79 collect_semantics_from_modifier,
80};
81pub use slices::{
82 ModifierNodeSlices, ModifierNodeSlicesDebugStats, collect_modifier_slices,
83 collect_modifier_slices_into, collect_slices_from_modifier,
84};
85
86pub use crate::draw::{DrawCacheBuilder, DrawCommand};
87use crate::modifier_nodes::ClipToBoundsElement;
88
89#[derive(Clone, Debug, Default)]
90pub struct InspectorInfo {
91 properties: Vec<InspectorProperty>,
92}
93
94impl InspectorInfo {
95 pub fn new() -> Self {
96 Self::default()
97 }
98
99 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
100 self.properties.push(InspectorProperty {
101 name,
102 value: value.into(),
103 });
104 }
105
106 pub fn properties(&self) -> &[InspectorProperty] {
107 &self.properties
108 }
109
110 pub fn is_empty(&self) -> bool {
111 self.properties.is_empty()
112 }
113
114 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
115 self.add_property(name, describe_dimension(constraint));
116 }
117
118 pub fn add_offset_components(
119 &mut self,
120 x_name: &'static str,
121 y_name: &'static str,
122 offset: Point,
123 ) {
124 self.add_property(x_name, offset.x.to_string());
125 self.add_property(y_name, offset.y.to_string());
126 }
127
128 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
129 where
130 A: fmt::Debug,
131 {
132 self.add_property(name, format!("{alignment:?}"));
133 }
134}
135
136#[derive(Clone, Debug, PartialEq)]
137pub struct InspectorProperty {
138 pub name: &'static str,
139 pub value: String,
140}
141
142#[derive(Clone, Debug, PartialEq)]
143pub struct ModifierInspectorRecord {
144 pub name: &'static str,
145 pub properties: Vec<InspectorProperty>,
146}
147
148#[derive(Clone, Debug)]
149pub(crate) struct InspectorMetadata {
150 name: &'static str,
151 info: InspectorInfo,
152}
153
154impl InspectorMetadata {
155 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
156 where
157 F: FnOnce(&mut InspectorInfo),
158 {
159 let mut info = InspectorInfo::new();
160 recorder(&mut info);
161 Self { name, info }
162 }
163
164 fn is_empty(&self) -> bool {
165 self.info.is_empty()
166 }
167
168 fn to_record(&self) -> ModifierInspectorRecord {
169 ModifierInspectorRecord {
170 name: self.name,
171 properties: self.info.properties().to_vec(),
172 }
173 }
174}
175
176fn describe_dimension(constraint: DimensionConstraint) -> String {
177 match constraint {
178 DimensionConstraint::Unspecified => "unspecified".to_string(),
179 DimensionConstraint::Points(value) => value.to_string(),
180 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
181 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
182 }
183}
184
185pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
186where
187 F: FnOnce(&mut InspectorInfo),
188{
189 if !inspector_metadata_enabled() {
190 return InspectorMetadata::new(name, |_| {});
191 }
192 InspectorMetadata::new(name, recorder)
193}
194
195pub(crate) fn modifier_debug_enabled() -> bool {
196 #[cfg(not(target_arch = "wasm32"))]
197 {
198 cranpose_core::env_flag!("COMPOSE_DEBUG_MODIFIERS")
199 }
200 #[cfg(target_arch = "wasm32")]
201 {
202 false
203 }
204}
205
206fn inspector_metadata_enabled() -> bool {
207 cfg!(test) || modifier_debug_enabled()
208}
209
210#[derive(Clone)]
211enum ModifierKind {
212 Empty,
213 Single {
214 elements: Rc<Vec<DynModifierElement>>,
215 inspector: Rc<Vec<InspectorMetadata>>,
216 },
217}
218
219const FINGERPRINT_KIND_EMPTY: u8 = 0;
220const FINGERPRINT_KIND_SINGLE: u8 = 1;
221
222const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
223const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
224const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
225const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
226const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
227const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
228const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
229
230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
231struct ModifierFingerprints {
232 strict: u64,
233 structural: u64,
234}
235
236#[inline]
237fn mix_fingerprint_bits(mut value: u64) -> u64 {
238 value ^= value >> 33;
239 value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
240 value ^= value >> 33;
241 value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
242 value ^ (value >> 33)
243}
244
245#[inline]
246fn fold_fingerprint(state: u64, value: u64) -> u64 {
247 mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
248 .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
249}
250
251#[inline]
252fn empty_fingerprints() -> ModifierFingerprints {
253 ModifierFingerprints {
254 strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
255 structural: fold_fingerprint(
256 FINGERPRINT_EMPTY_STRUCTURAL_SEED,
257 FINGERPRINT_KIND_EMPTY as u64,
258 ),
259 }
260}
261
262#[inline]
263fn single_fingerprint_seed() -> ModifierFingerprints {
264 let strict = fold_fingerprint(
265 FINGERPRINT_SINGLE_STRICT_SEED,
266 FINGERPRINT_KIND_SINGLE as u64,
267 );
268 let structural = fold_fingerprint(
269 FINGERPRINT_SINGLE_STRUCTURAL_SEED,
270 FINGERPRINT_KIND_SINGLE as u64,
271 );
272 ModifierFingerprints { strict, structural }
273}
274
275#[inline]
276fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
277 let mut hasher = default::new();
278 element.element_type().hash(&mut hasher);
279 element.capabilities().bits().hash(&mut hasher);
280 hasher.finish()
281}
282
283#[inline]
284fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
285 let common = element_common_fingerprint(element);
286 let requires_update = element.requires_update();
287 let strict_payload = if requires_update {
288 let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
289 element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
290 } else {
291 element.hash_code()
292 };
293 let strict = mix_fingerprint_bits(common ^ strict_payload);
294
295 let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
296 let structural_payload = if is_draw_only {
297 FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
298 } else {
299 element.hash_code()
300 };
301 let structural = mix_fingerprint_bits(common ^ structural_payload);
302
303 ModifierFingerprints { strict, structural }
304}
305
306#[inline]
307fn append_fingerprints(
308 mut fingerprints: ModifierFingerprints,
309 elements: &[DynModifierElement],
310) -> ModifierFingerprints {
311 for element in elements {
312 let element_fingerprints = element_fingerprints(element);
313 fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
314 fingerprints.structural =
315 fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
316 }
317 fingerprints
318}
319
320fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
321 append_fingerprints(single_fingerprint_seed(), elements)
322}
323
324pub struct ModifierElementIterator<'a> {
325 inner: std::slice::Iter<'a, DynModifierElement>,
326}
327
328impl<'a> Iterator for ModifierElementIterator<'a> {
329 type Item = &'a DynModifierElement;
330
331 #[inline]
332 fn next(&mut self) -> Option<Self::Item> {
333 self.inner.next()
334 }
335
336 #[inline]
337 fn size_hint(&self) -> (usize, Option<usize>) {
338 self.inner.size_hint()
339 }
340}
341
342impl ExactSizeIterator for ModifierElementIterator<'_> {}
343
344pub(crate) struct ModifierInspectorIterator<'a> {
345 inner: std::slice::Iter<'a, InspectorMetadata>,
346}
347
348impl<'a> Iterator for ModifierInspectorIterator<'a> {
349 type Item = &'a InspectorMetadata;
350
351 #[inline]
352 fn next(&mut self) -> Option<Self::Item> {
353 self.inner.next()
354 }
355
356 #[inline]
357 fn size_hint(&self) -> (usize, Option<usize>) {
358 self.inner.size_hint()
359 }
360}
361
362impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
363
364#[derive(Clone)]
382pub struct Modifier {
383 kind: ModifierKind,
384 strict_fingerprint: u64,
385 structural_fingerprint: u64,
386 element_count: usize,
387}
388
389impl Default for Modifier {
390 fn default() -> Self {
391 let fingerprints = empty_fingerprints();
392 Self {
393 kind: ModifierKind::Empty,
394 strict_fingerprint: fingerprints.strict,
395 structural_fingerprint: fingerprints.structural,
396 element_count: 0,
397 }
398 }
399}
400
401impl Modifier {
402 pub fn empty() -> Self {
403 Self::default()
404 }
405
406 pub fn from_element<E>(element: E) -> Self
408 where
409 E: ModifierNodeElement,
410 {
411 Self::with_element(element)
412 }
413
414 pub fn clip_to_bounds(self) -> Self {
418 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
419 inspector_metadata("clipToBounds", |info| {
420 info.add_property("clipToBounds", "true");
421 }),
422 );
423 self.then(modifier)
424 }
425
426 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
427 where
428 T: 'static,
429 F: Fn() -> T + 'static,
430 {
431 let element = ModifierLocalProviderElement::new(key, value);
432 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
433 self.then(modifier)
434 }
435
436 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
437 where
438 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
439 {
440 let element = ModifierLocalConsumerElement::new(consumer);
441 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
442 self.then(modifier)
443 }
444
445 pub fn semantics_spec(self, spec: cranpose_foundation::SemanticsSpec) -> Self {
456 self.semantics(move |config: &mut SemanticsConfiguration| config.merge(&spec))
457 }
458 pub fn semantics<F>(self, recorder: F) -> Self
459 where
460 F: Fn(&mut SemanticsConfiguration) + 'static,
461 {
462 let recorder: std::rc::Rc<dyn Fn(&mut SemanticsConfiguration)> = std::rc::Rc::new(recorder);
463 let metadata = if inspector_metadata_enabled() {
464 let mut preview = SemanticsConfiguration::default();
465 recorder(&mut preview);
466 let description = preview.content_description.clone();
467 let state_description = preview.state_description.clone();
468 let role = preview.role;
469 let is_clickable = preview.is_activatable();
470 let canvas_children = preview.canvas_children.len();
471 inspector_metadata("semantics", move |info| {
472 if let Some(desc) = &description {
473 info.add_property("contentDescription", desc.clone());
474 }
475 if let Some(state) = &state_description {
476 info.add_property("stateDescription", state.clone());
477 }
478 if let Some(role) = role {
479 info.add_property("role", format!("{role:?}"));
480 }
481 if is_clickable {
482 info.add_property("isClickable", "true");
483 }
484 if canvas_children > 0 {
485 info.add_property("canvasSemanticsChildren", canvas_children.to_string());
486 }
487 })
488 } else {
489 inspector_metadata("semantics", |_| {})
490 };
491 let element = SemanticsElement::new(recorder);
492 let modifier =
493 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
494 self.then(modifier)
495 }
496
497 pub fn progress_semantics(self, current: f32, start: f32, end: f32, steps: u32) -> Self {
506 let info = ProgressBarRangeInfo::new(current, start, end, steps);
507 self.semantics(move |config| config.progress = Some(info))
508 }
509
510 pub fn dropdown_list(self) -> Self {
514 self.role(cranpose_foundation::SemanticsWidgetRole::DropdownList)
515 }
516
517 pub fn value_picker(self) -> Self {
520 self.role(cranpose_foundation::SemanticsWidgetRole::ValuePicker)
521 }
522
523 pub fn expand(self, action: impl Fn() -> bool + 'static) -> Self {
528 let action = cranpose_foundation::SemanticsExpand::new(action);
529 self.semantics(move |config| config.expand = Some(action.clone()))
530 }
531
532 pub fn on_long_click(
542 self,
543 label: impl Into<String>,
544 action: impl Fn() -> bool + 'static,
545 ) -> Self {
546 let label = label.into();
547 let action = cranpose_foundation::SemanticsLongClick::new(action);
548 self.semantics(move |config| {
549 config.on_long_click_label = Some(label.clone());
550 config.on_long_click = Some(action.clone());
551 })
552 }
553
554 pub fn on_magic_tap(
559 self,
560 label: impl Into<String>,
561 action: impl Fn() -> bool + 'static,
562 ) -> Self {
563 let label = label.into();
564 let action = cranpose_foundation::SemanticsMagicTap::new(action);
565 self.semantics(move |config| {
566 config.on_magic_tap_label = Some(label.clone());
567 config.on_magic_tap = Some(action.clone());
568 })
569 }
570
571 pub fn input_labels<S: Into<String>>(self, labels: impl IntoIterator<Item = S>) -> Self {
575 let labels: Vec<String> = labels.into_iter().map(Into::into).collect();
576 self.semantics(move |config| config.input_labels.clone_from(&labels))
577 }
578
579 pub fn language(self, tag: impl Into<String>) -> Self {
583 let tag = tag.into();
584 self.semantics(move |config| config.language = Some(tag.clone()))
585 }
586
587 pub fn collapse(self, action: impl Fn() -> bool + 'static) -> Self {
591 let action = cranpose_foundation::SemanticsExpand::new(action);
592 self.semantics(move |config| config.collapse = Some(action.clone()))
593 }
594
595 pub fn dismiss(self, action: impl Fn() -> bool + 'static) -> Self {
600 let action = cranpose_foundation::SemanticsDismiss::new(action);
601 self.semantics(move |config| config.dismiss = Some(action.clone()))
602 }
603
604 pub fn scroll_to_index(self, action: impl Fn(usize) -> bool + 'static) -> Self {
610 let action = cranpose_foundation::SemanticsScrollToIndex::new(action);
611 self.semantics(move |config| config.scroll_to_index = Some(action.clone()))
612 }
613
614 pub fn traversal_index(self, index: f32) -> Self {
620 self.semantics(move |config| config.traversal_index = index)
621 }
622
623 pub fn password(self) -> Self {
628 self.semantics(|config| config.password = true)
629 }
630
631 pub fn error(self, message: impl Into<String>) -> Self {
635 let message = message.into();
636 self.semantics(move |config| config.error = Some(message.clone()))
637 }
638
639 pub fn pane_title(self, title: impl Into<String>) -> Self {
643 let title = title.into();
644 self.semantics(move |config| config.pane_title = Some(title.clone()))
645 }
646
647 pub fn selectable_group(self) -> Self {
652 self.semantics(|config| config.selectable_group = true)
653 }
654
655 pub fn merge_descendants(self) -> Self {
660 self.semantics(|config| config.merge_descendants = true)
661 }
662
663 pub fn hide_from_accessibility(self) -> Self {
668 self.semantics(|config| config.hidden = true)
669 }
670
671 pub fn heading(self) -> Self {
676 self.role(cranpose_foundation::SemanticsWidgetRole::Header)
677 }
678
679 pub fn role(self, role: cranpose_foundation::SemanticsWidgetRole) -> Self {
684 self.semantics(move |config| config.role = Some(role))
685 }
686
687 pub fn live_region(self, mode: cranpose_foundation::LiveRegionMode) -> Self {
693 self.semantics(move |config| config.live_region = Some(mode))
694 }
695
696 pub fn content_description(self, description: impl Into<String>) -> Self {
701 let description = description.into();
702 self.semantics(move |config| config.content_description = Some(description.clone()))
703 }
704
705 pub fn focus_target(self) -> Self {
711 let element = FocusTargetElement::new();
712 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
713 self.then(modifier)
714 }
715
716 pub fn on_focus_changed<F>(self, callback: F) -> Self
721 where
722 F: Fn(FocusState) + 'static,
723 {
724 let element = FocusTargetElement::with_callback(callback);
725 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
726 self.then(modifier)
727 }
728
729 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
736 let element = FocusRequesterElement::new(requester.clone());
737 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
738 self.then(modifier)
739 }
740
741 pub fn semantics_requester(self, requester: &SemanticsRequester) -> Self {
749 let element = SemanticsRequesterElement::new(requester.clone());
750 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
751 self.then(modifier)
752 }
753
754 pub fn debug_chain(self, tag: &'static str) -> Self {
772 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
773
774 #[derive(Clone)]
775 struct DebugChainElement {
776 tag: &'static str,
777 }
778
779 impl fmt::Debug for DebugChainElement {
780 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
781 f.debug_struct("DebugChainElement")
782 .field("tag", &self.tag)
783 .finish()
784 }
785 }
786
787 impl PartialEq for DebugChainElement {
788 fn eq(&self, other: &Self) -> bool {
789 self.tag == other.tag
790 }
791 }
792
793 impl Eq for DebugChainElement {}
794
795 impl std::hash::Hash for DebugChainElement {
796 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
797 self.tag.hash(state);
798 }
799 }
800
801 impl ModifierNodeElement for DebugChainElement {
802 type Node = DebugChainNode;
803
804 fn create(&self) -> Self::Node {
805 DebugChainNode::new(self.tag)
806 }
807
808 fn update(&self, node: &mut Self::Node) {
809 node.tag = self.tag;
810 }
811
812 fn capabilities(&self) -> NodeCapabilities {
813 NodeCapabilities::empty()
814 }
815 }
816
817 struct DebugChainNode {
818 tag: &'static str,
819 state: NodeState,
820 }
821
822 impl DebugChainNode {
823 fn new(tag: &'static str) -> Self {
824 Self {
825 tag,
826 state: NodeState::new(),
827 }
828 }
829 }
830
831 impl ModifierNode for DebugChainNode {
832 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
833 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
834 }
835
836 fn on_detach(&mut self) {
837 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
838 }
839
840 fn on_reset(&mut self) {
841 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
842 }
843 }
844
845 impl cranpose_foundation::DelegatableNode for DebugChainNode {
846 fn node_state(&self) -> &NodeState {
847 &self.state
848 }
849 }
850
851 let element = DebugChainElement { tag };
852 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
853 self.then(modifier)
854 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
855 info.add_property("tag", tag);
856 }))
857 }
858
859 pub fn then(&self, next: Modifier) -> Modifier {
864 if self.is_trivially_empty() {
865 return next;
866 }
867 if next.is_trivially_empty() {
868 return self.clone();
869 }
870
871 let Some((self_elements, self_inspector)) = self.single_parts() else {
872 return next;
873 };
874 let Some((next_elements, next_inspector)) = next.single_parts() else {
875 return self.clone();
876 };
877
878 let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
879 merged_elements.extend_from_slice(self_elements);
880 merged_elements.extend_from_slice(next_elements);
881
882 let mut merged_inspector = Vec::with_capacity(self_inspector.len() + next_inspector.len());
883 merged_inspector.extend_from_slice(self_inspector);
884 merged_inspector.extend_from_slice(next_inspector);
885
886 let fingerprints = append_fingerprints(
887 ModifierFingerprints {
888 strict: self.strict_fingerprint,
889 structural: self.structural_fingerprint,
890 },
891 next_elements,
892 );
893 Modifier {
894 kind: ModifierKind::Single {
895 elements: Rc::new(merged_elements),
896 inspector: Rc::new(merged_inspector),
897 },
898 strict_fingerprint: fingerprints.strict,
899 structural_fingerprint: fingerprints.structural,
900 element_count: self.element_count + next.element_count,
901 }
902 }
903
904 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
905 match &self.kind {
906 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
907 ModifierKind::Single { elements, .. } => ModifierElementIterator {
908 inner: elements.iter(),
909 },
910 }
911 }
912
913 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
914 match &self.kind {
915 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
916 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
917 inner: inspector.iter(),
918 },
919 }
920 }
921
922 #[cfg(test)]
923 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
924 match &self.kind {
925 ModifierKind::Empty => Vec::new(),
926 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
927 }
928 }
929
930 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
931 match &self.kind {
932 ModifierKind::Empty => Vec::new(),
933 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
934 }
935 }
936
937 pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
938 match &self.kind {
939 ModifierKind::Empty => Self::default(),
940 ModifierKind::Single {
941 elements,
942 inspector,
943 } => Self {
944 kind: ModifierKind::Single {
945 elements: Rc::new(elements.iter().cloned().collect()),
946 inspector: Rc::new(inspector.as_ref().clone()),
947 },
948 strict_fingerprint: self.strict_fingerprint,
949 structural_fingerprint: self.structural_fingerprint,
950 element_count: self.element_count,
951 },
952 }
953 }
954
955 pub fn total_padding(&self) -> f32 {
956 let padding = self.padding_values();
957 padding
958 .left
959 .max(padding.right)
960 .max(padding.top)
961 .max(padding.bottom)
962 }
963
964 pub fn explicit_size(&self) -> Option<Size> {
965 let props = self.layout_properties();
966 match (props.width, props.height) {
967 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
968 Some(Size { width, height })
969 }
970 _ => None,
971 }
972 }
973
974 pub fn padding_values(&self) -> EdgeInsets {
975 self.resolved_modifiers().padding()
976 }
977
978 pub(crate) fn layout_properties(&self) -> LayoutProperties {
979 self.resolved_modifiers().layout_properties()
980 }
981
982 pub fn box_alignment(&self) -> Option<Alignment> {
983 self.layout_properties().box_alignment()
984 }
985
986 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
987 self.layout_properties().column_alignment()
988 }
989
990 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
991 self.layout_properties().row_alignment()
992 }
993
994 pub fn draw_commands(&self) -> Vec<DrawCommand> {
995 collect_slices_from_modifier(self).draw_commands().to_vec()
996 }
997
998 pub fn clips_to_bounds(&self) -> bool {
999 collect_slices_from_modifier(self).clip_to_bounds()
1000 }
1001
1002 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
1004 self.inspector_metadata()
1005 .iter()
1006 .map(|metadata| metadata.to_record())
1007 .collect()
1008 }
1009
1010 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1011 let mut handle = ModifierChainHandle::new();
1012 let _ = handle.update(self);
1013 handle.resolved_modifiers()
1014 }
1015
1016 pub(crate) fn with_element<E>(element: E) -> Self
1017 where
1018 E: ModifierNodeElement,
1019 {
1020 let dyn_element = modifier_element(element);
1021 Self::from_parts(vec![dyn_element])
1022 }
1023
1024 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
1025 if elements.is_empty() {
1026 Self::default()
1027 } else {
1028 let element_count = elements.len();
1029 let fingerprints = single_fingerprints(elements.as_slice());
1030 Self {
1031 kind: ModifierKind::Single {
1032 elements: Rc::new(elements),
1033 inspector: Rc::new(Vec::new()),
1034 },
1035 strict_fingerprint: fingerprints.strict,
1036 structural_fingerprint: fingerprints.structural,
1037 element_count,
1038 }
1039 }
1040 }
1041
1042 fn is_trivially_empty(&self) -> bool {
1043 matches!(self.kind, ModifierKind::Empty)
1044 }
1045
1046 fn single_parts(&self) -> Option<(&[DynModifierElement], &[InspectorMetadata])> {
1047 match &self.kind {
1048 ModifierKind::Empty => None,
1049 ModifierKind::Single {
1050 elements,
1051 inspector,
1052 } => Some((elements.as_slice(), inspector.as_slice())),
1053 }
1054 }
1055
1056 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
1057 if metadata.is_empty() {
1058 return self;
1059 }
1060 match self.kind {
1061 ModifierKind::Empty => self,
1062 ModifierKind::Single {
1063 elements,
1064 inspector,
1065 } => {
1066 let mut new_inspector = inspector.as_ref().clone();
1067 new_inspector.push(metadata);
1068 Self {
1069 kind: ModifierKind::Single {
1070 elements,
1071 inspector: Rc::new(new_inspector),
1072 },
1073 strict_fingerprint: self.strict_fingerprint,
1074 structural_fingerprint: self.structural_fingerprint,
1075 element_count: self.element_count,
1076 }
1077 }
1078 }
1079 }
1080
1081 pub fn structural_eq(&self, other: &Self) -> bool {
1086 self.eq_internal(other, false)
1087 }
1088
1089 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
1090 if self.element_count != other.element_count {
1091 return false;
1092 }
1093 if consider_always_update {
1094 if self.strict_fingerprint != other.strict_fingerprint {
1095 return false;
1096 }
1097 } else if self.structural_fingerprint != other.structural_fingerprint {
1098 return false;
1099 }
1100
1101 match (&self.kind, &other.kind) {
1102 (ModifierKind::Empty, ModifierKind::Empty) => true,
1103 (
1104 ModifierKind::Single {
1105 elements: e1,
1106 inspector: _,
1107 },
1108 ModifierKind::Single {
1109 elements: e2,
1110 inspector: _,
1111 },
1112 ) => {
1113 if Rc::ptr_eq(e1, e2) {
1114 return true;
1115 }
1116
1117 if e1.len() != e2.len() {
1118 return false;
1119 }
1120
1121 for (a, b) in e1.iter().zip(e2.iter()) {
1122 if !consider_always_update
1123 && a.element_type() == b.element_type()
1124 && a.capabilities() == NodeCapabilities::DRAW
1125 && b.capabilities() == NodeCapabilities::DRAW
1126 {
1127 continue;
1128 }
1129
1130 if consider_always_update && (a.requires_update() || b.requires_update()) {
1131 if !Rc::ptr_eq(a, b) {
1132 return false;
1133 }
1134 continue;
1135 }
1136
1137 if !a.equals_element(&**b) {
1138 return false;
1139 }
1140 }
1141
1142 true
1143 }
1144 _ => false,
1145 }
1146 }
1147}
1148
1149impl PartialEq for Modifier {
1150 fn eq(&self, other: &Self) -> bool {
1151 self.eq_internal(other, true)
1152 }
1153}
1154
1155impl Eq for Modifier {}
1156
1157impl fmt::Display for Modifier {
1158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1159 match &self.kind {
1160 ModifierKind::Empty => write!(f, "Modifier.empty"),
1161 ModifierKind::Single { elements, .. } => {
1162 if elements.is_empty() {
1163 return write!(f, "Modifier.empty");
1164 }
1165 write!(f, "Modifier[")?;
1166 for (index, element) in elements.iter().enumerate() {
1167 if index > 0 {
1168 write!(f, ", ")?;
1169 }
1170 let name = element.inspector_name();
1171 let mut properties = Vec::new();
1172 element.record_inspector_properties(&mut |prop, value| {
1173 properties.push(format!("{prop}={value}"));
1174 });
1175 if properties.is_empty() {
1176 write!(f, "{name}")?;
1177 } else {
1178 write!(f, "{name}({})", properties.join(", "))?;
1179 }
1180 }
1181 write!(f, "]")
1182 }
1183 }
1184 }
1185}
1186
1187impl fmt::Debug for Modifier {
1188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1189 fmt::Display::fmt(self, f)
1190 }
1191}
1192
1193#[derive(Clone, Copy, Debug, PartialEq)]
1194pub struct ResolvedBackground {
1195 color: Color,
1196 shape: Option<RoundedCornerShape>,
1197}
1198
1199impl ResolvedBackground {
1200 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
1201 Self { color, shape }
1202 }
1203
1204 pub fn color(&self) -> Color {
1205 self.color
1206 }
1207
1208 pub fn shape(&self) -> Option<RoundedCornerShape> {
1209 self.shape
1210 }
1211
1212 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
1213 self.shape = shape;
1214 }
1215}
1216
1217#[derive(Clone, Copy, Debug, PartialEq, Default)]
1218pub struct ResolvedModifiers {
1219 padding: EdgeInsets,
1220 layout: LayoutProperties,
1221 offset: Point,
1222}
1223
1224impl ResolvedModifiers {
1225 pub fn padding(&self) -> EdgeInsets {
1226 self.padding
1227 }
1228
1229 pub fn layout_properties(&self) -> LayoutProperties {
1230 self.layout
1231 }
1232
1233 pub fn offset(&self) -> Point {
1234 self.offset
1235 }
1236
1237 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
1238 self.padding = padding;
1239 }
1240
1241 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1242 self.layout = layout;
1243 }
1244
1245 pub(crate) fn set_offset(&mut self, offset: Point) {
1246 self.offset = offset;
1247 }
1248}
1249
1250#[derive(Clone, Copy, Debug, Default, PartialEq)]
1251pub enum DimensionConstraint {
1252 #[default]
1253 Unspecified,
1254 Points(f32),
1255 Fraction(f32),
1256 Intrinsic(IntrinsicSize),
1257}
1258
1259#[derive(Clone, Copy, Debug, Default, PartialEq)]
1260pub struct LayoutWeight {
1261 pub weight: f32,
1262 pub fill: bool,
1263}
1264
1265#[derive(Clone, Copy, Debug, Default, PartialEq)]
1266pub struct LayoutProperties {
1267 padding: EdgeInsets,
1268 width: DimensionConstraint,
1269 height: DimensionConstraint,
1270 min_width: Option<f32>,
1271 min_height: Option<f32>,
1272 max_width: Option<f32>,
1273 max_height: Option<f32>,
1274 weight: Option<LayoutWeight>,
1275 box_alignment: Option<Alignment>,
1276 column_alignment: Option<HorizontalAlignment>,
1277 row_alignment: Option<VerticalAlignment>,
1278}
1279
1280impl LayoutProperties {
1281 pub fn padding(&self) -> EdgeInsets {
1282 self.padding
1283 }
1284
1285 pub fn width(&self) -> DimensionConstraint {
1286 self.width
1287 }
1288
1289 pub fn height(&self) -> DimensionConstraint {
1290 self.height
1291 }
1292
1293 pub fn min_width(&self) -> Option<f32> {
1294 self.min_width
1295 }
1296
1297 pub fn min_height(&self) -> Option<f32> {
1298 self.min_height
1299 }
1300
1301 pub fn max_width(&self) -> Option<f32> {
1302 self.max_width
1303 }
1304
1305 pub fn max_height(&self) -> Option<f32> {
1306 self.max_height
1307 }
1308
1309 pub fn weight(&self) -> Option<LayoutWeight> {
1310 self.weight
1311 }
1312
1313 pub fn box_alignment(&self) -> Option<Alignment> {
1314 self.box_alignment
1315 }
1316
1317 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1318 self.column_alignment
1319 }
1320
1321 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1322 self.row_alignment
1323 }
1324}
1325
1326#[cfg(test)]
1327#[path = "tests/modifier_tests.rs"]
1328mod tests;