zng_wgt/
visibility_props.rs1use crate::{event_property, node::VarEventNodeBuilder, prelude::*};
2
3use zng_app::widget::info::WIDGET_TREE_CHANGED_EVENT;
4
5#[property(CONTEXT, default(true))]
19pub fn visibility(child: impl IntoUiNode, visibility: impl IntoVar<Visibility>) -> UiNode {
20 let visibility = visibility.into_var();
21 let mut prev_vis = Visibility::Visible;
22
23 match_node(child, move |child, op| match op {
24 UiNodeOp::Init => {
25 WIDGET.sub_var(&visibility);
26 prev_vis = visibility.get();
27 }
28 UiNodeOp::Update { .. } => {
29 if let Some(vis) = visibility.get_new() {
30 use Visibility::*;
31 match (prev_vis, vis) {
32 (Collapsed, Visible) | (Visible, Collapsed) => {
33 WIDGET.layout().render();
34 }
35 (Hidden, Visible) | (Visible, Hidden) => {
36 WIDGET.render();
37 }
38 (Collapsed, Hidden) | (Hidden, Collapsed) => {
39 WIDGET.layout();
40 }
41 _ => {}
42 }
43 prev_vis = vis;
44 }
45 }
46
47 UiNodeOp::Measure { wm, desired_size } => {
48 if Visibility::Collapsed != visibility.get() {
49 *desired_size = child.measure(wm);
50 } else {
51 child.delegated();
52 }
53 }
54 UiNodeOp::Layout { wl, final_size } => {
55 if Visibility::Collapsed != visibility.get() {
56 *final_size = child.layout(wl);
57 } else {
58 wl.collapse();
59 child.delegated();
60 }
61 }
62
63 UiNodeOp::Render { frame } => match visibility.get() {
64 Visibility::Visible => child.render(frame),
65 Visibility::Hidden => frame.hide(|frame| child.render(frame)),
66 Visibility::Collapsed => {
67 child.delegated();
68 #[cfg(debug_assertions)]
69 {
70 tracing::error!(
71 "collapsed {} rendered, to fix, layout the widget, or `WidgetLayout::collapse_child` the widget",
72 WIDGET.trace_id()
73 )
74 }
75 }
76 },
77 UiNodeOp::RenderUpdate { update } => match visibility.get() {
78 Visibility::Visible => child.render_update(update),
79 Visibility::Hidden => update.hidden(|update| child.render_update(update)),
80 Visibility::Collapsed => {
81 child.delegated();
82 #[cfg(debug_assertions)]
83 {
84 tracing::error!(
85 "collapsed {} render-updated, to fix, layout the widget, or `WidgetLayout::collapse_child` the widget",
86 WIDGET.trace_id()
87 )
88 }
89 }
90 },
91 _ => {}
92 })
93}
94
95fn visibility_eq_state(child: impl IntoUiNode, state: impl IntoVar<bool>, expected: Visibility) -> UiNode {
96 let state = state.into_var();
97 match_node(child, move |_, op| {
98 if let UiNodeOp::Init = op {
99 let w_id = WINDOW.id();
100 let id = WIDGET.id();
101 WIDGET.push_var_handle(WIDGET_TREE_CHANGED_EVENT.var_bind(&state, move |a| {
102 if a.tree.window_id() == w_id
103 && let Some(w) = a.tree.get(id)
104 {
105 Some(w.visibility() == expected)
106 } else {
107 None
108 }
109 }));
110 }
111 })
112}
113#[property(CONTEXT)]
115pub fn is_visible(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
116 visibility_eq_state(child, state, Visibility::Visible)
117}
118#[property(CONTEXT)]
120pub fn is_hidden(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
121 visibility_eq_state(child, state, Visibility::Hidden)
122}
123#[property(CONTEXT)]
125pub fn is_collapsed(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
126 visibility_eq_state(child, state, Visibility::Collapsed)
127}
128
129#[property(CONTEXT, default(true))]
159pub fn auto_hide(child: impl IntoUiNode, enabled: impl IntoVar<bool>) -> UiNode {
160 let enabled = enabled.into_var();
161
162 match_node(child, move |_, op| match op {
163 UiNodeOp::Init => {
164 WIDGET.sub_var(&enabled);
165 }
166 UiNodeOp::Update { .. } => {
167 if let Some(new) = enabled.get_new()
168 && WIDGET.bounds().can_auto_hide() != new
169 {
170 WIDGET.layout().render();
171 }
172 }
173 UiNodeOp::Layout { wl, .. } => {
174 wl.allow_auto_hide(enabled.get());
175 }
176 _ => {}
177 })
178}
179
180macro_rules! visibility_var_event_source {
181 (|$visibility:ident| $map:expr, $default:expr) => {
182 $crate::node::VarEventNodeBuilder::new(|| {
183 let win_id = WINDOW.id();
184 let wgt_id = WIDGET.id();
185 WIDGET_TREE_CHANGED_EVENT.var_map(
186 move |a| {
187 if a.tree.window_id() == win_id
188 && let Some(w) = a.tree.get(wgt_id)
189 {
190 Some({
191 let $visibility = w.visibility();
192 $map
193 })
194 } else {
195 None
196 }
197 },
198 || $default,
199 )
200 })
201 };
202}
203
204event_property! {
205 #[property(EVENT)]
207 pub fn on_transform_changed(child: impl IntoUiNode, handler: Handler<PxTransform>) -> UiNode {
208 VarEventNodeBuilder::new(|| {
209 let win_id = WINDOW.id();
210 let wgt_id = WIDGET.id();
211 WIDGET_TREE_CHANGED_EVENT.var_map(
212 move |a| {
213 if a.tree.window_id() == win_id
214 && let Some(w) = a.tree.get(wgt_id)
215 {
216 Some(w.inner_transform())
217 } else {
218 None
219 }
220 },
221 PxTransform::identity,
222 )
223 })
224 .build::<false>(child, handler)
225 }
226
227 #[property(EVENT)]
235 pub fn on_visibility_changed(child: impl IntoUiNode, handler: Handler<Visibility>) -> UiNode {
236 visibility_var_event_source!(|v| v, Visibility::Visible).build::<false>(child, handler)
237 }
238
239 #[property(EVENT)]
247 pub fn on_visible(child: impl IntoUiNode, handler: Handler<()>) -> UiNode {
248 visibility_var_event_source!(|v| v.is_visible(), true)
249 .filter(|| |v| *v)
250 .map_args(|_| ())
251 .build::<false>(child, handler)
252 }
253
254 #[property(EVENT)]
260 pub fn on_hidden(child: impl IntoUiNode, handler: Handler<()>) -> UiNode {
261 visibility_var_event_source!(|v| v.is_hidden(), true)
262 .filter(|| |v| *v)
263 .map_args(|_| ())
264 .build::<false>(child, handler)
265 }
266
267 #[property(EVENT)]
273 pub fn on_collapsed(child: impl IntoUiNode, handler: Handler<()>) -> UiNode {
274 visibility_var_event_source!(|v| v.is_collapsed(), true)
275 .filter(|| |v| *v)
276 .map_args(|_| ())
277 .build::<false>(child, handler)
278 }
279}