1use std::{
9 fmt,
10 hash::{Hash, Hasher},
11 rc::Rc,
12};
13
14use cranpose_core::{ProvidedValue, hash::default};
15use smallvec::SmallVec;
16
17mod alignment;
18mod background;
19mod blur;
20mod chain;
21mod clickable;
22mod coordinator_geometry;
23mod drag_and_drop;
24mod draw_cache;
25mod fill;
26mod focus;
27mod focus_ring;
28mod graphics_layer;
29mod local;
30mod minimum_interactive;
31mod offset;
32mod padding;
33mod pointer_icon;
34pub(crate) mod pointer_input;
35mod rotary_input;
36mod scroll;
37mod selectable;
38mod semantics;
39mod shadow;
40mod size;
41mod slices;
42mod toggleable;
43mod weight;
44mod window_root;
45
46pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
47pub(crate) use coordinator_geometry::{CoordinatorGeometry, CoordinatorRect};
48pub use cranpose_foundation::{
49 AnyModifierElement, DynModifierElement, FocusState, PointerEvent, PointerEventKind,
50 PointerSource, RotaryScrollEvent, SemanticsConfiguration, modifier_element,
51};
52use cranpose_foundation::{ModifierNodeElement, NodeCapabilities, ProgressBarRangeInfo};
53#[expect(unused_imports)]
54pub use cranpose_ui_graphics::{
55 BlendMode, BlurredEdgeTreatment, Brush, Color, ColorFilter, CompositingStrategy, CornerRadii,
56 CursorIcon, CustomPointerIcon, CutDirection, Dp, DpOffset, EdgeInsets, GradientCutMaskSpec,
57 GradientFadeMaskSpec, GraphicsLayer, LayerShape, Point, PointerIcon, PointerIconError, Rect,
58 RenderEffect, RoundedCornerShape, RuntimeShader, Shadow, ShadowScope, Size, TransformOrigin,
59};
60use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
61pub use drag_and_drop::{
62 DragAndDropEvent, DragAndDropOutcome, DragAndDropPayload, DragAndDropPoint, DragAndDropSource,
63 DragAndDropSourceElement, DragAndDropSourceNode, DragAndDropState, DragAndDropTarget,
64 DragAndDropTargetElement, DragAndDropTargetNode,
65};
66use focus::FocusTargetElement;
67pub use focus::{FocusDirection, FocusRequestError, FocusRequester, FocusRequesterElement};
68pub use graphics_layer::GlassMaterial;
69pub(crate) use local::{
70 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
71};
72use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
73pub use local::{ModifierLocalKey, ModifierLocalReadScope};
74#[expect(unused_imports)]
75pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
76pub use rotary_input::RotaryInputModifierNode;
77#[cfg(test)]
78pub(crate) use scroll::lazy_scroll_semantics;
79#[cfg(feature = "test-helpers")]
80pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
81use semantics::SemanticsElement;
82pub use semantics::{
83 SemanticsRequester, SemanticsRequesterElement, collect_semantics_from_chain,
84 collect_semantics_from_modifier, semantics_reach_of_chain,
85};
86pub(crate) use slices::collect_modifier_slices_into_shared;
87pub use slices::{
88 ModifierNodeSlices, ModifierNodeSlicesDebugStats, collect_modifier_slices,
89 collect_slices_from_modifier,
90};
91pub use window_root::{
92 WindowRootDescriptor, WindowRootElement, WindowRootEntry, WindowRootNode, WindowRootRegistry,
93 is_window_root, nearest_window_root, nearest_window_roots, window_roots, window_roots_revision,
94};
95
96pub use crate::draw::{DrawCacheBuilder, DrawCommand};
97use crate::modifier_nodes::ClipToBoundsElement;
98
99#[derive(Clone, Debug, Default)]
100pub struct InspectorInfo {
101 properties: Vec<InspectorProperty>,
102}
103
104impl InspectorInfo {
105 pub fn new() -> Self {
106 Self::default()
107 }
108
109 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
110 self.properties.push(InspectorProperty {
111 name,
112 value: value.into(),
113 });
114 }
115
116 pub fn properties(&self) -> &[InspectorProperty] {
117 &self.properties
118 }
119
120 pub fn is_empty(&self) -> bool {
121 self.properties.is_empty()
122 }
123
124 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
125 self.add_property(name, describe_dimension(constraint));
126 }
127
128 pub fn add_offset_components(
129 &mut self,
130 x_name: &'static str,
131 y_name: &'static str,
132 offset: Point,
133 ) {
134 self.add_property(x_name, offset.x.to_string());
135 self.add_property(y_name, offset.y.to_string());
136 }
137
138 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
139 where
140 A: fmt::Debug,
141 {
142 self.add_property(name, format!("{alignment:?}"));
143 }
144}
145
146#[derive(Clone, Debug, PartialEq)]
147pub struct InspectorProperty {
148 pub name: &'static str,
149 pub value: String,
150}
151
152#[derive(Clone, Debug, PartialEq)]
153pub struct ModifierInspectorRecord {
154 pub name: &'static str,
155 pub properties: Vec<InspectorProperty>,
156}
157
158#[derive(Clone, Debug)]
159pub(crate) struct InspectorMetadata {
160 name: &'static str,
161 info: InspectorInfo,
162}
163
164impl InspectorMetadata {
165 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
166 where
167 F: FnOnce(&mut InspectorInfo),
168 {
169 let mut info = InspectorInfo::new();
170 recorder(&mut info);
171 Self { name, info }
172 }
173
174 fn is_empty(&self) -> bool {
175 self.info.is_empty()
176 }
177
178 fn to_record(&self) -> ModifierInspectorRecord {
179 ModifierInspectorRecord {
180 name: self.name,
181 properties: self.info.properties().to_vec(),
182 }
183 }
184}
185
186fn describe_dimension(constraint: DimensionConstraint) -> String {
187 match constraint {
188 DimensionConstraint::Unspecified => "unspecified".to_string(),
189 DimensionConstraint::Points(value) => value.to_string(),
190 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
191 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
192 }
193}
194
195fn inspector_slice(inspector: &Option<Rc<[InspectorMetadata]>>) -> &[InspectorMetadata] {
196 inspector.as_deref().unwrap_or(&[])
197}
198
199fn merged_inspector(
202 first: &[InspectorMetadata],
203 second: &[InspectorMetadata],
204) -> Option<Rc<[InspectorMetadata]>> {
205 if first.is_empty() && second.is_empty() {
206 return None;
207 }
208 Some(first.iter().chain(second).cloned().collect())
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
236type ModifierElements = SmallVec<[DynModifierElement; 4]>;
239
240#[derive(Clone)]
245enum Elements {
246 One(DynModifierElement),
247 Many(Rc<ModifierElements>),
248}
249
250impl Elements {
251 fn from_slice(elements: &[DynModifierElement]) -> Self {
252 match elements {
253 [element] => Self::One(element.clone()),
254 _ => Self::Many(Rc::new(elements.iter().cloned().collect())),
255 }
256 }
257
258 fn extend_from(&mut self, more: &[DynModifierElement]) {
259 match self {
260 Self::Many(shared) => match Rc::get_mut(shared) {
261 Some(owned) => owned.extend(more.iter().cloned()),
262 None => *shared = Rc::new(shared.iter().chain(more).cloned().collect()),
263 },
264 Self::One(first) => {
265 *self = Self::Many(Rc::new(
266 std::iter::once(&*first).chain(more).cloned().collect(),
267 ));
268 }
269 }
270 }
271
272 fn shares_storage(&self, other: &Self) -> bool {
274 match (self, other) {
275 (Self::One(a), Self::One(b)) => Rc::ptr_eq(a, b),
276 (Self::Many(a), Self::Many(b)) => Rc::ptr_eq(a, b),
277 _ => false,
278 }
279 }
280}
281
282impl std::ops::Deref for Elements {
283 type Target = [DynModifierElement];
284
285 fn deref(&self) -> &[DynModifierElement] {
286 match self {
287 Self::One(element) => std::slice::from_ref(element),
288 Self::Many(elements) => elements.as_slice(),
289 }
290 }
291}
292
293#[derive(Clone)]
294enum ModifierKind {
295 Empty,
296 Single {
297 elements: Elements,
298 inspector: Option<Rc<[InspectorMetadata]>>,
301 },
302}
303
304const FINGERPRINT_KIND_EMPTY: u8 = 0;
305const FINGERPRINT_KIND_SINGLE: u8 = 1;
306
307const FINGERPRINT_EMPTY_STRICT_SEED: u64 = 0x243f_6a88_85a3_08d3;
308const FINGERPRINT_EMPTY_STRUCTURAL_SEED: u64 = 0x1319_8a2e_0370_7344;
309const FINGERPRINT_SINGLE_STRICT_SEED: u64 = 0xa409_3822_299f_31d0;
310const FINGERPRINT_SINGLE_STRUCTURAL_SEED: u64 = 0x082e_fa98_ec4e_6c89;
311const FINGERPRINT_SEQUENCE_MUL: u64 = 0x9e37_79b1_85eb_ca87;
312const FINGERPRINT_STRICT_UPDATE_TAG: u64 = 0xdbe6_d5d5_fe4c_ce2f;
313const FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG: u64 = 0x94d0_49bb_1331_11eb;
314
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316struct ModifierFingerprints {
317 strict: u64,
318 structural: u64,
319}
320
321#[inline]
322fn mix_fingerprint_bits(mut value: u64) -> u64 {
323 value ^= value >> 33;
324 value = value.wrapping_mul(0xff51_afd7_ed55_8ccd);
325 value ^= value >> 33;
326 value = value.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
327 value ^ (value >> 33)
328}
329
330#[inline]
331fn fold_fingerprint(state: u64, value: u64) -> u64 {
332 mix_fingerprint_bits(state ^ value.wrapping_add(FINGERPRINT_SEQUENCE_MUL))
333 .wrapping_mul(FINGERPRINT_SEQUENCE_MUL)
334}
335
336#[inline]
337fn empty_fingerprints() -> ModifierFingerprints {
338 ModifierFingerprints {
339 strict: fold_fingerprint(FINGERPRINT_EMPTY_STRICT_SEED, FINGERPRINT_KIND_EMPTY as u64),
340 structural: fold_fingerprint(
341 FINGERPRINT_EMPTY_STRUCTURAL_SEED,
342 FINGERPRINT_KIND_EMPTY as u64,
343 ),
344 }
345}
346
347#[inline]
348fn single_fingerprint_seed() -> ModifierFingerprints {
349 let strict = fold_fingerprint(
350 FINGERPRINT_SINGLE_STRICT_SEED,
351 FINGERPRINT_KIND_SINGLE as u64,
352 );
353 let structural = fold_fingerprint(
354 FINGERPRINT_SINGLE_STRUCTURAL_SEED,
355 FINGERPRINT_KIND_SINGLE as u64,
356 );
357 ModifierFingerprints { strict, structural }
358}
359
360#[inline]
361fn element_common_fingerprint(element: &DynModifierElement) -> u64 {
362 let mut hasher = default::new();
363 element.element_type().hash(&mut hasher);
364 element.capabilities().bits().hash(&mut hasher);
365 hasher.finish()
366}
367
368#[inline]
369fn element_fingerprints(element: &DynModifierElement) -> ModifierFingerprints {
370 let common = element_common_fingerprint(element);
371 let requires_update = element.requires_update();
372 let strict_payload = if requires_update {
373 let element_ptr = Rc::as_ptr(element) as *const () as usize as u64;
374 element_ptr ^ FINGERPRINT_STRICT_UPDATE_TAG
375 } else {
376 element.hash_code()
377 };
378 let strict = mix_fingerprint_bits(common ^ strict_payload);
379
380 let is_draw_only = element.capabilities() == NodeCapabilities::DRAW;
381 let structural_payload = if is_draw_only {
382 FINGERPRINT_STRUCTURAL_DRAW_ONLY_TAG
383 } else {
384 element.hash_code()
385 };
386 let structural = mix_fingerprint_bits(common ^ structural_payload);
387
388 ModifierFingerprints { strict, structural }
389}
390
391#[inline]
392fn append_fingerprints(
393 mut fingerprints: ModifierFingerprints,
394 elements: &[DynModifierElement],
395) -> ModifierFingerprints {
396 for element in elements {
397 let element_fingerprints = element_fingerprints(element);
398 fingerprints.strict = fold_fingerprint(fingerprints.strict, element_fingerprints.strict);
399 fingerprints.structural =
400 fold_fingerprint(fingerprints.structural, element_fingerprints.structural);
401 }
402 fingerprints
403}
404
405fn single_fingerprints(elements: &[DynModifierElement]) -> ModifierFingerprints {
406 append_fingerprints(single_fingerprint_seed(), elements)
407}
408
409pub struct ModifierElementIterator<'a> {
410 inner: std::slice::Iter<'a, DynModifierElement>,
411}
412
413impl<'a> Iterator for ModifierElementIterator<'a> {
414 type Item = &'a DynModifierElement;
415
416 #[inline]
417 fn next(&mut self) -> Option<Self::Item> {
418 self.inner.next()
419 }
420
421 #[inline]
422 fn size_hint(&self) -> (usize, Option<usize>) {
423 self.inner.size_hint()
424 }
425}
426
427impl ExactSizeIterator for ModifierElementIterator<'_> {}
428
429pub(crate) struct ModifierInspectorIterator<'a> {
430 inner: std::slice::Iter<'a, InspectorMetadata>,
431}
432
433impl<'a> Iterator for ModifierInspectorIterator<'a> {
434 type Item = &'a InspectorMetadata;
435
436 #[inline]
437 fn next(&mut self) -> Option<Self::Item> {
438 self.inner.next()
439 }
440
441 #[inline]
442 fn size_hint(&self) -> (usize, Option<usize>) {
443 self.inner.size_hint()
444 }
445}
446
447impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
448
449#[derive(Clone)]
467pub struct Modifier {
468 kind: ModifierKind,
469 strict_fingerprint: u64,
470 structural_fingerprint: u64,
471 element_count: usize,
472 provides_composition_locals: bool,
473}
474
475impl Default for Modifier {
476 fn default() -> Self {
477 let fingerprints = empty_fingerprints();
478 Self {
479 kind: ModifierKind::Empty,
480 strict_fingerprint: fingerprints.strict,
481 structural_fingerprint: fingerprints.structural,
482 element_count: 0,
483 provides_composition_locals: false,
484 }
485 }
486}
487
488impl Modifier {
489 pub fn empty() -> Self {
490 Self::default()
491 }
492
493 pub fn from_element<E>(element: E) -> Self
495 where
496 E: ModifierNodeElement,
497 {
498 Self::with_element(element)
499 }
500
501 pub fn clip_to_bounds(self) -> Self {
505 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
506 inspector_metadata("clipToBounds", |info| {
507 info.add_property("clipToBounds", "true");
508 }),
509 );
510 self.then(modifier)
511 }
512
513 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
514 where
515 T: 'static,
516 F: Fn() -> T + 'static,
517 {
518 let element = ModifierLocalProviderElement::new(key, value);
519 let modifier = Modifier::from_parts(&[modifier_element(element)]);
520 self.then(modifier)
521 }
522
523 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
524 where
525 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
526 {
527 let element = ModifierLocalConsumerElement::new(consumer);
528 let modifier = Modifier::from_parts(&[modifier_element(element)]);
529 self.then(modifier)
530 }
531
532 pub fn semantics_spec(self, spec: cranpose_foundation::SemanticsSpec) -> Self {
543 self.stable_semantics(move |config: &mut SemanticsConfiguration| config.merge(&spec))
544 }
545
546 pub fn semantics<F>(self, recorder: F) -> Self
551 where
552 F: Fn(&mut SemanticsConfiguration) + 'static,
553 {
554 self.semantics_recorder(std::rc::Rc::new(recorder), true)
555 }
556
557 pub fn stable_semantics<F>(self, recorder: F) -> Self
564 where
565 F: Fn(&mut SemanticsConfiguration) + 'static,
566 {
567 self.semantics_recorder(std::rc::Rc::new(recorder), false)
568 }
569
570 fn semantics_recorder(
571 self,
572 recorder: std::rc::Rc<dyn Fn(&mut SemanticsConfiguration)>,
573 reads_live_state: bool,
574 ) -> Self {
575 let metadata = if inspector_metadata_enabled() {
576 let mut preview = SemanticsConfiguration::default();
577 recorder(&mut preview);
578 let description = preview.content_description.clone();
579 let state_description = preview.state_description.clone();
580 let role = preview.role;
581 let is_clickable = preview.is_activatable();
582 let canvas_children = preview.canvas_children.len();
583 inspector_metadata("semantics", move |info| {
584 if let Some(desc) = &description {
585 info.add_property("contentDescription", desc.clone());
586 }
587 if let Some(state) = &state_description {
588 info.add_property("stateDescription", state.clone());
589 }
590 if let Some(role) = role {
591 info.add_property("role", format!("{role:?}"));
592 }
593 if is_clickable {
594 info.add_property("isClickable", "true");
595 }
596 if canvas_children > 0 {
597 info.add_property("canvasSemanticsChildren", canvas_children.to_string());
598 }
599 })
600 } else {
601 inspector_metadata("semantics", |_| {})
602 };
603 let element = SemanticsElement::new(recorder, reads_live_state);
604 let modifier =
605 Modifier::from_parts(&[modifier_element(element)]).with_inspector_metadata(metadata);
606 self.then(modifier)
607 }
608
609 pub fn progress_semantics(self, current: f32, start: f32, end: f32, steps: u32) -> Self {
618 let info = ProgressBarRangeInfo::new(current, start, end, steps);
619 self.stable_semantics(move |config| config.progress = Some(info))
620 }
621
622 pub fn dropdown_list(self) -> Self {
626 self.role(cranpose_foundation::SemanticsWidgetRole::DropdownList)
627 }
628
629 pub fn value_picker(self) -> Self {
632 self.role(cranpose_foundation::SemanticsWidgetRole::ValuePicker)
633 }
634
635 pub fn expand(self, action: impl Fn() -> bool + 'static) -> Self {
640 let action = cranpose_foundation::SemanticsExpand::new(action);
641 self.stable_semantics(move |config| config.expand = Some(action.clone()))
642 }
643
644 pub fn on_long_click(
654 self,
655 label: impl Into<String>,
656 action: impl Fn() -> bool + 'static,
657 ) -> Self {
658 let label = label.into();
659 let action = cranpose_foundation::SemanticsLongClick::new(action);
660 self.stable_semantics(move |config| {
661 config.on_long_click_label = Some(label.clone());
662 config.on_long_click = Some(action.clone());
663 })
664 }
665
666 pub fn on_magic_tap(
671 self,
672 label: impl Into<String>,
673 action: impl Fn() -> bool + 'static,
674 ) -> Self {
675 let label = label.into();
676 let action = cranpose_foundation::SemanticsMagicTap::new(action);
677 self.stable_semantics(move |config| {
678 config.on_magic_tap_label = Some(label.clone());
679 config.on_magic_tap = Some(action.clone());
680 })
681 }
682
683 pub fn input_labels<S: Into<String>>(self, labels: impl IntoIterator<Item = S>) -> Self {
687 let labels: Vec<String> = labels.into_iter().map(Into::into).collect();
688 self.stable_semantics(move |config| config.input_labels.clone_from(&labels))
689 }
690
691 pub fn language(self, tag: impl Into<String>) -> Self {
695 let tag = tag.into();
696 self.stable_semantics(move |config| config.language = Some(tag.clone()))
697 }
698
699 pub fn collapse(self, action: impl Fn() -> bool + 'static) -> Self {
703 let action = cranpose_foundation::SemanticsExpand::new(action);
704 self.stable_semantics(move |config| config.collapse = Some(action.clone()))
705 }
706
707 pub fn dismiss(self, action: impl Fn() -> bool + 'static) -> Self {
712 let action = cranpose_foundation::SemanticsDismiss::new(action);
713 self.stable_semantics(move |config| config.dismiss = Some(action.clone()))
714 }
715
716 pub fn scroll_to_index(self, action: impl Fn(usize) -> bool + 'static) -> Self {
722 let action = cranpose_foundation::SemanticsScrollToIndex::new(action);
723 self.stable_semantics(move |config| config.scroll_to_index = Some(action.clone()))
724 }
725
726 pub fn traversal_index(self, index: f32) -> Self {
732 self.stable_semantics(move |config| config.traversal_index = index)
733 }
734
735 pub fn password(self) -> Self {
740 self.stable_semantics(|config| config.password = true)
741 }
742
743 pub fn error(self, message: impl Into<String>) -> Self {
747 let message = message.into();
748 self.stable_semantics(move |config| config.error = Some(message.clone()))
749 }
750
751 pub fn pane_title(self, title: impl Into<String>) -> Self {
755 let title = title.into();
756 self.stable_semantics(move |config| config.pane_title = Some(title.clone()))
757 }
758
759 pub fn selectable_group(self) -> Self {
764 self.stable_semantics(|config| config.selectable_group = true)
765 }
766
767 pub fn merge_descendants(self) -> Self {
772 self.stable_semantics(|config| config.merge_descendants = true)
773 }
774
775 pub fn hide_from_accessibility(self) -> Self {
780 self.stable_semantics(|config| config.hidden = true)
781 }
782
783 pub fn heading(self) -> Self {
788 self.role(cranpose_foundation::SemanticsWidgetRole::Header)
789 }
790
791 pub fn role(self, role: cranpose_foundation::SemanticsWidgetRole) -> Self {
796 self.stable_semantics(move |config| config.role = Some(role))
797 }
798
799 pub fn live_region(self, mode: cranpose_foundation::LiveRegionMode) -> Self {
805 self.stable_semantics(move |config| config.live_region = Some(mode))
806 }
807
808 pub fn content_description(self, description: impl Into<String>) -> Self {
813 let description = description.into();
814 self.stable_semantics(move |config| config.content_description = Some(description.clone()))
815 }
816
817 pub fn focus_target(self) -> Self {
823 let element = FocusTargetElement::new();
824 let modifier = Modifier::from_parts(&[modifier_element(element)]);
825 self.then(modifier)
826 }
827
828 pub fn on_focus_changed<F>(self, callback: F) -> Self
833 where
834 F: Fn(FocusState) + 'static,
835 {
836 let element = FocusTargetElement::with_callback(callback);
837 let modifier = Modifier::from_parts(&[modifier_element(element)]);
838 self.then(modifier)
839 }
840
841 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
848 let element = FocusRequesterElement::new(requester.clone());
849 let modifier = Modifier::from_parts(&[modifier_element(element)]);
850 self.then(modifier)
851 }
852
853 pub fn semantics_requester(self, requester: &SemanticsRequester) -> Self {
861 let element = SemanticsRequesterElement::new(requester.clone());
862 let modifier = Modifier::from_parts(&[modifier_element(element)]);
863 self.then(modifier)
864 }
865
866 pub fn debug_chain(self, tag: &'static str) -> Self {
884 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
885
886 #[derive(Clone)]
887 struct DebugChainElement {
888 tag: &'static str,
889 }
890
891 impl fmt::Debug for DebugChainElement {
892 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
893 f.debug_struct("DebugChainElement")
894 .field("tag", &self.tag)
895 .finish()
896 }
897 }
898
899 impl PartialEq for DebugChainElement {
900 fn eq(&self, other: &Self) -> bool {
901 self.tag == other.tag
902 }
903 }
904
905 impl Eq for DebugChainElement {}
906
907 impl std::hash::Hash for DebugChainElement {
908 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
909 self.tag.hash(state);
910 }
911 }
912
913 impl ModifierNodeElement for DebugChainElement {
914 type Node = DebugChainNode;
915
916 fn create(&self) -> Self::Node {
917 DebugChainNode::new(self.tag)
918 }
919
920 fn update(&self, node: &mut Self::Node) {
921 node.tag = self.tag;
922 }
923
924 fn capabilities(&self) -> NodeCapabilities {
925 NodeCapabilities::empty()
926 }
927 }
928
929 struct DebugChainNode {
930 tag: &'static str,
931 state: NodeState,
932 }
933
934 impl DebugChainNode {
935 fn new(tag: &'static str) -> Self {
936 Self {
937 tag,
938 state: NodeState::new(),
939 }
940 }
941 }
942
943 impl ModifierNode for DebugChainNode {
944 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
945 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
946 }
947
948 fn on_detach(&mut self) {
949 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
950 }
951
952 fn on_reset(&mut self) {
953 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
954 }
955 }
956
957 impl cranpose_foundation::DelegatableNode for DebugChainNode {
958 fn node_state(&self) -> &NodeState {
959 &self.state
960 }
961 }
962
963 let element = DebugChainElement { tag };
964 let modifier = Modifier::from_parts(&[modifier_element(element)]);
965 self.then(modifier)
966 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
967 info.add_property("tag", tag);
968 }))
969 }
970
971 pub fn then(self, next: Modifier) -> Modifier {
977 let Modifier {
978 kind,
979 strict_fingerprint,
980 structural_fingerprint,
981 element_count,
982 provides_composition_locals,
983 } = self;
984 let ModifierKind::Single {
985 mut elements,
986 inspector,
987 } = kind
988 else {
989 return next;
990 };
991 let ModifierKind::Single {
992 elements: next_elements,
993 inspector: next_inspector,
994 } = &next.kind
995 else {
996 return Modifier {
997 kind: ModifierKind::Single {
998 elements,
999 inspector,
1000 },
1001 strict_fingerprint,
1002 structural_fingerprint,
1003 element_count,
1004 provides_composition_locals,
1005 };
1006 };
1007
1008 let fingerprints = append_fingerprints(
1009 ModifierFingerprints {
1010 strict: strict_fingerprint,
1011 structural: structural_fingerprint,
1012 },
1013 next_elements,
1014 );
1015 elements.extend_from(next_elements);
1016 let inspector = if next_inspector.is_none() {
1017 inspector
1018 } else {
1019 merged_inspector(inspector_slice(&inspector), inspector_slice(next_inspector))
1020 };
1021 Modifier {
1022 kind: ModifierKind::Single {
1023 elements,
1024 inspector,
1025 },
1026 strict_fingerprint: fingerprints.strict,
1027 structural_fingerprint: fingerprints.structural,
1028 element_count: element_count + next.element_count,
1029 provides_composition_locals: provides_composition_locals
1030 || next.provides_composition_locals,
1031 }
1032 }
1033
1034 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
1035 match &self.kind {
1036 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
1037 ModifierKind::Single { elements, .. } => ModifierElementIterator {
1038 inner: elements.iter(),
1039 },
1040 }
1041 }
1042
1043 pub(crate) fn provided_composition_locals(&self) -> Vec<ProvidedValue> {
1044 if !self.provides_composition_locals {
1045 return Vec::new();
1046 }
1047 self.iter_elements()
1048 .flat_map(|element| element.provided_composition_locals())
1049 .collect()
1050 }
1051
1052 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
1053 match &self.kind {
1054 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
1055 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
1056 inner: inspector_slice(inspector).iter(),
1057 },
1058 }
1059 }
1060
1061 #[cfg(test)]
1062 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
1063 match &self.kind {
1064 ModifierKind::Empty => Vec::new(),
1065 ModifierKind::Single { elements, .. } => elements.to_vec(),
1066 }
1067 }
1068
1069 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
1070 match &self.kind {
1071 ModifierKind::Empty => Vec::new(),
1072 ModifierKind::Single { inspector, .. } => {
1073 inspector.as_deref().map(<[_]>::to_vec).unwrap_or_default()
1074 }
1075 }
1076 }
1077
1078 pub(crate) fn rehouse_for_live_compaction(&self) -> Self {
1079 match &self.kind {
1080 ModifierKind::Empty => Self::default(),
1081 ModifierKind::Single {
1082 elements,
1083 inspector,
1084 } => Self {
1085 kind: ModifierKind::Single {
1086 elements: Elements::from_slice(elements),
1087 inspector: inspector
1088 .as_ref()
1089 .map(|inspector| inspector.iter().cloned().collect()),
1090 },
1091 strict_fingerprint: self.strict_fingerprint,
1092 structural_fingerprint: self.structural_fingerprint,
1093 element_count: self.element_count,
1094 provides_composition_locals: self.provides_composition_locals,
1095 },
1096 }
1097 }
1098
1099 pub fn total_padding(&self) -> f32 {
1100 let padding = self.padding_values();
1101 padding
1102 .left
1103 .max(padding.right)
1104 .max(padding.top)
1105 .max(padding.bottom)
1106 }
1107
1108 pub fn explicit_size(&self) -> Option<Size> {
1109 let props = self.layout_properties();
1110 match (props.width, props.height) {
1111 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
1112 Some(Size { width, height })
1113 }
1114 _ => None,
1115 }
1116 }
1117
1118 pub fn padding_values(&self) -> EdgeInsets {
1119 self.resolved_modifiers().padding()
1120 }
1121
1122 pub(crate) fn layout_properties(&self) -> LayoutProperties {
1123 self.resolved_modifiers().layout_properties()
1124 }
1125
1126 pub fn box_alignment(&self) -> Option<Alignment> {
1127 self.layout_properties().box_alignment()
1128 }
1129
1130 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1131 self.layout_properties().column_alignment()
1132 }
1133
1134 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1135 self.layout_properties().row_alignment()
1136 }
1137
1138 pub fn draw_commands(&self) -> Vec<DrawCommand> {
1139 collect_slices_from_modifier(self).draw_commands().to_vec()
1140 }
1141
1142 pub fn clips_to_bounds(&self) -> bool {
1143 collect_slices_from_modifier(self).clip_to_bounds()
1144 }
1145
1146 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
1148 self.inspector_metadata()
1149 .iter()
1150 .map(InspectorMetadata::to_record)
1151 .collect()
1152 }
1153
1154 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1155 let mut handle = ModifierChainHandle::new();
1156 let _ = handle.update(self);
1157 handle.resolved_modifiers()
1158 }
1159
1160 pub fn with_element<E>(element: E) -> Self
1163 where
1164 E: ModifierNodeElement,
1165 {
1166 Self::from_parts(&[modifier_element(element)])
1167 }
1168
1169 pub(crate) fn from_parts(elements: &[DynModifierElement]) -> Self {
1171 if elements.is_empty() {
1172 Self::default()
1173 } else {
1174 let element_count = elements.len();
1175 let provides_composition_locals = elements
1176 .iter()
1177 .any(|element| element.provides_composition_locals());
1178 let fingerprints = single_fingerprints(elements);
1179 Self {
1180 kind: ModifierKind::Single {
1181 elements: Elements::from_slice(elements),
1182 inspector: None,
1183 },
1184 strict_fingerprint: fingerprints.strict,
1185 structural_fingerprint: fingerprints.structural,
1186 element_count,
1187 provides_composition_locals,
1188 }
1189 }
1190 }
1191
1192 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
1193 if metadata.is_empty() {
1194 return self;
1195 }
1196 match self.kind {
1197 ModifierKind::Empty => self,
1198 ModifierKind::Single {
1199 elements,
1200 inspector,
1201 } => {
1202 let new_inspector = inspector_slice(&inspector)
1203 .iter()
1204 .cloned()
1205 .chain(std::iter::once(metadata))
1206 .collect();
1207 Self {
1208 kind: ModifierKind::Single {
1209 elements,
1210 inspector: Some(new_inspector),
1211 },
1212 strict_fingerprint: self.strict_fingerprint,
1213 structural_fingerprint: self.structural_fingerprint,
1214 element_count: self.element_count,
1215 provides_composition_locals: self.provides_composition_locals,
1216 }
1217 }
1218 }
1219 }
1220
1221 pub fn structural_eq(&self, other: &Self) -> bool {
1226 self.eq_internal(other, false)
1227 }
1228
1229 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
1230 if self.element_count != other.element_count {
1231 return false;
1232 }
1233 if consider_always_update {
1234 if self.strict_fingerprint != other.strict_fingerprint {
1235 return false;
1236 }
1237 } else if self.structural_fingerprint != other.structural_fingerprint {
1238 return false;
1239 }
1240
1241 match (&self.kind, &other.kind) {
1242 (ModifierKind::Empty, ModifierKind::Empty) => true,
1243 (
1244 ModifierKind::Single {
1245 elements: e1,
1246 inspector: _,
1247 },
1248 ModifierKind::Single {
1249 elements: e2,
1250 inspector: _,
1251 },
1252 ) => {
1253 if e1.shares_storage(e2) {
1254 return true;
1255 }
1256
1257 if e1.len() != e2.len() {
1258 return false;
1259 }
1260
1261 for (a, b) in e1.iter().zip(e2.iter()) {
1262 if !consider_always_update
1263 && a.element_type() == b.element_type()
1264 && a.capabilities() == NodeCapabilities::DRAW
1265 && b.capabilities() == NodeCapabilities::DRAW
1266 {
1267 continue;
1268 }
1269
1270 if consider_always_update && (a.requires_update() || b.requires_update()) {
1271 if !Rc::ptr_eq(a, b) {
1272 return false;
1273 }
1274 continue;
1275 }
1276
1277 if !a.equals_element(&**b) {
1278 return false;
1279 }
1280 }
1281
1282 true
1283 }
1284 _ => false,
1285 }
1286 }
1287}
1288
1289impl PartialEq for Modifier {
1290 fn eq(&self, other: &Self) -> bool {
1291 self.eq_internal(other, true)
1292 }
1293}
1294
1295impl Eq for Modifier {}
1296
1297impl fmt::Display for Modifier {
1298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1299 match &self.kind {
1300 ModifierKind::Empty => write!(f, "Modifier.empty"),
1301 ModifierKind::Single { elements, .. } => {
1302 if elements.is_empty() {
1303 return write!(f, "Modifier.empty");
1304 }
1305 write!(f, "Modifier[")?;
1306 for (index, element) in elements.iter().enumerate() {
1307 if index > 0 {
1308 write!(f, ", ")?;
1309 }
1310 let name = element.inspector_name();
1311 let mut properties = Vec::new();
1312 element.record_inspector_properties(&mut |prop, value| {
1313 properties.push(format!("{prop}={value}"));
1314 });
1315 if properties.is_empty() {
1316 write!(f, "{name}")?;
1317 } else {
1318 write!(f, "{name}({})", properties.join(", "))?;
1319 }
1320 }
1321 write!(f, "]")
1322 }
1323 }
1324 }
1325}
1326
1327impl fmt::Debug for Modifier {
1328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1329 fmt::Display::fmt(self, f)
1330 }
1331}
1332
1333#[derive(Clone, Copy, Debug, PartialEq)]
1334pub struct ResolvedBackground {
1335 color: Color,
1336 shape: Option<RoundedCornerShape>,
1337}
1338
1339impl ResolvedBackground {
1340 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
1341 Self { color, shape }
1342 }
1343
1344 pub fn color(&self) -> Color {
1345 self.color
1346 }
1347
1348 pub fn shape(&self) -> Option<RoundedCornerShape> {
1349 self.shape
1350 }
1351
1352 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
1353 self.shape = shape;
1354 }
1355}
1356
1357#[derive(Clone, Copy, Debug, PartialEq, Default)]
1358pub struct ResolvedModifiers {
1359 padding: EdgeInsets,
1360 layout: LayoutProperties,
1361 offset: Point,
1362}
1363
1364impl ResolvedModifiers {
1365 pub fn padding(&self) -> EdgeInsets {
1366 self.padding
1367 }
1368
1369 pub fn layout_properties(&self) -> LayoutProperties {
1370 self.layout
1371 }
1372
1373 pub fn offset(&self) -> Point {
1374 self.offset
1375 }
1376
1377 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
1378 self.padding = padding;
1379 }
1380
1381 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
1382 self.layout = layout;
1383 }
1384
1385 pub(crate) fn set_offset(&mut self, offset: Point) {
1386 self.offset = offset;
1387 }
1388
1389 pub(crate) fn on_device_grid(&self, density: f32) -> Self {
1392 use cranpose_ui_layout::round_to_px;
1393
1394 let round = |value: Option<f32>| value.map(|value| round_to_px(value, density));
1395 let dimension = |dimension: DimensionConstraint| match dimension {
1396 DimensionConstraint::Points(value) => {
1397 DimensionConstraint::Points(round_to_px(value, density))
1398 }
1399 other => other,
1400 };
1401 Self {
1402 padding: crate::modifier_nodes::device_padding(self.padding, density),
1403 layout: LayoutProperties {
1404 padding: crate::modifier_nodes::device_padding(self.layout.padding, density),
1405 width: dimension(self.layout.width),
1406 height: dimension(self.layout.height),
1407 min_width: round(self.layout.min_width),
1408 min_height: round(self.layout.min_height),
1409 max_width: round(self.layout.max_width),
1410 max_height: round(self.layout.max_height),
1411 ..self.layout
1412 },
1413 offset: Point {
1414 x: round_to_px(self.offset.x, density),
1415 y: round_to_px(self.offset.y, density),
1416 },
1417 }
1418 }
1419}
1420
1421#[derive(Clone, Copy, Debug, Default, PartialEq)]
1422pub enum DimensionConstraint {
1423 #[default]
1424 Unspecified,
1425 Points(f32),
1426 Fraction(f32),
1427 Intrinsic(IntrinsicSize),
1428}
1429
1430#[derive(Clone, Copy, Debug, Default, PartialEq)]
1431pub struct LayoutWeight {
1432 pub weight: f32,
1433 pub fill: bool,
1434}
1435
1436#[derive(Clone, Copy, Debug, Default, PartialEq)]
1437pub struct LayoutProperties {
1438 padding: EdgeInsets,
1439 width: DimensionConstraint,
1440 height: DimensionConstraint,
1441 min_width: Option<f32>,
1442 min_height: Option<f32>,
1443 max_width: Option<f32>,
1444 max_height: Option<f32>,
1445 weight: Option<LayoutWeight>,
1446 box_alignment: Option<Alignment>,
1447 column_alignment: Option<HorizontalAlignment>,
1448 row_alignment: Option<VerticalAlignment>,
1449}
1450
1451impl LayoutProperties {
1452 pub fn padding(&self) -> EdgeInsets {
1453 self.padding
1454 }
1455
1456 pub fn width(&self) -> DimensionConstraint {
1457 self.width
1458 }
1459
1460 pub fn height(&self) -> DimensionConstraint {
1461 self.height
1462 }
1463
1464 pub fn min_width(&self) -> Option<f32> {
1465 self.min_width
1466 }
1467
1468 pub fn min_height(&self) -> Option<f32> {
1469 self.min_height
1470 }
1471
1472 pub fn max_width(&self) -> Option<f32> {
1473 self.max_width
1474 }
1475
1476 pub fn max_height(&self) -> Option<f32> {
1477 self.max_height
1478 }
1479
1480 pub fn weight(&self) -> Option<LayoutWeight> {
1481 self.weight
1482 }
1483
1484 pub fn box_alignment(&self) -> Option<Alignment> {
1485 self.box_alignment
1486 }
1487
1488 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1489 self.column_alignment
1490 }
1491
1492 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1493 self.row_alignment
1494 }
1495}
1496
1497#[cfg(test)]
1498#[path = "tests/modifier_tests.rs"]
1499mod tests;