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)]
340pub struct Modifier {
341 kind: ModifierKind,
342}
343
344impl Default for Modifier {
345 fn default() -> Self {
346 Self {
347 kind: ModifierKind::Empty,
348 }
349 }
350}
351
352impl Modifier {
353 pub fn empty() -> Self {
354 Self::default()
355 }
356
357 pub fn clip_to_bounds(self) -> Self {
361 let modifier = Self::with_element(ClipToBoundsElement::new()).with_inspector_metadata(
362 inspector_metadata("clipToBounds", |info| {
363 info.add_property("clipToBounds", "true");
364 }),
365 );
366 self.then(modifier)
367 }
368
369 pub fn modifier_local_provider<T, F>(self, key: ModifierLocalKey<T>, value: F) -> Self
370 where
371 T: 'static,
372 F: Fn() -> T + 'static,
373 {
374 let element = ModifierLocalProviderElement::new(key, value);
375 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
376 self.then(modifier)
377 }
378
379 pub fn modifier_local_consumer<F>(self, consumer: F) -> Self
380 where
381 F: for<'scope> Fn(&mut ModifierLocalReadScope<'scope>) + 'static,
382 {
383 let element = ModifierLocalConsumerElement::new(consumer);
384 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
385 self.then(modifier)
386 }
387
388 pub fn semantics<F>(self, recorder: F) -> Self
389 where
390 F: Fn(&mut SemanticsConfiguration) + 'static,
391 {
392 let mut preview = SemanticsConfiguration::default();
393 recorder(&mut preview);
394 let description = preview.content_description.clone();
395 let is_button = preview.is_button;
396 let is_clickable = preview.is_clickable;
397 let metadata = inspector_metadata("semantics", move |info| {
398 if let Some(desc) = &description {
399 info.add_property("contentDescription", desc.clone());
400 }
401 if is_button {
402 info.add_property("isButton", "true");
403 }
404 if is_clickable {
405 info.add_property("isClickable", "true");
406 }
407 });
408 let element = SemanticsElement::new(recorder);
409 let modifier =
410 Modifier::from_parts(vec![modifier_element(element)]).with_inspector_metadata(metadata);
411 self.then(modifier)
412 }
413
414 pub fn focus_target(self) -> Self {
420 let element = FocusTargetElement::new();
421 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
422 self.then(modifier)
423 }
424
425 pub fn on_focus_changed<F>(self, callback: F) -> Self
430 where
431 F: Fn(FocusState) + 'static,
432 {
433 let element = FocusTargetElement::with_callback(callback);
434 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
435 self.then(modifier)
436 }
437
438 pub fn focus_requester(self, requester: &FocusRequester) -> Self {
443 let element = FocusRequesterElement::new(requester.id());
444 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
445 self.then(modifier)
446 }
447
448 pub fn debug_chain(self, tag: &'static str) -> Self {
466 use cranpose_foundation::{ModifierNode, ModifierNodeContext, NodeCapabilities, NodeState};
467
468 #[derive(Clone)]
469 struct DebugChainElement {
470 tag: &'static str,
471 }
472
473 impl fmt::Debug for DebugChainElement {
474 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475 f.debug_struct("DebugChainElement")
476 .field("tag", &self.tag)
477 .finish()
478 }
479 }
480
481 impl PartialEq for DebugChainElement {
482 fn eq(&self, other: &Self) -> bool {
483 self.tag == other.tag
484 }
485 }
486
487 impl Eq for DebugChainElement {}
488
489 impl std::hash::Hash for DebugChainElement {
490 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
491 self.tag.hash(state);
492 }
493 }
494
495 impl ModifierNodeElement for DebugChainElement {
496 type Node = DebugChainNode;
497
498 fn create(&self) -> Self::Node {
499 DebugChainNode::new(self.tag)
500 }
501
502 fn update(&self, node: &mut Self::Node) {
503 node.tag = self.tag;
504 }
505
506 fn capabilities(&self) -> NodeCapabilities {
507 NodeCapabilities::empty()
508 }
509 }
510
511 struct DebugChainNode {
512 tag: &'static str,
513 state: NodeState,
514 }
515
516 impl DebugChainNode {
517 fn new(tag: &'static str) -> Self {
518 Self {
519 tag,
520 state: NodeState::new(),
521 }
522 }
523 }
524
525 impl ModifierNode for DebugChainNode {
526 fn on_attach(&mut self, _context: &mut dyn ModifierNodeContext) {
527 eprintln!("[debug_chain:{}] Modifier chain attached", self.tag);
528 }
529
530 fn on_detach(&mut self) {
531 eprintln!("[debug_chain:{}] Modifier chain detached", self.tag);
532 }
533
534 fn on_reset(&mut self) {
535 eprintln!("[debug_chain:{}] Modifier chain reset", self.tag);
536 }
537 }
538
539 impl cranpose_foundation::DelegatableNode for DebugChainNode {
540 fn node_state(&self) -> &NodeState {
541 &self.state
542 }
543 }
544
545 let element = DebugChainElement { tag };
546 let modifier = Modifier::from_parts(vec![modifier_element(element)]);
547 self.then(modifier)
548 .with_inspector_metadata(inspector_metadata("debugChain", move |info| {
549 info.add_property("tag", tag);
550 }))
551 }
552
553 pub fn then(&self, next: Modifier) -> Modifier {
561 if self.is_trivially_empty() {
562 return next;
563 }
564 if next.is_trivially_empty() {
565 return self.clone();
566 }
567 Modifier {
568 kind: ModifierKind::Combined {
569 outer: Rc::new(self.clone()),
570 inner: Rc::new(next),
571 },
572 }
573 }
574
575 pub(crate) fn iter_elements(&self) -> ModifierElementIterator<'_> {
581 ModifierElementIterator::new(self)
582 }
583
584 pub(crate) fn iter_inspector_metadata(&self) -> ModifierInspectorIterator<'_> {
585 ModifierInspectorIterator::new(self)
586 }
587
588 pub(crate) fn elements(&self) -> Vec<DynModifierElement> {
593 match &self.kind {
594 ModifierKind::Empty => Vec::new(),
595 ModifierKind::Single { elements, .. } => elements.as_ref().clone(),
596 ModifierKind::Combined { outer, inner } => {
597 let mut result = outer.elements();
598 result.extend(inner.elements());
599 result
600 }
601 }
602 }
603
604 pub(crate) fn inspector_metadata(&self) -> Vec<InspectorMetadata> {
608 match &self.kind {
609 ModifierKind::Empty => Vec::new(),
610 ModifierKind::Single { inspector, .. } => inspector.as_ref().clone(),
611 ModifierKind::Combined { outer, inner } => {
612 let mut result = outer.inspector_metadata();
613 result.extend(inner.inspector_metadata());
614 result
615 }
616 }
617 }
618
619 pub fn total_padding(&self) -> f32 {
620 let padding = self.padding_values();
621 padding
622 .left
623 .max(padding.right)
624 .max(padding.top)
625 .max(padding.bottom)
626 }
627
628 pub fn explicit_size(&self) -> Option<Size> {
629 let props = self.layout_properties();
630 match (props.width, props.height) {
631 (DimensionConstraint::Points(width), DimensionConstraint::Points(height)) => {
632 Some(Size { width, height })
633 }
634 _ => None,
635 }
636 }
637
638 pub fn padding_values(&self) -> EdgeInsets {
639 self.resolved_modifiers().padding()
640 }
641
642 pub(crate) fn layout_properties(&self) -> LayoutProperties {
643 self.resolved_modifiers().layout_properties()
644 }
645
646 pub fn box_alignment(&self) -> Option<Alignment> {
647 self.layout_properties().box_alignment()
648 }
649
650 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
651 self.layout_properties().column_alignment()
652 }
653
654 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
655 self.layout_properties().row_alignment()
656 }
657
658 pub fn draw_commands(&self) -> Vec<DrawCommand> {
659 collect_slices_from_modifier(self).draw_commands().to_vec()
660 }
661
662 pub fn clips_to_bounds(&self) -> bool {
663 collect_slices_from_modifier(self).clip_to_bounds()
664 }
665
666 pub fn collect_inspector_records(&self) -> Vec<ModifierInspectorRecord> {
668 self.inspector_metadata()
669 .iter()
670 .map(|metadata| metadata.to_record())
671 .collect()
672 }
673
674 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
675 let mut handle = ModifierChainHandle::new();
676 let _ = handle.update(self);
677 handle.resolved_modifiers()
678 }
679
680 fn with_element<E>(element: E) -> Self
681 where
682 E: ModifierNodeElement,
683 {
684 let dyn_element = modifier_element(element);
685 Self::from_parts(vec![dyn_element])
686 }
687
688 pub(crate) fn from_parts(elements: Vec<DynModifierElement>) -> Self {
689 if elements.is_empty() {
690 Self {
691 kind: ModifierKind::Empty,
692 }
693 } else {
694 Self {
695 kind: ModifierKind::Single {
696 elements: Rc::new(elements),
697 inspector: Rc::new(Vec::new()),
698 },
699 }
700 }
701 }
702
703 fn is_trivially_empty(&self) -> bool {
704 matches!(self.kind, ModifierKind::Empty)
705 }
706
707 pub(crate) fn with_inspector_metadata(self, metadata: InspectorMetadata) -> Self {
708 if metadata.is_empty() {
709 return self;
710 }
711 match self.kind {
712 ModifierKind::Empty => self,
713 ModifierKind::Single {
714 elements,
715 inspector,
716 } => {
717 let mut new_inspector = inspector.as_ref().clone();
718 new_inspector.push(metadata);
719 Self {
720 kind: ModifierKind::Single {
721 elements,
722 inspector: Rc::new(new_inspector),
723 },
724 }
725 }
726 ModifierKind::Combined { .. } => {
727 panic!("Cannot add inspector metadata to a combined modifier")
730 }
731 }
732 }
733
734 pub fn structural_eq(&self, other: &Self) -> bool {
739 self.eq_internal(other, false)
740 }
741
742 fn eq_internal(&self, other: &Self, consider_always_update: bool) -> bool {
743 match (&self.kind, &other.kind) {
744 (ModifierKind::Empty, ModifierKind::Empty) => true,
745 (
746 ModifierKind::Single {
747 elements: e1,
748 inspector: _,
749 },
750 ModifierKind::Single {
751 elements: e2,
752 inspector: _,
753 },
754 ) => {
755 if Rc::ptr_eq(e1, e2) {
756 return true;
757 }
758
759 if e1.len() != e2.len() {
760 return false;
761 }
762
763 for (a, b) in e1.iter().zip(e2.iter()) {
764 if consider_always_update && (a.requires_update() || b.requires_update()) {
765 if !Rc::ptr_eq(a, b) {
766 return false;
767 }
768 continue;
769 }
770
771 if !a.equals_element(&**b) {
772 return false;
773 }
774 }
775
776 true
777 }
778 (
779 ModifierKind::Combined {
780 outer: o1,
781 inner: i1,
782 },
783 ModifierKind::Combined {
784 outer: o2,
785 inner: i2,
786 },
787 ) => {
788 if Rc::ptr_eq(o1, o2) && Rc::ptr_eq(i1, i2) {
789 return true;
790 }
791 o1.as_ref().eq_internal(o2.as_ref(), consider_always_update)
792 && i1.as_ref().eq_internal(i2.as_ref(), consider_always_update)
793 }
794 _ => false,
795 }
796 }
797}
798
799impl PartialEq for Modifier {
800 fn eq(&self, other: &Self) -> bool {
801 self.eq_internal(other, true)
802 }
803}
804
805impl Eq for Modifier {}
806
807impl fmt::Display for Modifier {
808 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
809 match &self.kind {
810 ModifierKind::Empty => write!(f, "Modifier.empty"),
811 ModifierKind::Single { elements, .. } => {
812 if elements.is_empty() {
813 return write!(f, "Modifier.empty");
814 }
815 write!(f, "Modifier[")?;
816 for (index, element) in elements.iter().enumerate() {
817 if index > 0 {
818 write!(f, ", ")?;
819 }
820 let name = element.inspector_name();
821 let mut properties = Vec::new();
822 element.record_inspector_properties(&mut |prop, value| {
823 properties.push(format!("{prop}={value}"));
824 });
825 if properties.is_empty() {
826 write!(f, "{name}")?;
827 } else {
828 write!(f, "{name}({})", properties.join(", "))?;
829 }
830 }
831 write!(f, "]")
832 }
833 ModifierKind::Combined { outer: _, inner: _ } => {
834 write!(f, "[")?;
837 let elements = self.elements();
838 for (index, element) in elements.iter().enumerate() {
839 if index > 0 {
840 write!(f, ", ")?;
841 }
842 let name = element.inspector_name();
843 let mut properties = Vec::new();
844 element.record_inspector_properties(&mut |prop, value| {
845 properties.push(format!("{prop}={value}"));
846 });
847 if properties.is_empty() {
848 write!(f, "{name}")?;
849 } else {
850 write!(f, "{name}({})", properties.join(", "))?;
851 }
852 }
853 write!(f, "]")
854 }
855 }
856 }
857}
858
859impl fmt::Debug for Modifier {
860 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 fmt::Display::fmt(self, f)
862 }
863}
864
865#[derive(Clone, Copy, Debug, PartialEq)]
866pub struct ResolvedBackground {
867 color: Color,
868 shape: Option<RoundedCornerShape>,
869}
870
871impl ResolvedBackground {
872 pub fn new(color: Color, shape: Option<RoundedCornerShape>) -> Self {
873 Self { color, shape }
874 }
875
876 pub fn color(&self) -> Color {
877 self.color
878 }
879
880 pub fn shape(&self) -> Option<RoundedCornerShape> {
881 self.shape
882 }
883
884 pub fn set_shape(&mut self, shape: Option<RoundedCornerShape>) {
885 self.shape = shape;
886 }
887}
888
889#[derive(Clone, Copy, Debug, PartialEq, Default)]
890pub struct ResolvedModifiers {
891 padding: EdgeInsets,
892 layout: LayoutProperties,
893 offset: Point,
894}
895
896impl ResolvedModifiers {
897 pub fn padding(&self) -> EdgeInsets {
898 self.padding
899 }
900
901 pub fn layout_properties(&self) -> LayoutProperties {
902 self.layout
903 }
904
905 pub fn offset(&self) -> Point {
906 self.offset
907 }
908
909 pub(crate) fn set_padding(&mut self, padding: EdgeInsets) {
910 self.padding = padding;
911 }
912
913 pub(crate) fn set_layout_properties(&mut self, layout: LayoutProperties) {
914 self.layout = layout;
915 }
916
917 pub(crate) fn set_offset(&mut self, offset: Point) {
918 self.offset = offset;
919 }
920}
921
922#[derive(Clone, Copy, Debug, Default, PartialEq)]
923pub enum DimensionConstraint {
924 #[default]
925 Unspecified,
926 Points(f32),
927 Fraction(f32),
928 Intrinsic(IntrinsicSize),
929}
930
931#[derive(Clone, Copy, Debug, Default, PartialEq)]
932pub struct LayoutWeight {
933 pub weight: f32,
934 pub fill: bool,
935}
936
937#[derive(Clone, Copy, Debug, Default, PartialEq)]
938pub struct LayoutProperties {
939 padding: EdgeInsets,
940 width: DimensionConstraint,
941 height: DimensionConstraint,
942 min_width: Option<f32>,
943 min_height: Option<f32>,
944 max_width: Option<f32>,
945 max_height: Option<f32>,
946 weight: Option<LayoutWeight>,
947 box_alignment: Option<Alignment>,
948 column_alignment: Option<HorizontalAlignment>,
949 row_alignment: Option<VerticalAlignment>,
950}
951
952impl LayoutProperties {
953 pub fn padding(&self) -> EdgeInsets {
954 self.padding
955 }
956
957 pub fn width(&self) -> DimensionConstraint {
958 self.width
959 }
960
961 pub fn height(&self) -> DimensionConstraint {
962 self.height
963 }
964
965 pub fn min_width(&self) -> Option<f32> {
966 self.min_width
967 }
968
969 pub fn min_height(&self) -> Option<f32> {
970 self.min_height
971 }
972
973 pub fn max_width(&self) -> Option<f32> {
974 self.max_width
975 }
976
977 pub fn max_height(&self) -> Option<f32> {
978 self.max_height
979 }
980
981 pub fn weight(&self) -> Option<LayoutWeight> {
982 self.weight
983 }
984
985 pub fn box_alignment(&self) -> Option<Alignment> {
986 self.box_alignment
987 }
988
989 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
990 self.column_alignment
991 }
992
993 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
994 self.row_alignment
995 }
996}
997
998#[cfg(test)]
999#[path = "tests/modifier_tests.rs"]
1000mod tests;