zng_wgt/
node_events.rs

1use zng_app::widget::node::UiNodeMethod;
2
3use crate::prelude::*;
4
5/// Arguments for the node operation event properties.
6#[derive(Clone, Debug)]
7#[non_exhaustive]
8pub struct OnNodeOpArgs {
9    /// Operation.
10    ///
11    /// Event args must be static so access to the full [`UiNodeOp`] is not possible, you can quickly
12    /// declare a new property with [`property`] and [`match_node`] if you want to affect the widget this way.
13    ///
14    /// [`UiNodeOp`]: zng_app::widget::node::UiNodeOp
15    /// [`match_node`]: zng_app::widget::node::match_node
16    pub op: UiNodeMethod,
17    /// Number of times the handler was called.
18    ///
19    /// The number is `1` for the first call and is not reset if the widget is re-inited.
20    pub count: usize,
21    /// Instant the handler was called.
22    pub timestamp: DInstant,
23}
24impl OnNodeOpArgs {
25    /// New args.
26    pub fn new(op: UiNodeMethod, count: usize, timestamp: DInstant) -> Self {
27        Self { op, count, timestamp }
28    }
29    /// New args with timestamp now.
30    pub fn now(op: UiNodeMethod, count: usize) -> Self {
31        Self::new(op, count, INSTANT.now())
32    }
33}
34
35/// On any node operation.
36///
37/// This property calls `handler` for any widget node operation, after the widget content has processed the operation. This means
38/// that the `handler` is raised after any [`on_pre_node_op`] handler. Note that properties of [`NestGroup::EVENT`] or lower
39/// can still process the operation before this event.
40///
41/// # Handlers
42///
43/// This property accepts any [`Handler<A>`], including the async handlers. Use one of the handler macros, [`hn!`],
44/// [`hn_once!`], [`async_hn!`] or [`async_hn_once!`], to declare a handler closure.
45///
46/// ## Async
47///
48/// The async handlers spawn a task that is associated with the widget, it will only update when the widget updates,
49/// so the task *pauses* when the widget is deinited, and is *canceled* when the widget is dropped.
50///
51/// [`on_pre_node_op`]: fn@on_pre_node_op
52/// [`NestGroup::EVENT`]: zng_app::widget::builder::NestGroup::EVENT
53/// [`hn!`]: zng_app::handler::hn!
54/// [`async_hn!`]: zng_app::handler::async_hn!
55/// [`hn_once!`]: zng_app::handler::hn_once!
56/// [`Handler<A>`]: zng_app::handler::Handler
57/// [`async_hn_once!`]: zng_app::handler::async_hn_once!
58/// [`Handler`]: zng_app::handler::Handler
59#[property(EVENT)]
60pub fn on_node_op(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
61    on_node_op_impl(child.into_node(), handler, |_| true)
62}
63fn on_node_op_impl(child: UiNode, handler: Handler<OnNodeOpArgs>, filter: impl Fn(UiNodeMethod) -> bool + Send + 'static) -> UiNode {
64    let mut handler = handler.into_wgt_runner();
65    let mut count = 1;
66    match_node(child, move |child, op| {
67        let mtd = op.mtd();
68        child.op(op);
69
70        if filter(mtd) {
71            handler.event(&OnNodeOpArgs::now(mtd, count));
72            count = count.wrapping_add(1);
73        }
74
75        if let UiNodeMethod::Update = mtd {
76            handler.update();
77        } else if let UiNodeMethod::Deinit = mtd {
78            handler.deinit();
79        }
80    })
81}
82
83/// Preview [`on_node_op`] event.
84///
85/// This property calls `handler` for any widget node operation, before most of the widget content processes the operation. This means
86/// that the `handler` is raised before any [`on_node_op`] handler. Note that properties of [`NestGroup::EVENT`] or lower
87/// can still process the operation before this event.
88///
89/// # Handlers
90///
91/// This property accepts any [`Handler<A>`], including the async handlers. Use one of the handler macros, [`hn!`],
92/// [`hn_once!`], [`async_hn!`] or [`async_hn_once!`], to declare a handler closure.
93///
94/// ## Async
95///
96/// The async handlers spawn a task that is associated with the widget, it will only update when the widget updates,
97/// so the task *pauses* when the widget is deinited, and is *canceled* when the widget is dropped.
98///
99/// [`on_node_op`]: fn@on_node_op
100/// [`NestGroup::EVENT`]: zng_app::widget::builder::NestGroup::EVENT
101/// [`hn!`]: zng_app::handler::hn!
102/// [`hn_once!`]: zng_app::handler::hn_once!
103/// [`async_hn!`]: zng_app::handler::async_hn!
104/// [`async_hn_once!`]: zng_app::handler::async_hn_once!
105/// [`Handler`]: zng_app::handler::Handler
106#[property(EVENT)]
107pub fn on_pre_node_op(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
108    on_pre_node_op_impl(child.into_node(), handler, |_| true)
109}
110fn on_pre_node_op_impl(child: UiNode, handler: Handler<OnNodeOpArgs>, filter: impl Fn(UiNodeMethod) -> bool + Send + 'static) -> UiNode {
111    let mut count = 1;
112    let mut handler = handler.into_wgt_runner();
113    match_node(child, move |_, op| {
114        if let UiNodeOp::Update { .. } = &op {
115            handler.update();
116        }
117
118        let mtd = op.mtd();
119        if filter(mtd) {
120            handler.event(&OnNodeOpArgs::now(mtd, count));
121            count = count.wrapping_add(1);
122        }
123
124        if let UiNodeOp::Deinit = op {
125            handler.deinit();
126        }
127    })
128}
129
130/// Widget initialized.
131///
132/// This property calls `handler` when the widget and its content initializes. Note that widgets
133/// can be reinitialized, so the `handler` can be called more then once,
134/// you can use one of the *once* handlers to only be called once or use the arguments [`count`](OnNodeOpArgs::count).
135/// to determinate if you are in the first init.
136///
137/// Note that the widget is not in the [`WidgetInfoTree`] when this event happens, you can use [`on_info_init`] for initialization
138/// that depends on the widget info.
139///
140/// # Handlers
141///
142/// This property accepts any [`Handler<A>`], including the async handlers. Use one of the handler macros, [`hn!`],
143/// [`hn_once!`], [`async_hn!`] or [`async_hn_once!`], to declare a handler closure.
144///
145/// ## Async
146///
147/// The async handlers spawn a task that is associated with the widget, it will only update when the widget updates,
148/// so the task *pauses* when the widget is deinited, and is *canceled* when the widget is dropped.
149///
150/// [`on_info_init`]: fn@on_info_init
151/// [`WidgetInfoTree`]: zng_app::widget::info::WidgetInfoTree
152/// [`hn!`]: zng_app::handler::hn!
153/// [`hn_once!`]: zng_app::handler::hn_once!
154/// [`async_hn!`]: zng_app::handler::async_hn!
155/// [`async_hn_once!`]: zng_app::handler::async_hn_once!
156/// [`Handler`]: zng_app::handler::Handler
157#[property(EVENT)]
158pub fn on_init(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
159    on_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Init))
160}
161
162/// Preview [`on_init`] event.
163///
164/// [`on_init`]: fn@on_init
165#[property(EVENT)]
166pub fn on_pre_init(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
167    on_pre_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Init))
168}
169
170/// Widget info is now available.
171///
172/// This event fires after the first [`UiNode::info`] is built, after [`UiNode::init`]. This event can be used when
173/// some widget initialization needs to happen, but the widget must be in the [`WidgetInfoTree`] for it to work.
174///
175/// # Handlers
176///
177/// This property accepts any [`Handler<A>`], including the async handlers. Use one of the handler macros, [`hn!`],
178/// [`hn_once!`], [`async_hn!`] or [`async_hn_once!`], to declare a handler closure.
179///
180/// ## Async
181///
182/// The async handlers spawn a task that is associated with the widget, it will only update when the widget updates,
183/// so the task *pauses* when the widget is deinited, and is *canceled* when the widget is dropped.
184///
185/// [`WidgetInfoTree`]: zng_app::widget::info::WidgetInfoTree
186///
187/// [`UiNode::info`]: zng_app::widget::node::UiNode::info
188/// [`UiNode::init`]: zng_app::widget::node::UiNode::init
189/// [`hn!`]: zng_app::handler::hn!
190/// [`hn_once!`]: zng_app::handler::hn_once!
191/// [`async_hn!`]: zng_app::handler::async_hn!
192/// [`async_hn_once!`]: zng_app::handler::async_hn_once!
193/// [`Handler<A>`]: zng_app::handler::Handler
194#[property(EVENT)]
195pub fn on_info_init(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
196    let mut handler = handler.into_wgt_runner();
197    let mut count = 1;
198    enum State {
199        WaitInfo,
200        InfoInited,
201        Done,
202    }
203    let mut state = State::WaitInfo;
204    match_node(child, move |child, op| match op {
205        UiNodeOp::Init => {
206            state = State::WaitInfo;
207        }
208        UiNodeOp::Deinit => {
209            handler.deinit();
210        }
211        UiNodeOp::Info { .. } => {
212            if let State::WaitInfo = &state {
213                state = State::InfoInited;
214                WIDGET.update();
215            }
216        }
217        UiNodeOp::Update { updates } => {
218            child.update(updates);
219
220            if let State::InfoInited = &state {
221                state = State::Done;
222                handler.event(&OnNodeOpArgs::now(UiNodeMethod::Update, count));
223                count = count.wrapping_add(1);
224            }
225
226            handler.update();
227        }
228        _ => {}
229    })
230}
231
232/// Widget [`update`](UiNode::update) event.
233///
234/// This property calls `handler` every UI update, after the widget content updates. Updates happen in
235/// high volume in between idle moments, so the handler code should be considered a hot-path.
236///
237/// # Handlers
238///
239/// You can use one of the handler macros, [`hn!`] or [`hn_once!`], to declare a handler closure. You must avoid using the async
240/// handlers as they cause an update every time the UI task advances from an await point causing another task to spawn.
241///
242/// [`hn!`]: zng_app::handler::hn!
243/// [`hn_once!`]: zng_app::handler::hn_once!
244#[property(EVENT)]
245pub fn on_update(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
246    on_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Update))
247}
248
249/// Preview [`on_update`] event.
250///
251/// This property calls `handler` every time the UI updates, before the widget content updates. This means
252/// that the `handler` is raised before any [`on_init`] handler.
253///
254/// # Handlers
255///
256/// You can use one of the handler macros, [`hn!`] or [`hn_once!`], to declare a handler closure. You must avoid using the async
257/// handlers as they cause an update every time the UI task advances from an await point causing another task to spawn.
258///
259/// [`on_update`]: fn@on_update
260/// [`on_init`]: fn@on_init
261/// [`hn!`]: zng_app::handler::hn!
262/// [`hn_once!`]: zng_app::handler::hn_once!
263#[property(EVENT)]
264pub fn on_pre_update(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
265    on_pre_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Update))
266}
267
268/// Widget deinited.
269///
270/// This property calls `handler` when the widget deinits, after the widget content deinits. Note that
271/// widgets can be reinitialized so the `handler` can be called more then once,
272/// you can use one of the *once* handlers to only be called once or use the arguments [`count`](OnNodeOpArgs::count)
273/// to determinate if you are in the first deinit.
274///
275/// # Handlers
276///
277/// This property accepts the [`Handler`] that are not async. Use one of the handler macros, [`hn!`] or
278/// [`hn_once!`], to declare a handler closure.
279///
280/// Note that async handlers do not work here because widget bound async tasks only advance past the first `.await`
281/// during widget updates, but the widget is deinited before that. You can use [`UPDATES.run`] or [`task::spawn`] to start
282/// an async task on deinit.
283///
284/// # Preview
285///
286/// You can use the [`on_pre_deinit`] event to receive this event before the widget content deinits.
287///
288/// [`UPDATES.run`]: zng_app::update::UPDATES::run
289/// [`on_pre_deinit`]: fn@on_pre_deinit
290/// [`Handler`]: zng_app::handler::Handler
291/// [`hn!`]: zng_app::handler::hn!
292/// [`hn_once!`]: zng_app::handler::hn_once!
293/// [`task::spawn`]: zng_task::spawn
294#[property(EVENT)]
295pub fn on_deinit(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
296    on_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Deinit))
297}
298
299/// Preview [`on_deinit`] event.
300///
301/// [`on_deinit`]: fn@on_deinit
302#[property(EVENT)]
303pub fn on_pre_deinit(child: impl IntoUiNode, handler: Handler<OnNodeOpArgs>) -> UiNode {
304    on_pre_node_op_impl(child.into_node(), handler, |op| matches!(op, UiNodeMethod::Deinit))
305}
306
307/// If the widget has been initialized.
308///
309/// The `state` is set to `true` on init and to `false` on deinit. This property is useful for
310/// declaring transition animations that play on init using `when` blocks.
311#[property(CONTEXT)]
312pub fn is_inited(child: impl IntoUiNode, state: impl IntoVar<bool>) -> UiNode {
313    let state = state.into_var();
314    match_node(child, move |_, op| match op {
315        UiNodeOp::Init => {
316            state.set(true);
317        }
318        UiNodeOp::Deinit => {
319            state.set(false);
320        }
321        _ => {}
322    })
323}