zng_app/widget/
base.rs

1//! The widget base, nodes and properties used in most widgets.
2
3use std::{any::TypeId, cell::RefCell, fmt};
4
5use crate::{source_location, widget::WidgetId};
6
7use super::{
8    WIDGET,
9    builder::{
10        AnyPropertyBuildAction, Importance, PropertyArgs, PropertyId, SourceLocation, WhenBuildAction, WhenInfo, WhenInput, WidgetBuilder,
11        WidgetType,
12    },
13    node::{FillUiNode, UiNode, UiNodeOp},
14};
15
16use crate::widget::{
17    builder::{NestGroup, property_id},
18    node::match_node,
19    property,
20};
21use zng_var::{BoxedVar, IntoValue, context_var, impl_from_and_into_var};
22
23/// Base widget.
24///
25/// The base widget implements the [`id`] property, and uses [`node::include_intrinsics`] and [`node::widget`] to
26/// implement the minimum required intrinsics for a widget to be a part of the UI tree.
27///
28/// See also [`NonWidgetBase`] to declare types that are build like a widget but are never used in the UI tree.
29///
30/// [`id`]: WidgetBase::id
31pub struct WidgetBase {
32    builder: RefCell<Option<WidgetBuilder>>,
33    importance: Importance,
34    when: RefCell<Option<WhenInfo>>,
35}
36impl WidgetBase {
37    /// Gets the type of [`WidgetBase`](struct@WidgetBase).
38    pub fn widget_type() -> WidgetType {
39        WidgetType {
40            type_id: TypeId::of::<Self>(),
41            path: "$crate::widget::base::WidgetBase",
42            location: source_location!(),
43        }
44    }
45
46    /// Starts building a new [`WidgetBase`](struct@WidgetBase) instance.
47    pub fn widget_new() -> Self {
48        Self::inherit(Self::widget_type())
49    }
50
51    /// Returns a mutable reference to the widget builder.
52    pub fn widget_builder(&mut self) -> &mut WidgetBuilder {
53        self.builder.get_mut().as_mut().expect("already built")
54    }
55
56    /// Returns a mutable reference to the `when` block if called inside a when block.
57    pub fn widget_when(&mut self) -> Option<&mut WhenInfo> {
58        self.when.get_mut().as_mut()
59    }
60
61    /// Takes the widget builder, finishing the widget macro build.
62    ///
63    /// After this call trying to set a property using `self` will panic,
64    /// the returned builder can still be manipulated directly.
65    pub fn widget_take(&mut self) -> WidgetBuilder {
66        assert!(self.when.get_mut().is_none(), "cannot take builder with `when` pending");
67        self.builder.get_mut().take().expect("builder already taken")
68    }
69
70    /// Build the widget.
71    ///
72    /// After this call trying to set a property will panic.
73    pub fn widget_build(&mut self) -> impl UiNode + use<> {
74        let mut wgt = self.widget_take();
75        wgt.push_build_action(|wgt| {
76            if !wgt.has_child() {
77                wgt.set_child(FillUiNode);
78            }
79        });
80        node::build(wgt)
81    }
82
83    /// Returns a mutable reference to the importance of the next property assigns, unsets or when blocks.
84    ///
85    /// Note that during the `widget_intrinsic` call this is [`Importance::WIDGET`] and after it is [`Importance::INSTANCE`].
86    pub fn widget_importance(&mut self) -> &mut Importance {
87        &mut self.importance
88    }
89
90    /// Start building a `when` block, all properties set after this call are pushed in the when block.
91    pub fn start_when_block(&mut self, inputs: Box<[WhenInput]>, state: BoxedVar<bool>, expr: &'static str, location: SourceLocation) {
92        assert!(self.builder.get_mut().is_some(), "cannot start `when` after build");
93        assert!(self.when.get_mut().is_none(), "cannot nest `when` blocks");
94
95        *self.when.get_mut() = Some(WhenInfo {
96            inputs,
97            state,
98            assigns: vec![],
99            build_action_data: vec![],
100            expr,
101            location,
102        });
103    }
104
105    /// End the current `when` block, all properties set after this call are pushed in the widget.
106    pub fn end_when_block(&mut self) {
107        let when = self.when.get_mut().take().expect("no current `when` block to end");
108        self.builder.get_mut().as_mut().unwrap().push_when(self.importance, when);
109    }
110
111    fn widget_intrinsic(&mut self) {
112        node::include_intrinsics(self.widget_builder());
113    }
114
115    /// Push method property.
116    #[doc(hidden)]
117    pub fn mtd_property__(&self, args: Box<dyn PropertyArgs>) {
118        if let Some(when) = &mut *self.when.borrow_mut() {
119            when.assigns.push(args);
120        } else {
121            self.builder
122                .borrow_mut()
123                .as_mut()
124                .expect("cannot set after build")
125                .push_property(self.importance, args);
126        }
127    }
128
129    /// Push method unset property.
130    #[doc(hidden)]
131    pub fn mtd_property_unset__(&self, id: PropertyId) {
132        assert!(self.when.borrow().is_none(), "cannot unset in when assign");
133        self.builder
134            .borrow_mut()
135            .as_mut()
136            .expect("cannot unset after build")
137            .push_unset(self.importance, id);
138    }
139
140    #[doc(hidden)]
141    pub fn reexport__(&self, f: impl FnOnce(&mut Self)) {
142        let mut inner = Self {
143            builder: RefCell::new(self.builder.borrow_mut().take()),
144            importance: self.importance,
145            when: RefCell::new(self.when.borrow_mut().take()),
146        };
147        f(&mut inner);
148        *self.builder.borrow_mut() = inner.builder.into_inner();
149        *self.when.borrow_mut() = inner.when.into_inner();
150        debug_assert_eq!(self.importance, inner.importance);
151    }
152
153    #[doc(hidden)]
154    pub fn push_unset_property_build_action__(&mut self, property_id: PropertyId, action_name: &'static str) {
155        assert!(self.when.get_mut().is_none(), "cannot unset build actions in when assigns");
156
157        self.builder
158            .get_mut()
159            .as_mut()
160            .expect("cannot unset build actions after build")
161            .push_unset_property_build_action(property_id, action_name, self.importance);
162    }
163
164    #[doc(hidden)]
165    pub fn push_property_build_action__(
166        &mut self,
167        property_id: PropertyId,
168        action_name: &'static str,
169        input_actions: Vec<Box<dyn AnyPropertyBuildAction>>,
170    ) {
171        assert!(
172            self.when.get_mut().is_none(),
173            "cannot push property build action in `when`, use `push_when_build_action_data__`"
174        );
175
176        self.builder
177            .get_mut()
178            .as_mut()
179            .expect("cannot unset build actions after build")
180            .push_property_build_action(property_id, action_name, self.importance, input_actions);
181    }
182
183    #[doc(hidden)]
184    pub fn push_when_build_action_data__(&mut self, property_id: PropertyId, action_name: &'static str, data: WhenBuildAction) {
185        let when = self
186            .when
187            .get_mut()
188            .as_mut()
189            .expect("cannot push when build action data outside when blocks");
190        when.build_action_data.push(((property_id, action_name), data));
191    }
192}
193
194/// Trait implemented by all `#[widget]`.
195///
196/// This trait is used in widget mix-in implementations to constraint `P`, it is also used by the
197/// the generated widget code. You do not need to implement it directly.
198#[diagnostic::on_unimplemented(note = "`{Self}` is not an `#[widget]`")]
199pub trait WidgetImpl {
200    /// The inherit function.
201    fn inherit(widget: WidgetType) -> Self;
202
203    /// Reference the parent [`WidgetBase`](struct@WidgetBase).
204    fn base(&mut self) -> &mut WidgetBase;
205
206    #[doc(hidden)]
207    fn base_ref(&self) -> &WidgetBase;
208
209    #[doc(hidden)]
210    fn info_instance__() -> Self;
211
212    #[doc(hidden)]
213    fn widget_intrinsic(&mut self) {}
214}
215impl WidgetImpl for WidgetBase {
216    fn inherit(widget: WidgetType) -> Self {
217        let builder = WidgetBuilder::new(widget);
218        let mut w = Self {
219            builder: RefCell::new(Some(builder)),
220            importance: Importance::WIDGET,
221            when: RefCell::new(None),
222        };
223        w.widget_intrinsic();
224        w.importance = Importance::INSTANCE;
225        w
226    }
227
228    fn base(&mut self) -> &mut WidgetBase {
229        self
230    }
231
232    fn base_ref(&self) -> &WidgetBase {
233        self
234    }
235
236    fn info_instance__() -> Self {
237        WidgetBase {
238            builder: RefCell::new(None),
239            importance: Importance::INSTANCE,
240            when: RefCell::new(None),
241        }
242    }
243}
244
245#[doc(hidden)]
246pub trait WidgetExt {
247    #[doc(hidden)]
248    fn ext_property__(&mut self, args: Box<dyn PropertyArgs>);
249    #[doc(hidden)]
250    fn ext_property_unset__(&mut self, id: PropertyId);
251}
252impl WidgetExt for WidgetBase {
253    fn ext_property__(&mut self, args: Box<dyn PropertyArgs>) {
254        if let Some(when) = self.when.get_mut() {
255            when.assigns.push(args);
256        } else {
257            self.builder
258                .get_mut()
259                .as_mut()
260                .expect("cannot set after build")
261                .push_property(self.importance, args);
262        }
263    }
264
265    fn ext_property_unset__(&mut self, id: PropertyId) {
266        assert!(self.when.get_mut().is_none(), "cannot unset in when blocks");
267
268        self.builder
269            .get_mut()
270            .as_mut()
271            .expect("cannot unset after build")
272            .push_unset(self.importance, id);
273    }
274}
275
276#[doc(hidden)]
277#[macro_export]
278macro_rules! WidgetBaseMacro__ {
279    ($($tt:tt)*) => {
280        $crate::widget::widget_new! {
281            new {
282                let mut wgt__ = $crate::widget::base::WidgetBase::widget_new();
283                let wgt__ = &mut wgt__;
284            }
285            build {
286                let wgt__ = wgt__.widget_build();
287                wgt__
288            }
289            set { $($tt)* }
290        }
291    }
292}
293#[doc(hidden)]
294pub use WidgetBaseMacro__ as WidgetBase;
295
296/// Base *widget* for types that build to a custom type that is not used as a part of the UI tree.
297///
298/// This type can be used as base instead of [`WidgetBase`](struct@WidgetBase) for types that provide
299/// a custom build that outputs an instance that is not used as a widget in the UI tree.
300pub struct NonWidgetBase {
301    base: WidgetBase,
302}
303impl NonWidgetBase {
304    /// Gets the type of [`NonWidgetBase`](struct@NonWidgetBase).
305    pub fn widget_type() -> WidgetType {
306        WidgetType {
307            type_id: TypeId::of::<Self>(),
308            path: "$crate::widget::base::NonWidgetBase",
309            location: source_location!(),
310        }
311    }
312
313    /// Starts building a new [`NonWidgetBase`](struct@NonWidgetBase) instance.
314    pub fn widget_new() -> Self {
315        Self::inherit(Self::widget_type())
316    }
317
318    /// Returns a mutable reference to the widget builder.
319    pub fn widget_builder(&mut self) -> &mut WidgetBuilder {
320        self.base.widget_builder()
321    }
322
323    /// Returns a mutable reference to the `when` block if called inside a when block.
324    pub fn widget_when(&mut self) -> Option<&mut WhenInfo> {
325        self.base.widget_when()
326    }
327
328    /// Takes the widget builder, finishing the widget macro build.
329    ///
330    /// After this call trying to set a property using `self` will panic,
331    /// the returned builder can still be manipulated directly.
332    pub fn widget_take(&mut self) -> WidgetBuilder {
333        self.base.widget_take()
334    }
335
336    /// Finishes the build.
337    ///
338    /// This is the fallback build that simply returns the builder, inheritors should override this method.
339    pub fn widget_build(&mut self) -> WidgetBuilder {
340        self.widget_take()
341    }
342
343    /// Returns a mutable reference to the importance of the next property assigns, unsets or when blocks.
344    ///
345    /// Note that during the `widget_intrinsic` call this is [`Importance::WIDGET`] and after it is [`Importance::INSTANCE`].
346    pub fn widget_importance(&mut self) -> &mut Importance {
347        self.base.widget_importance()
348    }
349
350    /// Start building a `when` block, all properties set after this call are pushed in the when block.
351    pub fn start_when_block(&mut self, inputs: Box<[WhenInput]>, state: BoxedVar<bool>, expr: &'static str, location: SourceLocation) {
352        self.base.start_when_block(inputs, state, expr, location)
353    }
354
355    /// End the current `when` block, all properties set after this call are pushed in the widget.
356    pub fn end_when_block(&mut self) {
357        self.base.end_when_block()
358    }
359
360    fn widget_intrinsic(&mut self) {}
361
362    /// Push method property.
363    #[doc(hidden)]
364    pub fn mtd_property__(&self, args: Box<dyn PropertyArgs>) {
365        self.base.mtd_property__(args)
366    }
367
368    /// Push method unset property.
369    #[doc(hidden)]
370    pub fn mtd_property_unset__(&self, id: PropertyId) {
371        self.base.mtd_property_unset__(id)
372    }
373
374    #[doc(hidden)]
375    pub fn reexport__(&self, f: impl FnOnce(&mut WidgetBase)) {
376        self.base.reexport__(f)
377    }
378
379    #[doc(hidden)]
380    pub fn push_unset_property_build_action__(&mut self, property_id: PropertyId, action_name: &'static str) {
381        self.base.push_unset_property_build_action__(property_id, action_name)
382    }
383
384    #[doc(hidden)]
385    pub fn push_property_build_action__(
386        &mut self,
387        property_id: PropertyId,
388        action_name: &'static str,
389        input_actions: Vec<Box<dyn AnyPropertyBuildAction>>,
390    ) {
391        self.base.push_property_build_action__(property_id, action_name, input_actions)
392    }
393
394    #[doc(hidden)]
395    pub fn push_when_build_action_data__(&mut self, property_id: PropertyId, action_name: &'static str, data: WhenBuildAction) {
396        self.base.push_when_build_action_data__(property_id, action_name, data)
397    }
398}
399impl WidgetImpl for NonWidgetBase {
400    fn inherit(widget: WidgetType) -> Self {
401        let builder = WidgetBuilder::new(widget);
402        let mut w = Self {
403            base: WidgetBase {
404                builder: RefCell::new(Some(builder)),
405                importance: Importance::WIDGET,
406                when: RefCell::new(None),
407            },
408        };
409        w.widget_intrinsic();
410        w.base.importance = Importance::INSTANCE;
411        w
412    }
413
414    fn base(&mut self) -> &mut WidgetBase {
415        &mut self.base
416    }
417
418    fn base_ref(&self) -> &WidgetBase {
419        &self.base
420    }
421
422    fn info_instance__() -> Self {
423        Self {
424            base: WidgetBase {
425                builder: RefCell::new(None),
426                importance: Importance::INSTANCE,
427                when: RefCell::new(None),
428            },
429        }
430    }
431}
432impl WidgetExt for NonWidgetBase {
433    fn ext_property__(&mut self, args: Box<dyn PropertyArgs>) {
434        self.base.ext_property__(args)
435    }
436
437    fn ext_property_unset__(&mut self, id: PropertyId) {
438        self.base.ext_property_unset__(id)
439    }
440}
441
442/// Basic nodes for widgets, some used in [`WidgetBase`].
443///
444/// [`WidgetBase`]: struct@WidgetBase
445pub mod node {
446    use zng_layout::unit::{PxCornerRadius, PxRect, PxSize};
447    use zng_var::Var;
448
449    use crate::{
450        render::{FrameBuilder, FrameUpdate, FrameValueKey},
451        update::{EventUpdate, WidgetUpdates},
452        widget::{
453            WidgetCtx, WidgetUpdateMode,
454            info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
455            node::BoxedUiNode,
456        },
457    };
458
459    use super::*;
460
461    /// Insert [`widget_child`] and [`widget_inner`] in the widget.
462    pub fn include_intrinsics(wgt: &mut WidgetBuilder) {
463        wgt.push_build_action(|wgt| {
464            wgt.push_intrinsic(NestGroup::CHILD, "widget_child", node::widget_child);
465            wgt.push_intrinsic(NestGroup::WIDGET_INNER, "widget_inner", node::widget_inner);
466        });
467    }
468
469    /// Capture the [`id`] property and builds the base widget.
470    ///
471    /// Note that this function does not handle missing child node, it falls back to [`FillUiNode`]. The [`WidgetBase`]
472    /// widget uses the [`FillUiNode`] if none was set.
473    ///
474    /// [`WidgetBase`]: struct@WidgetBase
475    /// [`id`]: fn@id
476    pub fn build(mut wgt: WidgetBuilder) -> impl UiNode {
477        let id = wgt.capture_value_or_else(property_id!(id), WidgetId::new_unique);
478        let child = wgt.build();
479        node::widget(child, id)
480    }
481
482    /// Returns a node that wraps `child` and potentially applies child transforms if the `child` turns out
483    /// to not be a full widget or to be multiple children. This is important for making properties like *padding* or *content_align* work
484    /// for any [`UiNode`] as content.
485    ///
486    /// This node also pass through the `child` inline layout return info if the widget and child are inlining and the
487    /// widget has not set inline info before delegating measure.
488    ///
489    /// This node must be intrinsic at [`NestGroup::CHILD`], the [`WidgetBase`] default intrinsic inserts it.
490    ///
491    /// [`WidgetBase`]: struct@WidgetBase
492    pub fn widget_child(child: impl UiNode) -> impl UiNode {
493        let key = FrameValueKey::new_unique();
494        let mut define_ref_frame = false;
495
496        match_node(child, move |child, op| match op {
497            UiNodeOp::Measure { wm, desired_size } => {
498                *desired_size = child.measure(wm);
499
500                if let Some(inline) = wm.inline() {
501                    if inline.is_default() {
502                        if let Some(child_inline) = child
503                            .with_context(WidgetUpdateMode::Ignore, || WIDGET.bounds().measure_inline())
504                            .flatten()
505                        {
506                            // pass through child inline
507                            *inline = child_inline;
508                        }
509                    }
510                }
511            }
512            UiNodeOp::Layout { wl, final_size } => {
513                let (s, d) = wl.with_child(|wl| child.layout(wl));
514                *final_size = s;
515
516                if d != define_ref_frame {
517                    define_ref_frame = d;
518                    WIDGET.render();
519                }
520
521                if !define_ref_frame {
522                    // child maybe widget, try to copy inline
523                    if let Some(inline) = wl.inline() {
524                        if inline.is_default() {
525                            child.with_context(WidgetUpdateMode::Ignore, || {
526                                let bounds = WIDGET.bounds();
527                                let child_inline = bounds.inline();
528                                if let Some(child_inline) = child_inline {
529                                    inline.clone_from(&*child_inline);
530                                }
531                            });
532                        }
533                    }
534                }
535            }
536
537            UiNodeOp::Render { frame } => {
538                let offset = WIDGET.bounds().child_offset();
539                if define_ref_frame {
540                    frame.push_reference_frame(key.into(), key.bind(offset.into(), true), true, false, |frame| child.render(frame));
541                } else {
542                    frame.push_child(offset, |frame| {
543                        child.render(frame);
544                    });
545                }
546            }
547            UiNodeOp::RenderUpdate { update } => {
548                let offset = WIDGET.bounds().child_offset();
549                if define_ref_frame {
550                    update.with_transform(key.update(offset.into(), true), false, |update| child.render_update(update));
551                } else {
552                    update.with_child(offset, |update| child.render_update(update))
553                }
554            }
555            _ => {}
556        })
557    }
558
559    /// Returns a node that wraps `child` and marks the [`WidgetLayout::with_inner`] and [`FrameBuilder::push_inner`].
560    ///
561    /// This node renders the inner transform and implements the [`HitTestMode`] for the widget.
562    ///
563    /// This node must be intrinsic at [`NestGroup::BORDER`], the [`WidgetBase`] default intrinsic inserts it.
564    ///
565    /// [`WidgetBase`]: struct@WidgetBase
566    pub fn widget_inner(child: impl UiNode) -> impl UiNode {
567        #[derive(Default, PartialEq)]
568        struct HitClips {
569            bounds: PxSize,
570            corners: PxCornerRadius,
571        }
572
573        let transform_key = FrameValueKey::new_unique();
574        let mut clips = HitClips::default();
575
576        match_node(child, move |child, op| match op {
577            UiNodeOp::Init => {
578                WIDGET.sub_var_layout(&HIT_TEST_MODE_VAR);
579            }
580            UiNodeOp::Layout { wl, final_size } => {
581                *final_size = wl.with_inner(|wl| child.layout(wl));
582
583                let mode = HIT_TEST_MODE_VAR.get();
584                let c = if matches!(mode, HitTestMode::Bounds | HitTestMode::RoundedBounds) {
585                    HitClips {
586                        bounds: *final_size,
587                        corners: if matches!(mode, HitTestMode::RoundedBounds) {
588                            WIDGET.border().corner_radius()
589                        } else {
590                            PxCornerRadius::zero()
591                        },
592                    }
593                } else {
594                    HitClips::default()
595                };
596
597                if c != clips {
598                    clips = c;
599                    WIDGET.render();
600                }
601            }
602            UiNodeOp::Render { frame } => {
603                frame.push_inner(transform_key, true, |frame| {
604                    frame.hit_test().push_clips(
605                        |c| {
606                            if let Some(inline) = WIDGET.bounds().inline() {
607                                for r in inline.negative_space().iter() {
608                                    c.push_clip_rect(*r, true);
609                                }
610                            }
611                        },
612                        |h| match HIT_TEST_MODE_VAR.get() {
613                            HitTestMode::RoundedBounds => {
614                                h.push_rounded_rect(PxRect::from_size(clips.bounds), clips.corners);
615                            }
616                            HitTestMode::Bounds => {
617                                h.push_rect(PxRect::from_size(clips.bounds));
618                            }
619                            _ => {}
620                        },
621                    );
622
623                    child.render(frame);
624                });
625            }
626            UiNodeOp::RenderUpdate { update } => {
627                update.update_inner(transform_key, true, |update| child.render_update(update));
628            }
629            _ => {}
630        })
631    }
632
633    /// Create a widget node that wraps `child` and introduces a new widget context. The node defines
634    /// an [`WIDGET`] context and implements the widget in each specific node method.
635    ///
636    /// This node must wrap the outer-most context node in the build, it is the [`WidgetBase`] widget type.
637    ///
638    /// The node retains the widget state if build with `cfg(any(test, feature = "test_util")))`, otherwise
639    /// the state is cleared.
640    ///
641    /// [`WidgetBase`]: struct@WidgetBase
642    pub fn widget(child: impl UiNode, id: impl IntoValue<WidgetId>) -> impl UiNode {
643        struct WidgetNode<C> {
644            ctx: WidgetCtx,
645            child: C,
646
647            #[cfg(debug_assertions)]
648            inited: bool,
649            #[cfg(debug_assertions)]
650            info_built: bool,
651        }
652        impl<C: UiNode> UiNode for WidgetNode<C> {
653            fn init(&mut self) {
654                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
655                    #[cfg(debug_assertions)]
656                    if self.inited {
657                        tracing::error!(target: "widget_base", "`UiNode::init` called in inited widget {:?}", WIDGET.id());
658                    }
659
660                    self.child.init();
661                    WIDGET.update_info().layout().render();
662
663                    #[cfg(debug_assertions)]
664                    {
665                        self.inited = true;
666                        self.info_built = false;
667                    }
668                });
669                self.ctx.take_reinit(); // ignore reinit request
670            }
671
672            fn deinit(&mut self) {
673                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
674                    #[cfg(debug_assertions)]
675                    if !self.inited {
676                        tracing::error!(target: "widget_base", "`UiNode::deinit` called in not inited widget {:?}", WIDGET.id());
677                    }
678
679                    self.child.deinit();
680                    WIDGET.update_info().layout().render();
681
682                    #[cfg(debug_assertions)]
683                    {
684                        self.inited = false;
685                        self.info_built = false;
686                    }
687                });
688                self.ctx.deinit(cfg!(any(test, feature = "test_util")));
689            }
690
691            fn info(&mut self, info: &mut WidgetInfoBuilder) {
692                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
693                    #[cfg(debug_assertions)]
694                    if !self.inited {
695                        tracing::error!(target: "widget_base", "`UiNode::info` called in not inited widget {:?}", WIDGET.id());
696                    }
697
698                    #[cfg(debug_assertions)]
699                    {
700                        self.info_built = true;
701                    }
702
703                    info.push_widget(|info| {
704                        self.child.info(info);
705                    });
706                });
707
708                if self.ctx.is_pending_reinit() {
709                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
710                }
711            }
712
713            fn event(&mut self, update: &EventUpdate) {
714                if self.ctx.take_reinit() {
715                    self.deinit();
716                    self.init();
717                }
718
719                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
720                    #[cfg(debug_assertions)]
721                    if !self.inited {
722                        tracing::error!(target: "widget_base", "`UiNode::event::<{}>` called in not inited widget {:?}", update.event().name(), WIDGET.id());
723                    } else if !self.info_built {
724                        tracing::error!(target: "widget_base", "`UiNode::event::<{}>` called in widget {:?} before first info build", update.event().name(), WIDGET.id());
725                    }
726
727                    update.with_widget(|| {
728                        self.child.event(update);
729                    });
730                });
731
732                if self.ctx.take_reinit() {
733                    self.deinit();
734                    self.init();
735                }
736            }
737
738            fn update(&mut self, updates: &WidgetUpdates) {
739                if self.ctx.take_reinit() {
740                    self.deinit();
741                    self.init();
742                    return;
743                }
744
745                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
746                    #[cfg(debug_assertions)]
747                    if !self.inited {
748                        tracing::error!(target: "widget_base", "`UiNode::update` called in not inited widget {:?}", WIDGET.id());
749                    } else if !self.info_built {
750                        tracing::error!(target: "widget_base", "`UiNode::update` called in widget {:?} before first info build", WIDGET.id());
751                    }
752
753                    updates.with_widget(|| {
754                        self.child.update(updates);
755                    });
756                });
757
758                if self.ctx.take_reinit() {
759                    self.deinit();
760                    self.init();
761                }
762            }
763
764            fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
765                let desired_size = WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Ignore, || {
766                    #[cfg(debug_assertions)]
767                    if !self.inited {
768                        tracing::error!(target: "widget_base", "`UiNode::measure` called in not inited widget {:?}", WIDGET.id());
769                    } else if !self.info_built {
770                        tracing::error!(target: "widget_base", "`UiNode::measure` called in widget {:?} before first info build", WIDGET.id());
771                    }
772
773                    wm.with_widget(|wm| {
774                        let child_size = self.child.measure(wm);
775
776                        // verify that inline row segments fit in row size
777                        #[cfg(debug_assertions)]
778                        if let Some(inline) = wm.inline() {
779                            for (name, size, segs) in [
780                                ("first", inline.first, &inline.first_segs),
781                                ("last", inline.last, &inline.last_segs),
782                            ] {
783                                let width = size.width.0 as f32;
784                                let sum_width = segs.iter().map(|s| s.width).sum::<f32>();
785                                if sum_width > width + 0.1 {
786                                    tracing::error!(
787                                        "widget {:?} measured inline {name} row has {width} width, but row segs sum to {sum_width} width",
788                                        WIDGET.id()
789                                    );
790                                }
791                            }
792                        }
793
794                        child_size
795                    })
796                });
797
798                // ignore
799                let _ = self.ctx.take_reinit();
800
801                desired_size
802            }
803
804            fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
805                let final_size = WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
806                    #[cfg(debug_assertions)]
807                    if !self.inited {
808                        tracing::error!(target: "widget_base", "`UiNode::layout` called in not inited widget {:?}", WIDGET.id());
809                    } else if !self.info_built {
810                        tracing::error!(target: "widget_base", "`UiNode::layout` called in widget {:?} before first info build", WIDGET.id());
811                    }
812
813                    wl.with_widget(|wl| {
814                        let child_size = self.child.layout(wl);
815
816                        // verify that inline row segments fit in row rectangle
817                        #[cfg(debug_assertions)]
818                        if let Some(inline) = wl.inline() {
819                            use zng_layout::unit::Px;
820
821                            for (name, row, segs) in inline
822                                .rows
823                                .first()
824                                .iter()
825                                .map(|r| ("first", r, &inline.first_segs))
826                                .chain(inline.rows.last().iter().map(|r| ("last", r, &inline.last_segs)))
827                            {
828                                let width = row.width();
829                                let sum_width = segs.iter().map(|s| s.width).sum::<Px>();
830                                if (sum_width - width) > Px(1) {
831                                    tracing::error!(
832                                        "widget {:?} layout inline {name} row has {width} width, but row segs widths sum to {sum_width}",
833                                        WIDGET.id()
834                                    );
835                                }
836                            }
837                        }
838
839                        child_size
840                    })
841                });
842
843                if self.ctx.is_pending_reinit() {
844                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
845                }
846
847                final_size
848            }
849
850            fn render(&mut self, frame: &mut FrameBuilder) {
851                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
852                    #[cfg(debug_assertions)]
853                    if !self.inited {
854                        tracing::error!(target: "widget_base", "`UiNode::render` called in not inited widget {:?}", WIDGET.id());
855                    } else if !self.info_built {
856                        tracing::error!(target: "widget_base", "`UiNode::render` called in widget {:?} before first info build", WIDGET.id());
857                    }
858
859                    frame.push_widget(|frame| self.child.render(frame));
860                });
861
862                if self.ctx.is_pending_reinit() {
863                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
864                }
865            }
866
867            fn render_update(&mut self, update: &mut FrameUpdate) {
868                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
869                    #[cfg(debug_assertions)]
870                    if !self.inited {
871                        tracing::error!(target: "widget_base", "`UiNode::render_update` called in not inited widget {:?}", WIDGET.id());
872                    } else if !self.info_built {
873                        tracing::error!(target: "widget_base", "`UiNode::render_update` called in widget {:?} before first info build", WIDGET.id());
874                    }
875
876                    update.update_widget(|update| self.child.render_update(update));
877                });
878
879                if self.ctx.is_pending_reinit() {
880                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
881                }
882            }
883
884            fn is_widget(&self) -> bool {
885                true
886            }
887
888            fn with_context<R, F>(&mut self, update_mode: WidgetUpdateMode, f: F) -> Option<R>
889            where
890                F: FnOnce() -> R,
891            {
892                WIDGET.with_context(&mut self.ctx, update_mode, || Some(f()))
893            }
894
895            fn into_widget(self) -> BoxedUiNode
896            where
897                Self: Sized,
898            {
899                self.boxed()
900            }
901        }
902
903        WidgetNode {
904            ctx: WidgetCtx::new(id.into()),
905            child: child.cfg_boxed(),
906
907            #[cfg(debug_assertions)]
908            inited: false,
909            #[cfg(debug_assertions)]
910            info_built: false,
911        }
912        .cfg_boxed()
913    }
914}
915
916/// Unique ID of the widget instance.
917///
918/// Note that the `id` can convert from a `&'static str` unique name.
919#[property(CONTEXT, capture, default(WidgetId::new_unique()), widget_impl(WidgetBase))]
920pub fn id(id: impl IntoValue<WidgetId>) {}
921
922/// Defines if and how a widget is hit-tested.
923#[derive(Copy, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
924pub enum HitTestMode {
925    /// Widget is never hit.
926    ///
927    /// This mode affects the entire UI branch, if set it disables hit-testing for the widget and all its descendants,
928    /// even if they set explicitly set their hit-test mode to something else.
929    Disabled,
930    /// Widget is hit by any point that intersects the transformed inner bounds rectangle. If the widget is inlined
931    /// excludes the first row advance and the last row trailing space.
932    Bounds,
933    /// Same as `Bounds`, but also excludes the outside of rounded corners.
934    ///
935    /// This is the default mode.
936    #[default]
937    RoundedBounds,
938    /// Widget is hit by any point that intersects the hit-test shape defined on render by
939    /// [`FrameBuilder::hit_test`] and auto hit-test.
940    ///
941    /// [`FrameBuilder::hit_test`]: crate::render::FrameBuilder::hit_test
942    Detailed,
943}
944impl HitTestMode {
945    /// Returns `true` if is any mode other then [`Disabled`].
946    ///
947    /// [`Disabled`]: Self::Disabled
948    pub fn is_hit_testable(&self) -> bool {
949        !matches!(self, Self::Disabled)
950    }
951}
952impl fmt::Debug for HitTestMode {
953    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
954        if f.alternate() {
955            write!(f, "HitTestMode::")?;
956        }
957        match self {
958            Self::Disabled => write!(f, "Disabled"),
959            Self::Bounds => write!(f, "Bounds"),
960            Self::RoundedBounds => write!(f, "RoundedBounds"),
961            Self::Detailed => write!(f, "Detailed"),
962        }
963    }
964}
965impl_from_and_into_var! {
966    fn from(default_or_disabled: bool) -> HitTestMode {
967        if default_or_disabled {
968            HitTestMode::default()
969        } else {
970            HitTestMode::Disabled
971        }
972    }
973}
974
975bitflags::bitflags! {
976    /// Node list methods that are made parallel.
977    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
978    #[serde(transparent)]
979    pub struct Parallel: u8 {
980        /// Descendants [`UiNode::init`] can run in parallel.
981        const INIT   = 0b0000_0001;
982        /// Descendants [`UiNode::info`] can run in parallel.
983        const INFO   = 0b0001_0000;
984        /// Descendants [`UiNode::deinit`] can run in parallel.
985        const DEINIT = 0b0000_0010;
986        /// Descendants [`UiNode::event`] can run in parallel.
987        const EVENT  = 0b0000_0100;
988        /// Descendants [`UiNode::update`] can run in parallel.
989        const UPDATE = 0b0000_1000;
990        /// Descendants [`UiNode::measure`] and [`UiNode::layout`] can run in parallel.
991        const LAYOUT = 0b0010_0000;
992        /// Descendants [`UiNode::render`] and [`UiNode::render_update`] can run in parallel.
993        const RENDER = 0b0100_0000;
994    }
995}
996impl Default for Parallel {
997    fn default() -> Self {
998        Self::all()
999    }
1000}
1001context_var! {
1002    /// Defines what node list methods can run in parallel in a widget and descendants.
1003    ///
1004    /// This variable can be set using the `parallel` property.
1005    ///
1006    /// Is all enabled by default.
1007    pub static PARALLEL_VAR: Parallel = Parallel::default();
1008
1009    /// Defines the hit-test mode for a widget and descendants.
1010    ///
1011    /// This variable can be set using the `hit_test_mode` property.
1012    ///
1013    /// Note that hit-test is disabled for the entire sub-tree, even if a child sets to a
1014    /// different mode again, the `hit_test_mode` property already enforces this, custom
1015    /// nodes should avoid overriding `Disabled`, as the hit-test will still be disabled,
1016    /// but other custom code that depend on this variable will read an incorrect state.
1017    pub static HIT_TEST_MODE_VAR: HitTestMode = HitTestMode::default();
1018}
1019impl_from_and_into_var! {
1020    fn from(all: bool) -> Parallel {
1021        if all {
1022            Parallel::all()
1023        } else {
1024            Parallel::empty()
1025        }
1026    }
1027}