1use euclid::BoolVector2D;
2use zng_wgt::prelude::*;
3
4use crate::WIDGET_SIZE;
5
6#[property(SIZE, default(Size::default()))]
27pub fn size(child: impl IntoUiNode, size: impl IntoVar<Size>) -> UiNode {
28 let size = size.into_var();
29 match_node(child, move |child, op| match op {
30 UiNodeOp::Init => {
31 WIDGET.sub_var(&size);
32 child.init();
33 size.with(|l| WIDGET_SIZE.set(l));
34 }
35 UiNodeOp::Update { updates } => {
36 child.update(updates);
37 size.with_new(|s| {
38 WIDGET_SIZE.set(s);
39 WIDGET.layout();
40 });
41 }
42 UiNodeOp::Measure { wm, desired_size } => {
43 child.delegated();
44 *desired_size = SizeLayout::new(&size, || child.node().measure(wm)).measure(child.node(), wm);
45 }
46 UiNodeOp::Layout { wl, final_size } => {
47 child.delegated();
48 *final_size = SizeLayout::new(&size, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
49 }
50 _ => {}
51 })
52}
53struct SizeLayout {
54 parent_constraints: PxConstraints2d,
55 constraints: PxConstraints2d,
56 size: PxSize,
57 is_default: BoolVector2D,
58}
59impl SizeLayout {
60 pub fn new(size: &Var<Size>, measure: impl FnOnce() -> PxSize) -> Self {
62 let parent_constraints = LAYOUT.constraints();
63 let mut constraints = parent_constraints;
64 let mut is_default = BoolVector2D { x: true, y: true };
65 let mut size_px = PxSize::zero();
66 size.with(|s| {
67 let unit_constraints = parent_constraints.with_new_min(Px(0), Px(0));
68 let mut dft = PxSize::zero();
69 if !s.width.is_default() || s.height.is_default() && s.width.has_default() || s.height.has_default() {
70 dft = measure();
73 }
74 if !s.width.is_default() {
75 is_default.x = false;
76 size_px.width = LAYOUT.with_constraints(unit_constraints, || s.width.layout_dft_x(dft.width));
77 size_px.width = unit_constraints.x.clamp(size_px.width);
78 constraints.x = PxConstraints::new_exact(size_px.width);
79 }
80 if !s.height.is_default() {
81 is_default.y = false;
82 size_px.height = LAYOUT.with_constraints(unit_constraints, || s.height.layout_dft_y(dft.height));
83 size_px.height = unit_constraints.y.clamp(size_px.height);
84 constraints.y = PxConstraints::new_exact(size_px.height);
85 }
86 });
87 Self {
88 parent_constraints,
89 constraints,
90 size: size_px,
91 is_default,
92 }
93 }
94
95 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
96 if self.is_default.all() {
97 return child.measure(wm);
99 }
100
101 let size = if self.is_default.any() {
102 LAYOUT.with_constraints(self.constraints, || wm.measure_block(child))
103 } else {
104 wm.measure_block(&mut UiNode::nil());
105 self.size
106 };
107
108 self.clamp_outer_bounds(size)
109 }
110
111 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
112 if self.is_default.all() {
113 return child.layout(wl);
114 }
115
116 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
117
118 self.clamp_outer_bounds(size)
119 }
120
121 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
123 if !self.is_default.x {
124 size.width = Align::TOP_LEFT.measure_x(self.size.width, self.parent_constraints.x);
125 }
126 if !self.is_default.y {
127 size.height = Align::TOP_LEFT.measure_y(self.size.height, self.parent_constraints.y);
128 }
129 size
130 }
131}
132
133#[property(SIZE, default(Length::Default))]
151pub fn width(child: impl IntoUiNode, width: impl IntoVar<Length>) -> UiNode {
152 let width = width.into_var();
153 match_node(child, move |child, op| match op {
154 UiNodeOp::Init => {
155 WIDGET.sub_var(&width);
156 child.init();
157 width.with(|s| WIDGET_SIZE.set_width(s));
158 }
159 UiNodeOp::Update { updates } => {
160 child.update(updates);
161 width.with_new(|w| {
162 WIDGET_SIZE.set_width(w);
163 WIDGET.layout();
164 });
165 }
166 UiNodeOp::Measure { wm, desired_size } => {
167 child.delegated();
168 *desired_size = WidthLayout::new(&width, || child.node().measure(wm)).measure(child.node(), wm);
169 }
170 UiNodeOp::Layout { wl, final_size } => {
171 child.delegated();
172 *final_size = WidthLayout::new(&width, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
173 }
174 _ => {}
175 })
176}
177struct WidthLayout {
178 parent_constraints: PxConstraints2d,
179 constraints: PxConstraints2d,
180 width: Px,
181 is_default: bool,
182}
183impl WidthLayout {
184 pub fn new(width: &Var<Length>, measure: impl FnOnce() -> PxSize) -> Self {
185 let parent_constraints = LAYOUT.constraints();
186 let mut constraints = parent_constraints;
187 let mut is_default = true;
188 let mut width_px = Px(0);
189 width.with(|w| {
190 if !w.is_default() {
191 let mut dft = Px(0);
192 if w.has_default() {
193 dft = measure().width;
195 }
196
197 let unit_constraints = parent_constraints.with_new_min_x(Px(0));
198 is_default = false;
199
200 width_px = LAYOUT.with_constraints(unit_constraints, || w.layout_dft_x(dft));
201 width_px = unit_constraints.x.clamp(width_px);
202 constraints.x = PxConstraints::new_exact(width_px);
203 }
204 });
205 Self {
206 parent_constraints,
207 constraints,
208 width: width_px,
209 is_default,
210 }
211 }
212
213 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
214 if self.is_default {
215 return child.measure(wm);
216 }
217
218 let size = LAYOUT.with_constraints(self.constraints, || wm.measure_block(child));
219
220 self.clamp_outer_bounds(size)
221 }
222
223 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
224 if self.is_default {
225 return child.layout(wl);
226 }
227
228 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
229
230 self.clamp_outer_bounds(size)
231 }
232
233 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
234 if !self.is_default {
235 size.width = Align::TOP_LEFT.measure_x(self.width, self.parent_constraints.x);
236 }
237 size
238 }
239}
240
241#[property(SIZE, default(Length::Default))]
259pub fn height(child: impl IntoUiNode, height: impl IntoVar<Length>) -> UiNode {
260 let height = height.into_var();
261 match_node(child, move |child, op| match op {
262 UiNodeOp::Init => {
263 WIDGET.sub_var(&height);
264 child.init();
265 height.with(|s| WIDGET_SIZE.set_height(s));
266 }
267 UiNodeOp::Update { updates } => {
268 child.update(updates);
269
270 height.with_new(|h| {
271 WIDGET_SIZE.set_height(h);
272 WIDGET.layout();
273 });
274 }
275 UiNodeOp::Measure { wm, desired_size } => {
276 child.delegated();
277 *desired_size = HeightLayout::new(&height, || child.node().measure(wm)).measure(child.node(), wm);
278 }
279 UiNodeOp::Layout { wl, final_size } => {
280 child.delegated();
281 *final_size = HeightLayout::new(&height, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
282 }
283 _ => {}
284 })
285}
286struct HeightLayout {
287 parent_constraints: PxConstraints2d,
288 constraints: PxConstraints2d,
289 height: Px,
290 is_default: bool,
291}
292impl HeightLayout {
293 pub fn new(height: &Var<Length>, measure: impl FnOnce() -> PxSize) -> Self {
294 let parent_constraints = LAYOUT.constraints();
295 let mut constraints = parent_constraints;
296 let mut is_default = true;
297 let mut height_px = Px(0);
298 height.with(|h| {
299 if !h.is_default() {
300 let mut dft = Px(0);
301 if h.has_default() {
302 dft = measure().height;
304 }
305 let unit_constraints = parent_constraints.with_new_min_y(Px(0));
306 is_default = false;
307 height_px = LAYOUT.with_constraints(unit_constraints, || h.layout_dft_y(dft));
308 height_px = unit_constraints.y.clamp(height_px);
309 constraints.y = PxConstraints::new_exact(height_px);
310 }
311 });
312 Self {
313 parent_constraints,
314 constraints,
315 height: height_px,
316 is_default,
317 }
318 }
319
320 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
321 if self.is_default {
322 return child.measure(wm);
323 }
324
325 let size = LAYOUT.with_constraints(self.constraints, || wm.measure_block(child));
326
327 self.clamp_outer_bounds(size)
328 }
329
330 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
331 if self.is_default {
332 return child.layout(wl);
333 }
334
335 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
336
337 self.clamp_outer_bounds(size)
338 }
339
340 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
341 if !self.is_default {
342 size.height = Align::TOP_LEFT.measure_y(self.height, self.parent_constraints.y);
343 }
344 size
345 }
346}