Skip to main content

zng_wgt_size_offset/
sticky.rs

1use zng_wgt::prelude::*;
2
3/// Retain the widget's previous width if the new layout width is smaller.
4/// The widget is layout using its previous width as the minimum width constrain.
5///
6/// This property disables inline layout for the widget.
7#[property(SIZE, default(false))]
8pub fn sticky_width(child: impl IntoUiNode, sticky: impl IntoVar<bool>) -> UiNode {
9    let sticky = sticky.into_var();
10    let mut sticky_after_layout = false;
11    match_node(child, move |child, op| match op {
12        UiNodeOp::Init => {
13            WIDGET.sub_var(&sticky);
14        }
15        UiNodeOp::Deinit => {
16            sticky_after_layout = false;
17        }
18        UiNodeOp::Update { .. } if sticky.is_new() => {
19            sticky_after_layout = false;
20        }
21        UiNodeOp::Measure { wm, desired_size } if sticky_after_layout && sticky.get() => {
22            child.delegated();
23            let min = WIDGET.bounds().inner_size().width;
24            let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || wm.measure_block(child.node()));
25            size.width = size.width.max(min);
26            *desired_size = size;
27        }
28        UiNodeOp::Layout { wl, final_size } => {
29            let sticky = sticky.get();
30            if sticky_after_layout && sticky {
31                let min = WIDGET.bounds().inner_size().width;
32                let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_x(min), || child.layout(wl));
33                size.width = size.width.max(min);
34                *final_size = size;
35            }
36
37            // only enable after the `WIDGET.bounds().inner_size()` updates
38            sticky_after_layout = sticky;
39        }
40        _ => {}
41    })
42}
43
44/// Retain the widget's previous height if the new layout height is smaller.
45/// The widget is layout using its previous height as the minimum height constrain.
46///
47/// This property disables inline layout for the widget.
48#[property(SIZE, default(false))]
49pub fn sticky_height(child: impl IntoUiNode, sticky: impl IntoVar<bool>) -> UiNode {
50    let sticky = sticky.into_var();
51    let mut sticky_after_layout = false;
52    match_node(child, move |child, op| match op {
53        UiNodeOp::Init => {
54            WIDGET.sub_var(&sticky);
55        }
56        UiNodeOp::Deinit => {
57            sticky_after_layout = false;
58        }
59        UiNodeOp::Update { .. } if sticky.is_new() => {
60            sticky_after_layout = false;
61        }
62        UiNodeOp::Measure { wm, desired_size } if sticky_after_layout && sticky.get() => {
63            child.delegated();
64            let min = WIDGET.bounds().inner_size().height;
65            let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || wm.measure_block(child.node()));
66            size.height = size.height.max(min);
67            *desired_size = size;
68        }
69        UiNodeOp::Layout { wl, final_size } => {
70            let sticky = sticky.get();
71            if sticky_after_layout && sticky {
72                let min = WIDGET.bounds().inner_size().height;
73                let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_y(min), || child.layout(wl));
74                size.height = size.height.max(min);
75                *final_size = size;
76            }
77
78            // only enable after the `WIDGET.bounds().inner_size()` updates
79            sticky_after_layout = sticky;
80        }
81        _ => {}
82    })
83}
84
85/// Retain the widget's previous size if the new layout size is smaller.
86/// The widget is layout using its previous size as the minimum size constrain.
87///
88/// This property disables inline layout for the widget.
89#[property(SIZE, default(false))]
90pub fn sticky_size(child: impl IntoUiNode, sticky: impl IntoVar<bool>) -> UiNode {
91    let sticky = sticky.into_var();
92    let mut sticky_after_layout = false;
93    match_node(child, move |child, op| match op {
94        UiNodeOp::Init => {
95            WIDGET.sub_var(&sticky);
96        }
97        UiNodeOp::Deinit => {
98            sticky_after_layout = false;
99        }
100        UiNodeOp::Update { .. } if sticky.is_new() => {
101            sticky_after_layout = false;
102        }
103        UiNodeOp::Update { .. } => {}
104        UiNodeOp::Measure { wm, desired_size } if sticky_after_layout && sticky.get() => {
105            child.delegated();
106            let min = WIDGET.bounds().inner_size();
107            let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || wm.measure_block(child.node()));
108            size = size.max(min);
109            *desired_size = size;
110        }
111        UiNodeOp::Layout { wl, final_size } => {
112            let sticky = sticky.get();
113            if sticky_after_layout && sticky {
114                let min = WIDGET.bounds().inner_size();
115                let mut size = LAYOUT.with_constraints(LAYOUT.constraints().with_min_size(min), || child.layout(wl));
116                size = size.max(min);
117                *final_size = size;
118            }
119
120            // only enable after the `WIDGET.bounds().inner_size()` updates
121            sticky_after_layout = sticky;
122        }
123        _ => {}
124    })
125}