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, semantics_reach_of_chain,
82};
83pub(crate) use slices::collect_modifier_slices_into_shared;
84pub use slices::{
85 ModifierNodeSlices, ModifierNodeSlicesDebugStats, collect_modifier_slices,
86 collect_slices_from_modifier,
87};
88pub use window_root::{
89 WindowRootDescriptor, WindowRootElement, WindowRootEntry, WindowRootNode, WindowRootRegistry,
90 is_window_root, nearest_window_root, nearest_window_roots, window_roots, window_roots_revision,
91};
92
93pub use crate::draw::{DrawCacheBuilder, DrawCommand};
94use crate::modifier_nodes::ClipToBoundsElement;
95
96#[derive(Clone, Debug, Default)]
97pub struct InspectorInfo {
98 properties: Vec<InspectorProperty>,
99}
100
101impl InspectorInfo {
102 pub fn new() -> Self {
103 Self::default()
104 }
105
106 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
107 self.properties.push(InspectorProperty {
108 name,
109 value: value.into(),
110 });
111 }
112
113 pub fn properties(&self) -> &[InspectorProperty] {
114 &self.properties
115 }
116
117 pub fn is_empty(&self) -> bool {
118 self.properties.is_empty()
119 }
120
121 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
122 self.add_property(name, describe_dimension(constraint));
123 }
124
125 pub fn add_offset_components(
126 &mut self,
127 x_name: &'static str,
128 y_name: &'static str,
129 offset: Point,
130 ) {
131 self.add_property(x_name, offset.x.to_string());
132 self.add_property(y_name, offset.y.to_string());
133 }
134
135 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
136 where
137 A: fmt::Debug,
138 {
139 self.add_property(name, format!("{alignment:?}"));
140 }
141}
142
143#[derive(Clone, Debug, PartialEq)]
144pub struct InspectorProperty {
145 pub name: &'static str,
146 pub value: String,
147}
148
149#[derive(Clone, Debug, PartialEq)]
150pub struct ModifierInspectorRecord {
151 pub name: &'static str,
152 pub properties: Vec<InspectorProperty>,
153}
154
155#[derive(Clone, Debug)]
156pub(crate) struct InspectorMetadata {
157 name: &'static str,
158 info: InspectorInfo,
159}
160
161impl InspectorMetadata {
162 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
163 where
164 F: FnOnce(&mut InspectorInfo),
165 {
166 let mut info = InspectorInfo::new();
167 recorder(&mut info);
168 Self { name, info }
169 }
170
171 fn is_empty(&self) -> bool {
172 self.info.is_empty()
173 }
174
175 fn to_record(&self) -> ModifierInspectorRecord {
176 ModifierInspectorRecord {
177 name: self.name,
178 properties: self.info.properties().to_vec(),
179 }
180 }
181}
182
183fn describe_dimension(constraint: DimensionConstraint) -> String {
184 match constraint {
185 DimensionConstraint::Unspecified => "unspecified".to_string(),
186 DimensionConstraint::Points(value) => value.to_string(),
187 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
188 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
189 }
190}
191
192fn inspector_slice(inspector: &Option<Rc<Vec<InspectorMetadata>>>) -> &[InspectorMetadata] {
193 inspector.as_deref().map_or(&[], Vec::as_slice)
194}
195
196fn merged_inspector(
199 first: &[InspectorMetadata],
200 second: &[InspectorMetadata],
201) -> Option<Rc<Vec<InspectorMetadata>>> {
202 if first.is_empty() && second.is_empty() {
203 return None;
204 }
205 let mut merged = Vec::with_capacity(first.len() + second.len());
206 merged.extend_from_slice(first);
207 merged.extend_from_slice(second);
208 Some(Rc::new(merged))
209}
210
211pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
212where
213 F: FnOnce(&mut InspectorInfo),
214{
215 if !inspector_metadata_enabled() {
216 return InspectorMetadata::new(name, |_| {});
217 }
218 InspectorMetadata::new(name, recorder)
219}
220
221pub(crate) fn modifier_debug_enabled() -> bool {
222 #[cfg(not(target_arch = "wasm32"))]
223 {
224 cranpose_core::env_flag!("COMPOSE_DEBUG_MODIFIERS")
225 }
226 #[cfg(target_arch = "wasm32")]
227 {
228 false
229 }
230}
231
232fn inspector_metadata_enabled() -> bool {
233 cfg!(any(test, feature = "inspection")) || modifier_debug_enabled()
234}
235
236#[derive(Clone)]
237enum ModifierKind {
238 Empty,
239 Single {
240 elements: Rc<Vec<DynModifierElement>>,
241 inspector: Option<Rc<Vec<InspectorMetadata>>>,
244 },
245}
246
247const FINGERPRINT_KIND_EMPTY: u8 = 0;
248const FINGERPRINT_KIND_SINGLE: u8 = 1;
249
250const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
251const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
252const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
253const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
254const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
255const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
256const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
257
258#[derive(Clone, Copy, Debug, PartialEq, Eq)]
259struct ModifierFingerprints {
260 strict: u64,
261 structural: u64,
262}
263
264#[inline]
265fn mix_fingerprint_bits(mut value: u64) -> u64 {
266 value ^= value >> 33;
267 value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
268 value ^= value >> 33;
269 value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
270 value ^ (value >> 33)
271}
272
273#[inline]
274fn fold_fingerprint(state: u64, value: u64) -> u64 {
275 mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
276 .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
277}
278
279#[inline]
280fn empty_fingerprints() -> ModifierFingerprints {
281 ModifierFingerprints {
282 strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
283 structural: fold_fingerprint(
284 FINGERPRINT_EMPTY_STRUCTURAL_SEED,
285 FINGERPRINT_KIND_EMPTY as u64,
286 ),
287 }
288}
289
290#[inline]
291fn single_fingerprint_seed() -> ModifierFingerprints {
292 let strict = fold_fingerprint(
293 FINGERPRINT_SINGLE_STRICT_SEED,
294 FINGERPRINT_KIND_SINGLE as u64,
295 );
296 let structural = fold_fingerprint(
297 FINGERPRINT_SINGLE_STRUCTURAL_SEED,
298 FINGERPRINT_KIND_SINGLE as u64,
299 );
300 ModifierFingerprints { strict, structural }
301}
302
303#[inline]
304fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
305 let mut hasher = default::new();
306 element.element_type().hash(&mut hasher);
307 element.capabilities().bits().hash(&mut hasher);
308 hasher.finish()
309}
310
311#[inline]
312fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
313 let common = element_common_fingerprint(element);
314 let requires_update = element.requires_update();
315 let strict_payload = if requires_update {
316 let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
317 element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
318 } else {
319 element.hash_code()
320 };
321 let strict = mix_fingerprint_bits(common ^ strict_payload);
322
323 let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
324 let structural_payload = if is_draw_only {
325 FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
326 } else {
327 element.hash_code()
328 };
329 let structural = mix_fingerprint_bits(common ^ structural_payload);
330
331 ModifierFingerprints { strict, structural }
332}
333
334#[inline]
335fn append_fingerprints(
336 mut fingerprints: ModifierFingerprints,
337 elements: &[DynModifierElement],
338) -> ModifierFingerprints {
339 for element in elements {
340 let element_fingerprints = element_fingerprints(element);
341 fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
342 fingerprints.structural =
343 fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
344 }
345 fingerprints
346}
347
348fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
349 append_fingerprints(single_fingerprint_seed(), elements)
350}
351
352pub struct ModifierElementIterator<'a> {
353 inner: std::slice::Iter<'a, DynModifierElement>,
354}
355
356impl<'a> Iterator for ModifierElementIterator<'a> {
357 type Item = &'a DynModifierElement;
358
359 #[inline]
360 fn next(&mut self) -> Option<Self::Item> {
361 self.inner.next()
362 }
363
364 #[inline]
365 fn size_hint(&self) -> (usize, Option<usize>) {
366 self.inner.size_hint()
367 }
368}
369
370impl ExactSizeIterator for ModifierElementIterator<'_> {}
371
372pub(crate) struct ModifierInspectorIterator<'a> {
373 inner: std::slice::Iter<'a, InspectorMetadata>,
374}
375
376impl<'a> Iterator for ModifierInspectorIterator<'a> {
377 type Item = &'a InspectorMetadata;
378
379 #[inline]
380 fn next(&mut self) -> Option<Self::Item> {
381 self.inner.next()
382 }
383
384 #[inline]
385 fn size_hint(&self) -> (usize, Option<usize>) {
386 self.inner.size_hint()
387 }
388}
389
390impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
391
392#[derive(Clone)]
410pub struct Modifier {
411 kind: ModifierKind,
412 strict_fingerprint: u64,
413 structural_fingerprint: u64,
414 element_count: usize,
415 provides_composition_locals: bool,
416}
417
418impl Default for Modifier {
419 fn default() -> Self {
420 let fingerprints = empty_fingerprints();
421 Self {
422 kind: ModifierKind::Empty,
423 strict_fingerprint: fingerprints.strict,
424 structural_fingerprint: fingerprints.structural,
425 element_count: 0,
426 provides_composition_locals: false,
427 }
428 }
429}
430
431impl Modifier {
432 pub fn empty() -> Self {
433 Self::default()
434 }
435
436 pub fn from_element<E>(element: E) -> Self
438 where
439 E: ModifierNodeElement,
440 {
441 Self::with_element(element)
442 }
443
444 pub fn clip_to_bounds(self) -> Self {
448 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
449 inspector_metadata("clipToBounds", |info| {
450 info.add_property("clipToBounds", "true");
451 }),
452 );
453 self.then(modifier)
454 }
455
456 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
457 where
458 T: 'static,
459 F: Fn() -> T + 'static,
460 {
461 let element = ModifierLocalProviderElement::new(key, value);
462 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
463 self.then(modifier)
464 }
465
466 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
467 where
468 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
469 {
470 let element = ModifierLocalConsumerElement::new(consumer);
471 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
472 self.then(modifier)
473 }
474
475 pub fn semantics_spec(self, spec: cranpose_foundation::SemanticsSpec) -> Self {
486 self.semantics(move |config: &mut SemanticsConfiguration| config.merge(&spec))
487 }
488 pub fn semantics<F>(self, recorder: F) -> Self
489 where
490 F: Fn(&mut SemanticsConfiguration) + 'static,
491 {
492 let recorder: std::rc::Rc<dyn Fn(&mut SemanticsConfiguration)> = std::rc::Rc::new(recorder);
493 let metadata = if inspector_metadata_enabled() {
494 let mut preview = SemanticsConfiguration::default();
495 recorder(&mut preview);
496 let description = preview.content_description.clone();
497 let state_description = preview.state_description.clone();
498 let role = preview.role;
499 let is_clickable = preview.is_activatable();
500 let canvas_children = preview.canvas_children.len();
501 inspector_metadata("semantics", move |info| {
502 if let Some(desc) = &description {
503 info.add_property("contentDescription", desc.clone());
504 }
505 if let Some(state) = &state_description {
506 info.add_property("stateDescription", state.clone());
507 }
508 if let Some(role) = role {
509 info.add_property("role", format!("{role:?}"));
510 }
511 if is_clickable {
512 info.add_property("isClickable", "true");
513 }
514 if canvas_children > 0 {
515 info.add_property("canvasSemanticsChildren", canvas_children.to_string());
516 }
517 })
518 } else {
519 inspector_metadata("semantics", |_| {})
520 };
521 let element = SemanticsElement::new(recorder);
522 let modifier =
523 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
524 self.then(modifier)
525 }
526
527 pub fn progress_semantics(self, current: f32, start: f32, end: f32, steps: u32) -> Self {
536 let info = ProgressBarRangeInfo::new(current, start, end, steps);
537 self.semantics(move |config| config.progress = Some(info))
538 }
539
540 pub fn dropdown_list(self) -> Self {
544 self.role(cranpose_foundation::SemanticsWidgetRole::DropdownList)
545 }
546
547 pub fn value_picker(self) -> Self {
550 self.role(cranpose_foundation::SemanticsWidgetRole::ValuePicker)
551 }
552
553 pub fn expand(self, action: impl Fn() -> bool + 'static) -> Self {
558 let action = cranpose_foundation::SemanticsExpand::new(action);
559 self.semantics(move |config| config.expand = Some(action.clone()))
560 }
561
562 pub fn on_long_click(
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::SemanticsLongClick::new(action);
578 self.semantics(move |config| {
579 config.on_long_click_label = Some(label.clone());
580 config.on_long_click = Some(action.clone());
581 })
582 }
583
584 pub fn on_magic_tap(
589 self,
590 label: impl Into<String>,
591 action: impl Fn() -> bool + 'static,
592 ) -> Self {
593 let label = label.into();
594 let action = cranpose_foundation::SemanticsMagicTap::new(action);
595 self.semantics(move |config| {
596 config.on_magic_tap_label = Some(label.clone());
597 config.on_magic_tap = Some(action.clone());
598 })
599 }
600
601 pub fn input_labels<S: Into<String>>(self, labels: impl IntoIterator<Item = S>) -> Self {
605 let labels: Vec<String> = labels.into_iter().map(Into::into).collect();
606 self.semantics(move |config| config.input_labels.clone_from(&labels))
607 }
608
609 pub fn language(self, tag: impl Into<String>) -> Self {
613 let tag = tag.into();
614 self.semantics(move |config| config.language = Some(tag.clone()))
615 }
616
617 pub fn collapse(self, action: impl Fn() -> bool + 'static) -> Self {
621 let action = cranpose_foundation::SemanticsExpand::new(action);
622 self.semantics(move |config| config.collapse = Some(action.clone()))
623 }
624
625 pub fn dismiss(self, action: impl Fn() -> bool + 'static) -> Self {
630 let action = cranpose_foundation::SemanticsDismiss::new(action);
631 self.semantics(move |config| config.dismiss = Some(action.clone()))
632 }
633
634 pub fn scroll_to_index(self, action: impl Fn(usize) -> bool + 'static) -> Self {
640 let action = cranpose_foundation::SemanticsScrollToIndex::new(action);
641 self.semantics(move |config| config.scroll_to_index = Some(action.clone()))
642 }
643
644 pub fn traversal_index(self, index: f32) -> Self {
650 self.semantics(move |config| config.traversal_index = index)
651 }
652
653 pub fn password(self) -> Self {
658 self.semantics(|config| config.password = true)
659 }
660
661 pub fn error(self, message: impl Into<String>) -> Self {
665 let message = message.into();
666 self.semantics(move |config| config.error = Some(message.clone()))
667 }
668
669 pub fn pane_title(self, title: impl Into<String>) -> Self {
673 let title = title.into();
674 self.semantics(move |config| config.pane_title = Some(title.clone()))
675 }
676
677 pub fn selectable_group(self) -> Self {
682 self.semantics(|config| config.selectable_group = true)
683 }
684
685 pub fn merge_descendants(self) -> Self {
690 self.semantics(|config| config.merge_descendants = true)
691 }
692
693 pub fn hide_from_accessibility(self) -> Self {
698 self.semantics(|config| config.hidden = true)
699 }
700
701 pub fn heading(self) -> Self {
706 self.role(cranpose_foundation::SemanticsWidgetRole::Header)
707 }
708
709 pub fn role(self, role: cranpose_foundation::SemanticsWidgetRole) -> Self {
714 self.semantics(move |config| config.role = Some(role))
715 }
716
717 pub fn live_region(self, mode: cranpose_foundation::LiveRegionMode) -> Self {
723 self.semantics(move |config| config.live_region = Some(mode))
724 }
725
726 pub fn content_description(self, description: impl Into<String>) -> Self {
731 let description = description.into();
732 self.semantics(move |config| config.content_description = Some(description.clone()))
733 }
734
735 pub fn focus_target(self) -> Self {
741 let element = FocusTargetElement::new();
742 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
743 self.then(modifier)
744 }
745
746 pub fn on_focus_changed<F>(self, callback: F) -> Self
751 where
752 F: Fn(FocusState) + 'static,
753 {
754 let element = FocusTargetElement::with_callback(callback);
755 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
756 self.then(modifier)
757 }
758
759 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
766 let element = FocusRequesterElement::new(requester.clone());
767 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
768 self.then(modifier)
769 }
770
771 pub fn semantics_requester(self, requester: &SemanticsRequester) -> Self {
779 let element = SemanticsRequesterElement::new(requester.clone());
780 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
781 self.then(modifier)
782 }
783
784 pub fn debug_chain(self, tag: &'static str) -> Self {
802 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
803
804 #[derive(Clone)]
805 struct DebugChainElement {
806 tag: &'static str,
807 }
808
809 impl fmt::Debug for DebugChainElement {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 f.debug_struct("DebugChainElement")
812 .field("tag", &self.tag)
813 .finish()
814 }
815 }
816
817 impl PartialEq for DebugChainElement {
818 fn eq(&self, other: &Self) -> bool {
819 self.tag == other.tag
820 }
821 }
822
823 impl Eq for DebugChainElement {}
824
825 impl std::hash::Hash for DebugChainElement {
826 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
827 self.tag.hash(state);
828 }
829 }
830
831 impl ModifierNodeElement for DebugChainElement {
832 type Node = DebugChainNode;
833
834 fn create(&self) -> Self::Node {
835 DebugChainNode::new(self.tag)
836 }
837
838 fn update(&self, node: &mut Self::Node) {
839 node.tag = self.tag;
840 }
841
842 fn capabilities(&self) -> NodeCapabilities {
843 NodeCapabilities::empty()
844 }
845 }
846
847 struct DebugChainNode {
848 tag: &'static str,
849 state: NodeState,
850 }
851
852 impl DebugChainNode {
853 fn new(tag: &'static str) -> Self {
854 Self {
855 tag,
856 state: NodeState::new(),
857 }
858 }
859 }
860
861 impl ModifierNode for DebugChainNode {
862 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
863 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
864 }
865
866 fn on_detach(&mut self) {
867 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
868 }
869
870 fn on_reset(&mut self) {
871 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
872 }
873 }
874
875 impl cranpose_foundation::DelegatableNode for DebugChainNode {
876 fn node_state(&self) -> &NodeState {
877 &self.state
878 }
879 }
880
881 let element = DebugChainElement { tag };
882 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
883 self.then(modifier)
884 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
885 info.add_property("tag", tag);
886 }))
887 }
888
889 pub fn then(&self, next: Modifier) -> Modifier {
894 if self.is_trivially_empty() {
895 return next;
896 }
897 if next.is_trivially_empty() {
898 return self.clone();
899 }
900
901 let Some((self_elements, self_inspector)) = self.single_parts() else {
902 return next;
903 };
904 let Some((next_elements, next_inspector)) = next.single_parts() else {
905 return self.clone();
906 };
907
908 let mut merged_elements = Vec::with_capacity(self_elements.len() + next_elements.len());
909 merged_elements.extend_from_slice(self_elements);
910 merged_elements.extend_from_slice(next_elements);
911
912 let merged_inspector = merged_inspector(self_inspector, next_inspector);
913
914 let fingerprints = append_fingerprints(
915 ModifierFingerprints {
916 strict: self.strict_fingerprint,
917 structural: self.structural_fingerprint,
918 },
919 next_elements,
920 );
921 Modifier {
922 kind: ModifierKind::Single {
923 elements: Rc::new(merged_elements),
924 inspector: merged_inspector,
925 },
926 strict_fingerprint: fingerprints.strict,
927 structural_fingerprint: fingerprints.structural,
928 element_count: self.element_count + next.element_count,
929 provides_composition_locals: self.provides_composition_locals
930 || next.provides_composition_locals,
931 }
932 }
933
934 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
935 match &self.kind {
936 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
937 ModifierKind::Single { elements, .. } => ModifierElementIterator {
938 inner: elements.iter(),
939 },
940 }
941 }
942
943 pub(crate) fn provided_composition_locals(&self) -> Vec<ProvidedValue> {
944 if !self.provides_composition_locals {
945 return Vec::new();
946 }
947 self.iter_elements()
948 .flat_map(|element| element.provided_composition_locals())
949 .collect()
950 }
951
952 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
953 match &self.kind {
954 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
955 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
956 inner: inspector_slice(inspector).iter(),
957 },
958 }
959 }
960
961 #[cfg(test)]
962 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
963 match &self.kind {
964 ModifierKind::Empty => Vec::new(),
965 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
966 }
967 }
968
969 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
970 match &self.kind {
971 ModifierKind::Empty => Vec::new(),
972 ModifierKind::Single { inspector, .. } => {
973 inspector.as_deref().cloned().unwrap_or_default()
974 }
975 }
976 }
977
978 pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
979 match &self.kind {
980 ModifierKind::Empty => Self::default(),
981 ModifierKind::Single {
982 elements,
983 inspector,
984 } => Self {
985 kind: ModifierKind::Single {
986 elements: Rc::new(elements.iter().cloned().collect()),
987 inspector: inspector
988 .as_ref()
989 .map(|inspector| Rc::new(inspector.as_ref().clone())),
990 },
991 strict_fingerprint: self.strict_fingerprint,
992 structural_fingerprint: self.structural_fingerprint,
993 element_count: self.element_count,
994 provides_composition_locals: self.provides_composition_locals,
995 },
996 }
997 }
998
999 pub fn total_padding(&self) -> f32 {
1000 let padding = self.padding_values();
1001 padding
1002 .left
1003 .max(padding.right)
1004 .max(padding.top)
1005 .max(padding.bottom)
1006 }
1007
1008 pub fn explicit_size(&self) -> Option<Size> {
1009 let props = self.layout_properties();
1010 match (props.width, props.height) {
1011 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
1012 Some(Size { width, height })
1013 }
1014 _ => None,
1015 }
1016 }
1017
1018 pub fn padding_values(&self) -> EdgeInsets {
1019 self.resolved_modifiers().padding()
1020 }
1021
1022 pub(crate) fn layout_properties(&self) -> LayoutProperties {
1023 self.resolved_modifiers().layout_properties()
1024 }
1025
1026 pub fn box_alignment(&self) -> Option<Alignment> {
1027 self.layout_properties().box_alignment()
1028 }
1029
1030 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1031 self.layout_properties().column_alignment()
1032 }
1033
1034 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1035 self.layout_properties().row_alignment()
1036 }
1037
1038 pub fn draw_commands(&self) -> Vec<DrawCommand> {
1039 collect_slices_from_modifier(self).draw_commands().to_vec()
1040 }
1041
1042 pub fn clips_to_bounds(&self) -> bool {
1043 collect_slices_from_modifier(self).clip_to_bounds()
1044 }
1045
1046 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
1048 self.inspector_metadata()
1049 .iter()
1050 .map(InspectorMetadata::to_record)
1051 .collect()
1052 }
1053
1054 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1055 let mut handle = ModifierChainHandle::new();
1056 let _ = handle.update(self);
1057 handle.resolved_modifiers()
1058 }
1059
1060 pub fn with_element<E>(element: E) -> Self
1063 where
1064 E: ModifierNodeElement,
1065 {
1066 let dyn_element = modifier_element(element);
1067 Self::from_parts(vec![dyn_element])
1068 }
1069
1070 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
1071 if elements.is_empty() {
1072 Self::default()
1073 } else {
1074 let element_count = elements.len();
1075 let provides_composition_locals = elements
1076 .iter()
1077 .any(|element| element.provides_composition_locals());
1078 let fingerprints = single_fingerprints(elements.as_slice());
1079 Self {
1080 kind: ModifierKind::Single {
1081 elements: Rc::new(elements),
1082 inspector: None,
1083 },
1084 strict_fingerprint: fingerprints.strict,
1085 structural_fingerprint: fingerprints.structural,
1086 element_count,
1087 provides_composition_locals,
1088 }
1089 }
1090 }
1091
1092 fn is_trivially_empty(&self) -> bool {
1093 matches!(self.kind, ModifierKind::Empty)
1094 }
1095
1096 fn single_parts(&self) -> Option<(&[DynModifierElement], &[InspectorMetadata])> {
1097 match &self.kind {
1098 ModifierKind::Empty => None,
1099 ModifierKind::Single {
1100 elements,
1101 inspector,
1102 } => Some((elements.as_slice(), inspector_slice(inspector))),
1103 }
1104 }
1105
1106 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
1107 if metadata.is_empty() {
1108 return self;
1109 }
1110 match self.kind {
1111 ModifierKind::Empty => self,
1112 ModifierKind::Single {
1113 elements,
1114 inspector,
1115 } => {
1116 let mut new_inspector = inspector.as_deref().cloned().unwrap_or_default();
1117 new_inspector.push(metadata);
1118 Self {
1119 kind: ModifierKind::Single {
1120 elements,
1121 inspector: Some(Rc::new(new_inspector)),
1122 },
1123 strict_fingerprint: self.strict_fingerprint,
1124 structural_fingerprint: self.structural_fingerprint,
1125 element_count: self.element_count,
1126 provides_composition_locals: self.provides_composition_locals,
1127 }
1128 }
1129 }
1130 }
1131
1132 pub fn structural_eq(&self, other: &Self) -> bool {
1137 self.eq_internal(other, false)
1138 }
1139
1140 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
1141 if self.element_count != other.element_count {
1142 return false;
1143 }
1144 if consider_always_update {
1145 if self.strict_fingerprint != other.strict_fingerprint {
1146 return false;
1147 }
1148 } else if self.structural_fingerprint != other.structural_fingerprint {
1149 return false;
1150 }
1151
1152 match (&self.kind, &other.kind) {
1153 (ModifierKind::Empty, ModifierKind::Empty) => true,
1154 (
1155 ModifierKind::Single {
1156 elements: e1,
1157 inspector: _,
1158 },
1159 ModifierKind::Single {
1160 elements: e2,
1161 inspector: _,
1162 },
1163 ) => {
1164 if Rc::ptr_eq(e1, e2) {
1165 return true;
1166 }
1167
1168 if e1.len() != e2.len() {
1169 return false;
1170 }
1171
1172 for (a, b) in e1.iter().zip(e2.iter()) {
1173 if !consider_always_update
1174 && a.element_type() == b.element_type()
1175 && a.capabilities() == NodeCapabilities::DRAW
1176 && b.capabilities() == NodeCapabilities::DRAW
1177 {
1178 continue;
1179 }
1180
1181 if consider_always_update && (a.requires_update() || b.requires_update()) {
1182 if !Rc::ptr_eq(a, b) {
1183 return false;
1184 }
1185 continue;
1186 }
1187
1188 if !a.equals_element(&**b) {
1189 return false;
1190 }
1191 }
1192
1193 true
1194 }
1195 _ => false,
1196 }
1197 }
1198}
1199
1200impl PartialEq for Modifier {
1201 fn eq(&self, other: &Self) -> bool {
1202 self.eq_internal(other, true)
1203 }
1204}
1205
1206impl Eq for Modifier {}
1207
1208impl fmt::Display for Modifier {
1209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1210 match &self.kind {
1211 ModifierKind::Empty => write!(f, "Modifier.empty"),
1212 ModifierKind::Single { elements, .. } => {
1213 if elements.is_empty() {
1214 return write!(f, "Modifier.empty");
1215 }
1216 write!(f, "Modifier[")?;
1217 for (index, element) in elements.iter().enumerate() {
1218 if index > 0 {
1219 write!(f, ", ")?;
1220 }
1221 let name = element.inspector_name();
1222 let mut properties = Vec::new();
1223 element.record_inspector_properties(&mut |prop, value| {
1224 properties.push(format!("{prop}={value}"));
1225 });
1226 if properties.is_empty() {
1227 write!(f, "{name}")?;
1228 } else {
1229 write!(f, "{name}({})", properties.join(", "))?;
1230 }
1231 }
1232 write!(f, "]")
1233 }
1234 }
1235 }
1236}
1237
1238impl fmt::Debug for Modifier {
1239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240 fmt::Display::fmt(self, f)
1241 }
1242}
1243
1244#[derive(Clone, Copy, Debug, PartialEq)]
1245pub struct ResolvedBackground {
1246 color: Color,
1247 shape: Option<RoundedCornerShape>,
1248}
1249
1250impl ResolvedBackground {
1251 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
1252 Self { color, shape }
1253 }
1254
1255 pub fn color(&self) -> Color {
1256 self.color
1257 }
1258
1259 pub fn shape(&self) -> Option<RoundedCornerShape> {
1260 self.shape
1261 }
1262
1263 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
1264 self.shape = shape;
1265 }
1266}
1267
1268#[derive(Clone, Copy, Debug, PartialEq, Default)]
1269pub struct ResolvedModifiers {
1270 padding: EdgeInsets,
1271 layout: LayoutProperties,
1272 offset: Point,
1273}
1274
1275impl ResolvedModifiers {
1276 pub fn padding(&self) -> EdgeInsets {
1277 self.padding
1278 }
1279
1280 pub fn layout_properties(&self) -> LayoutProperties {
1281 self.layout
1282 }
1283
1284 pub fn offset(&self) -> Point {
1285 self.offset
1286 }
1287
1288 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
1289 self.padding = padding;
1290 }
1291
1292 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1293 self.layout = layout;
1294 }
1295
1296 pub(crate) fn set_offset(&mut self, offset: Point) {
1297 self.offset = offset;
1298 }
1299}
1300
1301#[derive(Clone, Copy, Debug, Default, PartialEq)]
1302pub enum DimensionConstraint {
1303 #[default]
1304 Unspecified,
1305 Points(f32),
1306 Fraction(f32),
1307 Intrinsic(IntrinsicSize),
1308}
1309
1310#[derive(Clone, Copy, Debug, Default, PartialEq)]
1311pub struct LayoutWeight {
1312 pub weight: f32,
1313 pub fill: bool,
1314}
1315
1316#[derive(Clone, Copy, Debug, Default, PartialEq)]
1317pub struct LayoutProperties {
1318 padding: EdgeInsets,
1319 width: DimensionConstraint,
1320 height: DimensionConstraint,
1321 min_width: Option<f32>,
1322 min_height: Option<f32>,
1323 max_width: Option<f32>,
1324 max_height: Option<f32>,
1325 weight: Option<LayoutWeight>,
1326 box_alignment: Option<Alignment>,
1327 column_alignment: Option<HorizontalAlignment>,
1328 row_alignment: Option<VerticalAlignment>,
1329}
1330
1331impl LayoutProperties {
1332 pub fn padding(&self) -> EdgeInsets {
1333 self.padding
1334 }
1335
1336 pub fn width(&self) -> DimensionConstraint {
1337 self.width
1338 }
1339
1340 pub fn height(&self) -> DimensionConstraint {
1341 self.height
1342 }
1343
1344 pub fn min_width(&self) -> Option<f32> {
1345 self.min_width
1346 }
1347
1348 pub fn min_height(&self) -> Option<f32> {
1349 self.min_height
1350 }
1351
1352 pub fn max_width(&self) -> Option<f32> {
1353 self.max_width
1354 }
1355
1356 pub fn max_height(&self) -> Option<f32> {
1357 self.max_height
1358 }
1359
1360 pub fn weight(&self) -> Option<LayoutWeight> {
1361 self.weight
1362 }
1363
1364 pub fn box_alignment(&self) -> Option<Alignment> {
1365 self.box_alignment
1366 }
1367
1368 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1369 self.column_alignment
1370 }
1371
1372 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1373 self.row_alignment
1374 }
1375}
1376
1377#[cfg(test)]
1378#[path = "tests/modifier_tests.rs"]
1379mod tests;