zng_app/widget/node/
when.rs

1use zng_layout::unit::PxSize;
2use zng_var::Var;
3
4use crate::{
5    update::{EventUpdate, WidgetUpdates},
6    widget::{
7        WIDGET, WidgetHandlesCtx,
8        node::{IntoUiNode, UiNodeImpl},
9    },
10};
11
12use super::{UiNode, UiNodeListObserver};
13
14/// Builds a node that can be one of multiple options, selected by the first condition that is `true`, or a fallback default.
15///
16/// When the selected node changes the previous one is deinited and the new one is inited.
17///
18/// The when node delegates everything, children, list and widget to the selected node, it *becomes* the selected node.
19pub struct WhenUiNodeBuilder {
20    default: UiNode,
21    conditions: Vec<(Var<bool>, UiNode)>,
22}
23impl WhenUiNodeBuilder {
24    /// New with node that is used when no condition is active.
25    pub fn new(default: impl IntoUiNode) -> Self {
26        Self {
27            default: default.into_node(),
28            conditions: vec![],
29        }
30    }
31
32    /// Push a conditional node.
33    ///
34    /// When `condition` is `true` and no previous inserted condition is `true` the `node` is used.
35    pub fn push(&mut self, condition: Var<bool>, node: impl IntoUiNode) {
36        self.conditions.push((condition, node.into_node()));
37    }
38
39    /// Build a node that is always the first `true` condition or the default.
40    pub fn build(self) -> UiNode {
41        UiNode::new(WhenUiNode {
42            default: self.default,
43            conditions: self.conditions,
44            current: usize::MAX,
45            wgt_handles: WidgetHandlesCtx::new(),
46        })
47    }
48}
49
50struct WhenUiNode {
51    default: UiNode,
52    conditions: Vec<(Var<bool>, UiNode)>,
53    current: usize,
54    wgt_handles: WidgetHandlesCtx,
55}
56impl WhenUiNode {
57    fn child_mut_with_handles(&mut self) -> (&mut UiNode, &mut WidgetHandlesCtx) {
58        let child = if self.current == usize::MAX {
59            &mut self.default
60        } else {
61            &mut self.conditions[self.current].1
62        };
63        (child, &mut self.wgt_handles)
64    }
65
66    fn child_ref(&self) -> &UiNode {
67        if self.current == usize::MAX {
68            &self.default
69        } else {
70            &self.conditions[self.current].1
71        }
72    }
73
74    fn change_child(&mut self, new: usize) {
75        {
76            let (child, wgt_handles) = self.child_mut_with_handles();
77            WIDGET.with_handles(wgt_handles, || child.deinit());
78            wgt_handles.clear();
79        }
80
81        self.current = new;
82
83        {
84            let (child, wgt_handles) = self.child_mut_with_handles();
85            WIDGET.with_handles(wgt_handles, || child.init());
86        }
87
88        WIDGET.update_info().layout().render();
89    }
90
91    fn with<R>(&mut self, f: impl FnOnce(&mut UiNode) -> R) -> R {
92        let (child, wgt_handles) = self.child_mut_with_handles();
93        WIDGET.with_handles(wgt_handles, || f(child))
94    }
95
96    fn update_when(&mut self) -> bool {
97        let mut any = false;
98        for (i, (c, _)) in self.conditions.iter().enumerate() {
99            if i < self.current {
100                if c.get() {
101                    // if activated < current
102                    self.change_child(i);
103                    return true;
104                }
105            } else if i == self.current {
106                if c.get() {
107                    // if did not deactivate current
108                    any = true;
109                    break;
110                }
111            } else if c.get() {
112                // if deactivated current and had another active after
113                self.change_child(i);
114                return true;
115            }
116        }
117
118        if !any && self.current != usize::MAX {
119            // if no longer has not active condition.
120            self.change_child(usize::MAX);
121            return true;
122        }
123        false
124    }
125}
126impl UiNodeImpl for WhenUiNode {
127    fn children_len(&self) -> usize {
128        self.child_ref().0.children_len()
129    }
130
131    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
132        self.child_mut_with_handles().0.0.with_child(index, visitor);
133    }
134
135    fn init(&mut self) {
136        self.current = usize::MAX;
137        for (i, (c, _)) in self.conditions.iter().enumerate() {
138            if self.current == usize::MAX && c.get() {
139                self.current = i;
140            }
141            WIDGET.sub_var(c);
142        }
143        self.with(|c| c.0.init());
144    }
145
146    fn deinit(&mut self) {
147        self.with(|c| c.0.deinit());
148        self.wgt_handles.clear();
149    }
150
151    fn info(&mut self, info: &mut crate::widget::info::WidgetInfoBuilder) {
152        self.with(|c| c.0.info(info));
153    }
154
155    fn event(&mut self, update: &EventUpdate) {
156        self.with(|c| c.0.event(update));
157    }
158
159    fn update(&mut self, updates: &WidgetUpdates) {
160        if !self.update_when() {
161            // only update if did not change
162            // to not update before first info build
163            self.with(|c| c.0.update(updates));
164        }
165    }
166    fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
167        if self.update_when() {
168            observer.reset();
169        } else {
170            self.with(|c| c.0.update_list(updates, observer));
171        }
172    }
173
174    fn measure(&mut self, wm: &mut crate::widget::info::WidgetMeasure) -> PxSize {
175        self.with(|c| c.0.measure(wm))
176    }
177
178    fn layout(&mut self, wl: &mut crate::widget::info::WidgetLayout) -> PxSize {
179        self.with(|c| c.0.layout(wl))
180    }
181
182    fn render(&mut self, frame: &mut crate::render::FrameBuilder) {
183        self.with(|c| c.0.render(frame))
184    }
185
186    fn render_update(&mut self, update: &mut crate::render::FrameUpdate) {
187        self.with(|c| c.0.render_update(update))
188    }
189
190    fn is_list(&self) -> bool {
191        self.child_ref().0.is_list()
192    }
193
194    fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
195        self.child_mut_with_handles().0.0.for_each_child(visitor);
196    }
197
198    fn try_for_each_child(
199        &mut self,
200        visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue>,
201    ) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue> {
202        self.child_mut_with_handles().0.0.try_for_each_child(visitor)
203    }
204
205    fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
206        self.child_mut_with_handles().0.0.par_each_child(visitor);
207    }
208
209    fn par_fold_reduce(
210        &mut self,
211        identity: zng_var::BoxAnyVarValue,
212        fold: &(dyn Fn(zng_var::BoxAnyVarValue, usize, &mut UiNode) -> zng_var::BoxAnyVarValue + Sync),
213        reduce: &(dyn Fn(zng_var::BoxAnyVarValue, zng_var::BoxAnyVarValue) -> zng_var::BoxAnyVarValue + Sync),
214    ) -> zng_var::BoxAnyVarValue {
215        self.child_mut_with_handles().0.0.par_fold_reduce(identity, fold, reduce)
216    }
217
218    fn measure_list(
219        &mut self,
220        wm: &mut crate::widget::info::WidgetMeasure,
221        measure: &(dyn Fn(usize, &mut UiNode, &mut crate::widget::info::WidgetMeasure) -> PxSize + Sync),
222        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
223    ) -> PxSize {
224        self.with(|c| c.0.measure_list(wm, measure, fold_size))
225    }
226
227    fn layout_list(
228        &mut self,
229        wl: &mut crate::widget::info::WidgetLayout,
230        layout: &(dyn Fn(usize, &mut UiNode, &mut crate::widget::info::WidgetLayout) -> PxSize + Sync),
231        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
232    ) -> PxSize {
233        self.with(|c| c.0.layout_list(wl, layout, fold_size))
234    }
235
236    fn render_list(
237        &mut self,
238        frame: &mut crate::render::FrameBuilder,
239        render: &(dyn Fn(usize, &mut UiNode, &mut crate::render::FrameBuilder) + Sync),
240    ) {
241        self.with(|c| c.0.render_list(frame, render))
242    }
243
244    fn render_update_list(
245        &mut self,
246        update: &mut crate::render::FrameUpdate,
247        render_update: &(dyn Fn(usize, &mut UiNode, &mut crate::render::FrameUpdate) + Sync),
248    ) {
249        self.with(|c| c.0.render_update_list(update, render_update))
250    }
251
252    fn as_widget(&mut self) -> Option<&mut dyn super::WidgetUiNodeImpl> {
253        self.child_mut_with_handles().0.0.as_widget()
254    }
255}