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