zng_app/widget/
base.rs

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