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