1use std::{fmt, rc::Rc};
9
10use cranpose_core::ProvidedValue;
11use smallvec::SmallVec;
12
13mod alignment;
14mod background;
15mod blur;
16mod border;
17mod chain;
18mod clickable;
19mod coordinator_rect;
20mod drag_and_drop;
21mod draw_cache;
22mod fill;
23pub(crate) mod focus;
24mod focus_ring;
25mod graphics_layer;
26mod key_input;
27mod layout_data;
28mod local;
29mod long_press;
30mod minimum_interactive;
31mod offset;
32mod padding;
33mod pointer_icon;
34pub(crate) mod pointer_input;
35mod pointer_opaque;
36mod resolved_modifiers;
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;
48mod z_index;
49
50pub(crate) use chain::{
51 InPlaceReach, InPlaceUpdate, ModifierChainHandle, ModifierChainInspectorNode,
52 ModifierLocalsHandle,
53};
54pub(crate) use coordinator_rect::CoordinatorRect;
55use cranpose_foundation::{
56 DynModifierElement, ModifierNodeElement, NodeCapabilities, ProgressBarRangeInfo,
57 SemanticsConfiguration, modifier_element,
58};
59pub use cranpose_foundation::{
60 PointerButton, PointerButtons, PointerEvent, PointerEventKind, PointerSource, RotaryScrollEvent,
61};
62pub use cranpose_ui_graphics::{
63 BlendMode, Brush, Color, CompositingStrategy, CornerRadii, DpOffset, EdgeInsets, GraphicsLayer,
64 LayerShape, Point, Rect, RenderEffect, RoundedCornerShape, RuntimeShader, Shadow, ShadowScope,
65 Size, TransformOrigin,
66};
67use cranpose_ui_layout::{Alignment, HorizontalAlignment, VerticalAlignment};
68pub use drag_and_drop::{
69 DragAndDropEvent, DragAndDropOutcome, DragAndDropPayload, DragAndDropPoint, DragAndDropSource,
70 DragAndDropSourceElement, DragAndDropSourceNode, DragAndDropState, DragAndDropTarget,
71 DragAndDropTargetElement, DragAndDropTargetNode,
72};
73#[cfg(test)]
74pub(crate) use focus::FocusRequesterElement;
75pub use focus::{FocusDirection, FocusRequestError, FocusRequester};
76pub use graphics_layer::GlassMaterial;
77pub use key_input::KeyEventRoute;
78pub(crate) use local::{
79 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
80};
81use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
82pub use local::{ModifierLocalKey, ModifierLocalReadScope};
83pub use pointer_input::PointerInputScope;
84pub(crate) use resolved_modifiers::{DimensionConstraint, LayoutProperties, LayoutWeight};
85pub use resolved_modifiers::{ResolvedBackground, ResolvedModifiers};
86pub use rotary_input::RotaryInputModifierNode;
87#[cfg(feature = "test-helpers")]
88pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
89use semantics::SemanticsElement;
90pub use semantics::{SemanticsRequester, collect_semantics_from_modifier};
91pub(crate) use semantics::{
92 SemanticsRequesterElement, collect_semantics_from_chain, semantics_reach_of_chain,
93};
94pub(crate) use slices::SlicesSnapshot;
95pub use slices::{
96 ModifierNodeSlices, ModifierNodeSlicesDebugStats, SliceTextRef, collect_modifier_slices,
97 collect_slices_from_modifier,
98};
99pub use window_root::{
100 WindowRootDescriptor, WindowRootElement, WindowRootEntry, WindowRootNode, WindowRootRegistry,
101 WindowRootRoutingScratch, is_window_root, nearest_window_root, nearest_window_roots,
102 nearest_window_roots_into, window_roots, window_roots_revision,
103};
104pub use z_index::{z_index_in_use, z_index_modifier_in_use};
105
106pub(crate) use crate::draw::{DrawCacheBuilder, DrawCommand};
107use crate::modifier_nodes::ClipToBoundsElement;
108
109#[derive(Clone, Debug, Default)]
110pub(crate) struct InspectorInfo {
111 properties: Vec<InspectorProperty>,
112}
113
114impl InspectorInfo {
115 pub(crate) fn new() -> Self {
116 Self::default()
117 }
118
119 pub(crate) fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
120 self.properties.push(InspectorProperty {
121 name,
122 value: value.into(),
123 });
124 }
125
126 pub(crate) fn properties(&self) -> &[InspectorProperty] {
127 &self.properties
128 }
129
130 pub(crate) fn is_empty(&self) -> bool {
131 self.properties.is_empty()
132 }
133
134 pub(crate) fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
135 self.add_property(name, describe_dimension(constraint));
136 }
137
138 pub(crate) fn add_offset_components(
139 &mut self,
140 x_name: &'static str,
141 y_name: &'static str,
142 offset: Point,
143 ) {
144 self.add_property(x_name, offset.x.to_string());
145 self.add_property(y_name, offset.y.to_string());
146 }
147
148 pub(crate) fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
149 where
150 A: fmt::Debug,
151 {
152 self.add_property(name, format!("{alignment:?}"));
153 }
154}
155
156#[derive(Clone, Debug, PartialEq)]
157pub struct InspectorProperty {
158 pub name: &'static str,
159 pub value: String,
160}
161
162#[derive(Clone, Debug, PartialEq)]
163pub struct ModifierInspectorRecord {
164 pub name: &'static str,
165 pub properties: Vec<InspectorProperty>,
166}
167
168#[derive(Clone, Debug)]
169pub(crate) struct InspectorMetadata {
170 name: &'static str,
171 info: InspectorInfo,
172}
173
174impl InspectorMetadata {
175 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
176 where
177 F: FnOnce(&mut InspectorInfo),
178 {
179 let mut info = InspectorInfo::new();
180 recorder(&mut info);
181 Self { name, info }
182 }
183
184 fn is_empty(&self) -> bool {
185 self.info.is_empty()
186 }
187
188 fn to_record(&self) -> ModifierInspectorRecord {
189 ModifierInspectorRecord {
190 name: self.name,
191 properties: self.info.properties().to_vec(),
192 }
193 }
194}
195
196fn describe_dimension(constraint: DimensionConstraint) -> String {
197 match constraint {
198 DimensionConstraint::Unspecified => "unspecified".to_string(),
199 DimensionConstraint::Points(value) => value.to_string(),
200 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
201 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
202 }
203}
204
205fn inspector_slice(inspector: &Option<Rc<[InspectorMetadata]>>) -> &[InspectorMetadata] {
206 inspector.as_deref().unwrap_or(&[])
207}
208
209fn merged_inspector(
212 first: &[InspectorMetadata],
213 second: &[InspectorMetadata],
214) -> Option<Rc<[InspectorMetadata]>> {
215 if first.is_empty() && second.is_empty() {
216 return None;
217 }
218 Some(first.iter().chain(second).cloned().collect())
219}
220
221fn same_element(a: &DynModifierElement, b: &DynModifierElement) -> bool {
224 Rc::ptr_eq(a, b) || a.equals_element(&**b)
225}
226
227pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
228where
229 F: FnOnce(&mut InspectorInfo),
230{
231 if !inspector_metadata_enabled() {
232 return InspectorMetadata::new(name, |_| {});
233 }
234 InspectorMetadata::new(name, recorder)
235}
236
237pub(crate) fn modifier_debug_enabled() -> bool {
238 #[cfg(not(target_arch = "wasm32"))]
239 {
240 cranpose_core::DIAGNOSTICS.compose_debug_modifiers.is_set()
241 }
242 #[cfg(target_arch = "wasm32")]
243 {
244 false
245 }
246}
247
248fn inspector_metadata_enabled() -> bool {
249 cfg!(any(test, feature = "inspection")) || modifier_debug_enabled()
250}
251
252type ModifierElements = SmallVec<[DynModifierElement; 4]>;
255
256#[derive(Clone)]
261enum Elements {
262 One(DynModifierElement),
263 Many(Rc<ModifierElements>),
264}
265
266impl Elements {
267 fn joined(self, more: Self) -> Self {
270 let mut chain = match self {
271 Self::One(first) => {
272 let mut chain = ModifierElements::new();
273 chain.push(first);
274 Rc::new(chain)
275 }
276 Self::Many(chain) => chain,
277 };
278 match Rc::get_mut(&mut chain) {
279 Some(owned) => more.move_into(owned),
280 None => {
281 let mut copy: ModifierElements = chain.iter().cloned().collect();
282 more.move_into(&mut copy);
283 chain = Rc::new(copy);
284 }
285 }
286 Self::Many(chain)
287 }
288
289 fn move_into(self, chain: &mut ModifierElements) {
290 match self {
291 Self::One(element) => chain.push(element),
292 Self::Many(shared) => match Rc::try_unwrap(shared) {
293 Ok(owned) => chain.extend(owned),
294 Err(shared) => chain.extend(shared.iter().cloned()),
295 },
296 }
297 }
298
299 fn shares_storage(&self, other: &Self) -> bool {
301 match (self, other) {
302 (Self::One(a), Self::One(b)) => Rc::ptr_eq(a, b),
303 (Self::Many(a), Self::Many(b)) => Rc::ptr_eq(a, b),
304 _ => false,
305 }
306 }
307}
308
309impl std::ops::Deref for Elements {
310 type Target = [DynModifierElement];
311
312 fn deref(&self) -> &[DynModifierElement] {
313 match self {
314 Self::One(element) => std::slice::from_ref(element),
315 Self::Many(elements) => elements.as_slice(),
316 }
317 }
318}
319
320#[derive(Clone)]
321enum ModifierKind {
322 Empty,
323 Single {
324 elements: Elements,
325 inspector: Option<Rc<[InspectorMetadata]>>,
328 },
329}
330
331pub(crate) struct ModifierElementIterator<'a> {
332 inner: std::slice::Iter<'a, DynModifierElement>,
333}
334
335impl<'a> Iterator for ModifierElementIterator<'a> {
336 type Item = &'a DynModifierElement;
337
338 #[inline]
339 fn next(&mut self) -> Option<Self::Item> {
340 self.inner.next()
341 }
342
343 #[inline]
344 fn size_hint(&self) -> (usize, Option<usize>) {
345 self.inner.size_hint()
346 }
347}
348
349impl ExactSizeIterator for ModifierElementIterator<'_> {}
350
351pub(crate) struct ModifierInspectorIterator<'a> {
352 inner: std::slice::Iter<'a, InspectorMetadata>,
353}
354
355impl<'a> Iterator for ModifierInspectorIterator<'a> {
356 type Item = &'a InspectorMetadata;
357
358 #[inline]
359 fn next(&mut self) -> Option<Self::Item> {
360 self.inner.next()
361 }
362
363 #[inline]
364 fn size_hint(&self) -> (usize, Option<usize>) {
365 self.inner.size_hint()
366 }
367}
368
369impl ExactSizeIterator for ModifierInspectorIterator<'_> {}
370
371#[derive(Clone)]
389#[must_use = "a modifier does nothing until a composable receives it"]
390pub struct Modifier {
391 kind: ModifierKind,
392 element_count: usize,
393 provides_composition_locals: bool,
394}
395
396impl Default for Modifier {
397 fn default() -> Self {
398 Self {
399 kind: ModifierKind::Empty,
400 element_count: 0,
401 provides_composition_locals: false,
402 }
403 }
404}
405
406impl Modifier {
407 pub fn empty() -> Self {
409 Self::default()
410 }
411
412 pub fn from_element<E>(element: E) -> Self
414 where
415 E: ModifierNodeElement,
416 {
417 Self::with_element(element)
418 }
419
420 pub fn clip_to_bounds(self) -> Self {
424 let modifier = crate::thread_state::with(|state| {
425 state
426 .clip_to_bounds
427 .get_or_init(|| Self::with_element(ClipToBoundsElement::new()))
428 .clone()
429 });
430 let modifier =
431 modifier.with_inspector_metadata(inspector_metadata("clipToBounds", |info| {
432 info.add_property("clipToBounds", "true");
433 }));
434 self.then(modifier)
435 }
436
437 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
440 where
441 T: 'static,
442 F: Fn() -> T + 'static,
443 {
444 let element = ModifierLocalProviderElement::new(key, value);
445 let modifier = Self::from_dyn_element(modifier_element(element));
446 self.then(modifier)
447 }
448
449 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
452 where
453 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
454 {
455 let element = ModifierLocalConsumerElement::new(consumer);
456 let modifier = Self::from_dyn_element(modifier_element(element));
457 self.then(modifier)
458 }
459
460 pub fn semantics_spec(self, spec: cranpose_foundation::SemanticsSpec) -> Self {
471 self.stable_semantics(move |config: &mut SemanticsConfiguration| config.merge(&spec))
472 }
473
474 pub fn semantics<F>(self, recorder: F) -> Self
479 where
480 F: Fn(&mut SemanticsConfiguration) + 'static,
481 {
482 self.semantics_recorder(Rc::new(recorder), true)
483 }
484
485 pub fn stable_semantics<F>(self, recorder: F) -> Self
492 where
493 F: Fn(&mut SemanticsConfiguration) + 'static,
494 {
495 self.semantics_recorder(Rc::new(recorder), false)
496 }
497
498 fn semantics_recorder(
499 self,
500 recorder: Rc<dyn Fn(&mut SemanticsConfiguration)>,
501 reads_live_state: bool,
502 ) -> Self {
503 let metadata = if inspector_metadata_enabled() {
504 let mut preview = SemanticsConfiguration::default();
505 recorder(&mut preview);
506 let description = preview.content_description.clone();
507 let state_description = preview.state_description.clone();
508 let role = preview.role;
509 let is_clickable = preview.is_activatable();
510 let canvas_children = preview.canvas_children.len();
511 inspector_metadata("semantics", move |info| {
512 if let Some(desc) = &description {
513 info.add_property("contentDescription", desc.clone());
514 }
515 if let Some(state) = &state_description {
516 info.add_property("stateDescription", state.clone());
517 }
518 if let Some(role) = role {
519 info.add_property("role", format!("{role:?}"));
520 }
521 if is_clickable {
522 info.add_property("isClickable", "true");
523 }
524 if canvas_children > 0 {
525 info.add_property("canvasSemanticsChildren", canvas_children.to_string());
526 }
527 })
528 } else {
529 inspector_metadata("semantics", |_| {})
530 };
531 let element = SemanticsElement::new(recorder, reads_live_state);
532 let modifier =
533 Self::from_dyn_element(modifier_element(element)).with_inspector_metadata(metadata);
534 self.then(modifier)
535 }
536
537 pub fn progress_semantics(self, current: f32, start: f32, end: f32, steps: u32) -> Self {
546 let info = ProgressBarRangeInfo::new(current, start, end, steps);
547 self.stable_semantics(move |config| config.progress = Some(info))
548 }
549
550 pub fn dropdown_list(self) -> Self {
554 self.role(cranpose_foundation::SemanticsWidgetRole::DropdownList)
555 }
556
557 pub fn value_picker(self) -> Self {
560 self.role(cranpose_foundation::SemanticsWidgetRole::ValuePicker)
561 }
562
563 pub fn expand(self, action: impl Fn() -> bool + 'static) -> Self {
568 let action = cranpose_foundation::SemanticsExpand::new(action);
569 self.stable_semantics(move |config| config.expand = Some(action.clone()))
570 }
571
572 pub fn on_long_click(
582 self,
583 label: impl Into<String>,
584 action: impl Fn() -> bool + 'static,
585 ) -> Self {
586 let label = label.into();
587 let action = cranpose_foundation::SemanticsLongClick::new(action);
588 self.stable_semantics(move |config| {
589 config.on_long_click_label = Some(label.clone());
590 config.on_long_click = Some(action.clone());
591 })
592 }
593
594 pub fn on_magic_tap(
599 self,
600 label: impl Into<String>,
601 action: impl Fn() -> bool + 'static,
602 ) -> Self {
603 let label = label.into();
604 let action = cranpose_foundation::SemanticsMagicTap::new(action);
605 self.stable_semantics(move |config| {
606 config.on_magic_tap_label = Some(label.clone());
607 config.on_magic_tap = Some(action.clone());
608 })
609 }
610
611 pub fn input_labels<S: Into<String>>(self, labels: impl IntoIterator<Item = S>) -> Self {
615 let labels: Vec<String> = labels.into_iter().map(Into::into).collect();
616 self.stable_semantics(move |config| config.input_labels.clone_from(&labels))
617 }
618
619 pub fn language(self, tag: impl Into<String>) -> Self {
623 let tag = tag.into();
624 self.stable_semantics(move |config| config.language = Some(tag.clone()))
625 }
626
627 pub fn collapse(self, action: impl Fn() -> bool + 'static) -> Self {
631 let action = cranpose_foundation::SemanticsExpand::new(action);
632 self.stable_semantics(move |config| config.collapse = Some(action.clone()))
633 }
634
635 pub fn dismiss(self, action: impl Fn() -> bool + 'static) -> Self {
640 let action = cranpose_foundation::SemanticsDismiss::new(action);
641 self.stable_semantics(move |config| config.dismiss = Some(action.clone()))
642 }
643
644 pub fn scroll_to_index(self, action: impl Fn(usize) -> bool + 'static) -> Self {
650 let action = cranpose_foundation::SemanticsScrollToIndex::new(action);
651 self.stable_semantics(move |config| config.scroll_to_index = Some(action.clone()))
652 }
653
654 pub fn traversal_index(self, index: f32) -> Self {
660 self.stable_semantics(move |config| config.traversal_index = index)
661 }
662
663 pub fn password(self) -> Self {
668 self.stable_semantics(|config| config.password = true)
669 }
670
671 pub fn error(self, message: impl Into<String>) -> Self {
675 let message = message.into();
676 self.stable_semantics(move |config| config.error = Some(message.clone()))
677 }
678
679 pub fn hint_text(self, hint: impl Into<String>) -> Self {
683 let hint = hint.into();
684 self.stable_semantics(move |config| config.hint_text = Some(hint.clone()))
685 }
686
687 pub fn pane_title(self, title: impl Into<String>) -> Self {
691 let title = title.into();
692 self.stable_semantics(move |config| config.pane_title = Some(title.clone()))
693 }
694
695 pub fn selectable_group(self) -> Self {
700 self.stable_semantics(|config| config.selectable_group = true)
701 }
702
703 pub fn merge_descendants(self) -> Self {
708 self.stable_semantics(|config| config.merge_descendants = true)
709 }
710
711 pub fn hide_from_accessibility(self) -> Self {
716 self.stable_semantics(|config| config.hidden = true)
717 }
718
719 pub fn heading(self) -> Self {
724 self.role(cranpose_foundation::SemanticsWidgetRole::Header)
725 }
726
727 pub fn role(self, role: cranpose_foundation::SemanticsWidgetRole) -> Self {
732 self.stable_semantics(move |config| config.role = Some(role))
733 }
734
735 pub fn live_region(self, mode: cranpose_foundation::LiveRegionMode) -> Self {
741 self.stable_semantics(move |config| config.live_region = Some(mode))
742 }
743
744 pub fn content_description(self, description: impl Into<String>) -> Self {
749 let description = description.into();
750 self.stable_semantics(move |config| config.content_description = Some(description.clone()))
751 }
752
753 pub fn test_tag(self, tag: impl Into<String>) -> Self {
758 let tag = tag.into();
759 self.stable_semantics(move |config| config.test_tag = Some(tag.clone()))
760 }
761
762 pub fn semantics_requester(self, requester: &SemanticsRequester) -> Self {
770 let element = SemanticsRequesterElement::new(requester.clone());
771 let modifier = Self::from_dyn_element(modifier_element(element));
772 self.then(modifier)
773 }
774
775 pub fn debug_chain(self, tag: &'static str) -> Self {
793 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
794
795 #[derive(Clone)]
796 struct DebugChainElement {
797 tag: &'static str,
798 }
799
800 impl fmt::Debug for DebugChainElement {
801 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802 f.debug_struct("DebugChainElement")
803 .field("tag", &self.tag)
804 .finish()
805 }
806 }
807
808 impl PartialEq for DebugChainElement {
809 fn eq(&self, other: &Self) -> bool {
810 self.tag == other.tag
811 }
812 }
813
814 impl Eq for DebugChainElement {}
815
816 impl std::hash::Hash for DebugChainElement {
817 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
818 self.tag.hash(state);
819 }
820 }
821
822 impl ModifierNodeElement for DebugChainElement {
823 type Node = DebugChainNode;
824
825 fn create(&self) -> Self::Node {
826 DebugChainNode::new(self.tag)
827 }
828
829 fn update(&self, node: &mut Self::Node) {
830 node.tag = self.tag;
831 }
832
833 fn capabilities(&self) -> NodeCapabilities {
834 NodeCapabilities::empty()
835 }
836 }
837
838 struct DebugChainNode {
839 tag: &'static str,
840 state: NodeState,
841 }
842
843 impl DebugChainNode {
844 fn new(tag: &'static str) -> Self {
845 Self {
846 tag,
847 state: NodeState::new(),
848 }
849 }
850 }
851
852 #[expect(clippy::print_stderr, reason = "debug_chain prints its lifecycle")]
853 impl ModifierNode for DebugChainNode {
854 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
855 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
856 }
857
858 fn on_detach(&mut self) {
859 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
860 }
861
862 fn on_reset(&mut self) {
863 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
864 }
865 }
866
867 impl cranpose_foundation::DelegatableNode for DebugChainNode {
868 fn node_state(&self) -> &NodeState {
869 &self.state
870 }
871 }
872
873 let element = DebugChainElement { tag };
874 let modifier = Self::from_dyn_element(modifier_element(element));
875 self.then(modifier)
876 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
877 info.add_property("tag", tag);
878 }))
879 }
880
881 pub fn then(self, next: Self) -> Self {
887 let ModifierKind::Single {
888 elements,
889 inspector,
890 } = self.kind
891 else {
892 return next;
893 };
894 let ModifierKind::Single {
895 elements: next_elements,
896 inspector: next_inspector,
897 } = next.kind
898 else {
899 return Self {
900 kind: ModifierKind::Single {
901 elements,
902 inspector,
903 },
904 ..self
905 };
906 };
907
908 let inspector = if next_inspector.is_none() {
909 inspector
910 } else {
911 merged_inspector(
912 inspector_slice(&inspector),
913 inspector_slice(&next_inspector),
914 )
915 };
916 Self {
917 kind: ModifierKind::Single {
918 elements: elements.joined(next_elements),
919 inspector,
920 },
921 element_count: self.element_count + next.element_count,
922 provides_composition_locals: self.provides_composition_locals
923 || next.provides_composition_locals,
924 }
925 }
926
927 pub(crate) fn element_count(&self) -> usize {
929 self.element_count
930 }
931
932 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
933 match &self.kind {
934 ModifierKind::Empty => ModifierElementIterator { inner: [].iter() },
935 ModifierKind::Single { elements, .. } => ModifierElementIterator {
936 inner: elements.iter(),
937 },
938 }
939 }
940
941 pub(crate) fn provided_composition_locals(&self) -> Vec<ProvidedValue> {
942 if !self.provides_composition_locals {
943 return Vec::new();
944 }
945 self.iter_elements()
946 .flat_map(|element| element.provided_composition_locals())
947 .collect()
948 }
949
950 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
951 match &self.kind {
952 ModifierKind::Empty => ModifierInspectorIterator { inner: [].iter() },
953 ModifierKind::Single { inspector, .. } => ModifierInspectorIterator {
954 inner: inspector_slice(inspector).iter(),
955 },
956 }
957 }
958
959 #[cfg(test)]
960 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
961 match &self.kind {
962 ModifierKind::Empty => Vec::new(),
963 ModifierKind::Single { elements, .. } => elements.to_vec(),
964 }
965 }
966
967 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
968 match &self.kind {
969 ModifierKind::Empty => Vec::new(),
970 ModifierKind::Single { inspector, .. } => {
971 inspector.as_deref().map(<[_]>::to_vec).unwrap_or_default()
972 }
973 }
974 }
975
976 pub fn total_padding(&self) -> f32 {
978 let padding = self.padding_values();
979 padding
980 .left
981 .max(padding.right)
982 .max(padding.top)
983 .max(padding.bottom)
984 }
985
986 pub fn explicit_size(&self) -> Option<Size> {
989 let props = self.layout_properties();
990 match (props.width, props.height) {
991 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
992 Some(Size { width, height })
993 }
994 _ => None,
995 }
996 }
997
998 pub fn padding_values(&self) -> EdgeInsets {
1000 self.resolved_modifiers().padding()
1001 }
1002
1003 pub(crate) fn layout_properties(&self) -> LayoutProperties {
1004 self.resolved_modifiers().layout_properties()
1005 }
1006
1007 pub fn box_alignment(&self) -> Option<Alignment> {
1009 self.layout_properties().box_alignment()
1010 }
1011
1012 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
1014 self.layout_properties().column_alignment()
1015 }
1016
1017 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
1019 self.layout_properties().row_alignment()
1020 }
1021
1022 pub fn draw_commands(&self) -> Vec<DrawCommand> {
1024 collect_slices_from_modifier(self).draw_commands().to_vec()
1025 }
1026
1027 pub fn clips_to_bounds(&self) -> bool {
1029 collect_slices_from_modifier(self).clip_to_bounds()
1030 }
1031
1032 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
1034 self.inspector_metadata()
1035 .iter()
1036 .map(InspectorMetadata::to_record)
1037 .collect()
1038 }
1039
1040 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1042 let mut handle = ModifierChainHandle::new();
1043 let _ = handle.update(self);
1044 handle.resolved_modifiers()
1045 }
1046
1047 pub fn with_element<E>(element: E) -> Self
1050 where
1051 E: ModifierNodeElement,
1052 {
1053 Self::from_dyn_element(modifier_element(element))
1054 }
1055
1056 pub(crate) fn from_dyn_element(element: DynModifierElement) -> Self {
1058 Self {
1059 provides_composition_locals: element.provides_composition_locals(),
1060 kind: ModifierKind::Single {
1061 elements: Elements::One(element),
1062 inspector: None,
1063 },
1064 element_count: 1,
1065 }
1066 }
1067
1068 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
1069 if metadata.is_empty() {
1070 return self;
1071 }
1072 match self.kind {
1073 ModifierKind::Empty => self,
1074 ModifierKind::Single {
1075 elements,
1076 inspector,
1077 } => {
1078 let new_inspector = inspector_slice(&inspector)
1079 .iter()
1080 .cloned()
1081 .chain(std::iter::once(metadata))
1082 .collect();
1083 Self {
1084 kind: ModifierKind::Single {
1085 elements,
1086 inspector: Some(new_inspector),
1087 },
1088 element_count: self.element_count,
1089 provides_composition_locals: self.provides_composition_locals,
1090 }
1091 }
1092 }
1093 }
1094
1095 pub fn structural_eq(&self, other: &Self) -> bool {
1100 self.eq_internal(other, false)
1101 }
1102
1103 pub(crate) fn in_place_update(&self, other: &Self) -> Option<InPlaceUpdate> {
1110 if self.element_count != other.element_count || self.element_count > u64::BITS as usize {
1111 return None;
1112 }
1113 let mut update = InPlaceUpdate {
1114 reach: InPlaceReach::Node,
1115 elements: 0,
1116 };
1117 for (index, (a, b)) in self.iter_elements().zip(other.iter_elements()).enumerate() {
1118 if a.requires_update() || b.requires_update() {
1119 return None;
1120 }
1121 let element_type = a.element_type();
1122 match InPlaceReach::of(element_type) {
1123 Some(InPlaceReach::Node) if b.element_type() == element_type => {
1125 update.elements |= 1 << index;
1126 }
1127 Some(InPlaceReach::Resolved) if b.element_type() == element_type => {
1128 if !same_element(a, b) {
1129 update.reach = InPlaceReach::Resolved;
1130 update.elements |= 1 << index;
1131 }
1132 }
1133 _ => {
1134 if !same_element(a, b) {
1135 return None;
1136 }
1137 }
1138 }
1139 }
1140 Some(update)
1141 }
1142
1143 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
1144 if self.element_count != other.element_count {
1145 return false;
1146 }
1147
1148 match (&self.kind, &other.kind) {
1149 (ModifierKind::Empty, ModifierKind::Empty) => true,
1150 (
1151 ModifierKind::Single {
1152 elements: e1,
1153 inspector: _,
1154 },
1155 ModifierKind::Single {
1156 elements: e2,
1157 inspector: _,
1158 },
1159 ) => {
1160 if e1.shares_storage(e2) {
1161 return true;
1162 }
1163
1164 if e1.len() != e2.len() {
1165 return false;
1166 }
1167
1168 for (a, b) in e1.iter().zip(e2.iter()) {
1169 if Rc::ptr_eq(a, b) {
1171 continue;
1172 }
1173 if !consider_always_update
1174 && a.element_type() == b.element_type()
1175 && a.capabilities() == NodeCapabilities::DRAW
1176 && b.capabilities() == NodeCapabilities::DRAW
1177 {
1178 continue;
1179 }
1180
1181 if consider_always_update && (a.requires_update() || b.requires_update()) {
1182 if !Rc::ptr_eq(a, b) {
1183 return false;
1184 }
1185 continue;
1186 }
1187
1188 if !a.equals_element(&**b) {
1189 return false;
1190 }
1191 }
1192
1193 true
1194 }
1195 _ => false,
1196 }
1197 }
1198}
1199
1200impl PartialEq for Modifier {
1201 fn eq(&self, other: &Self) -> bool {
1202 self.eq_internal(other, true)
1203 }
1204}
1205
1206impl Eq for Modifier {}
1207
1208impl fmt::Display for Modifier {
1209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1210 match &self.kind {
1211 ModifierKind::Empty => write!(f, "Modifier.empty"),
1212 ModifierKind::Single { elements, .. } => {
1213 if elements.is_empty() {
1214 return write!(f, "Modifier.empty");
1215 }
1216 write!(f, "Modifier[")?;
1217 for (index, element) in elements.iter().enumerate() {
1218 if index > 0 {
1219 write!(f, ", ")?;
1220 }
1221 let name = element.inspector_name();
1222 let mut properties = Vec::new();
1223 element.record_inspector_properties(&mut |prop, value| {
1224 properties.push(format!("{prop}={value}"));
1225 });
1226 if properties.is_empty() {
1227 write!(f, "{name}")?;
1228 } else {
1229 write!(f, "{name}({})", properties.join(", "))?;
1230 }
1231 }
1232 write!(f, "]")
1233 }
1234 }
1235 }
1236}
1237
1238impl fmt::Debug for Modifier {
1239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240 fmt::Display::fmt(self, f)
1241 }
1242}
1243
1244#[cfg(test)]
1245#[path = "tests/modifier_tests.rs"]
1246mod tests;