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::{IntoValue, Var, 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) -> UiNode {
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: Var<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: Var<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
448    use crate::{
449        render::{FrameBuilder, FrameUpdate, FrameValueKey},
450        update::{EventUpdate, WidgetUpdates},
451        widget::{
452            WidgetCtx, WidgetUpdateMode,
453            info::{WidgetInfoBuilder, WidgetLayout, WidgetMeasure},
454            node::{IntoUiNode, UiNode, UiNodeImpl, WidgetUiNodeImpl},
455        },
456    };
457
458    use super::*;
459
460    /// Insert [`widget_child`] and [`widget_inner`] in the widget.
461    pub fn include_intrinsics(wgt: &mut WidgetBuilder) {
462        wgt.push_build_action(|wgt| {
463            wgt.push_intrinsic(NestGroup::CHILD, "widget_child", node::widget_child);
464            wgt.push_intrinsic(NestGroup::WIDGET_INNER, "widget_inner", node::widget_inner);
465        });
466    }
467
468    /// Capture the [`id`] property and builds the base widget.
469    ///
470    /// Note that this function does not handle missing child node, it falls back to [`FillUiNode`]. The [`WidgetBase`]
471    /// widget uses the [`FillUiNode`] if none was set.
472    ///
473    /// [`WidgetBase`]: struct@WidgetBase
474    /// [`id`]: fn@id
475    pub fn build(mut wgt: WidgetBuilder) -> UiNode {
476        let id = wgt.capture_value_or_else(property_id!(id), WidgetId::new_unique);
477        let child = wgt.build();
478        node::widget(child, id)
479    }
480
481    /// Returns a node that wraps `child` and potentially applies child transforms if the `child` turns out
482    /// to not be a full widget or to be multiple children. This is important for making properties like *padding* or *content_align* work
483    /// for any [`UiNode`] as content.
484    ///
485    /// This node also pass through the `child` inline layout return info if the widget and child are inlining and the
486    /// widget has not set inline info before delegating measure.
487    ///
488    /// This node must be intrinsic at [`NestGroup::CHILD`], the [`WidgetBase`] default intrinsic inserts it.
489    ///
490    /// [`WidgetBase`]: struct@WidgetBase
491    pub fn widget_child(child: impl IntoUiNode) -> UiNode {
492        let key = FrameValueKey::new_unique();
493        let mut define_ref_frame = false;
494
495        match_node(child, move |child, op| match op {
496            UiNodeOp::Measure { wm, desired_size } => {
497                *desired_size = child.measure(wm);
498
499                if let Some(inline) = wm.inline()
500                    && inline.is_default()
501                    && let Some(child_inline) = child
502                        .node()
503                        .as_widget()
504                        .and_then(|mut w| w.with_context(WidgetUpdateMode::Ignore, || WIDGET.bounds().measure_inline()))
505                {
506                    // pass through child inline
507                    *inline = child_inline;
508                }
509            }
510            UiNodeOp::Layout { wl, final_size } => {
511                let (s, d) = wl.with_child(|wl| child.layout(wl));
512                *final_size = s;
513
514                if d != define_ref_frame {
515                    define_ref_frame = d;
516                    WIDGET.render();
517                }
518
519                if !define_ref_frame {
520                    // child maybe widget, try to copy inline
521                    if let Some(inline) = wl.inline()
522                        && inline.is_default()
523                        && let Some(mut wgt) = child.node().as_widget()
524                    {
525                        wgt.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            UiNodeOp::Render { frame } => {
537                let offset = WIDGET.bounds().child_offset();
538                if define_ref_frame {
539                    frame.push_reference_frame(key.into(), key.bind(offset.into(), true), true, false, |frame| child.render(frame));
540                } else {
541                    frame.push_child(offset, |frame| {
542                        child.render(frame);
543                    });
544                }
545            }
546            UiNodeOp::RenderUpdate { update } => {
547                let offset = WIDGET.bounds().child_offset();
548                if define_ref_frame {
549                    update.with_transform(key.update(offset.into(), true), false, |update| child.render_update(update));
550                } else {
551                    update.with_child(offset, |update| child.render_update(update))
552                }
553            }
554            _ => {}
555        })
556    }
557
558    /// Returns a node that wraps `child` and marks the [`WidgetLayout::with_inner`] and [`FrameBuilder::push_inner`].
559    ///
560    /// This node renders the inner transform and implements the [`HitTestMode`] for the widget.
561    ///
562    /// This node must be intrinsic at [`NestGroup::BORDER`], the [`WidgetBase`] default intrinsic inserts it.
563    ///
564    /// [`WidgetBase`]: struct@WidgetBase
565    pub fn widget_inner(child: impl IntoUiNode) -> UiNode {
566        #[derive(Default, PartialEq)]
567        struct HitClips {
568            bounds: PxSize,
569            corners: PxCornerRadius,
570        }
571
572        let transform_key = FrameValueKey::new_unique();
573        let mut clips = HitClips::default();
574
575        match_node(child, move |child, op| match op {
576            UiNodeOp::Init => {
577                WIDGET.sub_var_layout(&HIT_TEST_MODE_VAR);
578            }
579            UiNodeOp::Layout { wl, final_size } => {
580                *final_size = wl.with_inner(|wl| child.layout(wl));
581
582                let mode = HIT_TEST_MODE_VAR.get();
583                let c = if matches!(mode, HitTestMode::Bounds | HitTestMode::RoundedBounds) {
584                    HitClips {
585                        bounds: *final_size,
586                        corners: if matches!(mode, HitTestMode::RoundedBounds) {
587                            WIDGET.border().corner_radius()
588                        } else {
589                            PxCornerRadius::zero()
590                        },
591                    }
592                } else {
593                    HitClips::default()
594                };
595
596                if c != clips {
597                    clips = c;
598                    WIDGET.render();
599                }
600            }
601            UiNodeOp::Render { frame } => {
602                frame.push_inner(transform_key, true, |frame| {
603                    frame.hit_test().push_clips(
604                        |c| {
605                            if let Some(inline) = WIDGET.bounds().inline() {
606                                for r in inline.negative_space().iter() {
607                                    c.push_clip_rect(*r, true);
608                                }
609                            }
610                        },
611                        |h| match HIT_TEST_MODE_VAR.get() {
612                            HitTestMode::RoundedBounds => {
613                                h.push_rounded_rect(PxRect::from_size(clips.bounds), clips.corners);
614                            }
615                            HitTestMode::Bounds => {
616                                h.push_rect(PxRect::from_size(clips.bounds));
617                            }
618                            _ => {}
619                        },
620                    );
621
622                    child.render(frame);
623                });
624            }
625            UiNodeOp::RenderUpdate { update } => {
626                update.update_inner(transform_key, true, |update| child.render_update(update));
627            }
628            _ => {}
629        })
630    }
631
632    /// Create a widget node that wraps `child` and introduces a new widget context. The node defines
633    /// an [`WIDGET`] context and implements the widget in each specific node method.
634    ///
635    /// This node must wrap the outer-most context node in the build, it is the [`WidgetBase`] widget type.
636    ///
637    /// The node retains the widget state if build with `cfg(any(test, feature = "test_util")))`, otherwise
638    /// the state is cleared.
639    ///
640    /// [`WidgetBase`]: struct@WidgetBase
641    pub fn widget(child: impl IntoUiNode, id: impl IntoValue<WidgetId>) -> UiNode {
642        struct WidgetNode {
643            ctx: WidgetCtx,
644            child: UiNode,
645
646            #[cfg(debug_assertions)]
647            inited: bool,
648            #[cfg(debug_assertions)]
649            info_built: bool,
650        }
651        impl WidgetUiNodeImpl for WidgetNode {
652            fn with_context(&mut self, update_mode: WidgetUpdateMode, visitor: &mut dyn FnMut()) {
653                WIDGET.with_context(&mut self.ctx, update_mode, visitor);
654            }
655        }
656        impl UiNodeImpl for WidgetNode {
657            fn children_len(&self) -> usize {
658                1
659            }
660
661            fn with_child(&mut self, index: usize, visitor: &mut dyn FnMut(&mut UiNode)) {
662                if index == 0 {
663                    visitor(&mut self.child);
664                }
665            }
666
667            fn as_widget(&mut self) -> Option<&mut dyn WidgetUiNodeImpl> {
668                Some(self)
669            }
670
671            fn init(&mut self) {
672                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
673                    #[cfg(debug_assertions)]
674                    if self.inited {
675                        tracing::error!(target: "widget_base", "`UiNode::init` called in inited widget {:?}", WIDGET.id());
676                    }
677
678                    self.child.init();
679                    WIDGET.update_info().layout().render();
680
681                    #[cfg(debug_assertions)]
682                    {
683                        self.inited = true;
684                        self.info_built = false;
685                    }
686                });
687                self.ctx.take_reinit(); // ignore reinit request
688            }
689
690            fn deinit(&mut self) {
691                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
692                    #[cfg(debug_assertions)]
693                    if !self.inited {
694                        tracing::error!(target: "widget_base", "`UiNode::deinit` called in not inited widget {:?}", WIDGET.id());
695                    }
696
697                    self.child.deinit();
698                    WIDGET.update_info().layout().render();
699
700                    #[cfg(debug_assertions)]
701                    {
702                        self.inited = false;
703                        self.info_built = false;
704                    }
705                });
706                self.ctx.deinit(cfg!(any(test, feature = "test_util")));
707            }
708
709            fn info(&mut self, info: &mut WidgetInfoBuilder) {
710                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
711                    #[cfg(debug_assertions)]
712                    if !self.inited {
713                        tracing::error!(target: "widget_base", "`UiNode::info` called in not inited widget {:?}", WIDGET.id());
714                    }
715
716                    #[cfg(debug_assertions)]
717                    {
718                        self.info_built = true;
719                    }
720
721                    info.push_widget(|info| {
722                        self.child.info(info);
723                    });
724                });
725
726                if self.ctx.is_pending_reinit() {
727                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
728                }
729            }
730
731            fn event(&mut self, update: &EventUpdate) {
732                if self.ctx.take_reinit() {
733                    self.deinit();
734                    self.init();
735                }
736
737                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
738                    #[cfg(debug_assertions)]
739                    if !self.inited {
740                        tracing::error!(target: "widget_base", "`UiNode::event::<{}>` called in not inited widget {:?}", update.event().name(), WIDGET.id());
741                    } else if !self.info_built {
742                        tracing::error!(target: "widget_base", "`UiNode::event::<{}>` called in widget {:?} before first info build", update.event().name(), WIDGET.id());
743                    }
744
745                    update.with_widget(|| {
746                        self.child.event(update);
747                    });
748                });
749
750                if self.ctx.take_reinit() {
751                    self.deinit();
752                    self.init();
753                }
754            }
755
756            fn update(&mut self, updates: &WidgetUpdates) {
757                if self.ctx.take_reinit() {
758                    self.deinit();
759                    self.init();
760                    return;
761                }
762
763                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
764                    #[cfg(debug_assertions)]
765                    if !self.inited {
766                        tracing::error!(target: "widget_base", "`UiNode::update` called in not inited widget {:?}", WIDGET.id());
767                    } else if !self.info_built {
768                        tracing::error!(target: "widget_base", "`UiNode::update` called in widget {:?} before first info build", WIDGET.id());
769                    }
770
771                    updates.with_widget(|| {
772                        self.child.update(updates);
773                    });
774                });
775
776                if self.ctx.take_reinit() {
777                    self.deinit();
778                    self.init();
779                }
780            }
781
782            fn measure(&mut self, wm: &mut WidgetMeasure) -> PxSize {
783                let desired_size = WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Ignore, || {
784                    #[cfg(debug_assertions)]
785                    if !self.inited {
786                        tracing::error!(target: "widget_base", "`UiNode::measure` called in not inited widget {:?}", WIDGET.id());
787                    } else if !self.info_built {
788                        tracing::error!(target: "widget_base", "`UiNode::measure` called in widget {:?} before first info build", WIDGET.id());
789                    }
790
791                    wm.with_widget(|wm| {
792                        let child_size = self.child.measure(wm);
793
794                        // verify that inline row segments fit in row size
795                        #[cfg(debug_assertions)]
796                        if let Some(inline) = wm.inline() {
797                            for (name, size, segs) in [
798                                ("first", inline.first, &inline.first_segs),
799                                ("last", inline.last, &inline.last_segs),
800                            ] {
801                                let width = size.width.0 as f32;
802                                let sum_width = segs.iter().map(|s| s.width).sum::<f32>();
803                                if sum_width > width + 0.1 {
804                                    tracing::error!(
805                                        "widget {:?} measured inline {name} row has {width} width, but row segs sum to {sum_width} width",
806                                        WIDGET.id()
807                                    );
808                                }
809                            }
810                        }
811
812                        child_size
813                    })
814                });
815
816                // ignore
817                let _ = self.ctx.take_reinit();
818
819                desired_size
820            }
821
822            fn layout(&mut self, wl: &mut WidgetLayout) -> PxSize {
823                let final_size = WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
824                    #[cfg(debug_assertions)]
825                    if !self.inited {
826                        tracing::error!(target: "widget_base", "`UiNode::layout` called in not inited widget {:?}", WIDGET.id());
827                    } else if !self.info_built {
828                        tracing::error!(target: "widget_base", "`UiNode::layout` called in widget {:?} before first info build", WIDGET.id());
829                    }
830
831                    wl.with_widget(|wl| {
832                        let child_size = self.child.layout(wl);
833
834                        // verify that inline row segments fit in row rectangle
835                        #[cfg(debug_assertions)]
836                        if let Some(inline) = wl.inline() {
837                            use zng_layout::unit::Px;
838
839                            for (name, row, segs) in inline
840                                .rows
841                                .first()
842                                .iter()
843                                .map(|r| ("first", r, &inline.first_segs))
844                                .chain(inline.rows.last().iter().map(|r| ("last", r, &inline.last_segs)))
845                            {
846                                let width = row.width();
847                                let sum_width = segs.iter().map(|s| s.width).sum::<Px>();
848                                if (sum_width - width) > Px(1) {
849                                    tracing::error!(
850                                        "widget {:?} layout inline {name} row has {width} width, but row segs widths sum to {sum_width}",
851                                        WIDGET.id()
852                                    );
853                                }
854                            }
855                        }
856
857                        child_size
858                    })
859                });
860
861                if self.ctx.is_pending_reinit() {
862                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
863                }
864
865                final_size
866            }
867
868            fn render(&mut self, frame: &mut FrameBuilder) {
869                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
870                    #[cfg(debug_assertions)]
871                    if !self.inited {
872                        tracing::error!(target: "widget_base", "`UiNode::render` called in not inited widget {:?}", WIDGET.id());
873                    } else if !self.info_built {
874                        tracing::error!(target: "widget_base", "`UiNode::render` called in widget {:?} before first info build", WIDGET.id());
875                    }
876
877                    frame.push_widget(|frame| self.child.render(frame));
878                });
879
880                if self.ctx.is_pending_reinit() {
881                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
882                }
883            }
884
885            fn render_update(&mut self, update: &mut FrameUpdate) {
886                WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || {
887                    #[cfg(debug_assertions)]
888                    if !self.inited {
889                        tracing::error!(target: "widget_base", "`UiNode::render_update` called in not inited widget {:?}", WIDGET.id());
890                    } else if !self.info_built {
891                        tracing::error!(target: "widget_base", "`UiNode::render_update` called in widget {:?} before first info build", WIDGET.id());
892                    }
893
894                    update.update_widget(|update| self.child.render_update(update));
895                });
896
897                if self.ctx.is_pending_reinit() {
898                    WIDGET.with_context(&mut self.ctx, WidgetUpdateMode::Bubble, || WIDGET.update());
899                }
900            }
901        }
902
903        WidgetNode {
904            ctx: WidgetCtx::new(id.into()),
905            child: child.into_node(),
906
907            #[cfg(debug_assertions)]
908            inited: false,
909            #[cfg(debug_assertions)]
910            info_built: false,
911        }
912        .into_node()
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 { Parallel::all() } else { Parallel::empty() }
1022    }
1023}