zng_app/widget/node/
extend.rs

1use std::sync::Arc;
2
3use parking_lot::Mutex;
4
5use crate::widget::WidgetUpdateMode;
6
7use super::*;
8
9/// Create a widget node that wraps the `widget` with any number of other non-widget nodes and
10/// still delegates [`with_context`] to the `widget`.
11///
12/// Note that the [`with_context`] is called in the context of `widget`, not in the context of the `build_extension` nodes.
13/// Other node operations are delegated to the `build_extension` nodes, and they in turn must delegate to the input child
14/// node that is `widget`.
15///
16/// [`with_context`]: WidgetUiNode::with_context
17pub fn extend_widget(widget: impl IntoUiNode, build_extension: impl FnOnce(UiNode) -> UiNode) -> UiNode {
18    let widget = Arc::new(Mutex::new(widget.into_node()));
19    let child = build_extension(UiNode::new(ExtendWidgetChildNode { widget: widget.clone() }));
20    UiNode::new(ExtendWidgetNode { widget, child })
21}
22
23struct ExtendWidgetChildNode {
24    widget: Arc<Mutex<UiNode>>,
25}
26impl UiNodeImpl for ExtendWidgetChildNode {
27    fn children_len(&self) -> usize {
28        1
29    }
30
31    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
32        if index == 0 {
33            visitor(&mut self.widget.lock())
34        }
35    }
36
37    fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
38        self.widget.lock().0.as_widget()?;
39        Some(self)
40    }
41}
42impl WidgetUiNodeImpl for ExtendWidgetChildNode {
43    fn with_context(&mut self, update_mode: WidgetUpdateMode, visitor: &mut dyn FnMut()) {
44        if let Some(wgt) = self.widget.lock().0.as_widget() {
45            wgt.with_context(update_mode, visitor);
46        } else {
47            // this could be intentional, like nodes that only become widgets on init
48            tracing::debug!("extend_widget child is not a widget");
49        }
50    }
51}
52
53struct ExtendWidgetNode {
54    widget: Arc<Mutex<UiNode>>,
55    child: UiNode,
56}
57impl UiNodeImpl for ExtendWidgetNode {
58    fn children_len(&self) -> usize {
59        1
60    }
61
62    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
63        if index == 0 {
64            visitor(&mut self.child)
65        }
66    }
67
68    fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
69        self.widget.lock().0.as_widget()?;
70        Some(self)
71    }
72}
73impl WidgetUiNodeImpl for ExtendWidgetNode {
74    fn with_context(&mut self, update_mode: WidgetUpdateMode, visitor: &mut dyn FnMut()) {
75        if let Some(wgt) = self.widget.lock().0.as_widget() {
76            wgt.with_context(update_mode, visitor);
77        } else {
78            // this could be intentional, like nodes that only become widgets on init
79            tracing::debug!("extend_widget child is not a widget");
80        }
81    }
82}