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
661impl PartialEq for Modifier {
662 fn eq(&self, other: &Self) -> bool {
663 match (&self.kind, &other.kind) {
664 (ModifierKind::Empty, ModifierKind::Empty) => true,
665 (
666 ModifierKind::Single {
667 elements: e1,
668 inspector: _,
669 },
670 ModifierKind::Single {
671 elements: e2,
672 inspector: _,
673 },
674 ) => {
675 if Rc::ptr_eq(e1, e2) {
677 return true;
678 }
679
680 if e1.len() != e2.len() {
682 return false;
683 }
684
685 for (a, b) in e1.iter().zip(e2.iter()) {
686 if !a.equals_element(&**b) {
687 return false;
688 }
689 }
690 true
691 }
692 (
693 ModifierKind::Combined {
694 outer: o1,
695 inner: i1,
696 },
697 ModifierKind::Combined {
698 outer: o2,
699 inner: i2,
700 },
701 ) => {
702 if Rc::ptr_eq(o1, o2) && Rc::ptr_eq(i1, i2) {
704 return true;
705 }
706 o1 == o2 && i1 == i2
708 }
709 _ => false,
710 }
711 }
712}
713
714impl Eq for Modifier {}
715
716impl fmt::Display for Modifier {
717 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
718 match &self.kind {
719 ModifierKind::Empty => write!(f, "Modifier.empty"),
720 ModifierKind::Single { elements, .. } => {
721 if elements.is_empty() {
722 return write!(f, "Modifier.empty");
723 }
724 write!(f, "Modifier[")?;
725 for (index, element) in elements.iter().enumerate() {
726 if index > 0 {
727 write!(f, ", ")?;
728 }
729 let name = element.inspector_name();
730 let mut properties = Vec::new();
731 element.record_inspector_properties(&mut |prop, value| {
732 properties.push(format!("{prop}={value}"));
733 });
734 if properties.is_empty() {
735 write!(f, "{name}")?;
736 } else {
737 write!(f, "{name}({})", properties.join(", "))?;
738 }
739 }
740 write!(f, "]")
741 }
742 ModifierKind::Combined { outer: _, inner: _ } => {
743 write!(f, "[")?;
746 let elements = self.elements();
747 for (index, element) in elements.iter().enumerate() {
748 if index > 0 {
749 write!(f, ", ")?;
750 }
751 let name = element.inspector_name();
752 let mut properties = Vec::new();
753 element.record_inspector_properties(&mut |prop, value| {
754 properties.push(format!("{prop}={value}"));
755 });
756 if properties.is_empty() {
757 write!(f, "{name}")?;
758 } else {
759 write!(f, "{name}({})", properties.join(", "))?;
760 }
761 }
762 write!(f, "]")
763 }
764 }
765 }
766}
767
768impl fmt::Debug for Modifier {
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 fmt::Display::fmt(self, f)
771 }
772}
773
774#[derive(Clone, Copy, Debug, PartialEq)]
775pub struct ResolvedBackground {
776 color: Color,
777 shape: Option<RoundedCornerShape>,
778}
779
780impl ResolvedBackground {
781 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
782 Self { color, shape }
783 }
784
785 pub fn color(&self) -> Color {
786 self.color
787 }
788
789 pub fn shape(&self) -> Option<RoundedCornerShape> {
790 self.shape
791 }
792
793 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
794 self.shape = shape;
795 }
796}
797
798#[derive(Clone, Copy, Debug, PartialEq, Default)]
799pub struct ResolvedModifiers {
800 padding: EdgeInsets,
801 layout: LayoutProperties,
802 offset: Point,
803}
804
805impl ResolvedModifiers {
806 pub fn padding(&self) -> EdgeInsets {
807 self.padding
808 }
809
810 pub fn layout_properties(&self) -> LayoutProperties {
811 self.layout
812 }
813
814 pub fn offset(&self) -> Point {
815 self.offset
816 }
817
818 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
819 self.padding = padding;
820 }
821
822 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
823 self.layout = layout;
824 }
825
826 pub(crate) fn set_offset(&mut self, offset: Point) {
827 self.offset = offset;
828 }
829}
830
831#[derive(Clone, Copy, Debug, Default, PartialEq)]
832pub enum DimensionConstraint {
833 #[default]
834 Unspecified,
835 Points(f32),
836 Fraction(f32),
837 Intrinsic(IntrinsicSize),
838}
839
840#[derive(Clone, Copy, Debug, Default, PartialEq)]
841pub struct LayoutWeight {
842 pub weight: f32,
843 pub fill: bool,
844}
845
846#[derive(Clone, Copy, Debug, Default, PartialEq)]
847pub struct LayoutProperties {
848 padding: EdgeInsets,
849 width: DimensionConstraint,
850 height: DimensionConstraint,
851 min_width: Option<f32>,
852 min_height: Option<f32>,
853 max_width: Option<f32>,
854 max_height: Option<f32>,
855 weight: Option<LayoutWeight>,
856 box_alignment: Option<Alignment>,
857 column_alignment: Option<HorizontalAlignment>,
858 row_alignment: Option<VerticalAlignment>,
859}
860
861impl LayoutProperties {
862 pub fn padding(&self) -> EdgeInsets {
863 self.padding
864 }
865
866 pub fn width(&self) -> DimensionConstraint {
867 self.width
868 }
869
870 pub fn height(&self) -> DimensionConstraint {
871 self.height
872 }
873
874 pub fn min_width(&self) -> Option<f32> {
875 self.min_width
876 }
877
878 pub fn min_height(&self) -> Option<f32> {
879 self.min_height
880 }
881
882 pub fn max_width(&self) -> Option<f32> {
883 self.max_width
884 }
885
886 pub fn max_height(&self) -> Option<f32> {
887 self.max_height
888 }
889
890 pub fn weight(&self) -> Option<LayoutWeight> {
891 self.weight
892 }
893
894 pub fn box_alignment(&self) -> Option<Alignment> {
895 self.box_alignment
896 }
897
898 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
899 self.column_alignment
900 }
901
902 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
903 self.row_alignment
904 }
905}
906
907#[cfg(test)]
908#[path = "tests/modifier_tests.rs"]
909mod tests;