zng_wgt/
node_events.rs

1use zng_app::widget::node::UiNodeOpMethod;
2
3use crate::prelude::*;
4
5/// Arguments for the node operation event properties.
6#[derive(Clone, Debug)]
7pub struct OnNodeOpArgs {
8    /// Operation.
9    ///
10    /// Event args must be static so access to the full [`UiNodeOp`] is not possible, you can quickly
11    /// declare a new property with [`property`] and [`match_node`] if you want to affect the widget this way.
12    ///
13    /// [`UiNodeOp`]: zng_app::widget::node::UiNodeOp
14    /// [`match_node`]: zng_app::widget::node::match_node
15    pub op: UiNodeOpMethod,
16    /// Number of times the handler was called.
17    ///
18    /// The number is `1` for the first call and is not reset if the widget is re-inited.
19    pub count: usize,
20    /// Instant the handler was called.
21    pub timestamp: DInstant,
22}
23impl OnNodeOpArgs {
24    /// New args.
25    pub fn new(op: UiNodeOpMethod, count: usize, timestamp: DInstant) -> Self {
26        Self { op, count, timestamp }
27    }
28    /// New args with timestamp now.
29    pub fn now(op: UiNodeOpMethod, count: usize) -> Self {
30        Self::new(op, count, INSTANT.now())
31    }
32}
33
34/// On any node operation.
35///
36/// This property calls `handler` for any widget node operation, after the widget content has processed the operation. This means
37/// that the `handler` is raised after any [`on_pre_node_op`] handler. Note that properties of [`NestGroup::EVENT`] or lower
38/// can still process the operation before this event.
39///
40/// # Handlers
41///
42/// This property accepts any [`WidgetHandler`], including the async handlers. Use one of the handler macros, [`hn!`],
43/// [`hn_once!`], [`async_hn!`] or [`async_hn_once!`], to declare a handler closure.
44///
45/// ## Async
46///
47/// The async handlers spawn a task that is associated with the widget, it will only update when the widget updates,
48/// so the task *pauses* when the widget is deinited, and is *canceled* when the widget is dropped.
49///
50/// [`on_pre_node_op`]: fn@on_pre_node_op
51/// [`NestGroup::EVENT`]: zng_app::widget::builder::NestGroup::EVENT
52/// [`hn!`]: zng_app::handler::hn!
53/// [`async_hn!`]: zng_app::handler::async_hn!
54/// [`hn_once!`]: zng_app::handler::hn_once!
55/// [`async_hn_once!`]: zng_app::handler::async_hn_once!
56/// [`WidgetHandler`]: zng_app::handler::WidgetHandler
57#[property(EVENT)]
58pub fn on_node_op(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
59    on_node_op_impl(child, handler, |_| true)
60}
61fn on_node_op_impl(
62    child: impl UiNode,
63    handler: impl WidgetHandler<OnNodeOpArgs>,
64    filter: impl Fn(UiNodeOpMethod) -> bool + Send + 'static,
65) -> impl UiNode {
66    let mut handler = handler.cfg_boxed();
67    let mut count = 1;
68    match_node(child, move |child, op| {
69        let mtd = op.mtd();
70        child.op(op);
71
72        if filter(mtd) {
73            handler.event(&OnNodeOpArgs::now(mtd, count));
74            count = count.wrapping_add(1);
75        }
76
77        if let UiNodeOpMethod::Update = mtd {
78            handler.update();
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 [`WidgetHandler`], 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/// [`WidgetHandler`]: zng_app::handler::WidgetHandler
106#[property(EVENT)]
107pub fn on_pre_node_op(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
108    on_pre_node_op_impl(child, handler, |_| true)
109}
110fn on_pre_node_op_impl(
111    child: impl UiNode,
112    handler: impl WidgetHandler<OnNodeOpArgs>,
113    filter: impl Fn(UiNodeOpMethod) -> bool + Send + 'static,
114) -> impl UiNode {
115    let mut handler = handler.cfg_boxed();
116    let mut count = 1;
117    match_node(child, move |_, op| {
118        if let UiNodeOp::Update { .. } = &op {
119            handler.update();
120        }
121
122        let mtd = op.mtd();
123        if filter(mtd) {
124            handler.event(&OnNodeOpArgs::now(mtd, count));
125            count = count.wrapping_add(1);
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 [`WidgetHandler`], 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/// [`WidgetHandler`]: zng_app::handler::WidgetHandler
157#[property(EVENT)]
158pub fn on_init(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
159    on_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::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 UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
167    on_pre_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::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 [`WidgetHandler`], 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/// [`WidgetHandler`]: zng_app::handler::WidgetHandler
194#[property(EVENT)]
195pub fn on_info_init(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
196    let mut handler = handler.cfg_boxed();
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::Info { .. } => {
209            if let State::WaitInfo = &state {
210                state = State::InfoInited;
211                WIDGET.update();
212            }
213        }
214        UiNodeOp::Update { updates } => {
215            child.update(updates);
216
217            if let State::InfoInited = &state {
218                state = State::Done;
219                handler.event(&OnNodeOpArgs::now(UiNodeOpMethod::Update, count));
220                count = count.wrapping_add(1);
221            }
222
223            handler.update();
224        }
225        _ => {}
226    })
227}
228
229/// Widget [`update`](UiNode::update) event.
230///
231/// This property calls `handler` every UI update, after the widget content updates. Updates happen in
232/// high volume in between idle moments, so the handler code should be considered a hot-path.
233///
234/// # Handlers
235///
236/// You can use one of the handler macros, [`hn!`] or [`hn_once!`], to declare a handler closure. You must avoid using the async
237/// handlers as they cause an update every time the UI task advances from an await point causing another task to spawn.
238///
239/// [`hn!`]: zng_app::handler::hn!
240/// [`hn_once!`]: zng_app::handler::hn_once!
241#[property(EVENT)]
242pub fn on_update(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
243    on_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::Update))
244}
245
246/// Preview [`on_update`] event.
247///
248/// This property calls `handler` every time the UI updates, before the widget content updates. This means
249/// that the `handler` is raised before any [`on_init`] handler.
250///
251/// # Handlers
252///
253/// You can use one of the handler macros, [`hn!`] or [`hn_once!`], to declare a handler closure. You must avoid using the async
254/// handlers as they cause an update every time the UI task advances from an await point causing another task to spawn.
255///
256/// [`on_update`]: fn@on_update
257/// [`on_init`]: fn@on_init
258/// [`hn!`]: zng_app::handler::hn!
259/// [`hn_once!`]: zng_app::handler::hn_once!
260#[property(EVENT)]
261pub fn on_pre_update(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
262    on_pre_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::Update))
263}
264
265/// Widget deinited.
266///
267/// This property calls `handler` when the widget deinits, after the widget content deinits. Note that
268/// widgets can be reinitialized so the `handler` can be called more then once,
269/// you can use one of the *once* handlers to only be called once or use the arguments [`count`](OnNodeOpArgs::count)
270/// to determinate if you are in the first deinit.
271///
272/// # Handlers
273///
274/// This property accepts the [`WidgetHandler`] that are not async. Use one of the handler macros, [`hn!`] or
275/// [`hn_once!`], to declare a handler closure.
276///
277/// Note that async handlers do not work here because widget bound async tasks only advance past the first `.await`
278/// during widget updates, but the widget is deinited before that. You can use [`UPDATES.run`] or [`task::spawn`] to start
279/// an async task on deinit.
280///
281/// # Preview
282///
283/// You can use the [`on_pre_deinit`] event to receive this event before the widget content deinits.
284///
285/// [`UPDATES.run`]: zng_app::update::UPDATES::run
286/// [`on_pre_deinit`]: fn@on_pre_deinit
287/// [`WidgetHandler`]: zng_app::handler::WidgetHandler
288/// [`hn!`]: zng_app::handler::hn!
289/// [`hn_once!`]: zng_app::handler::hn_once!
290/// [`task::spawn`]: zng_task::spawn
291#[property(EVENT)]
292pub fn on_deinit(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
293    on_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::Deinit))
294}
295
296/// Preview [`on_deinit`] event.
297///
298/// [`on_deinit`]: fn@on_deinit
299#[property(EVENT)]
300pub fn on_pre_deinit(child: impl UiNode, handler: impl WidgetHandler<OnNodeOpArgs>) -> impl UiNode {
301    on_pre_node_op_impl(child, handler, |op| matches!(op, UiNodeOpMethod::Deinit))
302}
303
304/// If the widget has been initialized.
305///
306/// The `state` is set to `true` on init and to `false` on deinit. This property is useful for
307/// declaring transition animations that play on init using `when` blocks.
308#[property(CONTEXT)]
309pub fn is_inited(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNode {
310    let state = state.into_var();
311    match_node(child, move |_, op| match op {
312        UiNodeOp::Init => {
313            let _ = state.set(true);
314        }
315        UiNodeOp::Deinit => {
316            let _ = state.set(false);
317        }
318        _ => {}
319    })
320}