1use euclid::BoolVector2D;
2use zng_wgt::prelude::*;
3
4use crate::WIDGET_SIZE;
5
6#[property(SIZE, default(Size::default()))]
21pub fn force_size(child: impl IntoUiNode, size: impl IntoVar<Size>) -> UiNode {
22 let size = size.into_var();
23 match_node(child, move |child, op| match op {
24 UiNodeOp::Init => {
25 WIDGET.sub_var(&size);
26 child.init();
27 size.with(|l| WIDGET_SIZE.set(l));
28 }
29 UiNodeOp::Update { updates } => {
30 child.update(updates);
31 size.with_new(|s| {
32 WIDGET_SIZE.set(s);
33 WIDGET.layout();
34 });
35 }
36 UiNodeOp::Measure { wm, desired_size } => {
37 child.delegated();
38 *desired_size = ForceSizeLayout::new(&size, || child.node().measure(wm)).measure(child.node(), wm);
39 }
40 UiNodeOp::Layout { wl, final_size } => {
41 child.delegated();
42 *final_size = ForceSizeLayout::new(&size, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
43 }
44 _ => {}
45 })
46}
47struct ForceSizeLayout {
48 parent_constraints: PxConstraints2d,
49 constraints: PxConstraints2d,
50 size: PxSize,
51 is_default: BoolVector2D,
52}
53impl ForceSizeLayout {
54 pub fn new(size: &Var<Size>, measure: impl FnOnce() -> PxSize) -> Self {
55 let parent_constraints = LAYOUT.constraints();
56 let mut constraints = parent_constraints;
57 let mut is_default = BoolVector2D { x: true, y: true };
58 let mut size_px = PxSize::zero();
59 size.with(|s| {
60 let mut dft = PxSize::zero();
61 if !s.width.is_default() || s.height.is_default() && s.width.has_default() || s.height.has_default() {
62 dft = measure();
65 }
66
67 if !s.width.is_default() {
68 is_default.x = false;
69 size_px.width = LAYOUT.with_constraints(parent_constraints.with_fill_x(parent_constraints.x.is_bounded()), || {
70 s.width.layout_dft_x(dft.width)
71 });
72 constraints.x = PxConstraints::new_exact(size_px.width);
73 }
74 if !s.height.is_default() {
75 is_default.y = false;
76 size_px.height = LAYOUT.with_constraints(parent_constraints.with_fill_y(parent_constraints.y.is_bounded()), || {
77 s.width.layout_dft_y(dft.height)
78 });
79 constraints.y = PxConstraints::new_exact(size_px.height);
80 }
81 });
82 Self {
83 parent_constraints,
84 constraints,
85 size: size_px,
86 is_default,
87 }
88 }
89
90 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
91 if self.is_default.all() {
92 return child.measure(wm);
93 }
94
95 let size = if self.is_default.any() {
96 LAYOUT.with_constraints(self.constraints, || wm.measure_block(child))
97 } else {
98 wm.measure_block(&mut UiNode::nil());
99 self.size
100 };
101
102 self.clamp_outer_bounds(size)
103 }
104
105 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
106 if self.is_default.all() {
107 return child.layout(wl);
108 }
109
110 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
111
112 self.clamp_outer_bounds(size)
113 }
114
115 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
116 if !self.is_default.x {
117 size.width = Align::TOP_LEFT.measure_x(self.size.width, self.parent_constraints.x);
118 }
119 if !self.is_default.y {
120 size.height = Align::TOP_LEFT.measure_y(self.size.height, self.parent_constraints.y);
121 }
122 size
123 }
124}
125
126#[property(SIZE, default(Length::Default))]
138pub fn force_width(child: impl IntoUiNode, width: impl IntoVar<Length>) -> UiNode {
139 let width = width.into_var();
140 match_node(child, move |child, op| match op {
141 UiNodeOp::Init => {
142 WIDGET.sub_var(&width);
143 child.init();
144 width.with(|s| WIDGET_SIZE.set_width(s));
145 }
146 UiNodeOp::Update { updates } => {
147 child.update(updates);
148 width.with_new(|w| {
149 WIDGET_SIZE.set_width(w);
150 WIDGET.layout();
151 });
152 }
153 UiNodeOp::Measure { wm, desired_size } => {
154 child.delegated();
155 *desired_size = ForceWidthLayout::new(&width, || child.node().measure(wm)).measure(child.node(), wm);
156 }
157 UiNodeOp::Layout { wl, final_size } => {
158 child.delegated();
159 *final_size = ForceWidthLayout::new(&width, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
160 }
161 _ => {}
162 })
163}
164struct ForceWidthLayout {
165 parent_constraints: PxConstraints2d,
166 constraints: PxConstraints2d,
167 width: Px,
168 is_default: bool,
169}
170impl ForceWidthLayout {
171 pub fn new(width: &Var<Length>, measure: impl FnOnce() -> PxSize) -> Self {
172 let parent_constraints = LAYOUT.constraints();
173 let mut constraints = parent_constraints;
174 let mut is_default = true;
175 let mut width_px = Px(0);
176 width.with(|w| {
177 if !w.is_default() {
178 is_default = false;
179 let mut dft = Px(0);
180 if w.has_default() {
181 dft = measure().width;
183 }
184 width_px = LAYOUT.with_constraints(parent_constraints.with_fill_x(parent_constraints.x.is_bounded()), || {
185 w.layout_dft_x(dft)
186 });
187 constraints.x = PxConstraints::new_exact(width_px);
188 }
189 });
190 Self {
191 parent_constraints,
192 constraints,
193 width: width_px,
194 is_default,
195 }
196 }
197
198 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
199 if self.is_default {
200 return child.measure(wm);
201 }
202
203 let size = LAYOUT.with_constraints(self.constraints, || wm.measure_block(child));
204
205 self.clamp_outer_bounds(size)
206 }
207
208 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
209 if self.is_default {
210 return child.layout(wl);
211 }
212
213 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
214
215 self.clamp_outer_bounds(size)
216 }
217
218 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
219 if !self.is_default {
220 size.width = Align::TOP_LEFT.measure_x(self.width, self.parent_constraints.x);
221 }
222 size
223 }
224}
225
226#[property(SIZE, default(Length::Default))]
238pub fn force_height(child: impl IntoUiNode, height: impl IntoVar<Length>) -> UiNode {
239 let height = height.into_var();
240 match_node(child, move |child, op| match op {
241 UiNodeOp::Init => {
242 WIDGET.sub_var(&height);
243 child.init();
244 height.with(|s| WIDGET_SIZE.set_height(s));
245 }
246 UiNodeOp::Update { updates } => {
247 child.update(updates);
248
249 height.with_new(|h| {
250 WIDGET_SIZE.set_height(h);
251 WIDGET.layout();
252 });
253 }
254 UiNodeOp::Measure { wm, desired_size } => {
255 child.delegated();
256 *desired_size = ForceHeightLayout::new(&height, || child.node().measure(wm)).measure(child.node(), wm);
257 }
258 UiNodeOp::Layout { wl, final_size } => {
259 child.delegated();
260 *final_size = ForceHeightLayout::new(&height, || child.node().measure(&mut wl.to_measure(None))).layout(child.node(), wl);
261 }
262 _ => {}
263 })
264}
265struct ForceHeightLayout {
266 parent_constraints: PxConstraints2d,
267 constraints: PxConstraints2d,
268 height: Px,
269 is_default: bool,
270}
271impl ForceHeightLayout {
272 pub fn new(height: &Var<Length>, measure: impl FnOnce() -> PxSize) -> Self {
273 let parent_constraints = LAYOUT.constraints();
274 let mut constraints = parent_constraints;
275 let mut is_default = true;
276 let mut height_px = Px(0);
277 height.with(|h| {
278 if !h.is_default() {
279 is_default = false;
280 let mut dft = Px(0);
281 if h.has_default() {
282 dft = measure().height;
284 }
285 height_px = LAYOUT.with_constraints(parent_constraints.with_fill_y(parent_constraints.y.is_bounded()), || {
286 h.layout_dft_y(dft)
287 });
288 constraints.y = PxConstraints::new_exact(height_px);
289 }
290 });
291 Self {
292 parent_constraints,
293 constraints,
294 height: height_px,
295 is_default,
296 }
297 }
298
299 pub fn measure(&self, child: &mut UiNode, wm: &mut WidgetMeasure) -> PxSize {
300 if self.is_default {
301 return child.measure(wm);
302 }
303
304 let size = LAYOUT.with_constraints(self.constraints, || wm.measure_block(child));
305
306 self.clamp_outer_bounds(size)
307 }
308
309 pub fn layout(&self, child: &mut UiNode, wl: &mut WidgetLayout) -> PxSize {
310 if self.is_default {
311 return child.layout(wl);
312 }
313
314 let size = LAYOUT.with_constraints(self.constraints, || child.layout(wl));
315
316 self.clamp_outer_bounds(size)
317 }
318
319 fn clamp_outer_bounds(&self, mut size: PxSize) -> PxSize {
320 if !self.is_default {
321 size.height = Align::TOP_LEFT.measure_y(self.height, self.parent_constraints.y);
322 }
323 size
324 }
325}