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