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