1#![allow(non_snake_case)]
9
10use std::fmt;
11use std::rc::Rc;
12
13mod alignment;
14mod background;
15mod chain;
16mod clickable;
17mod draw_cache;
18mod fill;
19mod focus;
20mod graphics_layer;
21mod local;
22mod offset;
23mod padding;
24mod pointer_input;
25mod scroll;
26mod semantics;
27mod size;
28mod slices;
29mod weight;
30
31pub use crate::draw::{DrawCacheBuilder, DrawCommand};
32#[allow(unused_imports)]
33pub use chain::{ModifierChainHandle, ModifierChainInspectorNode, ModifierLocalsHandle};
34use cranpose_foundation::ModifierNodeElement;
35pub use cranpose_foundation::{
36 modifier_element, AnyModifierElement, DynModifierElement, FocusState, PointerEvent,
37 PointerEventKind, SemanticsConfiguration,
38};
39pub use cranpose_ui_graphics::{
40 Brush, Color, CornerRadii, EdgeInsets, GraphicsLayer, Point, Rect, RoundedCornerShape, Size,
41};
42use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
43#[allow(unused_imports)]
44pub use focus::{FocusDirection, FocusRequester};
45pub(crate) use local::{
46 ModifierLocalAncestorResolver, ModifierLocalSource, ModifierLocalToken, ResolvedModifierLocal,
47};
48#[allow(unused_imports)]
49pub use local::{ModifierLocalKey, ModifierLocalReadScope};
50#[allow(unused_imports)]
51pub use pointer_input::{AwaitPointerEventScope, PointerInputScope};
52pub use semantics::{collect_semantics_from_chain, collect_semantics_from_modifier};
53pub use slices::{collect_modifier_slices, collect_slices_from_modifier, ModifierNodeSlices};
54#[cfg(feature = "test-helpers")]
56pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
57
58use crate::modifier_nodes::ClipToBoundsElement;
59use focus::{FocusRequesterElement, FocusTargetElement};
60use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
61use semantics::SemanticsElement;
62
63#[derive(Clone, Debug, Default)]
65pub struct InspectorInfo {
66 properties: Vec<InspectorProperty>,
67}
68
69impl InspectorInfo {
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
75 self.properties.push(InspectorProperty {
76 name,
77 value: value.into(),
78 });
79 }
80
81 pub fn properties(&self) -> &[InspectorProperty] {
82 &self.properties
83 }
84
85 pub fn is_empty(&self) -> bool {
86 self.properties.is_empty()
87 }
88
89 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
90 self.add_property(name, describe_dimension(constraint));
91 }
92
93 pub fn add_offset_components(
94 &mut self,
95 x_name: &'static str,
96 y_name: &'static str,
97 offset: Point,
98 ) {
99 self.add_property(x_name, offset.x.to_string());
100 self.add_property(y_name, offset.y.to_string());
101 }
102
103 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
104 where
105 A: fmt::Debug,
106 {
107 self.add_property(name, format!("{alignment:?}"));
108 }
109
110 #[allow(dead_code)] pub fn debug_properties(&self) -> Vec<(&'static str, String)> {
112 self.properties
113 .iter()
114 .map(|property| (property.name, property.value.clone()))
115 .collect()
116 }
117
118 #[allow(dead_code)] pub fn describe(&self) -> String {
120 self.properties
121 .iter()
122 .map(|property| format!("{}={}", property.name, property.value))
123 .collect::<Vec<_>>()
124 .join(", ")
125 }
126}
127
128#[derive(Clone, Debug, PartialEq)]
130pub struct InspectorProperty {
131 pub name: &'static str,
132 pub value: String,
133}
134
135#[derive(Clone, Debug, PartialEq)]
137pub struct ModifierInspectorRecord {
138 pub name: &'static str,
139 pub properties: Vec<InspectorProperty>,
140}
141
142#[derive(Clone, Debug)]
144pub(crate) struct InspectorMetadata {
145 name: &'static str,
146 info: InspectorInfo,
147}
148
149impl InspectorMetadata {
150 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
151 where
152 F: FnOnce(&mut InspectorInfo),
153 {
154 let mut info = InspectorInfo::new();
155 recorder(&mut info);
156 Self { name, info }
157 }
158
159 fn is_empty(&self) -> bool {
160 self.info.is_empty()
161 }
162
163 fn to_record(&self) -> ModifierInspectorRecord {
164 ModifierInspectorRecord {
165 name: self.name,
166 properties: self.info.properties().to_vec(),
167 }
168 }
169}
170
171fn describe_dimension(constraint: DimensionConstraint) -> String {
172 match constraint {
173 DimensionConstraint::Unspecified => "unspecified".to_string(),
174 DimensionConstraint::Points(value) => value.to_string(),
175 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
176 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
177 }
178}
179
180pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
181where
182 F: FnOnce(&mut InspectorInfo),
183{
184 InspectorMetadata::new(name, recorder)
185}
186
187#[derive(Clone)]
191enum ModifierKind {
192 Empty,
194 Single {
196 elements: Rc<Vec<DynModifierElement>>,
197 inspector: Rc<Vec<InspectorMetadata>>,
198 },
199 Combined {
201 outer: Rc<Modifier>,
202 inner: Rc<Modifier>,
203 },
204}
205
206pub struct ModifierElementIterator<'a> {
211 stack: Vec<&'a Modifier>,
213 current_elements: Option<(&'a [DynModifierElement], usize)>,
215}
216
217impl<'a> ModifierElementIterator<'a> {
218 fn new(modifier: &'a Modifier) -> Self {
219 let mut iter = Self {
220 stack: Vec::new(),
221 current_elements: None,
222 };
223 iter.push_modifier(modifier);
224 iter
225 }
226
227 fn push_modifier(&mut self, modifier: &'a Modifier) {
228 match &modifier.kind {
229 ModifierKind::Empty => {}
230 ModifierKind::Single { elements, .. } => {
231 if !elements.is_empty() {
232 self.current_elements = Some((elements.as_slice(), 0));
233 }
234 }
235 ModifierKind::Combined { outer, inner } => {
236 self.stack.push(inner.as_ref());
238 self.push_modifier(outer.as_ref());
239 }
240 }
241 }
242}
243
244impl<'a> Iterator for ModifierElementIterator<'a> {
245 type Item = &'a DynModifierElement;
246
247 fn next(&mut self) -> Option<Self::Item> {
248 loop {
249 if let Some((elements, index)) = &mut self.current_elements {
251 if *index < elements.len() {
252 let element = &elements[*index];
253 *index += 1;
254 return Some(element);
255 }
256 self.current_elements = None;
257 }
258
259 let next_modifier = self.stack.pop()?;
261 self.push_modifier(next_modifier);
262 }
263 }
264}
265
266#[derive(Clone)]
270pub struct Modifier {
271 kind: ModifierKind,
272}
273
274impl Default for Modifier {
275 fn default() -> Self {
276 Self {
277 kind: ModifierKind::Empty,
278 }
279 }
280}
281
282impl Modifier {
283 pub fn empty() -> Self {
284 Self::default()
285 }
286
287 pub fn clip_to_bounds(self) -> Self {
291 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
292 inspector_metadata("clipToBounds", |info| {
293 info.add_property("clipToBounds", "true");
294 }),
295 );
296 self.then(modifier)
297 }
298
299 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
300 where
301 T: 'static,
302 F: Fn() -> T + 'static,
303 {
304 let element = ModifierLocalProviderElement::new(key, value);
305 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
306 self.then(modifier)
307 }
308
309 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
310 where
311 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
312 {
313 let element = ModifierLocalConsumerElement::new(consumer);
314 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
315 self.then(modifier)
316 }
317
318 pub fn semantics<F>(self, recorder: F) -> Self
319 where
320 F: Fn(&mut SemanticsConfiguration) + 'static,
321 {
322 let mut preview = SemanticsConfiguration::default();
323 recorder(&mut preview);
324 let description = preview.content_description.clone();
325 let is_button = preview.is_button;
326 let is_clickable = preview.is_clickable;
327 let metadata = inspector_metadata("semantics", move |info| {
328 if let Some(desc) = &description {
329 info.add_property("contentDescription", desc.clone());
330 }
331 if is_button {
332 info.add_property("isButton", "true");
333 }
334 if is_clickable {
335 info.add_property("isClickable", "true");
336 }
337 });
338 let element = SemanticsElement::new(recorder);
339 let modifier =
340 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
341 self.then(modifier)
342 }
343
344 pub fn focus_target(self) -> Self {
350 let element = FocusTargetElement::new();
351 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
352 self.then(modifier)
353 }
354
355 pub fn on_focus_changed<F>(self, callback: F) -> Self
360 where
361 F: Fn(FocusState) + 'static,
362 {
363 let element = FocusTargetElement::with_callback(callback);
364 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
365 self.then(modifier)
366 }
367
368 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
373 let element = FocusRequesterElement::new(requester.id());
374 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
375 self.then(modifier)
376 }
377
378 pub fn debug_chain(self, tag: &'static str) -> Self {
396 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
397
398 #[derive(Clone)]
399 struct DebugChainElement {
400 tag: &'static str,
401 }
402
403 impl fmt::Debug for DebugChainElement {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 f.debug_struct("DebugChainElement")
406 .field("tag", &self.tag)
407 .finish()
408 }
409 }
410
411 impl PartialEq for DebugChainElement {
412 fn eq(&self, other: &Self) -> bool {
413 self.tag == other.tag
414 }
415 }
416
417 impl Eq for DebugChainElement {}
418
419 impl std::hash::Hash for DebugChainElement {
420 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
421 self.tag.hash(state);
422 }
423 }
424
425 impl ModifierNodeElement for DebugChainElement {
426 type Node = DebugChainNode;
427
428 fn create(&self) -> Self::Node {
429 DebugChainNode::new(self.tag)
430 }
431
432 fn update(&self, node: &mut Self::Node) {
433 node.tag = self.tag;
434 }
435
436 fn capabilities(&self) -> NodeCapabilities {
437 NodeCapabilities::empty()
438 }
439 }
440
441 struct DebugChainNode {
442 tag: &'static str,
443 state: NodeState,
444 }
445
446 impl DebugChainNode {
447 fn new(tag: &'static str) -> Self {
448 Self {
449 tag,
450 state: NodeState::new(),
451 }
452 }
453 }
454
455 impl ModifierNode for DebugChainNode {
456 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
457 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
458 }
459
460 fn on_detach(&mut self) {
461 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
462 }
463
464 fn on_reset(&mut self) {
465 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
466 }
467 }
468
469 impl cranpose_foundation::DelegatableNode for DebugChainNode {
470 fn node_state(&self) -> &NodeState {
471 &self.state
472 }
473 }
474
475 let element = DebugChainElement { tag };
476 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
477 self.then(modifier)
478 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
479 info.add_property("tag", tag);
480 }))
481 }
482
483 pub fn then(&self, next: Modifier) -> Modifier {
491 if self.is_trivially_empty() {
492 return next;
493 }
494 if next.is_trivially_empty() {
495 return self.clone();
496 }
497 Modifier {
498 kind: ModifierKind::Combined {
499 outer: Rc::new(self.clone()),
500 inner: Rc::new(next),
501 },
502 }
503 }
504
505 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
511 ModifierElementIterator::new(self)
512 }
513
514 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
519 match &self.kind {
520 ModifierKind::Empty => Vec::new(),
521 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
522 ModifierKind::Combined { outer, inner } => {
523 let mut result = outer.elements();
524 result.extend(inner.elements());
525 result
526 }
527 }
528 }
529
530 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
534 match &self.kind {
535 ModifierKind::Empty => Vec::new(),
536 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
537 ModifierKind::Combined { outer, inner } => {
538 let mut result = outer.inspector_metadata();
539 result.extend(inner.inspector_metadata());
540 result
541 }
542 }
543 }
544
545 pub fn total_padding(&self) -> f32 {
546 let padding = self.padding_values();
547 padding
548 .left
549 .max(padding.right)
550 .max(padding.top)
551 .max(padding.bottom)
552 }
553
554 pub fn explicit_size(&self) -> Option<Size> {
555 let props = self.layout_properties();
556 match (props.width, props.height) {
557 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
558 Some(Size { width, height })
559 }
560 _ => None,
561 }
562 }
563
564 pub fn padding_values(&self) -> EdgeInsets {
565 self.resolved_modifiers().padding()
566 }
567
568 pub(crate) fn layout_properties(&self) -> LayoutProperties {
569 self.resolved_modifiers().layout_properties()
570 }
571
572 pub fn box_alignment(&self) -> Option<Alignment> {
573 self.layout_properties().box_alignment()
574 }
575
576 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
577 self.layout_properties().column_alignment()
578 }
579
580 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
581 self.layout_properties().row_alignment()
582 }
583
584 pub fn draw_commands(&self) -> Vec<DrawCommand> {
585 collect_slices_from_modifier(self).draw_commands().to_vec()
586 }
587
588 pub fn clips_to_bounds(&self) -> bool {
589 collect_slices_from_modifier(self).clip_to_bounds()
590 }
591
592 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
594 self.inspector_metadata()
595 .iter()
596 .map(|metadata| metadata.to_record())
597 .collect()
598 }
599
600 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
601 let mut handle = ModifierChainHandle::new();
602 let _ = handle.update(self);
603 handle.resolved_modifiers()
604 }
605
606 fn with_element<E>(element: E) -> Self
607 where
608 E: ModifierNodeElement,
609 {
610 let dyn_element = modifier_element(element);
611 Self::from_parts(vec![dyn_element])
612 }
613
614 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
615 if elements.is_empty() {
616 Self {
617 kind: ModifierKind::Empty,
618 }
619 } else {
620 Self {
621 kind: ModifierKind::Single {
622 elements: Rc::new(elements),
623 inspector: Rc::new(Vec::new()),
624 },
625 }
626 }
627 }
628
629 fn is_trivially_empty(&self) -> bool {
630 matches!(self.kind, ModifierKind::Empty)
631 }
632
633 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
634 if metadata.is_empty() {
635 return self;
636 }
637 match self.kind {
638 ModifierKind::Empty => self,
639 ModifierKind::Single {
640 elements,
641 inspector,
642 } => {
643 let mut new_inspector = inspector.as_ref().clone();
644 new_inspector.push(metadata);
645 Self {
646 kind: ModifierKind::Single {
647 elements,
648 inspector: Rc::new(new_inspector),
649 },
650 }
651 }
652 ModifierKind::Combined { .. } => {
653 panic!("Cannot add inspector metadata to a combined modifier")
656 }
657 }
658 }
659
660 pub fn structural_eq(&self, other: &Self) -> bool {
665 self.eq_internal(other, false)
666 }
667
668 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
669 match (&self.kind, &other.kind) {
670 (ModifierKind::Empty, ModifierKind::Empty) => true,
671 (
672 ModifierKind::Single {
673 elements: e1,
674 inspector: _,
675 },
676 ModifierKind::Single {
677 elements: e2,
678 inspector: _,
679 },
680 ) => {
681 if Rc::ptr_eq(e1, e2) {
682 return true;
683 }
684
685 if e1.len() != e2.len() {
686 return false;
687 }
688
689 for (a, b) in e1.iter().zip(e2.iter()) {
690 if consider_always_update && (a.requires_update() || b.requires_update()) {
691 if !Rc::ptr_eq(a, b) {
692 return false;
693 }
694 continue;
695 }
696
697 if !a.equals_element(&**b) {
698 return false;
699 }
700 }
701
702 true
703 }
704 (
705 ModifierKind::Combined {
706 outer: o1,
707 inner: i1,
708 },
709 ModifierKind::Combined {
710 outer: o2,
711 inner: i2,
712 },
713 ) => {
714 if Rc::ptr_eq(o1, o2) && Rc::ptr_eq(i1, i2) {
715 return true;
716 }
717 o1.as_ref().eq_internal(o2.as_ref(), consider_always_update)
718 && i1.as_ref().eq_internal(i2.as_ref(), consider_always_update)
719 }
720 _ => false,
721 }
722 }
723}
724
725impl PartialEq for Modifier {
726 fn eq(&self, other: &Self) -> bool {
727 self.eq_internal(other, true)
728 }
729}
730
731impl Eq for Modifier {}
732
733impl fmt::Display for Modifier {
734 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
735 match &self.kind {
736 ModifierKind::Empty => write!(f, "Modifier.empty"),
737 ModifierKind::Single { elements, .. } => {
738 if elements.is_empty() {
739 return write!(f, "Modifier.empty");
740 }
741 write!(f, "Modifier[")?;
742 for (index, element) in elements.iter().enumerate() {
743 if index > 0 {
744 write!(f, ", ")?;
745 }
746 let name = element.inspector_name();
747 let mut properties = Vec::new();
748 element.record_inspector_properties(&mut |prop, value| {
749 properties.push(format!("{prop}={value}"));
750 });
751 if properties.is_empty() {
752 write!(f, "{name}")?;
753 } else {
754 write!(f, "{name}({})", properties.join(", "))?;
755 }
756 }
757 write!(f, "]")
758 }
759 ModifierKind::Combined { outer: _, inner: _ } => {
760 write!(f, "[")?;
763 let elements = self.elements();
764 for (index, element) in elements.iter().enumerate() {
765 if index > 0 {
766 write!(f, ", ")?;
767 }
768 let name = element.inspector_name();
769 let mut properties = Vec::new();
770 element.record_inspector_properties(&mut |prop, value| {
771 properties.push(format!("{prop}={value}"));
772 });
773 if properties.is_empty() {
774 write!(f, "{name}")?;
775 } else {
776 write!(f, "{name}({})", properties.join(", "))?;
777 }
778 }
779 write!(f, "]")
780 }
781 }
782 }
783}
784
785impl fmt::Debug for Modifier {
786 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
787 fmt::Display::fmt(self, f)
788 }
789}
790
791#[derive(Clone, Copy, Debug, PartialEq)]
792pub struct ResolvedBackground {
793 color: Color,
794 shape: Option<RoundedCornerShape>,
795}
796
797impl ResolvedBackground {
798 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
799 Self { color, shape }
800 }
801
802 pub fn color(&self) -> Color {
803 self.color
804 }
805
806 pub fn shape(&self) -> Option<RoundedCornerShape> {
807 self.shape
808 }
809
810 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
811 self.shape = shape;
812 }
813}
814
815#[derive(Clone, Copy, Debug, PartialEq, Default)]
816pub struct ResolvedModifiers {
817 padding: EdgeInsets,
818 layout: LayoutProperties,
819 offset: Point,
820}
821
822impl ResolvedModifiers {
823 pub fn padding(&self) -> EdgeInsets {
824 self.padding
825 }
826
827 pub fn layout_properties(&self) -> LayoutProperties {
828 self.layout
829 }
830
831 pub fn offset(&self) -> Point {
832 self.offset
833 }
834
835 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
836 self.padding = padding;
837 }
838
839 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
840 self.layout = layout;
841 }
842
843 pub(crate) fn set_offset(&mut self, offset: Point) {
844 self.offset = offset;
845 }
846}
847
848#[derive(Clone, Copy, Debug, Default, PartialEq)]
849pub enum DimensionConstraint {
850 #[default]
851 Unspecified,
852 Points(f32),
853 Fraction(f32),
854 Intrinsic(IntrinsicSize),
855}
856
857#[derive(Clone, Copy, Debug, Default, PartialEq)]
858pub struct LayoutWeight {
859 pub weight: f32,
860 pub fill: bool,
861}
862
863#[derive(Clone, Copy, Debug, Default, PartialEq)]
864pub struct LayoutProperties {
865 padding: EdgeInsets,
866 width: DimensionConstraint,
867 height: DimensionConstraint,
868 min_width: Option<f32>,
869 min_height: Option<f32>,
870 max_width: Option<f32>,
871 max_height: Option<f32>,
872 weight: Option<LayoutWeight>,
873 box_alignment: Option<Alignment>,
874 column_alignment: Option<HorizontalAlignment>,
875 row_alignment: Option<VerticalAlignment>,
876}
877
878impl LayoutProperties {
879 pub fn padding(&self) -> EdgeInsets {
880 self.padding
881 }
882
883 pub fn width(&self) -> DimensionConstraint {
884 self.width
885 }
886
887 pub fn height(&self) -> DimensionConstraint {
888 self.height
889 }
890
891 pub fn min_width(&self) -> Option<f32> {
892 self.min_width
893 }
894
895 pub fn min_height(&self) -> Option<f32> {
896 self.min_height
897 }
898
899 pub fn max_width(&self) -> Option<f32> {
900 self.max_width
901 }
902
903 pub fn max_height(&self) -> Option<f32> {
904 self.max_height
905 }
906
907 pub fn weight(&self) -> Option<LayoutWeight> {
908 self.weight
909 }
910
911 pub fn box_alignment(&self) -> Option<Alignment> {
912 self.box_alignment
913 }
914
915 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
916 self.column_alignment
917 }
918
919 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
920 self.row_alignment
921 }
922}
923
924#[cfg(test)]
925#[path = "tests/modifier_tests.rs"]
926mod tests;