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