zng_wgt/
node.rs

1//! Helper nodes.
2//!
3//! This module defines some foundational nodes that can be used for declaring properties and widgets.
4
5use std::{any::Any, sync::Arc};
6
7use crate::WidgetFn;
8use zng_app::{
9    event::{AnyEventArgs, Command, CommandArgs, CommandHandle, CommandScope, Event, EventArgs, EventPropagationHandle},
10    handler::{Handler, HandlerExt as _},
11    render::{FrameBuilder, FrameValueKey},
12    update::WidgetUpdates,
13    widget::{
14        VarLayout, WIDGET,
15        border::{BORDER, BORDER_ALIGN_VAR, BORDER_OVER_VAR},
16        info::Interactivity,
17        node::*,
18    },
19    window::WINDOW,
20};
21use zng_app_context::{ContextLocal, LocalContext};
22use zng_layout::{
23    context::LAYOUT,
24    unit::{PxConstraints2d, PxCornerRadius, PxPoint, PxRect, PxSideOffsets, PxSize, PxVector, SideOffsets},
25};
26use zng_state_map::{StateId, StateMapRef, StateValue};
27use zng_var::*;
28
29#[doc(hidden)]
30pub use pastey::paste;
31
32#[doc(hidden)]
33pub mod __macro_util {
34    pub use zng_app::{
35        event::CommandArgs,
36        handler::{Handler, hn},
37        widget::{
38            node::{IntoUiNode, UiNode},
39            property,
40        },
41    };
42    pub use zng_var::{IntoVar, context_var};
43}
44
45/// Helper for declaring properties that sets a context var.
46///
47/// The generated [`UiNode`] delegates each method to `child` inside a call to [`ContextVar::with_context`].
48///
49/// # Examples
50///
51/// A simple context property declaration:
52///
53/// ```
54/// # fn main() -> () { }
55/// # use zng_app::{*, widget::{node::*, *}};
56/// # use zng_var::*;
57/// # use zng_wgt::node::*;
58/// #
59/// context_var! {
60///     pub static FOO_VAR: u32 = 0u32;
61/// }
62///
63/// /// Sets the [`FOO_VAR`] in the widgets and its content.
64/// #[property(CONTEXT, default(FOO_VAR))]
65/// pub fn foo(child: impl IntoUiNode, value: impl IntoVar<u32>) -> UiNode {
66///     with_context_var(child, FOO_VAR, value)
67/// }
68/// ```
69///
70/// When set in a widget, the `value` is accessible in all inner nodes of the widget, using `FOO_VAR.get`, and if `value` is set to a
71/// variable the `FOO_VAR` will also reflect its [`is_new`] and [`read_only`]. If the `value` var is not read-only inner nodes
72/// can modify it using `FOO_VAR.set` or `FOO_VAR.modify`.
73///
74/// Also note that the property [`default`] is set to the same `FOO_VAR`, this causes the property to *pass-through* the outer context
75/// value, as if it was not set.
76///
77/// **Tip:** You can use a [`merge_var!`] to merge a new value to the previous context value:
78///
79/// ```
80/// # fn main() -> () { }
81/// # use zng_app::{*, widget::{node::*, *}};
82/// # use zng_var::*;
83/// # use zng_wgt::node::*;
84/// #
85/// #[derive(Debug, Clone, Default, PartialEq)]
86/// pub struct Config {
87///     pub foo: bool,
88///     pub bar: bool,
89/// }
90///
91/// context_var! {
92///     pub static CONFIG_VAR: Config = Config::default();
93/// }
94///
95/// /// Sets the *foo* config.
96/// #[property(CONTEXT, default(false))]
97/// pub fn foo(child: impl IntoUiNode, value: impl IntoVar<bool>) -> UiNode {
98///     with_context_var(
99///         child,
100///         CONFIG_VAR,
101///         merge_var!(CONFIG_VAR, value.into_var(), |c, &v| {
102///             let mut c = c.clone();
103///             c.foo = v;
104///             c
105///         }),
106///     )
107/// }
108///
109/// /// Sets the *bar* config.
110/// #[property(CONTEXT, default(false))]
111/// pub fn bar(child: impl IntoUiNode, value: impl IntoVar<bool>) -> UiNode {
112///     with_context_var(
113///         child,
114///         CONFIG_VAR,
115///         merge_var!(CONFIG_VAR, value.into_var(), |c, &v| {
116///             let mut c = c.clone();
117///             c.bar = v;
118///             c
119///         }),
120///     )
121/// }
122/// ```
123///
124/// When set in a widget, the [`merge_var!`] will read the context value of the parent properties, modify a clone of the value and
125/// the result will be accessible to the inner properties, the widget user can then set with the composed value in steps and
126/// the final consumer of the composed value only need to monitor to a single context variable.
127///
128/// [`is_new`]: zng_var::AnyVar::is_new
129/// [`read_only`]: zng_var::Var::read_only
130/// [`default`]: zng_app::widget::property#default
131/// [`merge_var!`]: zng_var::merge_var
132/// [`UiNode`]: zng_app::widget::node::UiNode
133/// [`ContextVar::with_context`]: zng_var::ContextVar::with_context
134pub fn with_context_var<T: VarValue>(child: impl IntoUiNode, context_var: ContextVar<T>, value: impl IntoVar<T>) -> UiNode {
135    let value = value.into_var();
136    let mut actual_value = None;
137    let mut id = None;
138
139    match_node(child, move |child, op| {
140        let mut is_deinit = false;
141        match &op {
142            UiNodeOp::Init => {
143                id = Some(ContextInitHandle::new());
144                actual_value = Some(Arc::new(value.current_context().into()));
145            }
146            UiNodeOp::Deinit => {
147                is_deinit = true;
148            }
149            _ => {}
150        }
151
152        context_var.with_context(id.clone().expect("node not inited"), &mut actual_value, || child.op(op));
153
154        if is_deinit {
155            id = None;
156            actual_value = None;
157        }
158    })
159}
160
161/// Helper for declaring properties that sets a context var to a value generated on init.
162///
163/// The method calls the `init_value` closure on init to produce a *value* var that is presented as the [`ContextVar<T>`]
164/// in the widget and widget descendants. The closure can be called more than once if the returned node is reinited.
165///
166/// Apart from the value initialization this behaves just like [`with_context_var`].
167///
168/// [`ContextVar<T>`]: zng_var::ContextVar
169pub fn with_context_var_init<T: VarValue>(
170    child: impl IntoUiNode,
171    var: ContextVar<T>,
172    mut init_value: impl FnMut() -> Var<T> + Send + 'static,
173) -> UiNode {
174    let mut id = None;
175    let mut value = None;
176    match_node(child, move |child, op| {
177        let mut is_deinit = false;
178        match &op {
179            UiNodeOp::Init => {
180                id = Some(ContextInitHandle::new());
181                value = Some(Arc::new(init_value().current_context().into()));
182            }
183            UiNodeOp::Deinit => {
184                is_deinit = true;
185            }
186            _ => {}
187        }
188
189        var.with_context(id.clone().expect("node not inited"), &mut value, || child.op(op));
190
191        if is_deinit {
192            id = None;
193            value = None;
194        }
195    })
196}
197
198///<span data-del-macro-root></span> Declare one or more event properties.
199///
200/// Each declaration expands to two properties `on_$event` and `on_pre_$event`.
201/// The preview properties call [`on_pre_event`], the main event properties call [`on_event`].
202///
203/// # Examples
204///
205/// ```
206/// # fn main() { }
207/// # use zng_app::event::*;
208/// # use zng_wgt::node::*;
209/// # #[derive(Clone, Debug, PartialEq)] pub enum KeyState { Pressed }
210/// # event_args! { pub struct KeyInputArgs { pub state: KeyState, .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) { } } }
211/// # event! { pub static KEY_INPUT_EVENT: KeyInputArgs; }
212/// event_property! {
213///     /// on_key_input docs.
214///     pub fn key_input {
215///         event: KEY_INPUT_EVENT,
216///         args: KeyInputArgs,
217///         // default filter is |args| true,
218///     }
219///
220///     pub(crate) fn key_down {
221///         event: KEY_INPUT_EVENT,
222///         args: KeyInputArgs,
223///         // optional filter:
224///         filter: |args| args.state == KeyState::Pressed,
225///     }
226/// }
227/// ```
228///
229/// # Filter
230///
231/// App events are delivered to all widgets that are both in the [`UpdateDeliveryList`] and event subscribers list,
232/// event properties can specialize further by defining a filter predicate.
233///
234/// The `filter:` predicate is called if [`propagation`] is not stopped. It must return `true` if the event arguments
235/// are relevant in the context of the widget and event property. If it returns `true` the `handler` closure is called.
236/// See [`on_event`] and [`on_pre_event`] for more information.
237///
238/// If you don't provide a filter predicate the default always allows, so all app events targeting the widget and not already handled
239/// are allowed by default. Note that events that represent an *interaction* with the widget are send for both [`ENABLED`] and [`DISABLED`]
240/// widgets, event properties should probably distinguish if they fire on normal interactions versus on *disabled* interactions.
241///
242/// # Async
243///
244/// Async event handlers are supported by properties generated by this macro, but only the code before the first `.await` executes
245/// in the event track, subsequent code runs in widget updates.
246///
247/// # Commands
248///
249/// You can use [`command_property`] to declare command event properties.
250///
251/// # Implement For
252///
253/// You can implement the new properties for a widget or mixin using the `widget_impl:` directive:
254///
255/// ```
256/// # fn main() { }
257/// # use zng_app::{event::*, widget::{node::{UiNode, IntoUiNode}, widget_mixin}};
258/// # use zng_wgt::node::*;
259/// # event_args! { pub struct KeyInputArgs { .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) {} } }
260/// # event! { pub static KEY_INPUT_EVENT: KeyInputArgs; }
261/// # fn some_node(child: impl IntoUiNode) -> UiNode { child.into_node() }
262/// /// Keyboard events.
263/// #[widget_mixin]
264/// pub struct KeyboardMix<P>(P);
265///
266/// event_property! {
267///     pub fn key_input {
268///         event: KEY_INPUT_EVENT,
269///         args: KeyInputArgs,
270///         widget_impl: KeyboardMix<P>,
271///     }
272/// }
273/// ```
274///
275/// # With Extra Nodes
276///
277/// You can wrap the event handler node with extra nodes by setting the optional `with:` closure:
278///
279/// ```
280/// # fn main() { }
281/// # use zng_app::{event::*, widget::node::{UiNode, IntoUiNode}};
282/// # use zng_wgt::node::*;
283/// # event_args! { pub struct KeyInputArgs { .. fn delivery_list(&self, _l: &mut UpdateDeliveryList) {} } }
284/// # event! { pub static KEY_INPUT_EVENT: KeyInputArgs; }
285/// # fn some_node(child: impl IntoUiNode) -> UiNode { child.into_node() }
286/// event_property! {
287///     pub fn key_input {
288///         event: KEY_INPUT_EVENT,
289///         args: KeyInputArgs,
290///         with: |child, _preview| some_node(child),
291///     }
292/// }
293/// ```
294///
295/// The closure receives two arguments, the handler `UiNode` and a `bool` that is `true` if the closure is called in the *on_pre_*
296/// property or `false` when called in the *on_* property.
297///
298/// [`on_pre_event`]: crate::node::on_pre_event
299/// [`on_event`]: crate::node::on_event
300/// [`propagation`]: zng_app::event::AnyEventArgs::propagation
301/// [`ENABLED`]: zng_app::widget::info::Interactivity::ENABLED
302/// [`DISABLED`]: zng_app::widget::info::Interactivity::DISABLED
303/// [`UpdateDeliveryList`]: zng_app::update::UpdateDeliveryList
304#[macro_export]
305macro_rules! event_property {
306    ($(
307        $(#[$on_event_attrs:meta])*
308        $vis:vis fn $event:ident {
309            event: $EVENT:path,
310            args: $Args:path
311            $(, filter: $filter:expr)?
312            $(, widget_impl: $Wgt:ty)?
313            $(, with: $with:expr)?
314            $(,)?
315        }
316    )+) => {$(
317        $crate::__event_property! {
318            done {
319                sig { $(#[$on_event_attrs])* $vis fn $event { event: $EVENT, args: $Args, } }
320            }
321
322            $(filter: $filter,)?
323            $(widget_impl: $Wgt,)?
324            $(with: $with,)?
325        }
326    )+};
327}
328
329#[doc(hidden)]
330#[macro_export]
331macro_rules! __event_property {
332    // match filter:
333    (
334        done {
335            $($done:tt)+
336        }
337        filter: $filter:expr,
338        $($rest:tt)*
339    ) => {
340        $crate::__event_property! {
341            done {
342                $($done)+
343                filter { $filter }
344            }
345            $($rest)*
346        }
347    };
348    // match widget_impl:
349    (
350        done {
351            $($done:tt)+
352        }
353        widget_impl: $Wgt:ty,
354        $($rest:tt)*
355    ) => {
356        $crate::__event_property! {
357            done {
358                $($done)+
359                widget_impl { , widget_impl($Wgt) }
360            }
361            $($rest)*
362        }
363    };
364    // match with:
365    (
366        done {
367            $($done:tt)+
368        }
369        with: $with:expr,
370    ) => {
371        $crate::__event_property! {
372            done {
373                $($done)+
374                with { $with }
375            }
376        }
377    };
378    // match done sig
379    (
380        done {
381            sig { $($sig:tt)+ }
382        }
383    ) => {
384        $crate::__event_property! {
385            done {
386                sig { $($sig)+ }
387                filter { |_args| true }
388                widget_impl { }
389                with { }
390            }
391        }
392    };
393    // match done sig+filter
394    (
395        done {
396            sig { $($sig:tt)+ }
397            filter { $($filter:tt)+ }
398        }
399    ) => {
400        $crate::__event_property! {
401            done {
402                sig { $($sig)+ }
403                filter { $($filter)+ }
404                widget_impl { }
405                with { }
406            }
407        }
408    };
409    // match done sig+widget_impl
410    (
411        done {
412            sig { $($sig:tt)+ }
413            widget_impl { $($widget_impl:tt)+ }
414        }
415    ) => {
416        $crate::__event_property! {
417            done {
418                sig { $($sig)+ }
419                filter { |_args| true }
420                widget_impl { $($widget_impl)+ }
421                with { }
422            }
423        }
424    };
425    // match done sig+with
426    (
427        done {
428            sig { $($sig:tt)+ }
429            with { $($with:tt)+ }
430        }
431    ) => {
432        $crate::__event_property! {
433            done {
434                sig { $($sig)+ }
435                filter { |_args| true }
436                widget_impl { }
437                with { $($with)+ }
438            }
439        }
440    };
441    // match done sig+filter+widget_impl
442    (
443        done {
444            sig { $($sig:tt)+ }
445            filter { $($filter:tt)+ }
446            widget_impl { $($widget_impl:tt)+ }
447        }
448    ) => {
449        $crate::__event_property! {
450            done {
451                sig { $($sig)+ }
452                filter { $($filter)+ }
453                widget_impl { $($widget_impl)+ }
454                with { }
455            }
456        }
457    };
458    // match done sig+filter+with
459    (
460        done {
461            sig { $($sig:tt)+ }
462            filter { $($filter:tt)+ }
463            with { $($with:tt)+ }
464        }
465    ) => {
466        $crate::__event_property! {
467            done {
468                sig { $($sig)+ }
469                filter { $($filter)+ }
470                widget_impl { }
471                with { $($with)+ }
472            }
473        }
474    };
475    // match done sig+filter+widget_impl+with
476    (
477        done {
478            sig { $(#[$on_event_attrs:meta])* $vis:vis fn $event:ident { event: $EVENT:path, args: $Args:path, } }
479            filter { $filter:expr }
480            widget_impl { $($widget_impl:tt)* }
481            with { $($with:expr)? }
482        }
483    ) => {
484        $crate::node::paste! {
485            $(#[$on_event_attrs])*
486            ///
487            /// # Preview
488            ///
489            #[doc = "You can preview this event using [`on_pre_"$event "`](fn.on_pre_"$event ".html)."]
490            /// Otherwise the handler is only called after the widget content has a chance to stop propagation.
491            ///
492            /// # Async
493            ///
494            /// You can use async event handlers with this property.
495            #[$crate::node::__macro_util::property(
496                EVENT,
497                default( $crate::node::__macro_util::hn!(|_|{}) )
498                $($widget_impl)*
499            )]
500            $vis fn [<on_ $event>](
501                child: impl $crate::node::__macro_util::IntoUiNode,
502                handler: $crate::node::__macro_util::Handler<$Args>,
503            ) -> $crate::node::__macro_util::UiNode {
504                $crate::__event_property!(=> with($crate::node::on_event(child, $EVENT, $filter, handler), false, $($with)?))
505            }
506
507            #[doc = "Preview [`on_"$event "`](fn.on_"$event ".html) event."]
508            ///
509            /// # Preview
510            ///
511            /// Preview event properties call the handler before the main event property and before the widget content, if you stop
512            /// the propagation of a preview event the main event handler is not called.
513            ///
514            /// # Async
515            ///
516            /// You can use async event handlers with this property, note that only the code before the fist `.await` is *preview*,
517            /// subsequent code runs in widget updates.
518            #[$crate::node::__macro_util::property(
519                EVENT,
520                default( $crate::node::__macro_util::hn!(|_|{}) )
521                $($widget_impl)*
522            )]
523            $vis fn [<on_pre_ $event>](
524                child: impl $crate::node::__macro_util::IntoUiNode,
525                handler: $crate::node::__macro_util::Handler<$Args>,
526            ) -> $crate::node::__macro_util::UiNode {
527                $crate::__event_property!(=> with($crate::node::on_pre_event(child, $EVENT, $filter, handler), true, $($with)?))
528            }
529        }
530    };
531
532    (=> with($child:expr, $preview:expr,)) => { $child };
533    (=> with($child:expr, $preview:expr, $with:expr)) => { ($with)($child, $preview) };
534}
535
536/// Helper for declaring event properties.
537///
538/// This function is used by the [`event_property!`] macro.
539///
540/// # Filter
541///
542/// The `filter` predicate is called if [`propagation`] was not stopped. It must return `true` if the event arguments are
543/// relevant in the context of the widget. If it returns `true` the `handler` closure is called. Note that events that represent
544/// an *interaction* with the widget are send for both [`ENABLED`] and [`DISABLED`] targets, event properties should probably distinguish
545/// if they fire on normal interactions vs on *disabled* interactions.
546///
547/// # Route
548///
549/// The event `handler` is called after the [`on_pre_event`] equivalent at the same context level. If the event
550/// `filter` allows more then one widget and one widget contains the other, the `handler` is called on the inner widget first.
551///
552/// # Async
553///
554/// Async event handlers are called like normal, but code after the first `.await` only runs in subsequent updates. This means
555/// that [`propagation`] must be stopped before the first `.await`, otherwise you are only signaling
556/// other async tasks handling the same event, if they are monitoring the propagation handle.
557///
558/// # Commands
559///
560/// You can use [`on_command`] to declare command event properties.
561///
562/// [`propagation`]: zng_app::event::AnyEventArgs::propagation
563/// [`ENABLED`]: zng_app::widget::info::Interactivity::ENABLED
564/// [`DISABLED`]: zng_app::widget::info::Interactivity::DISABLED
565pub fn on_event<C, A, F>(child: C, event: Event<A>, filter: F, handler: Handler<A>) -> UiNode
566where
567    C: IntoUiNode,
568    A: EventArgs,
569    F: FnMut(&A) -> bool + Send + 'static,
570{
571    on_event_impl(child.into_node(), event, filter, handler)
572}
573fn on_event_impl<A, F>(child: UiNode, event: Event<A>, mut filter: F, handler: Handler<A>) -> UiNode
574where
575    A: EventArgs,
576    F: FnMut(&A) -> bool + Send + 'static,
577{
578    let mut handler = handler.into_wgt_runner();
579    match_node(child, move |child, op| match op {
580        UiNodeOp::Init => {
581            WIDGET.sub_event(&event);
582        }
583        UiNodeOp::Deinit => {
584            handler.deinit();
585        }
586        UiNodeOp::Event { update } => {
587            child.event(update);
588
589            if let Some(args) = event.on(update)
590                && !args.propagation().is_stopped()
591                && filter(args)
592            {
593                #[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
594                let t = std::time::Instant::now();
595                #[cfg(all(debug_assertions, target_arch = "wasm32"))]
596                let t = web_time::Instant::now();
597
598                handler.event(args);
599
600                #[cfg(debug_assertions)]
601                {
602                    let t = t.elapsed();
603                    if t > std::time::Duration::from_millis(300) {
604                        tracing::warn!(
605                            "event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
606                            event.as_any().name(),
607                            WIDGET.id()
608                        );
609                    }
610                }
611            }
612        }
613        UiNodeOp::Update { updates } => {
614            child.update(updates);
615            handler.update();
616        }
617        _ => {}
618    })
619}
620
621/// Helper for declaring preview event properties.
622///
623/// This function is used by the [`event_property!`] macro.
624///
625/// # Filter
626///
627/// The `filter` predicate is called if [`propagation`] was not stopped. It must return `true` if the event arguments are
628/// relevant in the context of the widget. If it returns `true` the `handler` closure is called. Note that events that represent
629/// an *interaction* with the widget are send for both [`ENABLED`] and [`DISABLED`] targets, event properties should probably distinguish
630/// if they fire on normal interactions vs on *disabled* interactions.
631///
632/// # Route
633///
634/// The event `handler` is called before the [`on_event`] equivalent at the same context level. If the event
635/// `filter` allows more then one widget and one widget contains the other, the `handler` is called on the inner widget first.
636///
637/// # Async
638///
639/// Async event handlers are called like normal, but code after the first `.await` only runs in subsequent event updates. This means
640/// that [`propagation`] must be stopped before the first `.await`, otherwise you are only signaling
641/// other async tasks handling the same event, if they are monitoring the propagation handle.
642///
643/// # Commands
644///
645/// You can use [`on_pre_command`] to declare command event properties.
646///
647/// [`propagation`]: zng_app::event::AnyEventArgs::propagation
648/// [`ENABLED`]: zng_app::widget::info::Interactivity::ENABLED
649/// [`DISABLED`]: zng_app::widget::info::Interactivity::DISABLED
650pub fn on_pre_event<C, A, F>(child: C, event: Event<A>, filter: F, handler: Handler<A>) -> UiNode
651where
652    C: IntoUiNode,
653    A: EventArgs,
654    F: FnMut(&A) -> bool + Send + 'static,
655{
656    on_pre_event_impl(child.into_node(), event, filter, handler)
657}
658fn on_pre_event_impl<A, F>(child: UiNode, event: Event<A>, mut filter: F, handler: Handler<A>) -> UiNode
659where
660    A: EventArgs,
661    F: FnMut(&A) -> bool + Send + 'static,
662{
663    let mut handler = handler.into_wgt_runner();
664    match_node(child, move |_, op| match op {
665        UiNodeOp::Init => {
666            WIDGET.sub_event(&event);
667        }
668        UiNodeOp::Deinit => {
669            handler.deinit();
670        }
671        UiNodeOp::Event { update } => {
672            if let Some(args) = event.on(update)
673                && !args.propagation().is_stopped()
674                && filter(args)
675            {
676                #[cfg(debug_assertions)]
677                let t = std::time::Instant::now();
678
679                handler.event(args);
680
681                #[cfg(debug_assertions)]
682                {
683                    let t = t.elapsed();
684                    if t > std::time::Duration::from_millis(300) {
685                        tracing::warn!(
686                            "preview event handler for `{}` in {:?} blocked for {t:?}, consider using `async_hn!`",
687                            event.as_any().name(),
688                            WIDGET.id()
689                        );
690                    }
691                }
692            }
693        }
694        UiNodeOp::Update { .. } => {
695            handler.update();
696        }
697        _ => {}
698    })
699}
700
701#[doc(hidden)]
702#[macro_export]
703macro_rules! __command_property {
704    // final match, command_property! never calls this directly
705    (
706        $(#[$on_cmd_attrs:meta])*
707        $vis:vis fn $command:ident {
708            cmd { $cmd_init:expr }
709            widget_impl { $($widget_impl:tt)* }
710            enabled_var { $enabled_var:expr }
711            generate_can_property: false
712        }
713    ) => { $crate::node::paste! {
714        $(#[$on_cmd_attrs])*
715        ///
716        /// # Preview
717        ///
718        #[doc = "You can preview this command event using [`on_pre_"$command "`](fn.on_pre_"$command ".html)."]
719        /// Otherwise the handler is only called after the widget content has a chance to stop propagation.
720        ///
721        /// # Async
722        ///
723        /// You can use async event handlers with this property.
724        #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ))]
725        $vis fn [<on_ $command>](
726            child: impl $crate::node::__macro_util::IntoUiNode,
727            handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
728        ) -> $crate::node::__macro_util::UiNode {
729            $crate::node::on_command(child, || $cmd_init, || $enabled_var, handler)
730        }
731
732        #[doc = "Preview [`on_"$command "`](fn.on_"$command ".html) command."]
733        ///
734        /// # Preview
735        ///
736        /// Preview event properties call the handler before the main event property and before the widget content, if you stop
737        /// the propagation of a preview event the main event handler is not called.
738        ///
739        /// # Async
740        ///
741        /// You can use async event handlers with this property, note that only the code before the fist `.await` is *preview*,
742        /// subsequent code runs in widget updates.
743        #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ) $($widget_impl)*)]
744        $vis fn [<on_pre_ $command>](
745            child: impl $crate::node::__macro_util::IntoUiNode,
746            handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
747        ) -> $crate::node::__macro_util::UiNode {
748            $crate::node::on_pre_command(child, || $cmd_init, || $enabled_var, handler)
749        }
750    } };
751    (
752        $(#[$on_cmd_attrs:meta])*
753        $vis:vis fn $command:ident {
754            cmd { $cmd_init:expr }
755            widget_impl { $($widget_impl:tt)* }
756            generate_can_property: true
757        }
758    ) => { $crate::node::paste! {
759        $(#[$on_cmd_attrs])*
760        ///
761        /// # Preview
762        ///
763        #[doc = "You can preview this command event using [`on_pre_"$command "`](fn.on_pre_"$command ".html)."]
764        /// Otherwise the handler is only called after the widget content has a chance to stop propagation.
765        ///
766        /// # Async
767        ///
768        /// You can use async event handlers with this property.
769        ///
770        /// # Enabled
771        ///
772        #[doc = "You can use the [`can_"$command "`] context property to disable the command handle."]
773        /// When disabled the `handler` is not called and the command handle signals as disabled.
774        #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ))]
775        $vis fn [<on_ $command>](
776            child: impl $crate::node::__macro_util::IntoUiNode,
777            handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
778        ) -> $crate::node::__macro_util::UiNode {
779            $crate::node::on_command(child, || $cmd_init, || $crate::node::__macro_util::IntoVar::into_var(self::[<CAN_ $command:upper _VAR>]), handler)
780        }
781
782        #[doc = "Preview [`on_"$command "`](fn.on_"$command ".html) command."]
783        ///
784        /// # Preview
785        ///
786        /// Preview event properties call the handler before the main event property and before the widget content, if you stop
787        /// the propagation of a preview event the main event handler is not called.
788        ///
789        /// # Async
790        ///
791        /// You can use async event handlers with this property, note that only the code before the fist `.await` is *preview*,
792        /// subsequent code runs in widget updates.
793        ///
794        /// # Enabled
795        ///
796        #[doc = "You can use the [`can_"$command "`](fn@can_"$command ") context property to disable the command handle."]
797        #[$crate::node::__macro_util::property(EVENT, default( $crate::node::__macro_util::hn!(|_|{}) ) $($widget_impl)*)]
798        $vis fn [<on_pre_ $command>](
799            child: impl $crate::node::__macro_util::IntoUiNode,
800            handler: $crate::node::__macro_util::Handler<$crate::node::__macro_util::CommandArgs>,
801        ) -> $crate::node::__macro_util::UiNode {
802            $crate::node::on_pre_command(child, || $cmd_init, || $crate::node::__macro_util::IntoVar::into_var(self::[<CAN_ $command:upper _VAR>]), handler)
803        }
804
805        $crate::node::__macro_util::context_var! {
806            #[doc = "Enable/disable [`on_"$command "`](fn@on_"$command ") and [`on_pre_"$command "`](fn@on_pre_"$command ") command handles."]
807            ///
808            #[doc = "Enabled by default, use the [`can_"$command "`](fn@can_" $command ") property to set."]
809            $vis static [<CAN_ $command:upper _VAR>]: bool = true;
810        }
811
812        #[doc = "Defines if [`on_"$command "`](fn@on_"$command ") and [`on_pre_"$command "`](fn@on_pre_"$command ") command handles"]
813        /// are enabled in the context.
814        ///
815        /// Is enabled by default.
816        ///
817        #[doc = "Sets the [`CAN_"$command:upper "_VAR`]."]
818        #[$crate::node::__macro_util::property(CONTEXT, default(true) $($widget_impl)*)]
819        $vis fn [<can_ $command>](
820            child: impl $crate::node::__macro_util::IntoUiNode,
821            enabled: impl $crate::node::__macro_util::IntoVar<bool>,
822        ) -> $crate::node::__macro_util::UiNode {
823            $crate::node::with_context_var(child, self::[<CAN_ $command:upper _VAR>], enabled)
824        }
825    } };
826
827    // custom enabled + widget_impl
828    (
829        $(#[$on_cmd_attrs:meta])*
830        $vis:vis fn $command:ident {
831            cmd { $cmd_init:expr }
832            enabled { $enabled_var:expr }
833            widget_impl_ty { $Wgt:ty }
834        }
835    ) => {
836        $crate::__command_property! {
837            $(#[$on_cmd_attrs])*
838            $vis fn $command {
839                cmd { $cmd_init }
840                widget_impl { , widget_impl($Wgt) }
841                enabled_var { $enabled_var }
842                generate_can_property: false
843            }
844        }
845    };
846
847    // default enabled + widget_impl
848    (
849        $(#[$on_cmd_attrs:meta])*
850        $vis:vis fn $command:ident {
851            cmd { $cmd_init:expr }
852            widget_impl_ty { $Wgt:ty }
853        }
854    ) => {
855        $crate::node::paste! {
856            $crate::__command_property! {
857                $(#[$on_cmd_attrs])*
858                $vis fn $command {
859                    cmd { $cmd_init }
860                    widget_impl { , widget_impl($Wgt) }
861                    generate_can_property: true
862                }
863            }
864        }
865    };
866
867    // custom enabled, no widget_impl
868    (
869        $(#[$on_cmd_attrs:meta])*
870        $vis:vis fn $command:ident {
871            cmd { $cmd_init:expr }
872            enabled { $enabled_var:expr }
873        }
874    ) => {
875        $crate::__command_property! {
876            $(#[$on_cmd_attrs])*
877            $vis fn $command {
878                cmd { $cmd_init }
879                widget_impl { }
880                enabled_var { $enabled_var }
881                generate_can_property: false
882            }
883        }
884    };
885
886    // default enabled, no widget_impl
887    (
888        $(#[$on_cmd_attrs:meta])*
889        $vis:vis fn $command:ident {
890            cmd { $cmd_init:expr }
891        }
892    ) => {
893        $crate::__command_property! {
894            $(#[$on_cmd_attrs])*
895            $vis fn $command {
896                cmd { $cmd_init }
897                widget_impl { }
898                generate_can_property: true
899            }
900        }
901    };
902}
903
904///<span data-del-macro-root></span> Declare one or more command event properties.
905///
906/// Each declaration expands to two properties `on_$command` and `on_pre_$command`.
907/// The preview properties call [`on_pre_command`], the main event properties call [`on_command`].
908///
909/// By default a `CAN_$COMMAND_VAR` and `can_$command` var and property are also generated.
910///
911/// # Examples
912///
913/// ```
914/// # fn main() { }
915/// # use zng_app::{event::*, widget::*};
916/// # use zng_app::var::*;
917/// # use zng_wgt::node::*;
918/// # command! {
919/// # pub static PASTE_CMD;
920/// # }
921/// command_property! {
922///     /// Paste command property docs.
923///     pub fn paste {
924///         cmd: PASTE_CMD.scoped(WIDGET.id()),
925///     }
926/// }
927/// ```
928///
929/// The example above generates event properties `on_pre_paste` and `on_paste`, context property `can_paste` and
930/// context var `CAN_PASTE_VAR`.
931///
932/// The event properties will hold a [`CommandHandle`] when the widget is inited, the handle wil signal enabled based
933/// on the contextual var value. The context property sets that var in a widget context.
934///
935/// # Command
936///
937/// The `cmd:` expression evaluates on init to generate the command, this allows for the
938/// creation of widget scoped commands. The new command property event handler will receive events
939/// for the command and scope that target the widget where the property is set.
940///
941/// If the command is scoped on the root widget and the command property is set on the same root widget a second handle
942/// is taken for the window scope too, so callers can target the *window* using the window ID or the root widget ID.
943///
944/// # Enabled
945///
946/// The `enabled:` expression evaluates on init to generate a boolean variable that defines
947/// if the command handle is enabled. Command event handlers track both their existence and
948/// the enabled flag, see [`Command::subscribe`] for details.
949///
950/// If not provided a `CAN_$CMD_VAR` and `can_$cmd` contextual  var and property are generated.
951///
952/// ```
953/// # fn main() { }
954/// # use zng_app::{event::*, widget::*};
955/// # use zng_app::var::*;
956/// # use zng_wgt::node::*;
957/// # command! {
958/// # pub static PASTE_CMD;
959/// # }
960/// command_property! {
961///     /// Paste command property docs.
962///     pub fn paste {
963///         cmd: PASTE_CMD.scoped(WIDGET.id()),
964///         enabled: const_var(true), // command handle always enabled
965///     }
966/// }
967/// ```
968///
969/// In the example above `enabled:` is set to a custom variable, so the associated
970/// `CAN_PASTE_VAR` and `can_paste` will not be generated.
971///
972/// # Implement For
973///
974/// You can implement the new properties for a widget or mixin using the `widget_impl:` directive:
975///
976/// ```
977/// # fn main() { }
978/// # use zng_wgt::node::*;
979/// # use zng_app::{event::*, widget::*};
980/// # use zng_app::var::*;
981/// # use zng_wgt::node::*;
982/// # command! {
983/// # pub static PASTE_CMD;
984/// # }
985/// /// Clipboard handler.
986/// #[widget_mixin]
987/// pub struct ClipboardMix<P>(P);
988///
989/// command_property! {
990///     /// Paste command property docs.
991///     pub fn paste {
992///         cmd: PASTE_CMD.scoped(WIDGET.id()),
993///         widget_impl: ClipboardMix<P>,
994///     }
995/// }
996/// ```
997///
998/// [`Command::subscribe`]: zng_app::event::Command::subscribe
999#[macro_export]
1000macro_rules! command_property {
1001    ($(
1002        $(#[$on_cmd_attrs:meta])*
1003        $vis:vis fn $command:ident {
1004            cmd: $cmd_init:expr
1005            $(, enabled: $enabled_var:expr)?
1006            $(, widget_impl: $Wgt:ty)?
1007            $(,)?
1008        }
1009    )+) => {$(
1010        $crate::__command_property! {
1011            $(#[$on_cmd_attrs])*
1012            $vis fn $command {
1013                cmd { $cmd_init }
1014                $( enabled { $enabled_var } )?
1015                $( widget_impl_ty { $Wgt } )?
1016            }
1017        }
1018    )+};
1019}
1020
1021/// Helper for declaring command event properties.
1022///
1023/// This function is used by the [`command_property!`] macro.
1024///
1025/// # Command
1026///
1027/// The `command_builder` closure is called on init to generate the command, it is a closure to allow
1028/// creation of widget scoped commands. The event `handler` will receive events for the command
1029/// and scope that target the widget where it is set.
1030///
1031/// If the command is scoped on the root widget and `on_command` is set on the same root widget a second handle
1032/// is taken for the window scope too, so callers can target the *window* using the window ID or the root widget ID.
1033///
1034/// # Enabled
1035///
1036/// The `enabled_builder` closure is called on init to generate a boolean variable that defines
1037/// if the command handle is enabled. Command event handlers track both their existence and
1038/// the enabled flag, see [`Command::subscribe`] for details.
1039///
1040/// Note that the command handler can be enabled even when the widget is disabled, the widget
1041/// will receive the event while disabled in this case, you can use this to show feedback explaining
1042/// why the command cannot run.
1043///
1044/// # Route
1045///
1046/// The event `handler` is called after the [`on_pre_command`] equivalent at the same context level. If the command
1047/// event targets more then one widget and one widget contains the other, the `handler` is called on the inner widget first.
1048///
1049/// # Async
1050///
1051/// Async event handlers are called like normal, but code after the first `.await` only runs in subsequent updates. This means
1052/// that [`propagation`] must be stopped before the first `.await`, otherwise you are only signaling
1053/// other async tasks handling the same event, if they are monitoring the propagation handle.
1054///  
1055/// [`propagation`]: zng_app::event::AnyEventArgs::propagation
1056/// [`Command::subscribe`]: zng_app::event::Command::subscribe
1057pub fn on_command<U, CB, EB>(child: U, command_builder: CB, enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1058where
1059    U: IntoUiNode,
1060    CB: FnMut() -> Command + Send + 'static,
1061    EB: FnMut() -> Var<bool> + Send + 'static,
1062{
1063    on_command_impl(child.into_node(), command_builder, enabled_builder, handler)
1064}
1065fn on_command_impl<CB, EB>(child: UiNode, mut command_builder: CB, mut enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1066where
1067    CB: FnMut() -> Command + Send + 'static,
1068    EB: FnMut() -> Var<bool> + Send + 'static,
1069{
1070    let mut handler = handler.into_wgt_runner();
1071    let mut enabled = None;
1072    let mut handle = CommandHandle::dummy();
1073    let mut win_handle = CommandHandle::dummy();
1074    let mut command = NIL_CMD;
1075    let mut last_propagation = EventPropagationHandle::new();
1076
1077    match_node(child, move |child, op| match op {
1078        UiNodeOp::Init => {
1079            child.init();
1080
1081            let e = enabled_builder();
1082            WIDGET.sub_var(&e);
1083            let is_enabled = e.get();
1084            enabled = Some(e);
1085
1086            command = command_builder();
1087
1088            let id = WIDGET.id();
1089            handle = command.subscribe_wgt(is_enabled, id);
1090            if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
1091                // root scope, also include the window.
1092                win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
1093            }
1094        }
1095        UiNodeOp::Deinit => {
1096            child.deinit();
1097
1098            enabled = None;
1099            handle = CommandHandle::dummy();
1100            win_handle = CommandHandle::dummy();
1101            command = NIL_CMD;
1102            handler.deinit();
1103        }
1104
1105        UiNodeOp::Event { update } => {
1106            child.event(update);
1107
1108            if command.event().has(update) {
1109                if win_handle.is_dummy() {
1110                    if let Some(args) = command.on_unhandled(update) {
1111                        handler.event(args);
1112                    }
1113                } else if let Some(args) = command
1114                    .on_unhandled(update)
1115                    .or_else(|| command.scoped(WINDOW.id()).on_unhandled(update))
1116                    && &last_propagation != args.propagation()
1117                {
1118                    handler.event(args);
1119                    last_propagation = args.propagation().clone();
1120                }
1121            }
1122        }
1123        UiNodeOp::Update { updates } => {
1124            child.update(updates);
1125
1126            handler.update();
1127
1128            if let Some(enabled) = enabled.as_ref().expect("node not inited").get_new() {
1129                handle.set_enabled(enabled);
1130                win_handle.set_enabled(enabled);
1131            }
1132        }
1133
1134        _ => {}
1135    })
1136}
1137
1138zng_app::event::command! {
1139    static NIL_CMD;
1140}
1141
1142/// Helper for declaring command preview handlers.
1143///
1144/// Other then the route this helper behaves exactly like [`on_command`].
1145///
1146/// # Route
1147///
1148/// The event `handler` is called before the [`on_command`] equivalent at the same context level. If the command event
1149/// targets more then one widget and one widget contains the other, the `handler` is called on the inner widget first.
1150pub fn on_pre_command<U, CB, EB>(child: U, command_builder: CB, enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1151where
1152    U: IntoUiNode,
1153    CB: FnMut() -> Command + Send + 'static,
1154    EB: FnMut() -> Var<bool> + Send + 'static,
1155{
1156    on_pre_command_impl(child.into_node(), command_builder, enabled_builder, handler)
1157}
1158fn on_pre_command_impl<CB, EB>(child: UiNode, mut command_builder: CB, mut enabled_builder: EB, handler: Handler<CommandArgs>) -> UiNode
1159where
1160    CB: FnMut() -> Command + Send + 'static,
1161    EB: FnMut() -> Var<bool> + Send + 'static,
1162{
1163    let mut handler = handler.into_wgt_runner();
1164    let mut enabled = None;
1165    let mut handle = CommandHandle::dummy();
1166    let mut win_handle = CommandHandle::dummy();
1167    let mut command = NIL_CMD;
1168    let mut last_propagation = EventPropagationHandle::new();
1169
1170    match_node(child, move |child, op| match op {
1171        UiNodeOp::Init => {
1172            child.init();
1173
1174            let e = enabled_builder();
1175            WIDGET.sub_var(&e);
1176            let is_enabled = e.get();
1177            enabled = Some(e);
1178
1179            command = command_builder();
1180
1181            let id = WIDGET.id();
1182            handle = command.subscribe_wgt(is_enabled, id);
1183            if CommandScope::Widget(id) == command.scope() && WIDGET.parent_id().is_none() {
1184                // root scope, also include the window.
1185                win_handle = command.scoped(WINDOW.id()).subscribe_wgt(is_enabled, id);
1186            }
1187        }
1188        UiNodeOp::Deinit => {
1189            child.deinit();
1190
1191            enabled = None;
1192            handle = CommandHandle::dummy();
1193            win_handle = CommandHandle::dummy();
1194            command = NIL_CMD;
1195            handler.deinit();
1196        }
1197
1198        UiNodeOp::Event { update } => {
1199            if command.event().has(update) {
1200                if win_handle.is_dummy() {
1201                    if let Some(args) = command.on_unhandled(update) {
1202                        handler.event(args);
1203                    }
1204                } else if let Some(args) = command
1205                    .on_unhandled(update)
1206                    .or_else(|| command.scoped(WINDOW.id()).on_unhandled(update))
1207                    && &last_propagation != args.propagation()
1208                {
1209                    handler.event(args);
1210                    last_propagation = args.propagation().clone();
1211                }
1212            }
1213        }
1214        UiNodeOp::Update { .. } => {
1215            handler.update();
1216
1217            if let Some(enabled) = enabled.as_ref().expect("on_pre_command not initialized").get_new() {
1218                handle.set_enabled(enabled);
1219                win_handle.set_enabled(enabled);
1220            }
1221        }
1222
1223        _ => {}
1224    })
1225}
1226
1227/// Logs an error if the `_var` is always read-only.
1228pub fn validate_getter_var<T: VarValue>(_var: &Var<T>) {
1229    #[cfg(debug_assertions)]
1230    if _var.capabilities().is_always_read_only() {
1231        tracing::error!(
1232            "`is_`, `has_` or `get_` property inited with read-only var in `{}`",
1233            WIDGET.trace_id()
1234        );
1235    }
1236}
1237
1238/// Helper for declaring state properties that depend on a single event.
1239///
1240/// When the `event` is received `on_event` is called, if it provides a new state the `state` variable is set.
1241pub fn event_state<A: EventArgs, S: VarValue>(
1242    child: impl IntoUiNode,
1243    state: impl IntoVar<S>,
1244    default: S,
1245    event: Event<A>,
1246    mut on_event: impl FnMut(&A) -> Option<S> + Send + 'static,
1247) -> UiNode {
1248    let state = state.into_var();
1249    match_node(child, move |_, op| match op {
1250        UiNodeOp::Init => {
1251            validate_getter_var(&state);
1252            WIDGET.sub_event(&event);
1253            state.set(default.clone());
1254        }
1255        UiNodeOp::Deinit => {
1256            state.set(default.clone());
1257        }
1258        UiNodeOp::Event { update } => {
1259            if let Some(args) = event.on(update)
1260                && let Some(s) = on_event(args)
1261            {
1262                state.set(s);
1263            }
1264        }
1265        _ => {}
1266    })
1267}
1268
1269/// Helper for declaring state properties that depend on two other event states.
1270///
1271/// When the `event#` is received `on_event#` is called, if it provides a new value `merge` is called, if merge
1272/// provides a new value the `state` variable is set.
1273#[expect(clippy::too_many_arguments)]
1274pub fn event_state2<A0, A1, S0, S1, S>(
1275    child: impl IntoUiNode,
1276    state: impl IntoVar<S>,
1277    default: S,
1278    event0: Event<A0>,
1279    default0: S0,
1280    mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1281    event1: Event<A1>,
1282    default1: S1,
1283    mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1284    mut merge: impl FnMut(S0, S1) -> Option<S> + Send + 'static,
1285) -> UiNode
1286where
1287    A0: EventArgs,
1288    A1: EventArgs,
1289    S0: VarValue,
1290    S1: VarValue,
1291    S: VarValue,
1292{
1293    let state = state.into_var();
1294    let partial_default = (default0, default1);
1295    let mut partial = partial_default.clone();
1296
1297    match_node(child, move |child, op| match op {
1298        UiNodeOp::Init => {
1299            validate_getter_var(&state);
1300            WIDGET.sub_event(&event0).sub_event(&event1);
1301
1302            partial = partial_default.clone();
1303            state.set(default.clone());
1304        }
1305        UiNodeOp::Deinit => {
1306            state.set(default.clone());
1307        }
1308        UiNodeOp::Event { update } => {
1309            let mut updated = false;
1310            if let Some(args) = event0.on(update) {
1311                if let Some(state) = on_event0(args)
1312                    && partial.0 != state
1313                {
1314                    partial.0 = state;
1315                    updated = true;
1316                }
1317            } else if let Some(args) = event1.on(update)
1318                && let Some(state) = on_event1(args)
1319                && partial.1 != state
1320            {
1321                partial.1 = state;
1322                updated = true;
1323            }
1324            child.event(update);
1325
1326            if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone()) {
1327                state.set(value);
1328            }
1329        }
1330        _ => {}
1331    })
1332}
1333
1334/// Helper for declaring state properties that depend on three other event states.
1335///
1336/// When the `event#` is received `on_event#` is called, if it provides a new value `merge` is called, if merge
1337/// provides a new value the `state` variable is set.
1338#[expect(clippy::too_many_arguments)]
1339pub fn event_state3<A0, A1, A2, S0, S1, S2, S>(
1340    child: impl IntoUiNode,
1341    state: impl IntoVar<S>,
1342    default: S,
1343    event0: Event<A0>,
1344    default0: S0,
1345    mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1346    event1: Event<A1>,
1347    default1: S1,
1348    mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1349    event2: Event<A2>,
1350    default2: S2,
1351    mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1352    mut merge: impl FnMut(S0, S1, S2) -> Option<S> + Send + 'static,
1353) -> UiNode
1354where
1355    A0: EventArgs,
1356    A1: EventArgs,
1357    A2: EventArgs,
1358    S0: VarValue,
1359    S1: VarValue,
1360    S2: VarValue,
1361    S: VarValue,
1362{
1363    let state = state.into_var();
1364    let partial_default = (default0, default1, default2);
1365    let mut partial = partial_default.clone();
1366
1367    match_node(child, move |child, op| match op {
1368        UiNodeOp::Init => {
1369            validate_getter_var(&state);
1370            WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2);
1371
1372            partial = partial_default.clone();
1373            state.set(default.clone());
1374        }
1375        UiNodeOp::Deinit => {
1376            state.set(default.clone());
1377        }
1378        UiNodeOp::Event { update } => {
1379            let mut updated = false;
1380            if let Some(args) = event0.on(update) {
1381                if let Some(state) = on_event0(args)
1382                    && partial.0 != state
1383                {
1384                    partial.0 = state;
1385                    updated = true;
1386                }
1387            } else if let Some(args) = event1.on(update) {
1388                if let Some(state) = on_event1(args)
1389                    && partial.1 != state
1390                {
1391                    partial.1 = state;
1392                    updated = true;
1393                }
1394            } else if let Some(args) = event2.on(update)
1395                && let Some(state) = on_event2(args)
1396                && partial.2 != state
1397            {
1398                partial.2 = state;
1399                updated = true;
1400            }
1401            child.event(update);
1402
1403            if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone()) {
1404                state.set(value);
1405            }
1406        }
1407        _ => {}
1408    })
1409}
1410
1411/// Helper for declaring state properties that depend on four other event states.
1412///
1413/// When the `event#` is received `on_event#` is called, if it provides a new value `merge` is called, if merge
1414/// provides a new value the `state` variable is set.
1415#[expect(clippy::too_many_arguments)]
1416pub fn event_state4<A0, A1, A2, A3, S0, S1, S2, S3, S>(
1417    child: impl IntoUiNode,
1418    state: impl IntoVar<S>,
1419    default: S,
1420    event0: Event<A0>,
1421    default0: S0,
1422    mut on_event0: impl FnMut(&A0) -> Option<S0> + Send + 'static,
1423    event1: Event<A1>,
1424    default1: S1,
1425    mut on_event1: impl FnMut(&A1) -> Option<S1> + Send + 'static,
1426    event2: Event<A2>,
1427    default2: S2,
1428    mut on_event2: impl FnMut(&A2) -> Option<S2> + Send + 'static,
1429    event3: Event<A3>,
1430    default3: S3,
1431    mut on_event3: impl FnMut(&A3) -> Option<S3> + Send + 'static,
1432    mut merge: impl FnMut(S0, S1, S2, S3) -> Option<S> + Send + 'static,
1433) -> UiNode
1434where
1435    A0: EventArgs,
1436    A1: EventArgs,
1437    A2: EventArgs,
1438    A3: EventArgs,
1439    S0: VarValue,
1440    S1: VarValue,
1441    S2: VarValue,
1442    S3: VarValue,
1443    S: VarValue,
1444{
1445    let state = state.into_var();
1446    let partial_default = (default0, default1, default2, default3);
1447    let mut partial = partial_default.clone();
1448
1449    match_node(child, move |child, op| match op {
1450        UiNodeOp::Init => {
1451            validate_getter_var(&state);
1452            WIDGET.sub_event(&event0).sub_event(&event1).sub_event(&event2).sub_event(&event3);
1453
1454            partial = partial_default.clone();
1455            state.set(default.clone());
1456        }
1457        UiNodeOp::Deinit => {
1458            state.set(default.clone());
1459        }
1460        UiNodeOp::Event { update } => {
1461            let mut updated = false;
1462            if let Some(args) = event0.on(update) {
1463                if let Some(state) = on_event0(args)
1464                    && partial.0 != state
1465                {
1466                    partial.0 = state;
1467                    updated = true;
1468                }
1469            } else if let Some(args) = event1.on(update) {
1470                if let Some(state) = on_event1(args)
1471                    && partial.1 != state
1472                {
1473                    partial.1 = state;
1474                    updated = true;
1475                }
1476            } else if let Some(args) = event2.on(update) {
1477                if let Some(state) = on_event2(args)
1478                    && partial.2 != state
1479                {
1480                    partial.2 = state;
1481                    updated = true;
1482                }
1483            } else if let Some(args) = event3.on(update)
1484                && let Some(state) = on_event3(args)
1485                && partial.3 != state
1486            {
1487                partial.3 = state;
1488                updated = true;
1489            }
1490            child.event(update);
1491
1492            if updated && let Some(value) = merge(partial.0.clone(), partial.1.clone(), partial.2.clone(), partial.3.clone()) {
1493                state.set(value);
1494            }
1495        }
1496        _ => {}
1497    })
1498}
1499
1500/// Helper for declaring state properties that are controlled by a variable.
1501///
1502/// On init the `state` variable is set to `source` and bound to it, you can use this to create state properties
1503/// that map from a context variable or to create composite properties that merge other state properties.
1504pub fn bind_state<T: VarValue>(child: impl IntoUiNode, source: impl IntoVar<T>, state: impl IntoVar<T>) -> UiNode {
1505    let source = source.into_var();
1506    let state = state.into_var();
1507    let mut _binding = VarHandle::dummy();
1508
1509    match_node(child, move |_, op| match op {
1510        UiNodeOp::Init => {
1511            validate_getter_var(&state);
1512            state.set_from(&source);
1513            _binding = source.bind(&state);
1514        }
1515        UiNodeOp::Deinit => {
1516            _binding = VarHandle::dummy();
1517        }
1518        _ => {}
1519    })
1520}
1521
1522/// Helper for declaring state properties that are controlled by a variable.
1523///
1524/// On init the `state` closure is called to provide a variable, the variable is set to `source` and bound to it,
1525/// you can use this to create state properties that map from a context variable or to create composite properties
1526/// that merge other state properties.
1527pub fn bind_state_init<T>(child: impl IntoUiNode, source: impl Fn() -> Var<T> + Send + 'static, state: impl IntoVar<T>) -> UiNode
1528where
1529    T: VarValue,
1530{
1531    let state = state.into_var();
1532    let mut _source_var = None;
1533    let mut _binding = VarHandle::dummy();
1534
1535    match_node(child, move |_, op| match op {
1536        UiNodeOp::Init => {
1537            validate_getter_var(&state);
1538            let source = source();
1539            state.set_from(&source);
1540            _binding = source.bind(&state);
1541            _source_var = Some(source);
1542        }
1543        UiNodeOp::Deinit => {
1544            _binding = VarHandle::dummy();
1545            _source_var = None;
1546        }
1547        _ => {}
1548    })
1549}
1550
1551/// Helper for declaring state properties that are controlled by values in the widget state map.
1552///
1553/// The `predicate` closure is called with the widget state on init and every update, if the returned value changes the `state`
1554/// updates. The `deinit` closure is called on deinit to get the *reset* value.
1555pub fn widget_state_is_state(
1556    child: impl IntoUiNode,
1557    predicate: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1558    deinit: impl Fn(StateMapRef<WIDGET>) -> bool + Send + 'static,
1559    state: impl IntoVar<bool>,
1560) -> UiNode {
1561    let state = state.into_var();
1562
1563    match_node(child, move |child, op| match op {
1564        UiNodeOp::Init => {
1565            validate_getter_var(&state);
1566            child.init();
1567            let s = WIDGET.with_state(&predicate);
1568            if s != state.get() {
1569                state.set(s);
1570            }
1571        }
1572        UiNodeOp::Deinit => {
1573            child.deinit();
1574            let s = WIDGET.with_state(&deinit);
1575            if s != state.get() {
1576                state.set(s);
1577            }
1578        }
1579        UiNodeOp::Update { updates } => {
1580            child.update(updates);
1581            let s = WIDGET.with_state(&predicate);
1582            if s != state.get() {
1583                state.set(s);
1584            }
1585        }
1586        _ => {}
1587    })
1588}
1589
1590/// Helper for declaring state getter properties that are controlled by values in the widget state map.
1591///
1592/// The `get_new` closure is called with the widget state and current `state` every init and update, if it returns some value
1593/// the `state` updates. The `get_deinit` closure is called on deinit to get the *reset* value.
1594pub fn widget_state_get_state<T: VarValue>(
1595    child: impl IntoUiNode,
1596    get_new: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1597    get_deinit: impl Fn(StateMapRef<WIDGET>, &T) -> Option<T> + Send + 'static,
1598    state: impl IntoVar<T>,
1599) -> UiNode {
1600    let state = state.into_var();
1601    match_node(child, move |child, op| match op {
1602        UiNodeOp::Init => {
1603            validate_getter_var(&state);
1604            child.init();
1605            let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1606            if let Some(new) = new {
1607                state.set(new);
1608            }
1609        }
1610        UiNodeOp::Deinit => {
1611            child.deinit();
1612
1613            let new = state.with(|s| WIDGET.with_state(|w| get_deinit(w, s)));
1614            if let Some(new) = new {
1615                state.set(new);
1616            }
1617        }
1618        UiNodeOp::Update { updates } => {
1619            child.update(updates);
1620            let new = state.with(|s| WIDGET.with_state(|w| get_new(w, s)));
1621            if let Some(new) = new {
1622                state.set(new);
1623            }
1624        }
1625        _ => {}
1626    })
1627}
1628
1629/// Transforms and clips the `content` node according with the default widget border align behavior.
1630///
1631/// Properties that *fill* the widget can wrap their fill content in this node to automatically implement
1632/// the expected interaction with the widget borders, the content will be positioned, sized and clipped according to the
1633/// widget borders, corner radius and border align.
1634pub fn fill_node(content: impl IntoUiNode) -> UiNode {
1635    let mut clip_bounds = PxSize::zero();
1636    let mut clip_corners = PxCornerRadius::zero();
1637
1638    let mut offset = PxVector::zero();
1639    let offset_key = FrameValueKey::new_unique();
1640    let mut define_frame = false;
1641
1642    match_node(content, move |child, op| match op {
1643        UiNodeOp::Init => {
1644            WIDGET.sub_var_layout(&BORDER_ALIGN_VAR);
1645            define_frame = false;
1646            offset = PxVector::zero();
1647        }
1648        UiNodeOp::Measure { desired_size, .. } => {
1649            let offsets = BORDER.inner_offsets();
1650            let align = BORDER_ALIGN_VAR.get();
1651
1652            let our_offsets = offsets * align;
1653            let size_offset = offsets - our_offsets;
1654
1655            let size_increase = PxSize::new(size_offset.horizontal(), size_offset.vertical());
1656
1657            *desired_size = LAYOUT.constraints().fill_size() + size_increase;
1658        }
1659        UiNodeOp::Layout { wl, final_size } => {
1660            // We are inside the *inner* bounds AND inside border_nodes:
1661            //
1662            // .. ( layout ( new_border/inner ( border_nodes ( FILL_NODES ( new_child_context ( new_child_layout ( ..
1663
1664            let (bounds, corners) = BORDER.fill_bounds();
1665
1666            let mut new_offset = bounds.origin.to_vector();
1667
1668            if clip_bounds != bounds.size || clip_corners != corners {
1669                clip_bounds = bounds.size;
1670                clip_corners = corners;
1671                WIDGET.render();
1672            }
1673
1674            let (_, branch_offset) = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(bounds.size), || {
1675                wl.with_branch_child(|wl| child.layout(wl))
1676            });
1677            new_offset += branch_offset;
1678
1679            if offset != new_offset {
1680                offset = new_offset;
1681
1682                if define_frame {
1683                    WIDGET.render_update();
1684                } else {
1685                    define_frame = true;
1686                    WIDGET.render();
1687                }
1688            }
1689
1690            *final_size = bounds.size;
1691        }
1692        UiNodeOp::Render { frame } => {
1693            let mut render = |frame: &mut FrameBuilder| {
1694                let bounds = PxRect::from_size(clip_bounds);
1695                frame.push_clips(
1696                    |c| {
1697                        if clip_corners != PxCornerRadius::zero() {
1698                            c.push_clip_rounded_rect(bounds, clip_corners, false, false);
1699                        } else {
1700                            c.push_clip_rect(bounds, false, false);
1701                        }
1702
1703                        if let Some(inline) = WIDGET.bounds().inline() {
1704                            for r in inline.negative_space().iter() {
1705                                c.push_clip_rect(*r, true, false);
1706                            }
1707                        }
1708                    },
1709                    |f| child.render(f),
1710                );
1711            };
1712
1713            if define_frame {
1714                frame.push_reference_frame(offset_key.into(), offset_key.bind(offset.into(), false), true, false, |frame| {
1715                    render(frame);
1716                });
1717            } else {
1718                render(frame);
1719            }
1720        }
1721        UiNodeOp::RenderUpdate { update } => {
1722            if define_frame {
1723                update.with_transform(offset_key.update(offset.into(), false), false, |update| {
1724                    child.render_update(update);
1725                });
1726            } else {
1727                child.render_update(update);
1728            }
1729        }
1730        _ => {}
1731    })
1732}
1733
1734/// Creates a border node that delegates rendering to a `border_visual` and manages the `border_offsets` coordinating
1735/// with the other borders of the widget.
1736///
1737/// This node disables inline layout for the widget.
1738pub fn border_node(child: impl IntoUiNode, border_offsets: impl IntoVar<SideOffsets>, border_visual: impl IntoUiNode) -> UiNode {
1739    let offsets = border_offsets.into_var();
1740    let mut render_offsets = PxSideOffsets::zero();
1741    let mut border_rect = PxRect::zero();
1742
1743    match_node(ui_vec![child, border_visual], move |children, op| match op {
1744        UiNodeOp::Init => {
1745            WIDGET.sub_var_layout(&offsets).sub_var_render(&BORDER_OVER_VAR);
1746        }
1747        UiNodeOp::Measure { wm, desired_size } => {
1748            let offsets = offsets.layout();
1749            *desired_size = BORDER.measure_border(offsets, || {
1750                LAYOUT.with_sub_size(PxSize::new(offsets.horizontal(), offsets.vertical()), || {
1751                    children.node().with_child(0, |n| wm.measure_block(n))
1752                })
1753            });
1754            children.delegated();
1755        }
1756        UiNodeOp::Layout { wl, final_size } => {
1757            // We are inside the *inner* bounds or inside a parent border_node:
1758            //
1759            // .. ( layout ( new_border/inner ( BORDER_NODES ( fill_nodes ( new_child_context ( new_child_layout ( ..
1760            //
1761            // `wl` is targeting the child transform, child nodes are naturally inside borders, so we
1762            // need to add to the offset and take the size, fill_nodes optionally cancel this transform.
1763
1764            let offsets = offsets.layout();
1765            if render_offsets != offsets {
1766                render_offsets = offsets;
1767                WIDGET.render();
1768            }
1769
1770            let parent_offsets = BORDER.inner_offsets();
1771            let origin = PxPoint::new(parent_offsets.left, parent_offsets.top);
1772            if border_rect.origin != origin {
1773                border_rect.origin = origin;
1774                WIDGET.render();
1775            }
1776
1777            // layout child and border visual
1778            BORDER.layout_border(offsets, || {
1779                wl.translate(PxVector::new(offsets.left, offsets.top));
1780
1781                let taken_size = PxSize::new(offsets.horizontal(), offsets.vertical());
1782                border_rect.size = LAYOUT.with_sub_size(taken_size, || children.node().with_child(0, |n| n.layout(wl)));
1783
1784                // layout border visual
1785                LAYOUT.with_constraints(PxConstraints2d::new_exact_size(border_rect.size), || {
1786                    BORDER.with_border_layout(border_rect, offsets, || {
1787                        children.node().with_child(1, |n| n.layout(wl));
1788                    });
1789                });
1790            });
1791            children.delegated();
1792
1793            *final_size = border_rect.size;
1794        }
1795        UiNodeOp::Render { frame } => {
1796            if BORDER_OVER_VAR.get() {
1797                children.node().with_child(0, |c| c.render(frame));
1798                BORDER.with_border_layout(border_rect, render_offsets, || {
1799                    children.node().with_child(1, |c| c.render(frame));
1800                });
1801            } else {
1802                BORDER.with_border_layout(border_rect, render_offsets, || {
1803                    children.node().with_child(1, |c| c.render(frame));
1804                });
1805                children.node().with_child(0, |c| c.render(frame));
1806            }
1807            children.delegated();
1808        }
1809        UiNodeOp::RenderUpdate { update } => {
1810            children.node().with_child(0, |c| c.render_update(update));
1811            BORDER.with_border_layout(border_rect, render_offsets, || {
1812                children.node().with_child(1, |c| c.render_update(update));
1813            });
1814            children.delegated();
1815        }
1816        _ => {}
1817    })
1818}
1819
1820/// Helper for declaring nodes that sets a context local value.
1821///
1822/// See [`context_local!`] for more details about contextual values.
1823///
1824/// [`context_local!`]: crate::prelude::context_local
1825pub fn with_context_local<T: Any + Send + Sync + 'static>(
1826    child: impl IntoUiNode,
1827    context: &'static ContextLocal<T>,
1828    value: impl Into<T>,
1829) -> UiNode {
1830    let mut value = Some(Arc::new(value.into()));
1831
1832    match_node(child, move |child, op| {
1833        context.with_context(&mut value, || child.op(op));
1834    })
1835}
1836
1837/// Helper for declaring nodes that sets a context local value generated on init.
1838///
1839/// The method calls the `init_value` closure on init to produce a *value* var that is presented as the [`ContextLocal<T>`]
1840/// in the widget and widget descendants. The closure can be called more than once if the returned node is reinited.
1841///
1842/// Apart from the value initialization this behaves just like [`with_context_local`].
1843///
1844/// [`ContextLocal<T>`]: zng_app_context::ContextLocal
1845pub fn with_context_local_init<T: Any + Send + Sync + 'static>(
1846    child: impl IntoUiNode,
1847    context: &'static ContextLocal<T>,
1848    init_value: impl FnMut() -> T + Send + 'static,
1849) -> UiNode {
1850    with_context_local_init_impl(child.into_node(), context, init_value)
1851}
1852fn with_context_local_init_impl<T: Any + Send + Sync + 'static>(
1853    child: UiNode,
1854    context: &'static ContextLocal<T>,
1855    mut init_value: impl FnMut() -> T + Send + 'static,
1856) -> UiNode {
1857    let mut value = None;
1858
1859    match_node(child, move |child, op| {
1860        let mut is_deinit = false;
1861        match &op {
1862            UiNodeOp::Init => {
1863                value = Some(Arc::new(init_value()));
1864            }
1865            UiNodeOp::Deinit => {
1866                is_deinit = true;
1867            }
1868            _ => {}
1869        }
1870
1871        context.with_context(&mut value, || child.op(op));
1872
1873        if is_deinit {
1874            value = None;
1875        }
1876    })
1877}
1878
1879/// Helper for declaring widgets that are recontextualized to take in some of the context
1880/// of an *original* parent.
1881///
1882/// See [`LocalContext::with_context_blend`] for more details about `over`. The returned
1883/// node will delegate all node operations to inside the blend. The [`WidgetUiNode::with_context`]
1884/// will delegate to the `child` widget context, but the `ctx` is not blended for this method, only
1885/// for [`UiNodeOp`] methods.
1886///
1887/// # Warning
1888///
1889/// Properties, context vars and context locals are implemented with the assumption that all consumers have
1890/// released the context on return, that is even if the context was shared with worker threads all work was block-waited.
1891/// This node breaks this assumption, specially with `over: true` you may cause unexpected behavior if you don't consider
1892/// carefully what context is being captured and what context is being replaced.
1893///
1894/// As a general rule, only capture during init or update in [`NestGroup::CHILD`], only wrap full widgets and only place the wrapped
1895/// widget in a parent's [`NestGroup::CHILD`] for a parent that has no special expectations about the child.
1896///
1897/// As an example of things that can go wrong, if you capture during layout, the `LAYOUT` context is captured
1898/// and replaces `over` the actual layout context during all subsequent layouts in the actual parent.
1899///
1900/// # Panics
1901///
1902/// Panics during init if `ctx` is not from the same app as the init context.
1903///
1904/// [`NestGroup::CHILD`]: zng_app::widget::builder::NestGroup::CHILD
1905/// [`UiNodeOp`]: zng_app::widget::node::UiNodeOp
1906/// [`LocalContext::with_context_blend`]: zng_app_context::LocalContext::with_context_blend
1907pub fn with_context_blend(mut ctx: LocalContext, over: bool, child: impl IntoUiNode) -> UiNode {
1908    match_widget(child, move |c, op| {
1909        if let UiNodeOp::Init = op {
1910            let init_app = LocalContext::current_app();
1911            ctx.with_context_blend(over, || {
1912                let ctx_app = LocalContext::current_app();
1913                assert_eq!(init_app, ctx_app);
1914                c.op(op)
1915            });
1916        } else {
1917            ctx.with_context_blend(over, || c.op(op));
1918        }
1919    })
1920}
1921
1922/// Helper for declaring properties that set the widget state.
1923///
1924/// The state ID is set in [`WIDGET`] on init and is kept updated. On deinit it is set to the `default` value.
1925///
1926/// # Examples
1927///
1928/// ```
1929/// # fn main() -> () { }
1930/// # use zng_app::{widget::{property, node::{UiNode, IntoUiNode}, WIDGET, WidgetUpdateMode}};
1931/// # use zng_var::IntoVar;
1932/// # use zng_wgt::node::with_widget_state;
1933/// # use zng_state_map::{StateId, static_id};
1934/// #
1935/// static_id! {
1936///     pub static ref FOO_ID: StateId<u32>;
1937/// }
1938///
1939/// #[property(CONTEXT)]
1940/// pub fn foo(child: impl IntoUiNode, value: impl IntoVar<u32>) -> UiNode {
1941///     with_widget_state(child, *FOO_ID, || 0, value)
1942/// }
1943///
1944/// // after the property is used and the widget initializes:
1945///
1946/// /// Get the value from outside the widget.
1947/// fn get_foo_outer(widget: &mut UiNode) -> u32 {
1948///     if let Some(mut wgt) = widget.as_widget() {
1949///         wgt.with_context(WidgetUpdateMode::Ignore, || WIDGET.get_state(*FOO_ID))
1950///             .unwrap_or(0)
1951///     } else {
1952///         0
1953///     }
1954/// }
1955///
1956/// /// Get the value from inside the widget.
1957/// fn get_foo_inner() -> u32 {
1958///     WIDGET.get_state(*FOO_ID).unwrap_or_default()
1959/// }
1960/// ```
1961///
1962/// [`WIDGET`]: zng_app::widget::WIDGET
1963pub fn with_widget_state<U, I, T>(child: U, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> UiNode
1964where
1965    U: IntoUiNode,
1966    I: Fn() -> T + Send + 'static,
1967    T: StateValue + VarValue,
1968{
1969    with_widget_state_impl(child.into_node(), id.into(), default, value.into_var())
1970}
1971fn with_widget_state_impl<I, T>(child: UiNode, id: impl Into<StateId<T>>, default: I, value: impl IntoVar<T>) -> UiNode
1972where
1973    I: Fn() -> T + Send + 'static,
1974    T: StateValue + VarValue,
1975{
1976    let id = id.into();
1977    let value = value.into_var();
1978
1979    match_node(child, move |child, op| match op {
1980        UiNodeOp::Init => {
1981            child.init();
1982            WIDGET.sub_var(&value);
1983            WIDGET.set_state(id, value.get());
1984        }
1985        UiNodeOp::Deinit => {
1986            child.deinit();
1987            WIDGET.set_state(id, default());
1988        }
1989        UiNodeOp::Update { updates } => {
1990            child.update(updates);
1991            if let Some(v) = value.get_new() {
1992                WIDGET.set_state(id, v);
1993            }
1994        }
1995        _ => {}
1996    })
1997}
1998
1999/// Helper for declaring properties that set the widget state with a custom closure.
2000///
2001/// The `default` closure is used to init the state value, then the `modify` closure is used to modify the state using the variable value.
2002///
2003/// On deinit the `default` value is set on the state again.
2004///
2005/// See [`with_widget_state`] for more details.
2006pub fn with_widget_state_modify<U, S, V, I, M>(child: U, id: impl Into<StateId<S>>, value: impl IntoVar<V>, default: I, modify: M) -> UiNode
2007where
2008    U: IntoUiNode,
2009    S: StateValue,
2010    V: VarValue,
2011    I: Fn() -> S + Send + 'static,
2012    M: FnMut(&mut S, &V) + Send + 'static,
2013{
2014    with_widget_state_modify_impl(child.into_node(), id.into(), value.into_var(), default, modify)
2015}
2016fn with_widget_state_modify_impl<S, V, I, M>(
2017    child: UiNode,
2018    id: impl Into<StateId<S>>,
2019    value: impl IntoVar<V>,
2020    default: I,
2021    mut modify: M,
2022) -> UiNode
2023where
2024    S: StateValue,
2025    V: VarValue,
2026    I: Fn() -> S + Send + 'static,
2027    M: FnMut(&mut S, &V) + Send + 'static,
2028{
2029    let id = id.into();
2030    let value = value.into_var();
2031
2032    match_node(child, move |child, op| match op {
2033        UiNodeOp::Init => {
2034            child.init();
2035
2036            WIDGET.sub_var(&value);
2037
2038            value.with(|v| {
2039                WIDGET.with_state_mut(|mut s| {
2040                    modify(s.entry(id).or_insert_with(&default), v);
2041                })
2042            })
2043        }
2044        UiNodeOp::Deinit => {
2045            child.deinit();
2046
2047            WIDGET.set_state(id, default());
2048        }
2049        UiNodeOp::Update { updates } => {
2050            child.update(updates);
2051            value.with_new(|v| {
2052                WIDGET.with_state_mut(|mut s| {
2053                    modify(s.req_mut(id), v);
2054                })
2055            });
2056        }
2057        _ => {}
2058    })
2059}
2060
2061/// Create a node that controls interaction for all widgets inside `node`.
2062///
2063/// When the `interactive` var is `false` all descendant widgets are [`BLOCKED`].
2064///
2065/// Unlike the [`interactive`] property this does not apply to the contextual widget, only `child` and descendants.
2066///
2067/// The node works for either if the `child` is a widget or if it only contains widgets, the performance
2068/// is slightly better if the `child` is a widget.
2069///
2070/// [`interactive`]: fn@crate::interactive
2071/// [`BLOCKED`]: Interactivity::BLOCKED
2072pub fn interactive_node(child: impl IntoUiNode, interactive: impl IntoVar<bool>) -> UiNode {
2073    let interactive = interactive.into_var();
2074
2075    match_node(child, move |child, op| match op {
2076        UiNodeOp::Init => {
2077            WIDGET.sub_var_info(&interactive);
2078        }
2079        UiNodeOp::Info { info } => {
2080            if interactive.get() {
2081                child.info(info);
2082            } else if let Some(mut wgt) = child.node().as_widget() {
2083                let id = wgt.id();
2084                // child is a widget.
2085                info.push_interactivity_filter(move |args| {
2086                    if args.info.id() == id {
2087                        Interactivity::BLOCKED
2088                    } else {
2089                        Interactivity::ENABLED
2090                    }
2091                });
2092                child.info(info);
2093            } else {
2094                let block_range = info.with_children_range(|info| child.info(info));
2095                if !block_range.is_empty() {
2096                    // has child widgets.
2097
2098                    let id = WIDGET.id();
2099                    info.push_interactivity_filter(move |args| {
2100                        if let Some(parent) = args.info.parent()
2101                            && parent.id() == id
2102                        {
2103                            // check child range
2104                            for (i, item) in parent.children().enumerate() {
2105                                if item == args.info {
2106                                    return if !block_range.contains(&i) {
2107                                        Interactivity::ENABLED
2108                                    } else {
2109                                        Interactivity::BLOCKED
2110                                    };
2111                                } else if i >= block_range.end {
2112                                    break;
2113                                }
2114                            }
2115                        }
2116                        Interactivity::ENABLED
2117                    });
2118                }
2119            }
2120        }
2121        _ => {}
2122    })
2123}
2124
2125/// Helper for a property that gets the index of the widget in the parent panel.
2126///
2127/// See [`with_index_len_node`] for more details.
2128pub fn with_index_node(
2129    child: impl IntoUiNode,
2130    panel_list_id: impl Into<StateId<PanelListRange>>,
2131    mut update: impl FnMut(Option<usize>) + Send + 'static,
2132) -> UiNode {
2133    let panel_list_id = panel_list_id.into();
2134    let mut version = None;
2135    match_node(child, move |_, op| match op {
2136        UiNodeOp::Deinit => {
2137            update(None);
2138            version = None;
2139        }
2140        UiNodeOp::Update { .. } => {
2141            // parent PanelList requests updates for this widget every time there is an update.
2142            let info = WIDGET.info();
2143            if let Some(parent) = info.parent()
2144                && let Some(mut c) = PanelListRange::update(&parent, panel_list_id, &mut version)
2145            {
2146                let id = info.id();
2147                let p = c.position(|w| w.id() == id);
2148                update(p);
2149            }
2150        }
2151        _ => {}
2152    })
2153}
2154
2155/// Helper for a property that gets the reverse index of the widget in the parent panel.
2156///
2157/// See [`with_index_len_node`] for more details.
2158pub fn with_rev_index_node(
2159    child: impl IntoUiNode,
2160    panel_list_id: impl Into<StateId<PanelListRange>>,
2161    mut update: impl FnMut(Option<usize>) + Send + 'static,
2162) -> UiNode {
2163    let panel_list_id = panel_list_id.into();
2164    let mut version = None;
2165    match_node(child, move |_, op| match op {
2166        UiNodeOp::Deinit => {
2167            update(None);
2168            version = None;
2169        }
2170        UiNodeOp::Update { .. } => {
2171            let info = WIDGET.info();
2172            if let Some(parent) = info.parent()
2173                && let Some(c) = PanelListRange::update(&parent, panel_list_id, &mut version)
2174            {
2175                let id = info.id();
2176                let p = c.rev().position(|w| w.id() == id);
2177                update(p);
2178            }
2179        }
2180        _ => {}
2181    })
2182}
2183
2184/// Helper for a property that gets the index of the widget in the parent panel and the number of children.
2185///  
2186/// Panels must use [`PanelList::track_info_range`] to collect the `panel_list_id`, then implement getter properties
2187/// using the methods in this module. See the `stack!` getter properties for examples.
2188///
2189/// [`PanelList::track_info_range`]: zng_app::widget::node::PanelList::track_info_range
2190pub fn with_index_len_node(
2191    child: impl IntoUiNode,
2192    panel_list_id: impl Into<StateId<PanelListRange>>,
2193    mut update: impl FnMut(Option<(usize, usize)>) + Send + 'static,
2194) -> UiNode {
2195    let panel_list_id = panel_list_id.into();
2196    let mut version = None;
2197    match_node(child, move |_, op| match op {
2198        UiNodeOp::Deinit => {
2199            update(None);
2200            version = None;
2201        }
2202        UiNodeOp::Update { .. } => {
2203            let info = WIDGET.info();
2204            if let Some(parent) = info.parent()
2205                && let Some(mut iter) = PanelListRange::update(&parent, panel_list_id, &mut version)
2206            {
2207                let id = info.id();
2208                let mut p = 0;
2209                let mut count = 0;
2210                for c in &mut iter {
2211                    if c.id() == id {
2212                        p = count;
2213                        count += 1 + iter.count();
2214                        break;
2215                    } else {
2216                        count += 1;
2217                    }
2218                }
2219                update(Some((p, count)));
2220            }
2221        }
2222        _ => {}
2223    })
2224}
2225
2226/// Node that presents `data` using `wgt_fn`.
2227///
2228/// The node's child is always the result of `wgt_fn` called for the `data` value, it is reinited every time
2229/// either variable changes.
2230///
2231/// See also [`presenter_opt`] for a presenter that is nil with the data is `None`.
2232///
2233/// See also the [`present`](VarPresent::present) method that can be called on the `data`` variable and [`present_data`](VarPresentData::present_data)
2234/// that can be called on the `wgt_fn` variable.
2235pub fn presenter<D: VarValue>(data: impl IntoVar<D>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2236    let data = data.into_var();
2237    let wgt_fn = wgt_fn.into_var();
2238
2239    match_node(UiNode::nil(), move |c, op| match op {
2240        UiNodeOp::Init => {
2241            WIDGET.sub_var(&data).sub_var(&wgt_fn);
2242            *c.node() = wgt_fn.get()(data.get());
2243        }
2244        UiNodeOp::Deinit => {
2245            c.deinit();
2246            *c.node() = UiNode::nil();
2247        }
2248        UiNodeOp::Update { .. } => {
2249            if data.is_new() || wgt_fn.is_new() {
2250                c.node().deinit();
2251                *c.node() = wgt_fn.get()(data.get());
2252                c.node().init();
2253                c.delegated();
2254                WIDGET.update_info().layout().render();
2255            }
2256        }
2257        _ => {}
2258    })
2259}
2260
2261/// Node that presents `data` using `wgt_fn` if data is available, otherwise presents nil.
2262///
2263/// This behaves like [`presenter`], but `wgt_fn` is not called if `data` is `None`.
2264///
2265/// See also the [`present_opt`](VarPresentOpt::present_opt) method that can be called on the data variable.
2266pub fn presenter_opt<D: VarValue>(data: impl IntoVar<Option<D>>, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2267    let data = data.into_var();
2268    let wgt_fn = wgt_fn.into_var();
2269
2270    match_node(UiNode::nil(), move |c, op| match op {
2271        UiNodeOp::Init => {
2272            WIDGET.sub_var(&data).sub_var(&wgt_fn);
2273            if let Some(data) = data.get() {
2274                *c.node() = wgt_fn.get()(data);
2275            }
2276        }
2277        UiNodeOp::Deinit => {
2278            c.deinit();
2279            *c.node() = UiNode::nil();
2280        }
2281        UiNodeOp::Update { .. } => {
2282            if data.is_new() || wgt_fn.is_new() {
2283                if let Some(data) = data.get() {
2284                    c.node().deinit();
2285                    *c.node() = wgt_fn.get()(data);
2286                    c.node().init();
2287                    c.delegated();
2288                    WIDGET.update_info().layout().render();
2289                } else if !c.node().is_nil() {
2290                    c.node().deinit();
2291                    *c.node() = UiNode::nil();
2292                    c.delegated();
2293                    WIDGET.update_info().layout().render();
2294                }
2295            }
2296        }
2297        _ => {}
2298    })
2299}
2300
2301/// Node list that presents `list` using `item_fn` for each new list item.
2302///
2303/// The node's children is the list mapped to node items, it is kept in sync, any list update is propagated to the node list.
2304///
2305/// See also the [`present_list`](VarPresentList::present_list) method that can be called on the list variable.
2306pub fn list_presenter<D: VarValue>(list: impl IntoVar<ObservableVec<D>>, item_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2307    ListPresenter {
2308        list: list.into_var(),
2309        item_fn: item_fn.into_var(),
2310        view: ui_vec![],
2311        _e: std::marker::PhantomData,
2312    }
2313    .into_node()
2314}
2315
2316/// Node list that presents `list` using `item_fn` for each list item.
2317///
2318/// The node's children are **regenerated** for each change in `list`, if possible prefer using [`ObservableVec`] with [`list_presenter`].
2319///
2320/// See also the [`present_list_from_iter`](VarPresentListFromIter::present_list_from_iter) method that can be called on the list variable.
2321pub fn list_presenter_from_iter<D, L>(list: impl IntoVar<L>, item_fn: impl IntoVar<WidgetFn<D>>) -> UiNode
2322where
2323    D: VarValue,
2324    L: IntoIterator<Item = D> + VarValue,
2325{
2326    ListPresenterFromIter {
2327        list: list.into_var(),
2328        item_fn: item_fn.into_var(),
2329        view: ui_vec![],
2330        _e: std::marker::PhantomData,
2331    }
2332    .into_node()
2333}
2334
2335struct ListPresenter<D>
2336where
2337    D: VarValue,
2338{
2339    list: Var<ObservableVec<D>>,
2340    item_fn: Var<WidgetFn<D>>,
2341    view: UiVec,
2342    _e: std::marker::PhantomData<D>,
2343}
2344
2345impl<D> UiNodeImpl for ListPresenter<D>
2346where
2347    D: VarValue,
2348{
2349    fn children_len(&self) -> usize {
2350        self.view.len()
2351    }
2352
2353    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
2354        self.view.with_child(index, visitor)
2355    }
2356
2357    fn is_list(&self) -> bool {
2358        true
2359    }
2360
2361    fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
2362        self.view.for_each_child(visitor);
2363    }
2364
2365    fn try_for_each_child(
2366        &mut self,
2367        visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<BoxAnyVarValue>,
2368    ) -> std::ops::ControlFlow<BoxAnyVarValue> {
2369        self.view.try_for_each_child(visitor)
2370    }
2371
2372    fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
2373        self.view.par_each_child(visitor);
2374    }
2375
2376    fn par_fold_reduce(
2377        &mut self,
2378        identity: BoxAnyVarValue,
2379        fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
2380        reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
2381    ) -> BoxAnyVarValue {
2382        self.view.par_fold_reduce(identity, fold, reduce)
2383    }
2384
2385    fn init(&mut self) {
2386        debug_assert!(self.view.is_empty());
2387        self.view.clear();
2388
2389        WIDGET.sub_var(&self.list).sub_var(&self.item_fn);
2390
2391        let e_fn = self.item_fn.get();
2392        self.list.with(|l| {
2393            for el in l.iter() {
2394                let child = e_fn(el.clone());
2395                self.view.push(child);
2396            }
2397        });
2398
2399        self.view.init();
2400    }
2401
2402    fn deinit(&mut self) {
2403        self.view.deinit();
2404        self.view.clear();
2405    }
2406
2407    fn update(&mut self, updates: &WidgetUpdates) {
2408        self.update_list(updates, &mut ());
2409    }
2410
2411    fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
2412        let mut need_reset = self.item_fn.is_new();
2413
2414        let is_new = self
2415            .list
2416            .with_new(|l| {
2417                need_reset |= l.changes().is_empty() || l.changes() == [VecChange::Clear];
2418
2419                if need_reset {
2420                    return;
2421                }
2422
2423                // update before new items to avoid update before init.
2424                self.view.update_list(updates, observer);
2425
2426                let e_fn = self.item_fn.get();
2427
2428                for change in l.changes() {
2429                    match change {
2430                        VecChange::Insert { index, count } => {
2431                            for i in *index..(*index + count) {
2432                                let mut el = e_fn(l[i].clone());
2433                                el.init();
2434                                self.view.insert(i, el);
2435                                observer.inserted(i);
2436                            }
2437                        }
2438                        VecChange::Remove { index, count } => {
2439                            let mut count = *count;
2440                            let index = *index;
2441                            while count > 0 {
2442                                count -= 1;
2443
2444                                let mut el = self.view.remove(index);
2445                                el.deinit();
2446                                observer.removed(index);
2447                            }
2448                        }
2449                        VecChange::Move { from_index, to_index } => {
2450                            let el = self.view.remove(*from_index);
2451                            self.view.insert(*to_index, el);
2452                            observer.moved(*from_index, *to_index);
2453                        }
2454                        VecChange::Clear => unreachable!(),
2455                    }
2456                }
2457            })
2458            .is_some();
2459
2460        if !need_reset && !is_new && self.list.with(|l| l.len() != self.view.len()) {
2461            need_reset = true;
2462        }
2463
2464        if need_reset {
2465            self.view.deinit();
2466            self.view.clear();
2467
2468            let e_fn = self.item_fn.get();
2469            self.list.with(|l| {
2470                for el in l.iter() {
2471                    let child = e_fn(el.clone());
2472                    self.view.push(child);
2473                }
2474            });
2475
2476            self.view.init();
2477        } else if !is_new {
2478            self.view.update_list(updates, observer);
2479        }
2480    }
2481
2482    fn info(&mut self, info: &mut zng_app::widget::info::WidgetInfoBuilder) {
2483        self.view.info(info);
2484    }
2485
2486    fn event(&mut self, update: &zng_app::update::EventUpdate) {
2487        self.view.event(update);
2488    }
2489
2490    fn measure(&mut self, wm: &mut zng_app::widget::info::WidgetMeasure) -> PxSize {
2491        self.view.measure(wm)
2492    }
2493
2494    fn measure_list(
2495        &mut self,
2496        wm: &mut zng_app::widget::info::WidgetMeasure,
2497        measure: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetMeasure) -> PxSize + Sync),
2498        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2499    ) -> PxSize {
2500        self.view.measure_list(wm, measure, fold_size)
2501    }
2502
2503    fn layout(&mut self, wl: &mut zng_app::widget::info::WidgetLayout) -> PxSize {
2504        self.view.layout(wl)
2505    }
2506
2507    fn layout_list(
2508        &mut self,
2509        wl: &mut zng_app::widget::info::WidgetLayout,
2510        layout: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetLayout) -> PxSize + Sync),
2511        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2512    ) -> PxSize {
2513        self.view.layout_list(wl, layout, fold_size)
2514    }
2515
2516    fn render(&mut self, frame: &mut FrameBuilder) {
2517        self.view.render(frame);
2518    }
2519
2520    fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
2521        self.view.render_list(frame, render);
2522    }
2523
2524    fn render_update(&mut self, update: &mut zng_app::render::FrameUpdate) {
2525        self.view.render_update(update);
2526    }
2527
2528    fn render_update_list(
2529        &mut self,
2530        update: &mut zng_app::render::FrameUpdate,
2531        render_update: &(dyn Fn(usize, &mut UiNode, &mut zng_app::render::FrameUpdate) + Sync),
2532    ) {
2533        self.view.render_update_list(update, render_update);
2534    }
2535
2536    fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
2537        None
2538    }
2539}
2540
2541struct ListPresenterFromIter<D, L>
2542where
2543    D: VarValue,
2544    L: IntoIterator<Item = D> + VarValue,
2545{
2546    list: Var<L>,
2547    item_fn: Var<WidgetFn<D>>,
2548    view: UiVec,
2549    _e: std::marker::PhantomData<(D, L)>,
2550}
2551
2552impl<D, L> UiNodeImpl for ListPresenterFromIter<D, L>
2553where
2554    D: VarValue,
2555    L: IntoIterator<Item = D> + VarValue,
2556{
2557    fn children_len(&self) -> usize {
2558        self.view.len()
2559    }
2560
2561    fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
2562        self.view.with_child(index, visitor)
2563    }
2564
2565    fn for_each_child(&mut self, visitor: &mut dyn FnMut(usize, &mut UiNode)) {
2566        self.view.for_each_child(visitor)
2567    }
2568
2569    fn try_for_each_child(
2570        &mut self,
2571        visitor: &mut dyn FnMut(usize, &mut UiNode) -> std::ops::ControlFlow<BoxAnyVarValue>,
2572    ) -> std::ops::ControlFlow<BoxAnyVarValue> {
2573        self.view.try_for_each_child(visitor)
2574    }
2575
2576    fn par_each_child(&mut self, visitor: &(dyn Fn(usize, &mut UiNode) + Sync)) {
2577        self.view.par_each_child(visitor);
2578    }
2579
2580    fn par_fold_reduce(
2581        &mut self,
2582        identity: BoxAnyVarValue,
2583        fold: &(dyn Fn(BoxAnyVarValue, usize, &mut UiNode) -> BoxAnyVarValue + Sync),
2584        reduce: &(dyn Fn(BoxAnyVarValue, BoxAnyVarValue) -> BoxAnyVarValue + Sync),
2585    ) -> BoxAnyVarValue {
2586        self.view.par_fold_reduce(identity, fold, reduce)
2587    }
2588
2589    fn is_list(&self) -> bool {
2590        true
2591    }
2592
2593    fn init(&mut self) {
2594        debug_assert!(self.view.is_empty());
2595        self.view.clear();
2596
2597        WIDGET.sub_var(&self.list).sub_var(&self.item_fn);
2598
2599        let e_fn = self.item_fn.get();
2600
2601        self.view.extend(self.list.get().into_iter().map(&*e_fn));
2602        self.view.init();
2603    }
2604
2605    fn deinit(&mut self) {
2606        self.view.deinit();
2607        self.view.clear();
2608    }
2609
2610    fn update(&mut self, updates: &WidgetUpdates) {
2611        self.update_list(updates, &mut ())
2612    }
2613    fn update_list(&mut self, updates: &WidgetUpdates, observer: &mut dyn UiNodeListObserver) {
2614        if self.list.is_new() || self.item_fn.is_new() {
2615            self.view.deinit();
2616            self.view.clear();
2617            let e_fn = self.item_fn.get();
2618            self.view.extend(self.list.get().into_iter().map(&*e_fn));
2619            self.view.init();
2620            observer.reset();
2621        } else {
2622            self.view.update_list(updates, observer);
2623        }
2624    }
2625
2626    fn info(&mut self, info: &mut zng_app::widget::info::WidgetInfoBuilder) {
2627        self.view.info(info)
2628    }
2629
2630    fn event(&mut self, update: &zng_app::update::EventUpdate) {
2631        self.view.event(update);
2632    }
2633
2634    fn measure(&mut self, wm: &mut zng_app::widget::info::WidgetMeasure) -> PxSize {
2635        self.view.measure(wm)
2636    }
2637
2638    fn measure_list(
2639        &mut self,
2640        wm: &mut zng_app::widget::info::WidgetMeasure,
2641        measure: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetMeasure) -> PxSize + Sync),
2642        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2643    ) -> PxSize {
2644        self.view.measure_list(wm, measure, fold_size)
2645    }
2646
2647    fn layout(&mut self, wl: &mut zng_app::widget::info::WidgetLayout) -> PxSize {
2648        self.view.layout(wl)
2649    }
2650
2651    fn layout_list(
2652        &mut self,
2653        wl: &mut zng_app::widget::info::WidgetLayout,
2654        layout: &(dyn Fn(usize, &mut UiNode, &mut zng_app::widget::info::WidgetLayout) -> PxSize + Sync),
2655        fold_size: &(dyn Fn(PxSize, PxSize) -> PxSize + Sync),
2656    ) -> PxSize {
2657        self.view.layout_list(wl, layout, fold_size)
2658    }
2659
2660    fn render(&mut self, frame: &mut FrameBuilder) {
2661        self.view.render(frame);
2662    }
2663
2664    fn render_list(&mut self, frame: &mut FrameBuilder, render: &(dyn Fn(usize, &mut UiNode, &mut FrameBuilder) + Sync)) {
2665        self.view.render_list(frame, render);
2666    }
2667
2668    fn render_update(&mut self, update: &mut zng_app::render::FrameUpdate) {
2669        self.view.render_update(update);
2670    }
2671
2672    fn render_update_list(
2673        &mut self,
2674        update: &mut zng_app::render::FrameUpdate,
2675        render_update: &(dyn Fn(usize, &mut UiNode, &mut zng_app::render::FrameUpdate) + Sync),
2676    ) {
2677        self.view.render_update_list(update, render_update);
2678    }
2679
2680    fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
2681        None
2682    }
2683}
2684
2685/// Extension method to *convert* a variable to a node.
2686pub trait VarPresent<D: VarValue> {
2687    /// Present the variable data using a [`presenter`] node.
2688    fn present(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2689}
2690impl<D: VarValue> VarPresent<D> for Var<D> {
2691    fn present(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2692        presenter(self.clone(), wgt_fn)
2693    }
2694}
2695
2696/// Extension method to *convert* a variable to a node.
2697pub trait VarPresentOpt<D: VarValue> {
2698    /// Present the variable data using a [`presenter_opt`] node.
2699    fn present_opt(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2700}
2701impl<D: VarValue> VarPresentOpt<D> for Var<Option<D>> {
2702    fn present_opt(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2703        presenter_opt(self.clone(), wgt_fn)
2704    }
2705}
2706
2707/// Extension method fo *convert* a variable to a node list.
2708pub trait VarPresentList<D: VarValue> {
2709    /// Present the variable data using a [`list_presenter`] node list.
2710    fn present_list(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2711}
2712impl<D: VarValue> VarPresentList<D> for Var<ObservableVec<D>> {
2713    fn present_list(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2714        list_presenter(self.clone(), wgt_fn)
2715    }
2716}
2717
2718/// Extension method fo *convert* a variable to a node list.
2719pub trait VarPresentListFromIter<D: VarValue, L: IntoIterator<Item = D> + VarValue> {
2720    /// Present the variable data using a [`list_presenter_from_iter`] node list.
2721    fn present_list_from_iter(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode;
2722}
2723impl<D: VarValue, L: IntoIterator<Item = D> + VarValue> VarPresentListFromIter<D, L> for Var<L> {
2724    fn present_list_from_iter(&self, wgt_fn: impl IntoVar<WidgetFn<D>>) -> UiNode {
2725        list_presenter_from_iter(self.clone(), wgt_fn)
2726    }
2727}
2728
2729/// Extension method to *convert* a variable to a node.
2730pub trait VarPresentData<D: VarValue> {
2731    /// Present the `data` variable using a [`presenter`] node.
2732    fn present_data(&self, data: impl IntoVar<D>) -> UiNode;
2733}
2734impl<D: VarValue> VarPresentData<D> for Var<WidgetFn<D>> {
2735    fn present_data(&self, data: impl IntoVar<D>) -> UiNode {
2736        presenter(data, self.clone())
2737    }
2738}
2739
2740#[doc(inline)]
2741pub use crate::command_property;
2742#[doc(inline)]
2743pub use crate::event_property;