Skip to main content

cranpose_ui/modifier/
size.rs

1use cranpose_ui_layout::IntrinsicSize;
2
3use super::{DimensionConstraint, Modifier, Size, inspector_metadata};
4use crate::modifier_nodes::{IntrinsicSizeElement, SizeElement};
5
6impl Modifier {
7    /// Declare the preferred size of the content to be exactly `size`.
8    ///
9    /// The incoming measurement constraints may override this value, forcing the content
10    /// to be either smaller or larger.
11    ///
12    /// Matches Kotlin: `Modifier.size(size: Dp)`
13    ///
14    /// Example: `Modifier::empty().size(Size { width: 100.0, height: 200.0 })`
15    pub fn size(self, size: Size) -> Self {
16        let width = size.width;
17        let height = size.height;
18        let modifier = Self::with_element(SizeElement::new(Some(width), Some(height)))
19            .with_inspector_metadata(inspector_metadata("size", move |info| {
20                info.add_dimension("width", DimensionConstraint::Points(width));
21                info.add_dimension("height", DimensionConstraint::Points(height));
22            }));
23        self.then(modifier)
24    }
25
26    /// Declare the preferred size of the content to be exactly `width`dp by `height`dp.
27    ///
28    /// Convenience method for `size(Size { width, height })`.
29    ///
30    /// Example: `Modifier::empty().size_points(100.0, 200.0)`
31    pub fn size_points(self, width: f32, height: f32) -> Self {
32        self.size(Size { width, height })
33    }
34
35    /// Declare the preferred width of the content to be exactly `width`dp.
36    ///
37    /// The incoming measurement constraints may override this value, forcing the content
38    /// to be either smaller or larger.
39    ///
40    /// Matches Kotlin: `Modifier.width(width: Dp)`
41    ///
42    /// Example: `Modifier::empty().width(100.0).height(200.0)`
43    pub fn width(self, width: f32) -> Self {
44        let modifier = Self::with_element(SizeElement::new(Some(width), None))
45            .with_inspector_metadata(inspector_metadata("width", move |info| {
46                info.add_dimension("width", DimensionConstraint::Points(width));
47            }));
48        self.then(modifier)
49    }
50
51    /// Declare the preferred height of the content to be exactly `height`dp.
52    ///
53    /// The incoming measurement constraints may override this value, forcing the content
54    /// to be either smaller or larger.
55    ///
56    /// Matches Kotlin: `Modifier.height(height: Dp)`
57    ///
58    /// Example: `Modifier::empty().width(100.0).height(200.0)`
59    pub fn height(self, height: f32) -> Self {
60        let modifier = Self::with_element(SizeElement::new(None, Some(height)))
61            .with_inspector_metadata(inspector_metadata("height", move |info| {
62                info.add_dimension("height", DimensionConstraint::Points(height));
63            }));
64        self.then(modifier)
65    }
66
67    /// Declare the width of the content based on its intrinsic size.
68    ///
69    /// Matches Kotlin: `Modifier.width(IntrinsicSize)`
70    pub fn width_intrinsic(self, intrinsic: IntrinsicSize) -> Self {
71        let modifier = Self::with_element(IntrinsicSizeElement::width(intrinsic))
72            .with_inspector_metadata(inspector_metadata("widthIntrinsic", move |info| {
73                info.add_dimension("width", DimensionConstraint::Intrinsic(intrinsic));
74            }));
75        self.then(modifier)
76    }
77
78    /// Declare the height of the content based on its intrinsic size.
79    ///
80    /// Matches Kotlin: `Modifier.height(IntrinsicSize)`
81    pub fn height_intrinsic(self, intrinsic: IntrinsicSize) -> Self {
82        let modifier = Self::with_element(IntrinsicSizeElement::height(intrinsic))
83            .with_inspector_metadata(inspector_metadata("heightIntrinsic", move |info| {
84                info.add_dimension("height", DimensionConstraint::Intrinsic(intrinsic));
85            }));
86        self.then(modifier)
87    }
88
89    /// Declare the size of the content to be exactly `size`, ignoring incoming constraints.
90    ///
91    /// The incoming measurement constraints will not override this value. If the content
92    /// chooses a size that does not satisfy the incoming constraints, the parent layout
93    /// will be reported a size coerced in the constraints.
94    ///
95    /// Matches Kotlin: `Modifier.requiredSize(size: Dp)`
96    pub fn required_size(self, size: Size) -> Self {
97        let modifier = Self::with_element(SizeElement::with_constraints(
98            Some(size.width),
99            Some(size.width),
100            Some(size.height),
101            Some(size.height),
102            false,
103        ));
104        self.then(modifier)
105    }
106
107    /// Declare the width of the content to be exactly `width`, ignoring
108    /// incoming constraints; the height follows them.
109    ///
110    /// Matches Kotlin: `Modifier.requiredWidth(width: Dp)`
111    pub fn required_width(self, width: f32) -> Self {
112        let modifier = Self::with_element(SizeElement::with_constraints(
113            Some(width),
114            Some(width),
115            None,
116            None,
117            false,
118        ));
119        self.then(modifier)
120    }
121
122    /// Declare the height of the content to be exactly `height`, ignoring
123    /// incoming constraints; the width follows them.
124    ///
125    /// Matches Kotlin: `Modifier.requiredHeight(height: Dp)`
126    pub fn required_height(self, height: f32) -> Self {
127        let modifier = Self::with_element(SizeElement::with_constraints(
128            None,
129            None,
130            Some(height),
131            Some(height),
132            false,
133        ));
134        self.then(modifier)
135    }
136
137    /// Keep the width of the content between `min` and `max`, as far as the
138    /// incoming constraints allow. `f32::INFINITY` leaves the upper side open.
139    ///
140    /// Matches Kotlin: `Modifier.widthIn(min: Dp, max: Dp)`
141    ///
142    /// Example: `Modifier::empty().width_in(48.0, f32::INFINITY)`
143    pub fn width_in(self, min: f32, max: f32) -> Self {
144        let modifier = Self::with_element(SizeElement::with_constraints(
145            bound(min),
146            bound(max),
147            None,
148            None,
149            true,
150        ))
151        .with_inspector_metadata(inspector_metadata("widthIn", move |info| {
152            info.add_dimension("minWidth", DimensionConstraint::Points(min));
153        }));
154        self.then(modifier)
155    }
156
157    /// Keep the height of the content between `min` and `max`, as far as the
158    /// incoming constraints allow. `f32::INFINITY` leaves the upper side open.
159    /// A text field or a chip that a finger has to find gets its 24 points
160    /// this way without growing to the 48 of
161    /// [`minimum_interactive_component_size`](Self::minimum_interactive_component_size).
162    ///
163    /// Matches Kotlin: `Modifier.heightIn(min: Dp, max: Dp)`
164    ///
165    /// Example: `Modifier::empty().height_in(24.0, f32::INFINITY)`
166    pub fn height_in(self, min: f32, max: f32) -> Self {
167        let modifier = Self::with_element(SizeElement::with_constraints(
168            None,
169            None,
170            bound(min),
171            bound(max),
172            true,
173        ))
174        .with_inspector_metadata(inspector_metadata("heightIn", move |info| {
175            info.add_dimension("minHeight", DimensionConstraint::Points(min));
176        }));
177        self.then(modifier)
178    }
179
180    /// Keep the size of the content between `min` and `max` on each side, as
181    /// far as the incoming constraints allow. `f32::INFINITY` on a side of
182    /// `max` leaves it open.
183    ///
184    /// Matches Kotlin: `Modifier.sizeIn(minWidth, minHeight, maxWidth, maxHeight)`
185    ///
186    /// Example: `Modifier::empty().size_in(Size::new(24.0, 24.0), Size::new(f32::INFINITY, 40.0))`
187    pub fn size_in(self, min: Size, max: Size) -> Self {
188        let modifier = Self::with_element(SizeElement::with_constraints(
189            bound(min.width),
190            bound(max.width),
191            bound(min.height),
192            bound(max.height),
193            true,
194        ))
195        .with_inspector_metadata(inspector_metadata("sizeIn", move |info| {
196            info.add_dimension("minWidth", DimensionConstraint::Points(min.width));
197            info.add_dimension("minHeight", DimensionConstraint::Points(min.height));
198        }));
199        self.then(modifier)
200    }
201}
202
203/// A finite side of a size bound; an infinite or zero bound is no bound.
204fn bound(value: f32) -> Option<f32> {
205    (value.is_finite() && value > 0.0).then_some(value)
206}