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}