zng_app/widget/node/
when.rs

1use zng_layout::unit::PxSize;
2use zng_var::{BoxedVar, Var};
3
4use crate::{
5    update::{EventUpdate, WidgetUpdates},
6    widget::{WIDGET, WidgetHandlesCtx},
7};
8
9use super::{BoxedUiNode, BoxedUiNodeList, UiNode, UiNodeList, UiNodeListObserver};
10
11/// Builds a node that can be one of multiple options, selected by the first condition that is `true`, or a fallback default.
12///
13/// When the selected node changes the previous one is deinited and the new one is inited.
14pub struct WhenUiNodeBuilder {
15    default: BoxedUiNode,
16    conditions: Vec<(BoxedVar<bool>, BoxedUiNode)>,
17}
18impl WhenUiNodeBuilder {
19    /// New with node that is used when no condition is active.
20    pub fn new(default: impl UiNode) -> Self {
21        Self {
22            default: default.boxed(),
23            conditions: vec![],
24        }
25    }
26
27    /// Push a conditional node.
28    ///
29    /// When `condition` is `true` and no previous inserted condition is `true` the `node` is used.
30    pub fn push(&mut self, condition: impl Var<bool>, node: impl UiNode) {
31        self.conditions.push((condition.boxed(), node.boxed()));
32    }
33
34    /// Build a node that is always the first `true` condition or the default.
35    pub fn build(self) -> impl UiNode {
36        WhenUiNode {
37            default: self.default,
38            conditions: self.conditions,
39            current: usize::MAX,
40            wgt_handles: WidgetHandlesCtx::new(),
41        }
42    }
43}
44
45/// Builds a node list that can be one of multiple options, selected by the first condition that is `true`, or a fallback default.
46///
47/// When the selected list changes the previous one is deinited and the new one is inited.
48pub struct WhenUiNodeListBuilder {
49    default: BoxedUiNodeList,
50    conditions: Vec<(BoxedVar<bool>, BoxedUiNodeList)>,
51}
52impl WhenUiNodeListBuilder {
53    /// New with list that is used when no condition is active.
54    pub fn new(default: impl UiNodeList) -> Self {
55        Self {
56            default: default.boxed(),
57            conditions: vec![],
58        }
59    }
60
61    /// Push a conditional list.
62    ///
63    /// When `condition` is `true` and no previous inserted condition is `true` the `list` is used.
64    pub fn push(&mut self, condition: impl Var<bool>, list: impl UiNodeList) {
65        self.conditions.push((condition.boxed(), list.boxed()));
66    }
67
68    /// Build a list that is always the first `true` condition or the default.
69    pub fn build(self) -> impl UiNodeList {
70        WhenUiNodeList {
71            default: self.default,
72            conditions: self.conditions,
73            current: usize::MAX,
74            wgt_handles: WidgetHandlesCtx::new(),
75        }
76    }
77}
78
79struct WhenUiNode {
80    default: BoxedUiNode,
81    conditions: Vec<(BoxedVar<bool>, BoxedUiNode)>,
82    current: usize,
83    wgt_handles: WidgetHandlesCtx,
84}
85impl WhenUiNode {
86    fn child_mut_with_handles(&mut self) -> (&mut BoxedUiNode, &mut WidgetHandlesCtx) {
87        let child = if self.current == usize::MAX {
88            &mut self.default
89        } else {
90            &mut self.conditions[self.current].1
91        };
92        (child, &mut self.wgt_handles)
93    }
94
95    fn change_child(&mut self, new: usize) {
96        {
97            let (child, wgt_handles) = self.child_mut_with_handles();
98            WIDGET.with_handles(wgt_handles, || child.deinit());
99            wgt_handles.clear();
100        }
101
102        self.current = new;
103
104        {
105            let (child, wgt_handles) = self.child_mut_with_handles();
106            WIDGET.with_handles(wgt_handles, || child.init());
107        }
108
109        WIDGET.update_info().layout().render();
110    }
111
112    fn with<R>(&mut self, f: impl FnOnce(&mut BoxedUiNode) -> R) -> R {
113        let (child, wgt_handles) = self.child_mut_with_handles();
114        WIDGET.with_handles(wgt_handles, || f(child))
115    }
116}
117impl UiNode for WhenUiNode {
118    fn init(&mut self) {
119        self.current = usize::MAX;
120        for (i, (c, _)) in self.conditions.iter().enumerate() {
121            if self.current == usize::MAX && c.get() {
122                self.current = i;
123            }
124            WIDGET.sub_var(c);
125        }
126        self.with(|c| c.init());
127    }
128
129    fn deinit(&mut self) {
130        self.with(|c| c.deinit());
131        self.wgt_handles.clear();
132    }
133
134    fn info(&mut self, info: &mut crate::widget::info::WidgetInfoBuilder) {
135        self.with(|c| c.info(info));
136    }
137
138    fn event(&mut self, update: &EventUpdate) {
139        self.with(|c| c.event(update));
140    }
141
142    fn update(&mut self, updates: &WidgetUpdates) {
143        let mut any = false;
144        for (i, (c, _)) in self.conditions.iter().enumerate() {
145            if i < self.current {
146                if c.get() {
147                    // if activated < current
148                    self.change_child(i);
149                    return;
150                }
151            } else if i == self.current {
152                if c.get() {
153                    // if did not deactivate current
154                    any = true;
155                    break;
156                }
157            } else if c.get() {
158                // if deactivated current and had another active after
159                self.change_child(i);
160                return;
161            }
162        }
163
164        if !any && self.current != usize::MAX {
165            // if no longer has not active condition.
166            self.change_child(usize::MAX);
167            return;
168        }
169
170        // only update if did not change
171        // to not update before first info build
172        self.with(|c| c.update(updates));
173    }
174
175    fn measure(&mut self, wm: &mut crate::widget::info::WidgetMeasure) -> PxSize {
176        self.with(|c| c.measure(wm))
177    }
178
179    fn layout(&mut self, wl: &mut crate::widget::info::WidgetLayout) -> PxSize {
180        self.with(|c| c.layout(wl))
181    }
182
183    fn render(&mut self, frame: &mut crate::render::FrameBuilder) {
184        self.with(|c| c.render(frame))
185    }
186
187    fn render_update(&mut self, update: &mut crate::render::FrameUpdate) {
188        self.with(|c| c.render_update(update))
189    }
190}
191
192struct WhenUiNodeList {
193    default: BoxedUiNodeList,
194    conditions: Vec<(BoxedVar<bool>, BoxedUiNodeList)>,
195    current: usize,
196    wgt_handles: WidgetHandlesCtx,
197}
198impl WhenUiNodeList {
199    fn children(&self) -> &BoxedUiNodeList {
200        if self.current == usize::MAX {
201            &self.default
202        } else {
203            &self.conditions[self.current].1
204        }
205    }
206
207    fn children_mut_with_handles(&mut self) -> (&mut BoxedUiNodeList, &mut WidgetHandlesCtx) {
208        let child = if self.current == usize::MAX {
209            &mut self.default
210        } else {
211            &mut self.conditions[self.current].1
212        };
213        (child, &mut self.wgt_handles)
214    }
215
216    fn change_children(&mut self, observer: &mut dyn UiNodeListObserver, new: usize) {
217        {
218            let (child, wgt_handles) = self.children_mut_with_handles();
219            WIDGET.with_handles(wgt_handles, || child.deinit_all());
220            wgt_handles.clear();
221        }
222
223        self.current = new;
224
225        {
226            let (child, wgt_handles) = self.children_mut_with_handles();
227            WIDGET.with_handles(wgt_handles, || child.init_all());
228        }
229
230        observer.reset();
231        WIDGET.update_info().layout().render();
232    }
233}
234
235impl UiNodeList for WhenUiNodeList {
236    fn with_node<R, F>(&mut self, index: usize, f: F) -> R
237    where
238        F: FnOnce(&mut BoxedUiNode) -> R,
239    {
240        self.children_mut_with_handles().0.with_node(index, f)
241    }
242
243    fn for_each<F>(&mut self, f: F)
244    where
245        F: FnMut(usize, &mut BoxedUiNode),
246    {
247        self.children_mut_with_handles().0.for_each(f)
248    }
249
250    fn par_each<F>(&mut self, f: F)
251    where
252        F: Fn(usize, &mut BoxedUiNode) + Send + Sync,
253    {
254        self.children_mut_with_handles().0.par_each(f)
255    }
256
257    fn par_fold_reduce<T, I, F, R>(&mut self, identity: I, fold: F, reduce: R) -> T
258    where
259        T: Send + 'static,
260        I: Fn() -> T + Send + Sync,
261        F: Fn(T, usize, &mut BoxedUiNode) -> T + Send + Sync,
262        R: Fn(T, T) -> T + Send + Sync,
263    {
264        self.children_mut_with_handles().0.par_fold_reduce(identity, fold, reduce)
265    }
266
267    fn len(&self) -> usize {
268        self.children().len()
269    }
270
271    fn boxed(self) -> BoxedUiNodeList {
272        Box::new(self)
273    }
274
275    fn drain_into(&mut self, vec: &mut Vec<BoxedUiNode>) {
276        self.children_mut_with_handles().0.drain_into(vec)
277    }
278
279    fn init_all(&mut self) {
280        self.current = usize::MAX;
281        for (i, (c, _)) in self.conditions.iter().enumerate() {
282            if self.current == usize::MAX && c.get() {
283                self.current = i;
284            }
285            WIDGET.sub_var(c);
286        }
287
288        let (children, wgt_handles) = self.children_mut_with_handles();
289        WIDGET.with_handles(wgt_handles, || children.init_all());
290    }
291
292    fn deinit_all(&mut self) {
293        let (children, wgt_handles) = self.children_mut_with_handles();
294        WIDGET.with_handles(wgt_handles, || children.deinit_all());
295        wgt_handles.clear();
296    }
297
298    fn update_all(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
299        let mut any = false;
300        for (i, (c, _)) in self.conditions.iter().enumerate() {
301            if i < self.current {
302                // if activated < current
303                if c.get() {
304                    any = true;
305                    self.change_children(observer, i);
306
307                    break;
308                }
309            } else if i == self.current {
310                // if deactivated current
311                if c.get() {
312                    any = true;
313                    break;
314                }
315            } else if c.get() {
316                // if deactivated current and had another active after
317                any = true;
318                self.change_children(observer, i);
319
320                break;
321            }
322        }
323
324        if !any && self.current != usize::MAX {
325            // if no longer has not active condition.
326            self.change_children(observer, usize::MAX);
327        }
328
329        let (children, wgt_handles) = self.children_mut_with_handles();
330        WIDGET.with_handles(wgt_handles, || children.update_all(updates, observer));
331    }
332
333    fn info_all(&mut self, info: &mut crate::widget::info::WidgetInfoBuilder) {
334        let (children, wgt_handles) = self.children_mut_with_handles();
335        WIDGET.with_handles(wgt_handles, || {
336            children.info_all(info);
337        })
338    }
339
340    fn event_all(&mut self, update: &EventUpdate) {
341        let (children, wgt_handles) = self.children_mut_with_handles();
342        WIDGET.with_handles(wgt_handles, || {
343            children.event_all(update);
344        })
345    }
346}