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