masonry_core/core/
widget.rs

1// Copyright 2018 the Xilem Authors and the Druid Authors
2// SPDX-License-Identifier: Apache-2.0
3
4use std::any::{Any, TypeId};
5use std::fmt::{Debug, Display};
6use std::num::NonZeroU64;
7use std::sync::atomic::{AtomicU64, Ordering};
8
9use accesskit::{Node, Role};
10use smallvec::SmallVec;
11use tracing::field::DisplayValue;
12use tracing::{Span, trace_span};
13use vello::Scene;
14use vello::kurbo::{Point, Size};
15
16use crate::core::{
17    AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, CursorIcon, EventCtx, LayoutCtx, NewWidget,
18    PaintCtx, PointerEvent, Properties, PropertiesMut, PropertiesRef, QueryCtx, RegisterCtx,
19    TextEvent, Update, UpdateCtx, WidgetOptions, WidgetRef,
20};
21
22/// A unique identifier for a single [`Widget`].
23///
24/// `WidgetId`s are generated automatically for all widgets in the widget tree.
25/// More specifically, each [`WidgetPod`](crate::core::WidgetPod) has a unique `WidgetId`.
26///
27/// These ids are used internally to route events, and can be used to fetch a specific
28/// widget for testing or event handling.
29///
30/// A widget can retrieve its id via methods on the various contexts, such as
31/// [`UpdateCtx::widget_id`].
32///
33/// # Explicit `WidgetId`s.
34///
35/// Sometimes, you may want to construct a widget, in a way that lets you know its id,
36/// so you can refer to the widget later. You can use [`NewWidget::new_with_id`](crate::core::NewWidget::new_with_id) to pass
37/// an id to the `NewWidget` you're creating; various widgets which have methods to create
38/// children may have variants taking ids as parameters.
39///
40/// If you set a `WidgetId` directly, you are responsible for ensuring that it
41/// is unique. Two widgets must not be created with the same id.
42#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
43pub struct WidgetId(pub(crate) NonZeroU64);
44
45impl WidgetId {
46    /// A serialized representation of the `WidgetId` for debugging purposes.
47    pub fn trace(self) -> DisplayValue<Self> {
48        tracing::field::display(self)
49    }
50}
51
52#[doc(hidden)]
53/// A trait to access a [`Widget`] value as a trait object. It is implemented for all types that implement `Widget`.
54pub trait AsDynWidget {
55    fn as_box_dyn(self: Box<Self>) -> Box<dyn Widget>;
56    fn as_dyn(&self) -> &dyn Widget;
57    fn as_mut_dyn(&mut self) -> &mut dyn Widget;
58}
59
60impl<T: Widget> AsDynWidget for T {
61    fn as_box_dyn(self: Box<Self>) -> Box<dyn Widget> {
62        self
63    }
64
65    fn as_dyn(&self) -> &dyn Widget {
66        self as &dyn Widget
67    }
68
69    fn as_mut_dyn(&mut self) -> &mut dyn Widget {
70        self as &mut dyn Widget
71    }
72}
73
74/// A trait that lets functions either downcast to a `Sized` widget or keep a `dyn Widget`.
75pub trait FromDynWidget {
76    /// Downcast `widget` if `Self: Sized`, else return it as-is.
77    fn from_dyn(widget: &dyn Widget) -> Option<&Self>;
78    /// Downcast `widget` if `Self: Sized`, else return it as-is.
79    fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self>;
80}
81
82impl<T: Widget> FromDynWidget for T {
83    fn from_dyn(widget: &dyn Widget) -> Option<&Self> {
84        (widget as &dyn Any).downcast_ref()
85    }
86
87    fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self> {
88        (widget as &mut dyn Any).downcast_mut()
89    }
90}
91
92impl FromDynWidget for dyn Widget {
93    fn from_dyn(widget: &dyn Widget) -> Option<&Self> {
94        Some(widget)
95    }
96
97    fn from_dyn_mut(widget: &mut dyn Widget) -> Option<&mut Self> {
98        Some(widget)
99    }
100}
101
102/// A collection of widget ids, to be returned from [`Widget::children_ids`].
103///
104/// Internally, this uses a small vector optimisation, but you should treat it as an append-only `Vec<WidgetId>`.
105/// You can use `ChildrenIds::from_slice` with an array to make a list of children ids of known size,
106/// or use `ChildrenIds::new` then `push` to it.
107/// This type also implements [`FromIterator<WidgetId>`](core::iter::FromIterator).
108// TODO: Consider making our own wrapper type here, to make future breaking changes easier.?
109pub type ChildrenIds = SmallVec<[WidgetId; 16]>;
110
111/// The trait implemented by all widgets.
112///
113/// For details on how to implement this trait, see the [tutorials](crate::doc).
114///
115/// Whenever external events affect the given widget, methods
116/// [`on_pointer_event`](Self::on_pointer_event),
117/// [`on_text_event`](Self::on_text_event),
118/// [`on_access_event`](Self::on_access_event),
119/// [`on_anim_frame`](Self::on_anim_frame) and [`update`](Self::update) are called.
120///
121/// Later on, when the widget is laid out and displayed, methods
122/// [`layout`](Self::layout), [`compose`](Self::compose), [`paint`](Self::paint) and
123/// [`accessibility`](Self::accessibility) are called.
124///
125/// These trait methods are provided with a corresponding context. The widget can
126/// request things and cause actions by calling methods on that context.
127///
128/// Widgets also have a [`children_ids`](Self::children_ids) method. Leaf widgets return an empty array,
129/// whereas container widgets return an array of [`WidgetId`].
130/// Container widgets have some validity invariants to maintain regarding their children.
131///
132/// Generally speaking, widgets aren't used directly. They are stored by Masonry and accessed
133/// through [`WidgetPod`](crate::core::WidgetPod)s. Widget methods are called by Masonry, and a
134/// widget should only be mutated either during a method call or through a [`WidgetMut`](crate::core::WidgetMut).
135#[allow(unused_variables, reason = "Default impls don't use method arguments")]
136pub trait Widget: AsDynWidget + Any {
137    /// The action type that this widget will submit, through [`EventCtx::submit_action`]
138    /// (or the method of the same name on a different context).
139    /// The type of actions submitted by this widget will be validated against this type.
140    ///
141    /// If this widget never submits action, this can be an empty type
142    /// such as [`NoAction`](crate::core::NoAction).
143    type Action: Any + Debug
144    where
145        Self: Sized;
146
147    /// Handle a pointer event.
148    ///
149    /// Pointer events will target the widget under the pointer, and then the
150    /// event will bubble to each of its parents.
151    fn on_pointer_event(
152        &mut self,
153        ctx: &mut EventCtx<'_>,
154        props: &mut PropertiesMut<'_>,
155        event: &PointerEvent,
156    ) {
157    }
158
159    /// Handle a text event.
160    ///
161    /// Text events will target the [focused widget], then bubble to each parent.
162    ///
163    /// [focused widget]: crate::doc::masonry_concepts#text-focus
164    fn on_text_event(
165        &mut self,
166        ctx: &mut EventCtx<'_>,
167        props: &mut PropertiesMut<'_>,
168        event: &TextEvent,
169    ) {
170    }
171
172    /// Handle an event from the platform's accessibility API.
173    ///
174    /// Accessibility events target a specific widget id, then bubble to each parent.
175    fn on_access_event(
176        &mut self,
177        ctx: &mut EventCtx<'_>,
178        props: &mut PropertiesMut<'_>,
179        event: &AccessEvent,
180    ) {
181    }
182
183    /// Called at the beginning of a new animation frame.
184    ///
185    /// An animation frame does not implicitly request a repaint of this widget.
186    /// That is, if you change something which changes how this widget is
187    /// drawn, you should call
188    /// [`request_paint_only`](UpdateCtx::request_paint_only)
189    /// ([`request_render`](UpdateCtx::request_render) if an accessibility
190    /// update is also required). This method should itself call
191    /// [`request_anim`](UpdateCtx::request_anim_frame) unless the animation
192    /// has finished.
193    ///
194    /// On the first frame when transitioning from idle to animating, `interval`
195    /// will be 0. (This logic is presently per-window but might change to
196    /// per-widget to make it more consistent). Otherwise it is in nanoseconds.
197    ///
198    /// The `paint` method will often be called shortly after this event is finished.
199    /// For that reason, you should try to avoid doing anything computationally
200    /// intensive in response to an `AnimFrame` event: it might make the app miss
201    /// the monitor's refresh, causing lag or jerky animations.
202    fn on_anim_frame(
203        &mut self,
204        ctx: &mut UpdateCtx<'_>,
205        props: &mut PropertiesMut<'_>,
206        interval: u64,
207    ) {
208    }
209
210    // TODO - Reorder methods to match 02_implementing_widget.md
211
212    /// Register child widgets with Masonry.
213    ///
214    /// Leaf widgets can implement this with an empty body.
215    ///
216    /// Container widgets need to call [`RegisterCtx::register_child`] for all
217    /// their children. Forgetting to do so is a logic error and may lead to debug panics.
218    /// All the children returned by `children_ids` should be visited.
219    fn register_children(&mut self, ctx: &mut RegisterCtx<'_>);
220
221    /// Handle an update to the widget's state.
222    ///
223    /// This method is called to notify your widget of certain special events,
224    /// (available in the [`Update`] enum) that are generally related to
225    /// changes in the widget graph or in the state of your specific widget.
226    fn update(&mut self, ctx: &mut UpdateCtx<'_>, props: &mut PropertiesMut<'_>, event: &Update) {}
227
228    // TODO - Remove default implementation
229    /// Handle a property being added, changed, or removed.
230    fn property_changed(&mut self, ctx: &mut UpdateCtx<'_>, property_type: TypeId) {}
231
232    /// Compute layout and return the widget's size.
233    ///
234    /// A leaf widget should determine its size (subject to the provided
235    /// constraints) and return it.
236    ///
237    /// A container widget will recursively call [`LayoutCtx::run_layout`] on its
238    /// child widgets, providing each of them an appropriate box constraint,
239    /// run some layout logic, then call [`LayoutCtx::place_child`] on each of its children.
240    /// Finally, it should return the size of the container. The container
241    /// can recurse in any order, which can be helpful to, for example, compute
242    /// the size of non-flex widgets first, to determine the amount of space
243    /// available for the flex widgets.
244    ///
245    /// Forgetting to visit children is a logic error and may lead to debug panics.
246    /// All the children returned by `children_ids` should be visited.
247    ///
248    /// For efficiency, a container should only invoke layout of a child widget
249    /// once, though there is nothing enforcing this.
250    ///
251    /// **Container widgets should not add or remove children during layout.**
252    /// Doing so is a logic error and may trigger a debug assertion.
253    ///
254    /// While each widget should try to return a size that fits the input constraints,
255    /// **any widget may return a size that doesn't fit its constraints**, and container
256    /// widgets should handle those cases gracefully.
257    fn layout(
258        &mut self,
259        ctx: &mut LayoutCtx<'_>,
260        props: &mut PropertiesMut<'_>,
261        bc: &BoxConstraints,
262    ) -> Size;
263
264    /// Runs after the widget's final transform has been computed.
265    fn compose(&mut self, ctx: &mut ComposeCtx<'_>) {}
266
267    /// Paint the widget appearance.
268    ///
269    /// Container widgets can paint a background before recursing to their
270    /// children. To draw on top of children, see [`Widget::post_paint`].
271    fn paint(&mut self, ctx: &mut PaintCtx<'_>, _props: &PropertiesRef<'_>, scene: &mut Scene);
272
273    /// Second paint method, which paints on top of the widget's children.
274    ///
275    /// This method is not constrained by the clip defined in [`LayoutCtx::set_clip_path`], and can paint things outside the clip.
276    fn post_paint(&mut self, ctx: &mut PaintCtx<'_>, props: &PropertiesRef<'_>, scene: &mut Scene) {
277    }
278
279    /// Return what kind of "thing" the widget fundamentally is.
280    fn accessibility_role(&self) -> Role;
281
282    /// Describe the widget's contents for accessibility APIs.
283    ///
284    /// This method takes a mutable reference to a node which is already initialized
285    /// with some information about the current widget (coordinates, status flags), and
286    /// and mutates that node to set widget-specific information.
287    fn accessibility(
288        &mut self,
289        ctx: &mut AccessCtx<'_>,
290        _props: &PropertiesRef<'_>,
291        node: &mut Node,
292    );
293
294    /// Return ids of this widget's children.
295    ///
296    /// Leaf widgets return an empty array. Container widgets return ids of
297    /// their children.
298    ///
299    /// The list returned by this method is considered the "canonical" list of children
300    /// by Masonry.
301    ///
302    /// This method has some validity invariants. A widget's children list must be
303    /// consistent. If children are added or removed, the parent widget should call
304    /// `children_changed` on one of the Ctx parameters. Container widgets are
305    /// responsible for visiting all their children during `layout` and `register_children`.
306    fn children_ids(&self) -> ChildrenIds;
307
308    /// Whether this widget gets pointer events and [hovered] status. True by default.
309    ///
310    /// If false, the widget will be treated as "transparent" for the pointer, meaning
311    /// that the pointer will be considered as hovering whatever is under this widget.
312    ///
313    /// **Note:** The value returned by this method is cached at widget creation and can't be changed.
314    ///
315    /// [hovered]: crate::doc::masonry_concepts#hovered
316    fn accepts_pointer_interaction(&self) -> bool {
317        true
318    }
319
320    /// Whether this widget gets [text focus]. False by default.
321    ///
322    /// If true, pressing Tab can focus this widget.
323    ///
324    /// **Note:** The value returned by this method is cached at widget creation and can't be changed.
325    ///
326    /// [text focus]: crate::doc::masonry_concepts#text-focus
327    fn accepts_focus(&self) -> bool {
328        false
329    }
330
331    /// Whether this widget gets IME events. False by default.
332    ///
333    /// If true, focusing this widget will start an IME session.
334    ///
335    /// **Note:** The value returned by this method is cached at widget creation and can't be changed.
336    fn accepts_text_input(&self) -> bool {
337        false
338    }
339
340    // TODO - Write a generic default implementation once
341    // `const std::any::type_name` is stable.
342    // See https://github.com/rust-lang/rust/issues/63084
343    /// Return a span for tracing.
344    ///
345    /// As methods recurse through the widget tree, trace spans are added for each child
346    /// widget visited, and popped when control flow goes back to the parent. This method
347    /// returns a static span (that you can use to filter traces and logs).
348    fn make_trace_span(&self, id: WidgetId) -> Span {
349        trace_span!("Widget", r#type = self.short_type_name(), id = id.trace())
350    }
351
352    /// Return a small string representing important info about this widget instance.
353    ///
354    /// When using [`WidgetRef`]'s [`Debug`] implementation, widgets
355    /// will be displayed as a tree of values. Widgets which return a non-null value in
356    /// `get_debug_text` will appear with that text next to their type name. This can
357    /// be eg a label's text, or whether a checkbox is checked.
358    fn get_debug_text(&self) -> Option<String> {
359        None
360    }
361
362    /// Return the cursor icon for this widget.
363    ///
364    /// This will be called when the mouse moves or [`request_cursor_icon_change`](crate::core::MutateCtx::request_cursor_icon_change) is called.
365    ///
366    /// **pos** - the mouse position in global coordinates (e.g. `(0,0)` is the top-left corner of the
367    /// window).
368    fn get_cursor(&self, ctx: &QueryCtx<'_>, pos: Point) -> CursorIcon {
369        CursorIcon::Default
370    }
371
372    // --- Auto-generated implementations ---
373
374    /// Return the first innermost widget composed by this (including `self`), that contains/intersects with `pos` and accepts pointer interaction, if any.
375    ///
376    /// In case of overlapping children, the last child as determined by [`Widget::children_ids`] is chosen. No widget is
377    /// returned if `pos` is outside the widget's clip path.
378    ///
379    /// Has a default implementation that can be overridden to search children more efficiently.
380    /// Custom implementations must uphold the conditions outlined above.
381    ///
382    /// **pos** - the position in global coordinates (e.g. `(0,0)` is the top-left corner of the
383    /// window).
384    fn find_widget_under_pointer<'c>(
385        &'c self,
386        ctx: QueryCtx<'c>,
387        pos: Point,
388    ) -> Option<WidgetRef<'c, dyn Widget>> {
389        find_widget_under_pointer(self.as_dyn(), ctx, pos)
390    }
391
392    /// Get the (verbose) type name of the widget for debugging purposes.
393    /// You should not override this method.
394    #[doc(hidden)]
395    fn type_name(&self) -> &'static str {
396        std::any::type_name::<Self>()
397    }
398
399    /// Get the (abridged) type name of the widget for debugging purposes.
400    /// You should not override this method.
401    #[doc(hidden)]
402    fn short_type_name(&self) -> &'static str {
403        let name = self.type_name();
404        name.split('<')
405            .next()
406            .unwrap_or(name)
407            .split("::")
408            .last()
409            .unwrap_or(name)
410    }
411
412    /// Convenience method to create wrap this in a [`NewWidget`].
413    fn with_auto_id(self) -> NewWidget<Self>
414    where
415        Self: Sized,
416    {
417        NewWidget::new(self)
418    }
419
420    // TODO - We eventually want to remove the ability to reserve widget ids.
421    // See https://github.com/linebender/xilem/issues/1255
422    /// Convenience method to create wrap this in a [`NewWidget`] with the given id.
423    fn with_id(self, id: WidgetId) -> NewWidget<Self>
424    where
425        Self: Sized,
426    {
427        NewWidget::new_with_id(self, id)
428    }
429
430    /// Convenience method to create wrap this in a [`NewWidget`] with the given [`Properties`].
431    fn with_props(self, props: Properties) -> NewWidget<Self>
432    where
433        Self: Sized,
434    {
435        NewWidget::new_with(self, WidgetId::next(), WidgetOptions::default(), props)
436    }
437}
438
439/// See [`Widget::find_widget_under_pointer`] for more details.
440pub fn find_widget_under_pointer<'c>(
441    widget: &'c dyn Widget,
442    ctx: QueryCtx<'c>,
443    pos: Point,
444) -> Option<WidgetRef<'c, dyn Widget>> {
445    if !ctx.bounding_rect().contains(pos) {
446        return None;
447    }
448    if ctx.is_stashed() {
449        return None;
450    }
451
452    let local_pos = ctx.window_transform().inverse() * pos;
453
454    if let Some(clip) = ctx.clip_path()
455        && !clip.contains(local_pos)
456    {
457        return None;
458    }
459
460    // Assumes `Self::children_ids` is in increasing "z-order", picking the last child in case
461    // of overlapping children.
462    for child_id in widget.children_ids().iter().rev() {
463        let child_ref = ctx.get(*child_id);
464        if let Some(child) = child_ref
465            .widget
466            .find_widget_under_pointer(child_ref.ctx, pos)
467        {
468            return Some(child);
469        }
470    }
471
472    // If no child is under pointer, test the current widget.
473    if ctx.accepts_pointer_interaction() && ctx.size().to_rect().contains(local_pos) {
474        Some(WidgetRef { widget, ctx })
475    } else {
476        None
477    }
478}
479
480/// Marker trait for widgets whose parents can get a raw mutable reference to them.
481///
482/// "Raw mut" means using a mutable reference (eg `&mut MyWidget`) to the data
483/// structure, instead of going through the [`Widget`] trait methods
484/// (`on_text_event`, `update`, `layout`, etc) or through `WidgetMut`.
485///
486/// A parent widget can use [`EventCtx::get_raw_mut`], [`UpdateCtx::get_raw_mut`],
487/// or [`LayoutCtx::get_raw_mut`] to directly access a child widget. In that case,
488/// these methods return both a mutable reference to the child widget and a new
489/// [`RawCtx`](crate::core::RawCtx) context scoped to the child. The parent is
490/// responsible for calling the context methods (eg `request_layout`,
491/// `request_accessibility_update`) for the child.
492///
493/// Widgets implementing `AllowRawMut` are usually private widgets used as an
494/// internal implementation detail of public widgets.
495pub trait AllowRawMut: Widget {}
496
497impl WidgetId {
498    /// Allocate a new, unique `WidgetId`.
499    ///
500    /// All widgets are assigned ids automatically; you should only create
501    /// an explicit id if you need to know it ahead of time, for instance
502    /// if you want two sibling widgets to know each others' ids.
503    ///
504    /// You must ensure that a given `WidgetId` is only ever used for one
505    /// widget at a time.
506    pub fn next() -> Self {
507        static WIDGET_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
508        let id = WIDGET_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
509        Self(id.try_into().unwrap())
510    }
511
512    // TODO - Remove
513    /// Create a reserved `WidgetId`, suitable for reuse.
514    ///
515    /// The caller is responsible for ensuring that this ID is in fact assigned
516    /// to a single widget at any time, or your code may become haunted.
517    ///
518    /// The actual inner representation of the returned `WidgetId` will not
519    /// be the same as the raw value that is passed in; it will be
520    /// `u64::max_value() - raw`.
521    pub const fn reserved(raw: u16) -> Self {
522        let id = u64::MAX - raw as u64;
523        match NonZeroU64::new(id) {
524            Some(id) => Self(id),
525            // panic safety: u64::MAX - any u16 can never be zero
526            None => unreachable!(),
527        }
528    }
529
530    /// Returns the integer value of the `WidgetId`.
531    pub fn to_raw(self) -> u64 {
532        self.0.into()
533    }
534}
535
536impl From<WidgetId> for u64 {
537    fn from(id: WidgetId) -> Self {
538        id.0.into()
539    }
540}
541
542impl From<WidgetId> for accesskit::NodeId {
543    fn from(id: WidgetId) -> Self {
544        Self(id.0.into())
545    }
546}
547
548impl Display for WidgetId {
549    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
550        write!(f, "#{}", self.0)
551    }
552}
553
554impl PartialEq<accesskit::NodeId> for WidgetId {
555    fn eq(&self, other: &accesskit::NodeId) -> bool {
556        self.to_raw() == other.0
557    }
558}
559
560impl PartialEq<WidgetId> for accesskit::NodeId {
561    fn eq(&self, other: &WidgetId) -> bool {
562        self.0 == other.to_raw()
563    }
564}