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