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