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