1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use zng_wgt::prelude::*;
13
14#[property(LAYOUT, default((0, 0)))]
22pub fn offset(child: impl UiNode, offset: impl IntoVar<Vector>) -> impl UiNode {
23 let offset = offset.into_var();
24 match_node(child, move |child, op| match op {
25 UiNodeOp::Init => {
26 WIDGET.sub_var_layout(&offset);
27 }
28 UiNodeOp::Layout { wl, final_size } => {
29 let size = child.layout(wl);
30 let offset = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(LAYOUT.constraints().fill_size().max(size)), || {
31 offset.layout()
32 });
33 wl.translate(offset);
34 *final_size = size;
35 }
36 _ => {}
37 })
38}
39
40#[property(LAYOUT, default(0))]
48pub fn x(child: impl UiNode, x: impl IntoVar<Length>) -> impl UiNode {
49 let x = x.into_var();
50 match_node(child, move |child, op| match op {
51 UiNodeOp::Init => {
52 WIDGET.sub_var_layout(&x);
53 }
54 UiNodeOp::Layout { wl, final_size } => {
55 let size = child.layout(wl);
56
57 let x = with_fill_metrics(LAYOUT.constraints(), |_| x.layout_x());
58 wl.translate(PxVector::new(x, Px(0)));
59 *final_size = size;
60 }
61 _ => {}
62 })
63}
64
65#[property(LAYOUT, default(0))]
73pub fn y(child: impl UiNode, y: impl IntoVar<Length>) -> impl UiNode {
74 let y = y.into_var();
75 match_node(child, move |child, op| match op {
76 UiNodeOp::Init => {
77 WIDGET.sub_var_layout(&y);
78 }
79 UiNodeOp::Layout { wl, final_size } => {
80 let size = child.layout(wl);
81 let y = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(LAYOUT.constraints().fill_size().max(size)), || {
82 y.layout_y()
83 });
84 wl.translate(PxVector::new(Px(0), y));
85 *final_size = size;
86 }
87 _ => {}
88 })
89}
90
91#[property(SIZE-2, default((0, 0)))]
106pub fn min_size(child: impl UiNode, min_size: impl IntoVar<Size>) -> impl UiNode {
107 let min_size = min_size.into_var();
108 match_node(child, move |child, op| match op {
109 UiNodeOp::Init => {
110 WIDGET.sub_var_layout(&min_size);
111 }
112 UiNodeOp::Measure { wm, desired_size } => {
113 let c = LAYOUT.constraints();
114 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_size.layout());
115 let size = LAYOUT.with_constraints(c.with_min_size(min), || wm.measure_block(child));
116 *desired_size = size.max(min);
117 }
118 UiNodeOp::Layout { wl, final_size } => {
119 let c = LAYOUT.constraints();
120 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_size.layout());
121 let size = LAYOUT.with_constraints(c.with_min_size(min), || child.layout(wl));
122 *final_size = size.max(min);
123 }
124 _ => {}
125 })
126}
127
128#[property(SIZE-2, default(0))]
142pub fn min_width(child: impl UiNode, min_width: impl IntoVar<Length>) -> impl UiNode {
143 let min_width = min_width.into_var();
144 match_node(child, move |child, op| match op {
145 UiNodeOp::Init => {
146 WIDGET.sub_var_layout(&min_width);
147 }
148 UiNodeOp::Measure { wm, desired_size } => {
149 let c = LAYOUT.constraints();
150 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_width.layout_x());
151 let mut size = LAYOUT.with_constraints(c.with_min_x(min), || wm.measure_block(child));
152 size.width = size.width.max(min);
153 *desired_size = size;
154 }
155 UiNodeOp::Layout { wl, final_size } => {
156 let c = LAYOUT.constraints();
157 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_width.layout_x());
158 let mut size = LAYOUT.with_constraints(c.with_min_x(min), || child.layout(wl));
159 size.width = size.width.max(min);
160 *final_size = size;
161 }
162 _ => {}
163 })
164}
165
166#[property(SIZE-2, default(0))]
180pub fn min_height(child: impl UiNode, min_height: impl IntoVar<Length>) -> impl UiNode {
181 let min_height = min_height.into_var();
182 match_node(child, move |child, op| match op {
183 UiNodeOp::Init => {
184 WIDGET.sub_var_layout(&min_height);
185 }
186 UiNodeOp::Measure { wm, desired_size } => {
187 let c = LAYOUT.constraints();
188 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_height.layout_y());
189 let mut size = LAYOUT.with_constraints(c.with_min_y(min), || wm.measure_block(child));
190 size.height = size.height.max(min);
191 *desired_size = size;
192 }
193 UiNodeOp::Layout { wl, final_size } => {
194 let c = LAYOUT.constraints();
195 let min = LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || min_height.layout_y());
196 let mut size = LAYOUT.with_constraints(c.with_min_y(min), || child.layout(wl));
197 size.height = size.height.max(min);
198 *final_size = size;
199 }
200 _ => {}
201 })
202}
203
204#[property(SIZE-1, default(PxSize::splat(Px::MAX)))]
219pub fn max_size(child: impl UiNode, max_size: impl IntoVar<Size>) -> impl UiNode {
220 let max_size = max_size.into_var();
221 match_node(child, move |child, op| match op {
222 UiNodeOp::Init => {
223 WIDGET.sub_var_layout(&max_size);
224 }
225 UiNodeOp::Measure { wm, desired_size } => {
226 let parent_constraints = LAYOUT.constraints();
227 let max = with_fill_metrics(parent_constraints, |d| max_size.layout_dft(d));
228 let size = LAYOUT.with_constraints(parent_constraints.with_max_size(max), || wm.measure_block(child));
229 *desired_size = size.min(max);
230 *desired_size = Align::TOP_LEFT.measure(*desired_size, parent_constraints);
231 }
232 UiNodeOp::Layout { wl, final_size } => {
233 let parent_constraints = LAYOUT.constraints();
234 let max = with_fill_metrics(parent_constraints, |d| max_size.layout_dft(d));
235 let size = LAYOUT.with_constraints(parent_constraints.with_max_size(max), || child.layout(wl));
236 *final_size = Align::TOP_LEFT.measure(size.min(max), parent_constraints);
237 }
238 _ => {}
239 })
240}
241
242#[property(SIZE-1, default(Px::MAX))]
256pub fn max_width(child: impl UiNode, max_width: impl IntoVar<Length>) -> impl UiNode {
257 let max_width = max_width.into_var();
258 match_node(child, move |child, op| match op {
259 UiNodeOp::Init => {
260 WIDGET.sub_var_layout(&max_width);
261 }
262 UiNodeOp::Measure { wm, desired_size } => {
263 let parent_constraints = LAYOUT.constraints();
264 let max = with_fill_metrics(parent_constraints, |d| max_width.layout_dft_x(d.width));
265
266 let mut size = LAYOUT.with_constraints(parent_constraints.with_max_x(max), || wm.measure_block(child));
267 size.width = size.width.min(max);
268 *desired_size = size;
269 desired_size.width = Align::TOP_LEFT.measure_x(desired_size.width, parent_constraints.x);
270 }
271 UiNodeOp::Layout { wl, final_size } => {
272 let parent_constraints = LAYOUT.constraints();
273 let max = with_fill_metrics(parent_constraints, |d| max_width.layout_dft_x(d.width));
274
275 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_x(max), || child.layout(wl));
276 size.width = size.width.min(max);
277 *final_size = size;
278 final_size.width = Align::TOP_LEFT.measure_x(final_size.width, parent_constraints.x);
279 }
280 _ => {}
281 })
282}
283
284#[property(SIZE-1, default(Px::MAX))]
298pub fn max_height(child: impl UiNode, max_height: impl IntoVar<Length>) -> impl UiNode {
299 let max_height = max_height.into_var();
300 match_node(child, move |child, op| match op {
301 UiNodeOp::Init => {
302 WIDGET.sub_var_layout(&max_height);
303 }
304 UiNodeOp::Measure { wm, desired_size } => {
305 let parent_constraints = LAYOUT.constraints();
306 let max = with_fill_metrics(parent_constraints, |d| max_height.layout_dft_y(d.height));
307
308 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_y(max), || wm.measure_block(child));
309 size.height = size.height.min(max);
310 *desired_size = size;
311 desired_size.height = Align::TOP_LEFT.measure_y(desired_size.height, parent_constraints.y);
312 }
313 UiNodeOp::Layout { wl, final_size } => {
314 let parent_constraints = LAYOUT.constraints();
315 let max = with_fill_metrics(parent_constraints, |d| max_height.layout_dft_y(d.height));
316
317 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_max_y(max), || child.layout(wl));
318 size.height = size.height.min(max);
319 *final_size = size;
320 final_size.height = Align::TOP_LEFT.measure_y(final_size.height, parent_constraints.y);
321 }
322 _ => {}
323 })
324}
325
326#[property(SIZE, default(Size::default()))]
347pub fn size(child: impl UiNode, size: impl IntoVar<Size>) -> impl UiNode {
348 let size = size.into_var();
349 match_node(child, move |child, op| match op {
350 UiNodeOp::Init => {
351 WIDGET.sub_var(&size);
352 child.init();
353 size.with(|l| WIDGET_SIZE.set(l));
354 }
355 UiNodeOp::Update { updates } => {
356 child.update(updates);
357 size.with_new(|s| {
358 WIDGET_SIZE.set(s);
359 WIDGET.layout();
360 });
361 }
362 UiNodeOp::Measure { wm, desired_size } => {
363 child.delegated();
364 wm.measure_block(&mut NilUiNode); let parent_constraints = LAYOUT.constraints();
367
368 *desired_size = with_fill_metrics(parent_constraints.with_new_min(Px(0), Px(0)), |d| size.layout_dft(d));
369 *desired_size = Align::TOP_LEFT.measure(*desired_size, parent_constraints);
370 }
371 UiNodeOp::Layout { wl, final_size } => {
372 let parent_constraints = LAYOUT.constraints();
373 let constraints = parent_constraints.with_new_min(Px(0), Px(0));
374
375 let size = with_fill_metrics(constraints, |d| size.layout_dft(d));
376 let size = constraints.clamp_size(size);
377 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || child.layout(wl));
378
379 *final_size = Align::TOP_LEFT.measure(size, parent_constraints);
380 }
381 _ => {}
382 })
383}
384
385#[property(SIZE, default(Length::Default))]
403pub fn width(child: impl UiNode, width: impl IntoVar<Length>) -> impl UiNode {
404 let width = width.into_var();
405 match_node(child, move |child, op| match op {
406 UiNodeOp::Init => {
407 WIDGET.sub_var(&width);
408 child.init();
409 width.with(|s| WIDGET_SIZE.set_width(s));
410 }
411 UiNodeOp::Update { updates } => {
412 child.update(updates);
413 width.with_new(|w| {
414 WIDGET_SIZE.set_width(w);
415 WIDGET.layout();
416 });
417 }
418 UiNodeOp::Measure { wm, desired_size } => {
419 let parent_constraints = LAYOUT.constraints();
420 let constraints = parent_constraints.with_new_min_x(Px(0));
421
422 let width = with_fill_metrics(constraints, |d| width.layout_dft_x(d.width));
423 let width = constraints.x.clamp(width);
424 *desired_size = LAYOUT.with_constraints(constraints.with_exact_x(width), || wm.measure_block(child));
425 desired_size.width = Align::TOP_LEFT.measure_x(width, parent_constraints.x);
426 }
427 UiNodeOp::Layout { wl, final_size } => {
428 let parent_constraints = LAYOUT.constraints();
429 let constraints = parent_constraints.with_new_min_x(Px(0));
430
431 let width = with_fill_metrics(constraints, |d| width.layout_dft_x(d.width));
432 let width = constraints.x.clamp(width);
433 *final_size = LAYOUT.with_constraints(constraints.with_exact_x(width), || child.layout(wl));
434 final_size.width = Align::TOP_LEFT.measure_x(width, parent_constraints.x);
435 }
436 _ => {}
437 })
438}
439
440#[property(SIZE, default(Length::Default))]
458pub fn height(child: impl UiNode, height: impl IntoVar<Length>) -> impl UiNode {
459 let height = height.into_var();
460 match_node(child, move |child, op| match op {
461 UiNodeOp::Init => {
462 WIDGET.sub_var(&height);
463 child.init();
464 height.with(|s| WIDGET_SIZE.set_height(s));
465 }
466 UiNodeOp::Update { updates } => {
467 child.update(updates);
468
469 height.with_new(|h| {
470 WIDGET_SIZE.set_height(h);
471 WIDGET.layout();
472 });
473 }
474 UiNodeOp::Measure { wm, desired_size } => {
475 let parent_constraints = LAYOUT.constraints();
476 let constraints = parent_constraints.with_new_min_y(Px(0));
477
478 let height = with_fill_metrics(constraints, |d| height.layout_dft_x(d.height));
479 let height = constraints.x.clamp(height);
480 *desired_size = LAYOUT.with_constraints(constraints.with_exact_y(height), || wm.measure_block(child));
481 desired_size.height = Align::TOP_LEFT.measure_y(height, parent_constraints.y);
482 }
483 UiNodeOp::Layout { wl, final_size } => {
484 let parent_constraints = LAYOUT.constraints();
485 let constraints = parent_constraints.with_new_min_y(Px(0));
486
487 let height = with_fill_metrics(constraints, |d| height.layout_dft_y(d.height));
488 let height = constraints.y.clamp(height);
489 *final_size = LAYOUT.with_constraints(constraints.with_exact_y(height), || child.layout(wl));
490 final_size.height = Align::TOP_LEFT.measure_y(height, parent_constraints.y);
491 }
492 _ => {}
493 })
494}
495
496#[property(SIZE, default(Size::default()))]
511pub fn force_size(child: impl UiNode, size: impl IntoVar<Size>) -> impl UiNode {
512 let size = size.into_var();
513 match_node(child, move |child, op| match op {
514 UiNodeOp::Init => {
515 WIDGET.sub_var(&size);
516 child.init();
517 size.with(|l| WIDGET_SIZE.set(l));
518 }
519 UiNodeOp::Update { updates } => {
520 child.update(updates);
521 size.with_new(|s| {
522 WIDGET_SIZE.set(s);
523 WIDGET.layout();
524 });
525 }
526 UiNodeOp::Measure { wm, desired_size } => {
527 child.delegated();
528 let c = LAYOUT.constraints().with_new_min(Px(0), Px(0)).with_fill(false, false);
529 let size = with_fill_metrics(c, |d| size.layout_dft(d));
530 wm.measure_block(&mut NilUiNode);
531 *desired_size = Align::TOP_LEFT.measure(size, c);
532 }
533 UiNodeOp::Layout { wl, final_size } => {
534 let c = LAYOUT.constraints().with_new_min(Px(0), Px(0)).with_fill(false, false);
535 let size = with_fill_metrics(c, |d| size.layout_dft(d));
536 LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || child.layout(wl));
537 *final_size = Align::TOP_LEFT.measure(size, c);
538 }
539 _ => {}
540 })
541}
542
543#[property(SIZE, default(Length::Default))]
555pub fn force_width(child: impl UiNode, width: impl IntoVar<Length>) -> impl UiNode {
556 let width = width.into_var();
557 match_node(child, move |child, op| match op {
558 UiNodeOp::Init => {
559 WIDGET.sub_var(&width);
560 child.init();
561 width.with(|s| WIDGET_SIZE.set_width(s));
562 }
563 UiNodeOp::Update { updates } => {
564 child.update(updates);
565 width.with_new(|w| {
566 WIDGET_SIZE.set_width(w);
567 WIDGET.layout();
568 });
569 }
570 UiNodeOp::Measure { wm, desired_size } => {
571 let c = LAYOUT.constraints().with_new_min_x(Px(0)).with_fill_x(false);
572
573 let width = with_fill_metrics(c, |d| width.layout_dft_x(d.width));
574 let mut size = LAYOUT.with_constraints(c.with_unbounded_x().with_exact_x(width), || wm.measure_block(child));
575 size.width = Align::TOP_LEFT.measure_x(width, c.x);
576 *desired_size = size;
577 }
578 UiNodeOp::Layout { wl, final_size } => {
579 let c = LAYOUT.constraints().with_new_min_x(Px(0)).with_fill_x(false);
580
581 let width = with_fill_metrics(c, |d| width.layout_dft_x(d.width));
582 let mut size = LAYOUT.with_constraints(c.with_unbounded_x().with_exact_x(width), || child.layout(wl));
583 size.width = Align::TOP_LEFT.measure_x(width, c.x);
584 *final_size = size;
585 }
586 _ => {}
587 })
588}
589
590#[property(SIZE, default(Length::Default))]
602pub fn force_height(child: impl UiNode, height: impl IntoVar<Length>) -> impl UiNode {
603 let height = height.into_var();
604 match_node(child, move |child, op| match op {
605 UiNodeOp::Init => {
606 WIDGET.sub_var(&height);
607 child.init();
608 height.with(|s| WIDGET_SIZE.set_height(s));
609 }
610 UiNodeOp::Update { updates } => {
611 child.update(updates);
612
613 height.with_new(|h| {
614 WIDGET_SIZE.set_height(h);
615 WIDGET.layout();
616 });
617 }
618 UiNodeOp::Measure { wm, desired_size } => {
619 let c = LAYOUT.constraints().with_new_min_y(Px(0)).with_fill_y(false);
620
621 let height = with_fill_metrics(c, |d| height.layout_dft_y(d.height));
622 let mut size = LAYOUT.with_constraints(c.with_unbounded_y().with_exact_y(height), || wm.measure_block(child));
623 size.height = Align::TOP_LEFT.measure_y(height, c.y);
624 *desired_size = size;
625 }
626 UiNodeOp::Layout { wl, final_size } => {
627 let c = LAYOUT.constraints().with_new_min_y(Px(0)).with_fill_y(false);
628
629 let height = with_fill_metrics(c, |d| height.layout_dft_y(d.height));
630 let mut size = LAYOUT.with_constraints(c.with_unbounded_y().with_exact_y(height), || child.layout(wl));
631 size.height = Align::TOP_LEFT.measure_y(height, c.y);
632 *final_size = size;
633 }
634 _ => {}
635 })
636}
637
638fn with_fill_metrics<R>(c: PxConstraints2d, f: impl FnOnce(PxSize) -> R) -> R {
639 let dft = c.fill_size();
640 LAYOUT.with_constraints(c.with_fill_vector(c.is_bounded()), || f(dft))
641}
642
643#[property(BORDER, default(Length::Default))]
650pub fn baseline(child: impl UiNode, baseline: impl IntoVar<Length>) -> impl UiNode {
651 let baseline = baseline.into_var();
652 match_node(child, move |child, op| match op {
653 UiNodeOp::Init => {
654 WIDGET.sub_var_layout(&baseline);
655 }
656 UiNodeOp::Layout { wl, final_size } => {
657 let size = child.layout(wl);
658
659 let bounds = WIDGET.bounds();
660 let inner_size = bounds.inner_size();
661 let default = bounds.baseline();
662
663 let baseline = LAYOUT.with_constraints(LAYOUT.constraints().with_max_size(inner_size).with_fill(true, true), || {
664 baseline.layout_dft_y(default)
665 });
666 wl.set_baseline(baseline);
667
668 *final_size = size;
669 }
670 _ => {}
671 })
672}
673
674#[property(SIZE, default(false))]
679pub fn sticky_width(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
680 let sticky = sticky.into_var();
681 let mut sticky_after_layout = false;
682 match_node(child, move |child, op| match op {
683 UiNodeOp::Init => {
684 WIDGET.sub_var(&sticky);
685 }
686 UiNodeOp::Deinit => {
687 sticky_after_layout = false;
688 }
689 UiNodeOp::Update { .. } => {
690 if sticky.is_new() {
691 sticky_after_layout = false;
692 }
693 }
694 UiNodeOp::Measure { wm, desired_size } => {
695 if sticky_after_layout && sticky.get() {
696 let min = WIDGET.bounds().inner_size().width;
697 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || wm.measure_block(child));
698 size.width = size.width.max(min);
699 *desired_size = size;
700 }
701 }
702 UiNodeOp::Layout { wl, final_size } => {
703 let sticky = sticky.get();
704 if sticky_after_layout && sticky {
705 let min = WIDGET.bounds().inner_size().width;
706 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || child.layout(wl));
707 size.width = size.width.max(min);
708 *final_size = size;
709 }
710
711 sticky_after_layout = sticky;
713 }
714 _ => {}
715 })
716}
717
718#[property(SIZE, default(false))]
723pub fn sticky_height(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
724 let sticky = sticky.into_var();
725 let mut sticky_after_layout = false;
726 match_node(child, move |child, op| match op {
727 UiNodeOp::Init => {
728 WIDGET.sub_var(&sticky);
729 }
730 UiNodeOp::Deinit => {
731 sticky_after_layout = false;
732 }
733 UiNodeOp::Update { .. } => {
734 if sticky.is_new() {
735 sticky_after_layout = false;
736 }
737 }
738 UiNodeOp::Measure { wm, desired_size } => {
739 if sticky_after_layout && sticky.get() {
740 let min = WIDGET.bounds().inner_size().height;
741 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || wm.measure_block(child));
742 size.height = size.height.max(min);
743 *desired_size = size;
744 }
745 }
746 UiNodeOp::Layout { wl, final_size } => {
747 let sticky = sticky.get();
748 if sticky_after_layout && sticky {
749 let min = WIDGET.bounds().inner_size().height;
750 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || child.layout(wl));
751 size.height = size.height.max(min);
752 *final_size = size;
753 }
754
755 sticky_after_layout = sticky;
757 }
758 _ => {}
759 })
760}
761
762#[property(SIZE, default(false))]
767pub fn sticky_size(child: impl UiNode, sticky: impl IntoVar<bool>) -> impl UiNode {
768 let sticky = sticky.into_var();
769 let mut sticky_after_layout = false;
770 match_node(child, move |child, op| match op {
771 UiNodeOp::Init => {
772 WIDGET.sub_var(&sticky);
773 }
774 UiNodeOp::Deinit => {
775 sticky_after_layout = false;
776 }
777 UiNodeOp::Update { .. } => {
778 if sticky.is_new() {
779 sticky_after_layout = false;
780 }
781 }
782 UiNodeOp::Measure { wm, desired_size } => {
783 if sticky_after_layout && sticky.get() {
784 let min = WIDGET.bounds().inner_size();
785 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || wm.measure_block(child));
786 size = size.max(min);
787 *desired_size = size;
788 }
789 }
790 UiNodeOp::Layout { wl, final_size } => {
791 let sticky = sticky.get();
792 if sticky_after_layout && sticky {
793 let min = WIDGET.bounds().inner_size();
794 let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || child.layout(wl));
795 size = size.max(min);
796 *final_size = size;
797 }
798
799 sticky_after_layout = sticky;
801 }
802 _ => {}
803 })
804}
805
806#[expect(non_camel_case_types)]
816pub struct WIDGET_SIZE;
817impl WIDGET_SIZE {
818 pub fn set_width(&self, width: &Length) {
820 WIDGET.with_state_mut(|mut state| {
821 let width = width.into();
822 match state.entry(*WIDGET_SIZE_ID) {
823 state_map::StateMapEntry::Occupied(mut e) => e.get_mut().width = width,
824 state_map::StateMapEntry::Vacant(e) => {
825 e.insert(euclid::size2(width, WidgetLength::Default));
826 }
827 }
828 });
829 }
830
831 pub fn set_height(&self, height: &Length) {
833 WIDGET.with_state_mut(|mut state| {
834 let height = height.into();
835 match state.entry(*WIDGET_SIZE_ID) {
836 state_map::StateMapEntry::Occupied(mut e) => e.get_mut().height = height,
837 state_map::StateMapEntry::Vacant(e) => {
838 e.insert(euclid::size2(WidgetLength::Default, height));
839 }
840 }
841 })
842 }
843
844 pub fn set(&self, size: &Size) {
846 WIDGET.set_state(*WIDGET_SIZE_ID, euclid::size2((&size.width).into(), (&size.height).into()));
847 }
848
849 pub fn get(&self) -> euclid::Size2D<WidgetLength, ()> {
851 WIDGET.get_state(*WIDGET_SIZE_ID).unwrap_or_default()
852 }
853
854 pub fn get_wgt(&self, wgt: &mut impl UiNode) -> euclid::Size2D<WidgetLength, ()> {
856 wgt.with_context(WidgetUpdateMode::Ignore, || self.get()).unwrap_or_default()
857 }
858}
859
860static_id! {
861 static ref WIDGET_SIZE_ID: StateId<euclid::Size2D<WidgetLength, ()>>;
862}
863
864#[property(LAYOUT)]
866pub fn actual_size(child: impl UiNode, size: impl IntoVar<DipSize>) -> impl UiNode {
867 let size = size.into_var();
868 match_node(child, move |c, op| match op {
869 UiNodeOp::Render { frame } => {
870 c.render(frame);
871
872 let f = frame.scale_factor();
873 let s = WIDGET.info().bounds_info().inner_size().to_dip(f);
874 if size.get() != s {
875 let _ = size.set(s);
876 }
877 }
878 UiNodeOp::RenderUpdate { update } => {
879 c.render_update(update);
880
881 let info = WIDGET.info();
882 let f = info.tree().scale_factor();
883 let s = info.bounds_info().inner_size().to_dip(f);
884 if size.get() != s {
885 let _ = size.set(s);
886 }
887 }
888 _ => {}
889 })
890}
891
892#[property(LAYOUT)]
894pub fn actual_width(child: impl UiNode, width: impl IntoVar<Dip>) -> impl UiNode {
895 let width = width.into_var();
896 match_node(child, move |c, op| match op {
897 UiNodeOp::Render { frame } => {
898 c.render(frame);
899
900 let f = frame.scale_factor();
901 let w = WIDGET.info().bounds_info().inner_size().width.to_dip(f);
902 if width.get() != w {
903 let _ = width.set(w);
904 }
905 }
906 UiNodeOp::RenderUpdate { update } => {
907 c.render_update(update);
908
909 let info = WIDGET.info();
910 let f = info.tree().scale_factor();
911 let w = info.bounds_info().inner_size().width.to_dip(f);
912 if width.get() != w {
913 let _ = width.set(w);
914 }
915 }
916 _ => {}
917 })
918}
919
920#[property(LAYOUT)]
922pub fn actual_height(child: impl UiNode, height: impl IntoVar<Dip>) -> impl UiNode {
923 let height = height.into_var();
924 match_node(child, move |c, op| match op {
925 UiNodeOp::Render { frame } => {
926 c.render(frame);
927
928 let f = frame.scale_factor();
929 let h = WIDGET.info().bounds_info().inner_size().height.to_dip(f);
930 if height.get() != h {
931 let _ = height.set(h);
932 }
933 }
934 UiNodeOp::RenderUpdate { update } => {
935 c.render_update(update);
936
937 let info = WIDGET.info();
938 let f = info.tree().scale_factor();
939 let h = info.bounds_info().inner_size().height.to_dip(f);
940 if height.get() != h {
941 let _ = height.set(h);
942 }
943 }
944 _ => {}
945 })
946}
947
948#[property(LAYOUT)]
950pub fn actual_size_px(child: impl UiNode, size: impl IntoVar<PxSize>) -> impl UiNode {
951 let size = size.into_var();
952 match_node(child, move |c, op| match &op {
953 UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
954 c.op(op);
955 let s = WIDGET.info().bounds_info().inner_size();
956 if size.get() != s {
957 let _ = size.set(s);
959 }
960 }
961 _ => {}
962 })
963}
964
965#[property(LAYOUT)]
967pub fn actual_width_px(child: impl UiNode, width: impl IntoVar<Px>) -> impl UiNode {
968 let width = width.into_var();
969 match_node(child, move |c, op| match &op {
970 UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
971 c.op(op);
972 let w = WIDGET.info().bounds_info().inner_size().width;
973 if width.get() != w {
974 let _ = width.set(w);
975 }
976 }
977 _ => {}
978 })
979}
980
981#[property(LAYOUT)]
983pub fn actual_height_px(child: impl UiNode, height: impl IntoVar<Px>) -> impl UiNode {
984 let height = height.into_var();
985 match_node(child, move |c, op| match &op {
986 UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
987 c.op(op);
988 let h = WIDGET.info().bounds_info().inner_size().height;
989 if height.get() != h {
990 let _ = height.set(h);
991 }
992 }
993 _ => {}
994 })
995}
996
997#[property(LAYOUT)]
999pub fn actual_transform(child: impl UiNode, transform: impl IntoVar<PxTransform>) -> impl UiNode {
1000 let transform = transform.into_var();
1001 match_node(child, move |c, op| match &op {
1002 UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
1003 c.op(op);
1004 let t = WIDGET.info().bounds_info().inner_transform();
1005 if transform.get() != t {
1006 let _ = transform.set(t);
1007 }
1008 }
1009 _ => {}
1010 })
1011}
1012
1013#[property(LAYOUT)]
1015pub fn actual_bounds(child: impl UiNode, bounds: impl IntoVar<PxRect>) -> impl UiNode {
1016 let bounds = bounds.into_var();
1017 match_node(child, move |c, op| match &op {
1018 UiNodeOp::Render { .. } | UiNodeOp::RenderUpdate { .. } => {
1019 c.op(op);
1020 let t = WIDGET.info().bounds_info().inner_bounds();
1021 if bounds.get() != t {
1022 let _ = bounds.set(t);
1023 }
1024 }
1025 _ => {}
1026 })
1027}
1028
1029#[derive(Debug, Clone, Copy, PartialEq, Default, serde::Serialize, serde::Deserialize)]
1039pub enum WidgetLength {
1040 #[default]
1047 Default,
1048 Leftover(Factor),
1060 Exact,
1068}
1069
1070impl From<&Length> for WidgetLength {
1071 fn from(value: &Length) -> Self {
1072 match value {
1073 Length::Default => WidgetLength::Default,
1074 Length::Leftover(f) => WidgetLength::Leftover(*f),
1075 _ => WidgetLength::Exact,
1076 }
1077 }
1078}