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