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}