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}