zng_app/widget/node/
arc.rs

1use std::sync::{Arc, Weak};
2
3use crate::{
4    event::{Event, EventArgs},
5    update::UPDATES,
6    var::*,
7    widget::{WidgetHandlesCtx, WidgetId, WidgetUpdateMode, node::IntoUiNode},
8};
9
10type SlotId = usize;
11
12struct SlotData {
13    item: Mutex<UiNode>,
14    slots: Mutex<SlotsData>,
15}
16#[derive(Default)]
17struct SlotsData {
18    // id of the next slot created.
19    next_slot: SlotId,
20
21    // slot and context where the node is inited.
22    owner: Option<(SlotId, WidgetId)>,
23    // slot and context that has requested ownership.
24    move_request: Option<(SlotId, WidgetId)>,
25
26    // node instance that must replace the current in the active slot.
27    replacement: Option<UiNode>,
28}
29impl SlotsData {
30    fn next_slot(&mut self) -> SlotId {
31        let r = self.next_slot;
32        self.next_slot = self.next_slot.wrapping_add(1);
33        r
34    }
35}
36
37/// A reference counted [`UiNode`].
38///
39/// Nodes can only be used in one place at a time, this `struct` allows the
40/// creation of ***slots*** that are [`UiNode`] implementers that can ***exclusive take*** the
41/// referenced node as its child.
42///
43/// When a slot takes the node it is deinited in the previous place and reinited in the slot place.
44///
45/// Slots hold a strong reference to the node when they have it as their child and a weak reference when they don't.
46pub struct ArcNode(Arc<SlotData>);
47impl Clone for ArcNode {
48    fn clone(&self) -> Self {
49        Self(self.0.clone())
50    }
51}
52impl ArcNode {
53    /// New node.
54    pub fn new(node: impl IntoUiNode) -> Self {
55        Self::new_impl(node.into_node())
56    }
57    fn new_impl(node: UiNode) -> Self {
58        ArcNode(Arc::new(SlotData {
59            item: Mutex::new(node),
60            slots: Mutex::default(),
61        }))
62    }
63
64    /// New node that contains a weak reference to itself.
65    ///
66    /// Note that the weak reference cannot be [upgraded](WeakNode::upgrade) during the call to `node`.
67    pub fn new_cyclic(node: impl FnOnce(WeakNode) -> UiNode) -> Self {
68        Self(Arc::new_cyclic(|wk| {
69            let node = node(WeakNode(wk.clone()));
70            SlotData {
71                item: Mutex::new(node),
72                slots: Mutex::default(),
73            }
74        }))
75    }
76
77    /// Creates a [`WeakNode`] reference to this node.
78    pub fn downgrade(&self) -> WeakNode {
79        WeakNode(Arc::downgrade(&self.0))
80    }
81
82    /// Replace the current node with the `new_node` in the current slot.
83    ///
84    /// The previous node is deinited and the `new_node` is inited.
85    pub fn set(&self, new_node: impl IntoUiNode) {
86        self.set_impl(new_node.into_node())
87    }
88    fn set_impl(&self, new_node: UiNode) {
89        let mut slots = self.0.slots.lock();
90        let slots = &mut *slots;
91        if let Some((_, id)) = &slots.owner {
92            // current node inited on a slot, signal it to replace.
93            slots.replacement = Some(new_node);
94            let _ = UPDATES.update(*id);
95        } else {
96            // node already not inited, just replace.
97            *self.0.item.lock() = new_node;
98        }
99    }
100
101    /// Create a slot node that takes ownership of this node when `var` updates to `true`.
102    ///
103    /// The slot node also takes ownership on init if the `var` is already `true`.
104    pub fn take_when(&self, var: impl IntoVar<bool>) -> UiNode {
105        self.take_when_impl(var.into_var())
106    }
107    fn take_when_impl(&self, var: Var<bool>) -> UiNode {
108        impls::TakeSlot {
109            slot: self.0.slots.lock().next_slot(),
110            rc: self.0.clone(),
111            take: impls::TakeWhenVar { var: var.into_var() },
112            wgt_handles: WidgetHandlesCtx::new(),
113        }
114        .into_node()
115    }
116
117    /// Create a slot node that takes ownership of this node when `event` updates and `filter` returns `true`.
118    ///
119    /// The slot node also takes ownership on init if `take_on_init` is `true`.
120    pub fn take_on<A, F>(&self, event: Event<A>, filter: F, take_on_init: bool) -> UiNode
121    where
122        A: EventArgs,
123        F: FnMut(&A) -> bool + Send + 'static,
124    {
125        impls::TakeSlot {
126            slot: self.0.slots.lock().next_slot(),
127            rc: self.0.clone(),
128            take: impls::TakeOnEvent {
129                event,
130                filter,
131                take_on_init,
132            },
133            wgt_handles: WidgetHandlesCtx::new(),
134        }
135        .into_node()
136    }
137
138    /// Create a slot node that takes ownership of this node as soon as the node is inited.
139    ///
140    /// This is equivalent to `self.take_when(true)`.
141    pub fn take_on_init(&self) -> UiNode {
142        self.take_when(true)
143    }
144
145    /// Call `visitor` on a exclusive lock of the node.
146    ///
147    /// Note that the node is not visited in their current slot context, only use this to inspect state.
148    ///
149    /// Returns `None` if the node is locked, this will happen if calling from inside the node or an ancestor.
150    pub fn try_node<R>(&self, visitor: impl FnOnce(&mut UiNode) -> R) -> Option<R> {
151        Some(visitor(&mut *self.0.item.try_lock()?))
152    }
153
154    /// Calls `visitor` in the widget context of the node, if it is an widget.
155    ///
156    /// Note that only the widget context is loaded in `visitor`, not the full slot context.
157    ///
158    /// Returns `None` if the node is locked or is not an widget. The node will be locked if calling from inside the node or an ancestor.
159    pub fn try_context<R>(&self, update_mode: WidgetUpdateMode, visitor: impl FnOnce() -> R) -> Option<R> {
160        Some(self.0.item.try_lock()?.as_widget()?.with_context(update_mode, visitor))
161    }
162}
163
164/// Weak reference to a [`ArcNode`].
165pub struct WeakNode(Weak<SlotData>);
166impl Clone for WeakNode {
167    fn clone(&self) -> Self {
168        Self(Weak::clone(&self.0))
169    }
170}
171impl WeakNode {
172    /// Attempts to upgrade to a [`ArcNode`].
173    pub fn upgrade(&self) -> Option<ArcNode> {
174        self.0.upgrade().map(ArcNode)
175    }
176}
177
178use parking_lot::Mutex;
179
180use super::UiNode;
181
182mod impls {
183    use std::sync::Arc;
184
185    use zng_layout::unit::PxSize;
186    use zng_var::Var;
187
188    use crate::{
189        event::{Event, EventArgs},
190        render::{FrameBuilder, FrameUpdate},
191        update::{UPDATES, WidgetUpdates},
192        widget::{
193            WIDGET, WidgetHandlesCtx,
194            info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
195            node::{UiNode, UiNodeImpl, WidgetUiNodeImpl},
196        },
197    };
198
199    use super::{SlotData, SlotId};
200
201    pub(super) trait TakeOn: Send + 'static {
202        fn take_on_init(&mut self) -> bool {
203            false
204        }
205
206        fn take_on_update(&mut self, updates: &WidgetUpdates) -> bool {
207            let _ = updates;
208            false
209        }
210    }
211
212    pub(super) struct TakeWhenVar {
213        pub(super) var: Var<bool>,
214    }
215    impl TakeOn for TakeWhenVar {
216        fn take_on_init(&mut self) -> bool {
217            WIDGET.sub_var(&self.var);
218            self.var.get()
219        }
220
221        fn take_on_update(&mut self, _: &WidgetUpdates) -> bool {
222            self.var.get_new().unwrap_or(false)
223        }
224    }
225
226    pub(super) struct TakeOnEvent<A: EventArgs, F: FnMut(&A) -> bool + Send + 'static> {
227        pub(super) event: Event<A>,
228        pub(super) filter: F,
229        pub(super) take_on_init: bool,
230    }
231    impl<A: EventArgs, F: FnMut(&A) -> bool + Send + Send + 'static> TakeOn for TakeOnEvent<A, F> {
232        fn take_on_init(&mut self) -> bool {
233            WIDGET.sub_event(&self.event);
234            self.take_on_init
235        }
236
237        fn take_on_update(&mut self, _: &WidgetUpdates) -> bool {
238            let mut any = false;
239            self.event.each_update(true, |a| any |= (self.filter)(a));
240            any
241        }
242    }
243
244    pub(super) struct TakeSlot<T: TakeOn> {
245        pub(super) slot: SlotId,
246        pub(super) rc: Arc<SlotData>,
247        pub(super) take: T,
248
249        pub(super) wgt_handles: WidgetHandlesCtx,
250    }
251    impl<T: TakeOn> TakeSlot<T> {
252        fn on_init(&mut self) {
253            if self.take.take_on_init() {
254                self.take();
255            }
256        }
257
258        fn on_deinit(&mut self) {
259            let mut was_owner = false;
260            {
261                let mut slots = self.rc.slots.lock();
262                let slots = &mut *slots;
263                if let Some((slot, _)) = &slots.owner
264                    && *slot == self.slot
265                {
266                    slots.owner = None;
267                    was_owner = true;
268                }
269            }
270
271            if was_owner {
272                WIDGET.with_handles(&mut self.wgt_handles, || self.rc.item.lock().deinit());
273            }
274
275            self.wgt_handles.clear();
276        }
277
278        fn on_update(&mut self, updates: &WidgetUpdates) {
279            if self.is_owner() {
280                let mut slots = self.rc.slots.lock();
281                if let Some((_, id)) = slots.move_request {
282                    // deinit to move to other slot.
283
284                    let replacement = slots.replacement.take();
285                    slots.owner = None;
286
287                    drop(slots);
288
289                    let mut node = self.rc.item.lock();
290                    node.deinit();
291
292                    WIDGET.update_info().layout().render();
293
294                    if let Some(new) = replacement {
295                        *node = new;
296                    }
297
298                    UPDATES.update(id);
299                } else if let Some(mut new) = slots.replacement.take() {
300                    // apply replacement.
301
302                    drop(slots);
303
304                    let mut node = self.rc.item.lock();
305                    WIDGET.with_handles(&mut self.wgt_handles, || {
306                        node.deinit();
307                    });
308                    self.wgt_handles.clear();
309
310                    WIDGET.with_handles(&mut self.wgt_handles, || {
311                        new.init();
312                    });
313                    *node = new;
314
315                    WIDGET.update_info().layout().render();
316                }
317            } else if self.take.take_on_update(updates) {
318                // request ownership.
319                self.take();
320            } else {
321                let mut slots = self.rc.slots.lock();
322                if let Some((slot, _)) = &slots.move_request
323                    && *slot == self.slot
324                    && slots.owner.is_none()
325                {
326                    slots.move_request = None;
327                    // requested move in prev update, now can take ownership.
328                    drop(slots);
329                    self.take();
330                }
331            }
332        }
333
334        fn take(&mut self) {
335            {
336                let mut slots = self.rc.slots.lock();
337                let slots = &mut *slots;
338                if let Some((sl, id)) = &slots.owner {
339                    if *sl != self.slot {
340                        // currently inited in another slot, signal it to deinit.
341                        slots.move_request = Some((self.slot, WIDGET.id()));
342                        UPDATES.update(*id);
343                    }
344                } else {
345                    // no current owner, take ownership immediately.
346                    slots.owner = Some((self.slot, WIDGET.id()));
347                }
348            }
349
350            if self.is_owner() {
351                WIDGET.with_handles(&mut self.wgt_handles, || {
352                    self.rc.item.lock().init();
353                });
354                WIDGET.update_info().layout().render();
355            }
356        }
357
358        fn is_owner(&self) -> bool {
359            self.rc.slots.lock().owner.as_ref().map(|(sl, _)| *sl == self.slot).unwrap_or(false)
360        }
361
362        fn delegate_owned<R>(&self, del: impl FnOnce(&UiNode) -> R) -> Option<R> {
363            if self.is_owner() { Some(del(&self.rc.item.lock())) } else { None }
364        }
365        fn delegate_owned_mut<R>(&mut self, del: impl FnOnce(&mut UiNode) -> R) -> Option<R> {
366            if self.is_owner() {
367                Some(del(&mut self.rc.item.lock()))
368            } else {
369                None
370            }
371        }
372
373        fn delegate_owned_mut_with_handles<R>(&mut self, del: impl FnOnce(&mut UiNode) -> R) -> Option<R> {
374            if self.is_owner() {
375                WIDGET.with_handles(&mut self.wgt_handles, || Some(del(&mut self.rc.item.lock())))
376            } else {
377                None
378            }
379        }
380    }
381
382    impl<T: TakeOn> UiNodeImpl for TakeSlot<T> {
383        fn children_len(&self) -> usize {
384            self.delegate_owned(|n| n.0.children_len()).unwrap_or(0)
385        }
386
387        fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
388            self.delegate_owned_mut(|n| n.0.with_child(index, visitor));
389        }
390
391        fn init(&mut self) {
392            self.on_init();
393        }
394
395        fn deinit(&mut self) {
396            self.on_deinit();
397        }
398
399        fn info(&mut self, info: &mut WidgetInfoBuilder) {
400            self.delegate_owned_mut(|n| n.0.info(info));
401        }
402
403        fn update(&mut self, updates: &WidgetUpdates) {
404            self.delegate_owned_mut_with_handles(|n| n.0.update(updates));
405            self.on_update(updates);
406        }
407        fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn crate::widget::node::UiNodeListObserver) {
408            self.delegate_owned_mut(|n| n.0.update_list(updates, observer));
409            self.on_update(updates);
410        }
411
412        fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
413            self.delegate_owned_mut(|n| n.0.measure(wm)).unwrap_or_default()
414        }
415        fn measure_list(
416            &mut self,
417            wm: &mut WidgetMeasure,
418            measure: &(dyn Fn(usize, &mut UiNode, &mut WidgetMeasure) -> PxSize + Sync),
419            fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
420        ) -> PxSize {
421            self.delegate_owned_mut(|n| n.0.measure_list(wm, measure, fold_size))
422                .unwrap_or_default()
423        }
424
425        fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
426            self.delegate_owned_mut(|n| n.0.layout(wl)).unwrap_or_default()
427        }
428        fn layout_list(
429            &mut self,
430            wl: &mut WidgetLayout,
431            layout: &(dyn Fn(usize, &mut UiNode, &mut WidgetLayout) -> PxSize + Sync),
432            fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
433        ) -> PxSize {
434            self.delegate_owned_mut(|n| n.0.layout_list(wl, layout, fold_size))
435                .unwrap_or_default()
436        }
437
438        fn render(&mut self, frame: &mut FrameBuilder) {
439            self.delegate_owned_mut(|n| n.0.render(frame));
440        }
441        fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
442            self.delegate_owned_mut(|n| n.0.render_list(frame, render));
443        }
444
445        fn render_update(&mut self, update: &mut FrameUpdate) {
446            self.delegate_owned_mut(|n| n.0.render_update(update));
447        }
448        fn render_update_list(&mut self, update: &mut FrameUpdate, render_update: &(dyn Fn(usize, &mut UiNode, &mut FrameUpdate) + Sync)) {
449            self.delegate_owned_mut(|n| n.0.render_update_list(update, render_update));
450        }
451
452        fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
453            self.delegate_owned_mut(|n| n.0.for_each_child(visitor));
454        }
455
456        fn try_for_each_child(
457            &mut self,
458            visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue>,
459        ) -> std::ops::ControlFlow<zng_var::BoxAnyVarValue> {
460            self.delegate_owned_mut(|n| n.0.try_for_each_child(visitor))
461                .unwrap_or(std::ops::ControlFlow::Continue(()))
462        }
463
464        fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
465            self.delegate_owned_mut(|n| n.0.par_each_child(visitor));
466        }
467
468        fn par_fold_reduce(
469            &mut self,
470            identity: zng_var::BoxAnyVarValue,
471            fold: &(dyn Fn(zng_var::BoxAnyVarValue, usize, &mut UiNode) -> zng_var::BoxAnyVarValue + Sync),
472            reduce: &(dyn Fn(zng_var::BoxAnyVarValue, zng_var::BoxAnyVarValue) -> zng_var::BoxAnyVarValue + Sync),
473        ) -> zng_var::BoxAnyVarValue {
474            self.delegate_owned_mut(|n| n.0.par_fold_reduce(identity.clone(), fold, reduce))
475                .unwrap_or(identity)
476        }
477
478        fn is_list(&self) -> bool {
479            // check directly to avoid into_list wrapping lists nodes
480            self.rc.item.lock().0.is_list()
481        }
482
483        fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
484            if self.delegate_owned_mut(|w| w.as_widget().is_some()).unwrap_or(false) {
485                Some(self)
486            } else {
487                None
488            }
489        }
490    }
491    impl<T: TakeOn> WidgetUiNodeImpl for TakeSlot<T> {
492        fn with_context(&mut self, update_mode: crate::widget::WidgetUpdateMode, visitor: &mut dyn FnMut()) {
493            #[cfg(debug_assertions)]
494            let mut called = 0;
495            self.delegate_owned_mut_with_handles(|w| {
496                #[cfg(debug_assertions)]
497                {
498                    called = 1;
499                }
500                if let Some(mut w) = w.as_widget() {
501                    #[cfg(debug_assertions)]
502                    {
503                        called = 2;
504                    }
505                    w.with_context(update_mode, visitor)
506                }
507            });
508            #[cfg(debug_assertions)]
509            match called {
510                0 => tracing::error!("ArcNode TakeSlot node taken while as_widget is held"),
511                1 => tracing::error!("ArcNode TakeSlot node was widget when as_widget returned, but not anymore"),
512                _ => {}
513            }
514        }
515    }
516}