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