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::{
54 collect_modifier_slices, collect_modifier_slices_into, collect_slices_from_modifier,
55 ModifierNodeSlices,
56};
57#[cfg(feature = "test-helpers")]
59pub use scroll::{last_fling_velocity, reset_last_fling_velocity};
60
61use crate::modifier_nodes::ClipToBoundsElement;
62use focus::{FocusRequesterElement, FocusTargetElement};
63use local::{ModifierLocalConsumerElement, ModifierLocalProviderElement};
64use semantics::SemanticsElement;
65
66#[derive(Clone, Debug, Default)]
68pub struct InspectorInfo {
69 properties: Vec<InspectorProperty>,
70}
71
72impl InspectorInfo {
73 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn add_property<V: Into<String>>(&mut self, name: &'static str, value: V) {
78 self.properties.push(InspectorProperty {
79 name,
80 value: value.into(),
81 });
82 }
83
84 pub fn properties(&self) -> &[InspectorProperty] {
85 &self.properties
86 }
87
88 pub fn is_empty(&self) -> bool {
89 self.properties.is_empty()
90 }
91
92 pub fn add_dimension(&mut self, name: &'static str, constraint: DimensionConstraint) {
93 self.add_property(name, describe_dimension(constraint));
94 }
95
96 pub fn add_offset_components(
97 &mut self,
98 x_name: &'static str,
99 y_name: &'static str,
100 offset: Point,
101 ) {
102 self.add_property(x_name, offset.x.to_string());
103 self.add_property(y_name, offset.y.to_string());
104 }
105
106 pub fn add_alignment<A>(&mut self, name: &'static str, alignment: A)
107 where
108 A: fmt::Debug,
109 {
110 self.add_property(name, format!("{alignment:?}"));
111 }
112
113 #[allow(dead_code)] pub fn debug_properties(&self) -> Vec<(&'static str, String)> {
115 self.properties
116 .iter()
117 .map(|property| (property.name, property.value.clone()))
118 .collect()
119 }
120
121 #[allow(dead_code)] pub fn describe(&self) -> String {
123 self.properties
124 .iter()
125 .map(|property| format!("{}={}", property.name, property.value))
126 .collect::<Vec<_>>()
127 .join(", ")
128 }
129}
130
131#[derive(Clone, Debug, PartialEq)]
133pub struct InspectorProperty {
134 pub name: &'static str,
135 pub value: String,
136}
137
138#[derive(Clone, Debug, PartialEq)]
140pub struct ModifierInspectorRecord {
141 pub name: &'static str,
142 pub properties: Vec<InspectorProperty>,
143}
144
145#[derive(Clone, Debug)]
147pub(crate) struct InspectorMetadata {
148 name: &'static str,
149 info: InspectorInfo,
150}
151
152impl InspectorMetadata {
153 pub(crate) fn new<F>(name: &'static str, recorder: F) -> Self
154 where
155 F: FnOnce(&mut InspectorInfo),
156 {
157 let mut info = InspectorInfo::new();
158 recorder(&mut info);
159 Self { name, info }
160 }
161
162 fn is_empty(&self) -> bool {
163 self.info.is_empty()
164 }
165
166 fn to_record(&self) -> ModifierInspectorRecord {
167 ModifierInspectorRecord {
168 name: self.name,
169 properties: self.info.properties().to_vec(),
170 }
171 }
172}
173
174fn describe_dimension(constraint: DimensionConstraint) -> String {
175 match constraint {
176 DimensionConstraint::Unspecified => "unspecified".to_string(),
177 DimensionConstraint::Points(value) => value.to_string(),
178 DimensionConstraint::Fraction(value) => format!("fraction({value})"),
179 DimensionConstraint::Intrinsic(size) => format!("intrinsic({size:?})"),
180 }
181}
182
183pub(crate) fn inspector_metadata<F>(name: &'static str, recorder: F) -> InspectorMetadata
184where
185 F: FnOnce(&mut InspectorInfo),
186{
187 InspectorMetadata::new(name, recorder)
188}
189
190#[derive(Clone)]
194enum ModifierKind {
195 Empty,
197 Single {
199 elements: Rc<Vec<DynModifierElement>>,
200 inspector: Rc<Vec<InspectorMetadata>>,
201 },
202 Combined {
204 outer: Rc<Modifier>,
205 inner: Rc<Modifier>,
206 },
207}
208
209pub struct ModifierElementIterator<'a> {
214 stack: Vec<&'a Modifier>,
216 current_elements: Option<(&'a [DynModifierElement], usize)>,
218}
219
220impl<'a> ModifierElementIterator<'a> {
221 fn new(modifier: &'a Modifier) -> Self {
222 let mut iter = Self {
223 stack: Vec::new(),
224 current_elements: None,
225 };
226 iter.push_modifier(modifier);
227 iter
228 }
229
230 fn push_modifier(&mut self, modifier: &'a Modifier) {
231 match &modifier.kind {
232 ModifierKind::Empty => {}
233 ModifierKind::Single { elements, .. } => {
234 if !elements.is_empty() {
235 self.current_elements = Some((elements.as_slice(), 0));
236 }
237 }
238 ModifierKind::Combined { outer, inner } => {
239 self.stack.push(inner.as_ref());
241 self.push_modifier(outer.as_ref());
242 }
243 }
244 }
245}
246
247impl<'a> Iterator for ModifierElementIterator<'a> {
248 type Item = &'a DynModifierElement;
249
250 fn next(&mut self) -> Option<Self::Item> {
251 loop {
252 if let Some((elements, index)) = &mut self.current_elements {
254 if *index < elements.len() {
255 let element = &elements[*index];
256 *index += 1;
257 return Some(element);
258 }
259 self.current_elements = None;
260 }
261
262 let next_modifier = self.stack.pop()?;
264 self.push_modifier(next_modifier);
265 }
266 }
267}
268
269pub(crate) struct ModifierInspectorIterator<'a> {
271 stack: Vec<&'a Modifier>,
272 current_inspector: Option<(&'a [InspectorMetadata], usize)>,
273}
274
275impl<'a> ModifierInspectorIterator<'a> {
276 fn new(modifier: &'a Modifier) -> Self {
277 let mut iter = Self {
278 stack: Vec::new(),
279 current_inspector: None,
280 };
281 iter.push_modifier(modifier);
282 iter
283 }
284
285 fn push_modifier(&mut self, modifier: &'a Modifier) {
286 match &modifier.kind {
287 ModifierKind::Empty => {}
288 ModifierKind::Single { inspector, .. } => {
289 if !inspector.is_empty() {
290 self.current_inspector = Some((inspector.as_slice(), 0));
291 }
292 }
293 ModifierKind::Combined { outer, inner } => {
294 self.stack.push(inner.as_ref());
296 self.push_modifier(outer.as_ref());
297 }
298 }
299 }
300}
301
302impl<'a> Iterator for ModifierInspectorIterator<'a> {
303 type Item = &'a InspectorMetadata;
304
305 fn next(&mut self) -> Option<Self::Item> {
306 loop {
307 if let Some((inspector, index)) = &mut self.current_inspector {
308 if *index < inspector.len() {
309 let metadata = &inspector[*index];
310 *index += 1;
311 return Some(metadata);
312 }
313 self.current_inspector = None;
314 }
315
316 let next_modifier = self.stack.pop()?;
317 self.push_modifier(next_modifier);
318 }
319 }
320}
321
322#[derive(Clone)]
326pub struct Modifier {
327 kind: ModifierKind,
328}
329
330impl Default for Modifier {
331 fn default() -> Self {
332 Self {
333 kind: ModifierKind::Empty,
334 }
335 }
336}
337
338impl Modifier {
339 pub fn empty() -> Self {
340 Self::default()
341 }
342
343 pub fn clip_to_bounds(self) -> Self {
347 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
348 inspector_metadata("clipToBounds", |info| {
349 info.add_property("clipToBounds", "true");
350 }),
351 );
352 self.then(modifier)
353 }
354
355 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
356 where
357 T: 'static,
358 F: Fn() -> T + 'static,
359 {
360 let element = ModifierLocalProviderElement::new(key, value);
361 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
362 self.then(modifier)
363 }
364
365 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
366 where
367 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
368 {
369 let element = ModifierLocalConsumerElement::new(consumer);
370 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
371 self.then(modifier)
372 }
373
374 pub fn semantics<F>(self, recorder: F) -> Self
375 where
376 F: Fn(&mut SemanticsConfiguration) + 'static,
377 {
378 let mut preview = SemanticsConfiguration::default();
379 recorder(&mut preview);
380 let description = preview.content_description.clone();
381 let is_button = preview.is_button;
382 let is_clickable = preview.is_clickable;
383 let metadata = inspector_metadata("semantics", move |info| {
384 if let Some(desc) = &description {
385 info.add_property("contentDescription", desc.clone());
386 }
387 if is_button {
388 info.add_property("isButton", "true");
389 }
390 if is_clickable {
391 info.add_property("isClickable", "true");
392 }
393 });
394 let element = SemanticsElement::new(recorder);
395 let modifier =
396 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
397 self.then(modifier)
398 }
399
400 pub fn focus_target(self) -> Self {
406 let element = FocusTargetElement::new();
407 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
408 self.then(modifier)
409 }
410
411 pub fn on_focus_changed<F>(self, callback: F) -> Self
416 where
417 F: Fn(FocusState) + 'static,
418 {
419 let element = FocusTargetElement::with_callback(callback);
420 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
421 self.then(modifier)
422 }
423
424 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
429 let element = FocusRequesterElement::new(requester.id());
430 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
431 self.then(modifier)
432 }
433
434 pub fn debug_chain(self, tag: &'static str) -> Self {
452 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
453
454 #[derive(Clone)]
455 struct DebugChainElement {
456 tag: &'static str,
457 }
458
459 impl fmt::Debug for DebugChainElement {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 f.debug_struct("DebugChainElement")
462 .field("tag", &self.tag)
463 .finish()
464 }
465 }
466
467 impl PartialEq for DebugChainElement {
468 fn eq(&self, other: &Self) -> bool {
469 self.tag == other.tag
470 }
471 }
472
473 impl Eq for DebugChainElement {}
474
475 impl std::hash::Hash for DebugChainElement {
476 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
477 self.tag.hash(state);
478 }
479 }
480
481 impl ModifierNodeElement for DebugChainElement {
482 type Node = DebugChainNode;
483
484 fn create(&self) -> Self::Node {
485 DebugChainNode::new(self.tag)
486 }
487
488 fn update(&self, node: &mut Self::Node) {
489 node.tag = self.tag;
490 }
491
492 fn capabilities(&self) -> NodeCapabilities {
493 NodeCapabilities::empty()
494 }
495 }
496
497 struct DebugChainNode {
498 tag: &'static str,
499 state: NodeState,
500 }
501
502 impl DebugChainNode {
503 fn new(tag: &'static str) -> Self {
504 Self {
505 tag,
506 state: NodeState::new(),
507 }
508 }
509 }
510
511 impl ModifierNode for DebugChainNode {
512 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
513 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
514 }
515
516 fn on_detach(&mut self) {
517 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
518 }
519
520 fn on_reset(&mut self) {
521 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
522 }
523 }
524
525 impl cranpose_foundation::DelegatableNode for DebugChainNode {
526 fn node_state(&self) -> &NodeState {
527 &self.state
528 }
529 }
530
531 let element = DebugChainElement { tag };
532 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
533 self.then(modifier)
534 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
535 info.add_property("tag", tag);
536 }))
537 }
538
539 pub fn then(&self, next: Modifier) -> Modifier {
547 if self.is_trivially_empty() {
548 return next;
549 }
550 if next.is_trivially_empty() {
551 return self.clone();
552 }
553 Modifier {
554 kind: ModifierKind::Combined {
555 outer: Rc::new(self.clone()),
556 inner: Rc::new(next),
557 },
558 }
559 }
560
561 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
567 ModifierElementIterator::new(self)
568 }
569
570 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
571 ModifierInspectorIterator::new(self)
572 }
573
574 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
579 match &self.kind {
580 ModifierKind::Empty => Vec::new(),
581 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
582 ModifierKind::Combined { outer, inner } => {
583 let mut result = outer.elements();
584 result.extend(inner.elements());
585 result
586 }
587 }
588 }
589
590 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
594 match &self.kind {
595 ModifierKind::Empty => Vec::new(),
596 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
597 ModifierKind::Combined { outer, inner } => {
598 let mut result = outer.inspector_metadata();
599 result.extend(inner.inspector_metadata());
600 result
601 }
602 }
603 }
604
605 pub fn total_padding(&self) -> f32 {
606 let padding = self.padding_values();
607 padding
608 .left
609 .max(padding.right)
610 .max(padding.top)
611 .max(padding.bottom)
612 }
613
614 pub fn explicit_size(&self) -> Option<Size> {
615 let props = self.layout_properties();
616 match (props.width, props.height) {
617 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
618 Some(Size { width, height })
619 }
620 _ => None,
621 }
622 }
623
624 pub fn padding_values(&self) -> EdgeInsets {
625 self.resolved_modifiers().padding()
626 }
627
628 pub(crate) fn layout_properties(&self) -> LayoutProperties {
629 self.resolved_modifiers().layout_properties()
630 }
631
632 pub fn box_alignment(&self) -> Option<Alignment> {
633 self.layout_properties().box_alignment()
634 }
635
636 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
637 self.layout_properties().column_alignment()
638 }
639
640 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
641 self.layout_properties().row_alignment()
642 }
643
644 pub fn draw_commands(&self) -> Vec<DrawCommand> {
645 collect_slices_from_modifier(self).draw_commands().to_vec()
646 }
647
648 pub fn clips_to_bounds(&self) -> bool {
649 collect_slices_from_modifier(self).clip_to_bounds()
650 }
651
652 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
654 self.inspector_metadata()
655 .iter()
656 .map(|metadata| metadata.to_record())
657 .collect()
658 }
659
660 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
661 let mut handle = ModifierChainHandle::new();
662 let _ = handle.update(self);
663 handle.resolved_modifiers()
664 }
665
666 fn with_element<E>(element: E) -> Self
667 where
668 E: ModifierNodeElement,
669 {
670 let dyn_element = modifier_element(element);
671 Self::from_parts(vec![dyn_element])
672 }
673
674 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
675 if elements.is_empty() {
676 Self {
677 kind: ModifierKind::Empty,
678 }
679 } else {
680 Self {
681 kind: ModifierKind::Single {
682 elements: Rc::new(elements),
683 inspector: Rc::new(Vec::new()),
684 },
685 }
686 }
687 }
688
689 fn is_trivially_empty(&self) -> bool {
690 matches!(self.kind, ModifierKind::Empty)
691 }
692
693 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
694 if metadata.is_empty() {
695 return self;
696 }
697 match self.kind {
698 ModifierKind::Empty => self,
699 ModifierKind::Single {
700 elements,
701 inspector,
702 } => {
703 let mut new_inspector = inspector.as_ref().clone();
704 new_inspector.push(metadata);
705 Self {
706 kind: ModifierKind::Single {
707 elements,
708 inspector: Rc::new(new_inspector),
709 },
710 }
711 }
712 ModifierKind::Combined { .. } => {
713 panic!("Cannot add inspector metadata to a combined modifier")
716 }
717 }
718 }
719
720 pub fn structural_eq(&self, other: &Self) -> bool {
725 self.eq_internal(other, false)
726 }
727
728 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
729 match (&self.kind, &other.kind) {
730 (ModifierKind::Empty, ModifierKind::Empty) => true,
731 (
732 ModifierKind::Single {
733 elements: e1,
734 inspector: _,
735 },
736 ModifierKind::Single {
737 elements: e2,
738 inspector: _,
739 },
740 ) => {
741 if Rc::ptr_eq(e1, e2) {
742 return true;
743 }
744
745 if e1.len() != e2.len() {
746 return false;
747 }
748
749 for (a, b) in e1.iter().zip(e2.iter()) {
750 if consider_always_update && (a.requires_update() || b.requires_update()) {
751 if !Rc::ptr_eq(a, b) {
752 return false;
753 }
754 continue;
755 }
756
757 if !a.equals_element(&**b) {
758 return false;
759 }
760 }
761
762 true
763 }
764 (
765 ModifierKind::Combined {
766 outer: o1,
767 inner: i1,
768 },
769 ModifierKind::Combined {
770 outer: o2,
771 inner: i2,
772 },
773 ) => {
774 if Rc::ptr_eq(o1, o2) && Rc::ptr_eq(i1, i2) {
775 return true;
776 }
777 o1.as_ref().eq_internal(o2.as_ref(), consider_always_update)
778 && i1.as_ref().eq_internal(i2.as_ref(), consider_always_update)
779 }
780 _ => false,
781 }
782 }
783}
784
785impl PartialEq for Modifier {
786 fn eq(&self, other: &Self) -> bool {
787 self.eq_internal(other, true)
788 }
789}
790
791impl Eq for Modifier {}
792
793impl fmt::Display for Modifier {
794 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
795 match &self.kind {
796 ModifierKind::Empty => write!(f, "Modifier.empty"),
797 ModifierKind::Single { elements, .. } => {
798 if elements.is_empty() {
799 return write!(f, "Modifier.empty");
800 }
801 write!(f, "Modifier[")?;
802 for (index, element) in elements.iter().enumerate() {
803 if index > 0 {
804 write!(f, ", ")?;
805 }
806 let name = element.inspector_name();
807 let mut properties = Vec::new();
808 element.record_inspector_properties(&mut |prop, value| {
809 properties.push(format!("{prop}={value}"));
810 });
811 if properties.is_empty() {
812 write!(f, "{name}")?;
813 } else {
814 write!(f, "{name}({})", properties.join(", "))?;
815 }
816 }
817 write!(f, "]")
818 }
819 ModifierKind::Combined { outer: _, inner: _ } => {
820 write!(f, "[")?;
823 let elements = self.elements();
824 for (index, element) in elements.iter().enumerate() {
825 if index > 0 {
826 write!(f, ", ")?;
827 }
828 let name = element.inspector_name();
829 let mut properties = Vec::new();
830 element.record_inspector_properties(&mut |prop, value| {
831 properties.push(format!("{prop}={value}"));
832 });
833 if properties.is_empty() {
834 write!(f, "{name}")?;
835 } else {
836 write!(f, "{name}({})", properties.join(", "))?;
837 }
838 }
839 write!(f, "]")
840 }
841 }
842 }
843}
844
845impl fmt::Debug for Modifier {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 fmt::Display::fmt(self, f)
848 }
849}
850
851#[derive(Clone, Copy, Debug, PartialEq)]
852pub struct ResolvedBackground {
853 color: Color,
854 shape: Option<RoundedCornerShape>,
855}
856
857impl ResolvedBackground {
858 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
859 Self { color, shape }
860 }
861
862 pub fn color(&self) -> Color {
863 self.color
864 }
865
866 pub fn shape(&self) -> Option<RoundedCornerShape> {
867 self.shape
868 }
869
870 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
871 self.shape = shape;
872 }
873}
874
875#[derive(Clone, Copy, Debug, PartialEq, Default)]
876pub struct ResolvedModifiers {
877 padding: EdgeInsets,
878 layout: LayoutProperties,
879 offset: Point,
880}
881
882impl ResolvedModifiers {
883 pub fn padding(&self) -> EdgeInsets {
884 self.padding
885 }
886
887 pub fn layout_properties(&self) -> LayoutProperties {
888 self.layout
889 }
890
891 pub fn offset(&self) -> Point {
892 self.offset
893 }
894
895 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
896 self.padding = padding;
897 }
898
899 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
900 self.layout = layout;
901 }
902
903 pub(crate) fn set_offset(&mut self, offset: Point) {
904 self.offset = offset;
905 }
906}
907
908#[derive(Clone, Copy, Debug, Default, PartialEq)]
909pub enum DimensionConstraint {
910 #[default]
911 Unspecified,
912 Points(f32),
913 Fraction(f32),
914 Intrinsic(IntrinsicSize),
915}
916
917#[derive(Clone, Copy, Debug, Default, PartialEq)]
918pub struct LayoutWeight {
919 pub weight: f32,
920 pub fill: bool,
921}
922
923#[derive(Clone, Copy, Debug, Default, PartialEq)]
924pub struct LayoutProperties {
925 padding: EdgeInsets,
926 width: DimensionConstraint,
927 height: DimensionConstraint,
928 min_width: Option<f32>,
929 min_height: Option<f32>,
930 max_width: Option<f32>,
931 max_height: Option<f32>,
932 weight: Option<LayoutWeight>,
933 box_alignment: Option<Alignment>,
934 column_alignment: Option<HorizontalAlignment>,
935 row_alignment: Option<VerticalAlignment>,
936}
937
938impl LayoutProperties {
939 pub fn padding(&self) -> EdgeInsets {
940 self.padding
941 }
942
943 pub fn width(&self) -> DimensionConstraint {
944 self.width
945 }
946
947 pub fn height(&self) -> DimensionConstraint {
948 self.height
949 }
950
951 pub fn min_width(&self) -> Option<f32> {
952 self.min_width
953 }
954
955 pub fn min_height(&self) -> Option<f32> {
956 self.min_height
957 }
958
959 pub fn max_width(&self) -> Option<f32> {
960 self.max_width
961 }
962
963 pub fn max_height(&self) -> Option<f32> {
964 self.max_height
965 }
966
967 pub fn weight(&self) -> Option<LayoutWeight> {
968 self.weight
969 }
970
971 pub fn box_alignment(&self) -> Option<Alignment> {
972 self.box_alignment
973 }
974
975 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
976 self.column_alignment
977 }
978
979 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
980 self.row_alignment
981 }
982}
983
984#[cfg(test)]
985#[path = "tests/modifier_tests.rs"]
986mod tests;