Skip to main content

denise_ui/
widget.rs

1//! What a widget is, and the two contexts it is handed.
2//!
3//! A widget owns its own state and knows how to draw itself inside a rectangle it
4//! is given. It does not own its position, its children, its z-order or its
5//! damage — the tree owns those, which is what keeps the invalidation rules in one
6//! place instead of scattered across every widget.
7
8use alloc::boxed::Box;
9use alloc::vec::Vec;
10use core::any::Any;
11
12use denise::Pen;
13use denise::{InputEvent, Point, Rect, Size, Theme};
14use denise_text::TextEngine;
15
16use crate::motion::Wake;
17use crate::widgets::describe::DynDescribe;
18
19/// Upcast to [`Any`], so an application can get its concrete widget type back out
20/// of the tree. Blanket-implemented; never implement it by hand.
21pub trait AsAny: 'static {
22    /// Borrows as `dyn Any`.
23    fn as_any(&self) -> &dyn Any;
24    /// Mutably borrows as `dyn Any`.
25    fn as_any_mut(&mut self) -> &mut dyn Any;
26}
27
28impl<T: Any> AsAny for T {
29    #[inline]
30    fn as_any(&self) -> &dyn Any {
31        self
32    }
33    #[inline]
34    fn as_any_mut(&mut self) -> &mut dyn Any {
35        self
36    }
37}
38
39/// Visual state the tree tracks on the widget's behalf.
40///
41/// Widgets do not track hover or press themselves. The tree does, and it marks the
42/// node dirty when any of these change — which is the whole reason a stale-pixel
43/// bug cannot come from a widget forgetting to invalidate on hover.
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
45pub struct VisualState(u8);
46
47impl VisualState {
48    /// Nothing set.
49    pub const NONE: Self = Self(0);
50    /// The pointer is over this widget.
51    pub const HOVERED: Self = Self(1 << 0);
52    /// A pointer button went down on this widget and has not come up.
53    pub const PRESSED: Self = Self(1 << 1);
54    /// This widget has keyboard focus.
55    pub const FOCUSED: Self = Self(1 << 2);
56    /// This widget, or an ancestor, is disabled.
57    pub const DISABLED: Self = Self(1 << 3);
58
59    /// Returns `true` if every bit in `other` is set.
60    #[inline]
61    pub const fn contains(self, other: Self) -> bool {
62        self.0 & other.0 == other.0
63    }
64
65    /// Sets or clears `other`.
66    #[inline]
67    pub const fn set(self, other: Self, on: bool) -> Self {
68        Self(if on {
69            self.0 | other.0
70        } else {
71            self.0 & !other.0
72        })
73    }
74
75    /// Returns `true` if no bit is set.
76    #[inline]
77    pub const fn is_empty(self) -> bool {
78        self.0 == 0
79    }
80}
81
82impl core::ops::BitOr for VisualState {
83    type Output = Self;
84    #[inline]
85    fn bitor(self, rhs: Self) -> Self {
86        Self(self.0 | rhs.0)
87    }
88}
89
90/// Whether an event was consumed.
91#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
92pub enum Handled {
93    /// The widget ignored the event.
94    #[default]
95    No,
96    /// The widget acted on the event.
97    ///
98    /// **This also marks the node dirty.** A widget that consumes an event has
99    /// almost always changed what it draws, and the cost of being wrong is
100    /// repainting one widget-sized rectangle. Missing an invalidation costs a
101    /// stale frame that only shows up on hardware, so the default errs the cheap
102    /// way. Use [`EventCtx::invalidate`] for the rare change that consumes nothing.
103    Yes,
104}
105
106impl Handled {
107    /// Returns `true` for [`Handled::Yes`].
108    #[inline]
109    pub const fn is_handled(self) -> bool {
110        matches!(self, Handled::Yes)
111    }
112}
113
114/// Something a widget is asked to react to.
115#[derive(Debug)]
116#[non_exhaustive]
117pub enum Event<'a> {
118    /// Raw input, already routed to this widget by hit test or focus.
119    Input(&'a InputEvent),
120    /// This widget just took keyboard focus.
121    FocusGained,
122    /// This widget just lost keyboard focus.
123    FocusLost,
124    /// A press this widget was holding ended without a release.
125    ///
126    /// The tree drops a held press whenever the thing being pressed stops being
127    /// reachable — a scene pushed over it, the scene it lives in popped, its
128    /// node removed or disabled — and no pointer event describes that. A widget
129    /// that only tracks Down and Up would go on believing a finger is still
130    /// resting on it, which for anything driving a timer from that belief means
131    /// waking a panel that nobody is touching.
132    ///
133    /// Ordinary widgets need not handle it: the visual pressed state is cleared
134    /// by the tree either way.
135    PressCancelled,
136}
137
138/// What a caller can promise a widget about the space it will get.
139///
140/// Not a constraint in the layout-engine sense: there is no minimum, no maximum
141/// and nothing to satisfy. It is the one fact a widget may need in order to
142/// answer at all — an [`Alert`](crate::widgets::Alert) has no height until it
143/// knows the width its text wraps to, and a [`Rating`](crate::widgets::Rating)
144/// has no width until it knows how tall its stars are.
145///
146/// `None` on an axis means the caller cannot promise anything there, which is
147/// the usual case and the default.
148///
149/// ```
150/// # use denise_ui::Offer;
151/// // "You will get 300 pixels of width; the height is up to you."
152/// let offer = Offer::wide(300);
153/// assert_eq!(offer.width, Some(300));
154/// assert_eq!(offer.height, None);
155///
156/// assert_eq!(Offer::NOTHING, Offer::default());
157/// ```
158#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
159pub struct Offer {
160    /// The width the caller can promise, if it can promise one.
161    pub width: Option<i32>,
162    /// The height, likewise.
163    pub height: Option<i32>,
164}
165
166impl Offer {
167    /// Nothing promised on either axis.
168    pub const NOTHING: Self = Self {
169        width: None,
170        height: None,
171    };
172
173    /// This much width, and nothing said about the height.
174    #[must_use]
175    pub const fn wide(width: i32) -> Self {
176        Self {
177            width: Some(width),
178            height: None,
179        }
180    }
181
182    /// This much height, and nothing said about the width.
183    #[must_use]
184    pub const fn tall(height: i32) -> Self {
185        Self {
186            width: None,
187            height: Some(height),
188        }
189    }
190
191    /// Both axes promised.
192    #[must_use]
193    pub const fn exactly(size: Size) -> Self {
194        Self {
195            width: Some(size.width as i32),
196            height: Some(size.height as i32),
197        }
198    }
199}
200
201/// What a widget would like to be, on the axes it has an opinion about.
202///
203/// **Per axis, and both optional**, because that is what the widgets actually
204/// are. A [`List`](crate::widgets::List) has an opinion about both. An
205/// [`Alert`](crate::widgets::Alert) has one about its height and none about its
206/// width — it is a banner, and a banner is as wide as you make it. A
207/// [`Panel`](crate::widgets::Panel) has none about either, because it is the
208/// background other things sit on and has no content of its own.
209///
210/// A single `Option<Size>` would force a widget with an opinion about one axis
211/// to invent one about the other, and an invented size is worse than no size:
212/// no size, the caller notices and decides.
213///
214/// ```
215/// # use denise_ui::Measured;
216/// assert_eq!(Measured::NOTHING, Measured::default());
217/// assert_eq!(Measured::wide(120).width, Some(120));
218/// assert_eq!(Measured::wide(120).height, None);
219/// ```
220#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
221pub struct Measured {
222    /// The width it would like, if it has a view.
223    pub width: Option<i32>,
224    /// The height it would like, if it has a view.
225    pub height: Option<i32>,
226}
227
228impl Measured {
229    /// No opinion on either axis. The default, and what most widgets answer.
230    pub const NOTHING: Self = Self {
231        width: None,
232        height: None,
233    };
234
235    /// An opinion about the width only.
236    #[must_use]
237    pub const fn wide(width: i32) -> Self {
238        Self {
239            width: Some(width),
240            height: None,
241        }
242    }
243
244    /// An opinion about the height only.
245    #[must_use]
246    pub const fn tall(height: i32) -> Self {
247        Self {
248            width: None,
249            height: Some(height),
250        }
251    }
252
253    /// An opinion about both.
254    #[must_use]
255    pub const fn both(width: i32, height: i32) -> Self {
256        Self {
257            width: Some(width),
258            height: Some(height),
259        }
260    }
261}
262
263/// What a widget may consult while measuring itself.
264///
265/// The theme and the fonts, and deliberately nothing else. No bounds, because a
266/// widget being asked how big it wants to be must not answer with how big it
267/// currently is; no state, because a hovered button is not a wider button; no
268/// clock, because a size that changed every frame would be a layout that never
269/// settled.
270#[derive(Debug)]
271pub struct MeasureCtx<'a> {
272    /// The active theme. Sizes come from its metrics.
273    pub theme: &'a Theme,
274    /// Fonts and the glyph cache.
275    pub text: &'a mut TextEngine,
276}
277
278/// What a widget needs in order to draw itself.
279///
280/// Mutable, because [`PaintCtx::text`] is: measuring a string is what fills the
281/// glyph cache, so a widget that measures and then draws rasterises each glyph
282/// once rather than twice. The widget itself is still `&self`.
283#[derive(Debug)]
284pub struct PaintCtx<'a> {
285    /// The active theme. Widgets name roles, never colours.
286    pub theme: &'a Theme,
287    /// Fonts and the glyph cache.
288    pub text: &'a mut TextEngine,
289    /// Absolute bounds of this widget, in surface pixels.
290    pub bounds: Rect,
291    /// Hover, press, focus and enabled state, tracked by the tree.
292    pub state: VisualState,
293    /// Milliseconds from an arbitrary epoch, as last given to [`crate::Ui::tick`].
294    pub now_ms: u64,
295}
296
297/// What a widget can do while handling an event.
298#[derive(Debug)]
299pub struct EventCtx<'a, M> {
300    /// Absolute bounds of this widget, in surface pixels.
301    pub bounds: Rect,
302    /// The active theme.
303    pub theme: &'a Theme,
304    /// Fonts and the glyph cache.
305    ///
306    /// Needed while *handling* events, not only while painting: a text field with
307    /// a proportional font cannot work out where its caret is without measuring
308    /// the text in front of it.
309    pub text: &'a mut TextEngine,
310    /// Hover, press, focus and enabled state.
311    pub state: VisualState,
312    /// Milliseconds from an arbitrary epoch.
313    pub now_ms: u64,
314    messages: &'a mut Vec<M>,
315    dirty: bool,
316    wants_focus: bool,
317    wants_animation: bool,
318    reveal: Option<Rect>,
319    resize: Option<(i32, u64)>,
320    scrolled: Option<(Rect, Point)>,
321}
322
323impl<'a, M> EventCtx<'a, M> {
324    pub(crate) fn new(
325        bounds: Rect,
326        theme: &'a Theme,
327        text: &'a mut TextEngine,
328        state: VisualState,
329        now_ms: u64,
330        messages: &'a mut Vec<M>,
331    ) -> Self {
332        Self {
333            bounds,
334            theme,
335            text,
336            state,
337            now_ms,
338            messages,
339            dirty: false,
340            wants_focus: false,
341            wants_animation: false,
342            reveal: None,
343            resize: None,
344            scrolled: None,
345        }
346    }
347
348    /// Queues a message for the application to pick up with
349    /// [`Ui::drain_messages`](crate::Ui::drain_messages).
350    ///
351    /// This is the whole event-handling story: no callbacks, no `Rc<RefCell<_>>`,
352    /// no widget holding a reference to another widget. The application dispatches
353    /// centrally, where it can see all of its own state.
354    #[inline]
355    pub fn emit(&mut self, message: M) {
356        self.messages.push(message);
357    }
358
359    /// Marks this widget's rectangle for repaint.
360    ///
361    /// Rarely needed: returning [`Handled::Yes`] already does it.
362    #[inline]
363    pub fn invalidate(&mut self) {
364        self.dirty = true;
365    }
366
367    /// Asks the tree to move keyboard focus here.
368    #[inline]
369    pub fn request_focus(&mut self) {
370        self.wants_focus = true;
371    }
372
373    /// Asks the tree to start calling [`Widget::animate`] on this widget.
374    ///
375    /// Called at the moment the widget starts needing frames — a knob that just
376    /// began sliding, a caret whose field just took focus. The calls continue
377    /// until `animate` answers `next_ms: None`, which is the widget saying it
378    /// has arrived. See [`Widget::animate`] for what that hand-back means on a
379    /// device that is supposed to spend its day asleep.
380    #[inline]
381    pub fn request_animation(&mut self) {
382        self.wants_animation = true;
383    }
384
385    /// Asks the tree to scroll `rect` — in absolute surface coordinates, like
386    /// [`EventCtx::bounds`] — into view in every scrollable ancestor.
387    ///
388    /// For a widget whose *interior* moves: a list whose selection walked below
389    /// the fold reveals the selected row's rectangle, and the viewport follows
390    /// the selection the way it follows focus. Widgets that are themselves the
391    /// focus target need nothing — focus already reveals.
392    #[inline]
393    pub fn reveal(&mut self, rect: Rect) {
394        self.reveal = Some(rect);
395    }
396
397    /// Asks the tree to carry this node's **height** to `height` over
398    /// `duration_ms`, through the same tween
399    /// [`Ui::animate_layout`](crate::Ui::animate_layout) drives.
400    ///
401    /// A widget does not own its geometry — the tree does — which is why this is
402    /// a request rather than a call, and why it sits beside
403    /// [`reveal`](EventCtx::reveal) rather than anywhere else: those are the two
404    /// things a widget can want and cannot do.
405    ///
406    /// Reach for it only where nothing else can act. The one use in this crate
407    /// is a [`Collapse`](crate::widgets::Collapse) with no message: a section
408    /// that reports its toggles is telling the application to drive the fold,
409    /// and one that reports nothing has nobody else to.
410    pub fn resize_height(&mut self, height: i32, duration_ms: u64) {
411        self.resize = Some((height, duration_ms));
412    }
413
414    /// Records that this widget moved its own content by `by` inside
415    /// `within` — a rectangle in surface coordinates, like
416    /// [`bounds`](EventCtx::bounds) — and changed nothing else there.
417    ///
418    /// For a widget that scrolls itself: a log that keeps its own top line, a
419    /// table with a pinned header. It is what lets the tree move the rows
420    /// still on screen instead of drawing them again, the optimisation a
421    /// viewport scrolled through [`Ui::set_scroll`](crate::Ui::set_scroll)
422    /// already gets, on a target that can shift its own pixels. The rest of
423    /// the widget's rectangle is repainted as usual, which is where a
424    /// scrollbar that did not move belongs: leave it out of `within`.
425    ///
426    /// Only a vertical `by` is moved today; a sideways one repaints. The claim
427    /// is trusted — a widget that says its content moved by `by` and then
428    /// paints something else inside `within` gets whatever that looks like.
429    /// Answer the event `Handled::Yes` as usual; do not also
430    /// [`invalidate`](EventCtx::invalidate), which says "repaint me" and
431    /// takes the move back.
432    pub fn scrolled(&mut self, within: Rect, by: Point) {
433        self.scrolled = Some((within, by));
434    }
435
436    pub(crate) fn finish(self) -> Outcome {
437        Outcome {
438            dirty: self.dirty,
439            wants_focus: self.wants_focus,
440            wants_animation: self.wants_animation,
441            reveal: self.reveal,
442            resize: self.resize,
443            scrolled: self.scrolled,
444        }
445    }
446}
447
448/// What handling one event left the tree to do.
449///
450/// Grown from a tuple once it reached five fields, which is the point at which
451/// `(bool, bool, bool, Option<Rect>, Option<(i32, u64)>)` stops being readable
452/// at the call site.
453pub(crate) struct Outcome {
454    pub(crate) dirty: bool,
455    pub(crate) wants_focus: bool,
456    pub(crate) wants_animation: bool,
457    pub(crate) reveal: Option<Rect>,
458    pub(crate) resize: Option<(i32, u64)>,
459    pub(crate) scrolled: Option<(Rect, Point)>,
460}
461
462/// What a widget reports back after [`Widget::animate`].
463#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
464pub struct Animation {
465    /// `true` if the widget's appearance changed and it needs repainting.
466    pub repaint: bool,
467    /// When the widget wants to be asked again — at the tree's rate, at a
468    /// particular time, or never.
469    pub next: Wake,
470}
471
472impl Animation {
473    /// Nothing is animating.
474    pub const NONE: Self = Self {
475        repaint: false,
476        next: Wake::Never,
477    };
478
479    /// Moving: repaint, and come back at the tree's animation rate.
480    ///
481    /// The answer nearly every mid-transition widget wants, and the reason none
482    /// of them carries a frame-rate constant any more — how fast "the tree's
483    /// rate" is belongs to [`Motion`](crate::Motion).
484    pub const MOVING: Self = Self {
485        repaint: true,
486        next: Wake::Animating,
487    };
488
489    /// Waiting for a deadline, with nothing to repaint until it arrives.
490    ///
491    /// The toast arrangement: one wake at the moment something is due, rather
492    /// than a frame a tick spent noticing that it is not due yet.
493    #[inline]
494    pub const fn due_at(due_ms: u64) -> Self {
495        Self {
496            repaint: false,
497            next: Wake::At(due_ms),
498        }
499    }
500}
501
502/// A thing that draws itself in a rectangle and reacts to input.
503///
504/// `M` is the application's message type. A widget never calls back into the
505/// application; it emits an `M` and the application decides what that means.
506pub trait Widget<M>: AsAny {
507    /// Draws into `canvas`, which is already clipped to this widget's bounds
508    /// intersected with the damage region being repainted.
509    ///
510    /// Paint as though the whole widget were visible. The clip turns that into an
511    /// incremental repaint, so there is never a second draw path to keep in step
512    /// with the first.
513    fn paint(&self, ctx: &mut PaintCtx<'_>, canvas: &mut Pen<'_>);
514
515    /// Reacts to an event routed to this widget.
516    fn on_event(&mut self, event: &Event<'_>, ctx: &mut EventCtx<'_, M>) -> Handled {
517        let _ = (event, ctx);
518        Handled::No
519    }
520
521    /// How big this widget would like to be, given what the caller can promise.
522    ///
523    /// [`Measured::NOTHING`] — the default — means no opinion, which is the
524    /// honest answer for a [`Panel`], an [`Image`] or a [`Video`]: they are
525    /// whatever rectangle they are given.
526    ///
527    /// **The tree never calls this.** It is a query, offered for a caller that
528    /// is not holding the concrete widget and so cannot reach its inherent
529    /// `preferred_width`/`preferred_height`. That distinction is the line
530    /// between this toolkit and a layout engine, and it survives: an
531    /// intrinsic-size *protocol* is one where the tree asks every widget how big
532    /// it wants to be and then places it, and nothing in this crate consumes
533    /// this. See `docs/design.md`, and `docs/arrange.md` for the caller it was
534    /// written for.
535    ///
536    /// [`Panel`]: crate::widgets::Panel
537    /// [`Image`]: crate::widgets::Image
538    /// [`Video`]: crate::widgets::Video
539    fn measure(&self, ctx: &mut MeasureCtx<'_>, offered: Offer) -> Measured {
540        let _ = (ctx, offered);
541        Measured::NOTHING
542    }
543
544    /// Returns `true` if the pointer can hit this widget.
545    ///
546    /// Non-interactive widgets are invisible to hit testing, so a [`Label`] inside
547    /// a [`Button`] does not swallow the click — the button is still the topmost
548    /// hittable node under the pointer.
549    ///
550    /// [`Label`]: crate::widgets::Label
551    /// [`Button`]: crate::widgets::Button
552    fn accepts_pointer(&self) -> bool {
553        false
554    }
555
556    /// Returns `true` if this widget can take keyboard focus.
557    fn focusable(&self) -> bool {
558        false
559    }
560
561    /// Returns `true` if a press on this widget should leave focus exactly where
562    /// it is.
563    ///
564    /// Not the same as being unfocusable, and the difference is the whole point.
565    /// Pressing an ordinary non-focusable node — a [`Label`], a [`Panel`] — drops
566    /// focus, which is what makes a text field commit and stop blinking when the
567    /// user clicks away. A widget answering `true` here is asking for neither: it
568    /// takes no focus *and* costs none.
569    ///
570    /// An on-screen keyboard's keys are the reason this exists. A key is pressed
571    /// while a field is being typed into, and both the ordinary answers are wrong
572    /// — taking focus blurs the field, and dropping focus blurs it too.
573    ///
574    /// [`Label`]: crate::widgets::Label
575    /// [`Panel`]: crate::widgets::Panel
576    fn preserves_focus(&self) -> bool {
577        false
578    }
579
580    /// Advances time-based state. Called only while this widget has asked to
581    /// animate — see [`EventCtx::request_animation`] — and stops being called
582    /// the moment it answers [`Wake::Never`].
583    ///
584    /// The contract is deliberate about who holds the responsibility: **the
585    /// widget keeps itself animating, and must stop asking.** On a panel that
586    /// spends its day idle, a bounded transition — a knob crossing, a toast
587    /// fading — costs its duration and then hands the CPU back. An animation
588    /// that never answers [`Wake::Never`] keeps the device awake for as long as
589    /// its node is visible, which is legitimate for a spinner and ruinous for
590    /// anything that merely forgot. [`Ui::animating`](crate::Ui::animating)
591    /// exists so a test can prove a tree at rest holds nobody awake.
592    ///
593    /// May be called earlier than the time it asked for: the tree wakes for the
594    /// most impatient animation and asks everybody. Answer honestly for the
595    /// clock given and it comes out right.
596    ///
597    /// # Say what kind of waiting it is
598    ///
599    /// A widget that is *moving* answers [`Wake::Animating`] and lets
600    /// [`Motion`](crate::Motion) decide how often that is — one setting, tree
601    /// wide, which a widget with its own frame-rate constant would opt out of
602    /// without meaning to. A widget waiting for something to *happen* answers
603    /// [`Wake::At`] with the time, and the rate never touches it.
604    fn animate(&mut self, now_ms: u64) -> Animation {
605        let _ = now_ms;
606        Animation::NONE
607    }
608
609    /// Lands whatever is in flight at its end state, without animating it.
610    ///
611    /// Called instead of [`animate`](Widget::animate) while the tree's
612    /// [`Motion`](crate::Motion) is [`Motion::None`](crate::Motion::None) —
613    /// reduced motion, or a power budget with no room for movement. A knob
614    /// arrives, a slide is over, a fade is not a fade.
615    ///
616    /// The return is an ordinary [`Animation`], so a widget that has a
617    /// **schedule** as well as a motion keeps it: a carousel that lands its
618    /// slide instantly still answers [`Wake::At`] for its auto-advance, because
619    /// turning motion off is not the same as stopping the clock. [`Wake::
620    /// Animating`](Wake::Animating) is the one answer that means nothing here —
621    /// there is no rate to come back at — and the tree reads it as
622    /// [`Wake::Never`].
623    ///
624    /// The default settles nothing and asks for nothing, which is right for the
625    /// widgets that do not animate and for unbounded ones like a spinner: under
626    /// `Motion::None` a spinner simply does not turn.
627    fn snap(&mut self, now_ms: u64) -> Animation {
628        let _ = now_ms;
629        Animation::NONE
630    }
631
632    /// This widget's property description, if it has one.
633    ///
634    /// The tree stores widgets boxed, and [`Describe`](crate::widgets::Describe)
635    /// has associated constants, so it is not object-safe and cannot be reached
636    /// through a `dyn Widget<M>` directly. These two hand over the object-safe
637    /// half, and they are what
638    /// [`Ui::set_property`](crate::Ui::set_property) calls.
639    ///
640    /// Opting in is two lines in a widget's `Widget` implementation, once it
641    /// implements `Describe`:
642    ///
643    /// ```ignore
644    /// fn describe(&self) -> Option<&dyn DynDescribe> { Some(self) }
645    /// fn describe_mut(&mut self) -> Option<&mut dyn DynDescribe> { Some(self) }
646    /// ```
647    ///
648    /// The default answers `None`, so a one-off widget in an application is not
649    /// obliged to describe itself — it simply does not appear in a form file or
650    /// a property inspector, which for a widget nobody else will ever place is
651    /// the right amount of ceremony. Every widget this crate ships does opt in.
652    fn describe(&self) -> Option<&dyn DynDescribe> {
653        None
654    }
655
656    /// The mutable half of [`describe`](Widget::describe).
657    fn describe_mut(&mut self) -> Option<&mut dyn DynDescribe> {
658        None
659    }
660}
661
662/// A widget that draws nothing and hits nothing.
663///
664/// Used for scene roots and for grouping: a node exists to position and clip its
665/// children, and does not have to paint to do that.
666#[derive(Clone, Copy, Debug, Default)]
667pub struct Void;
668
669impl<M: 'static> Widget<M> for Void {
670    fn paint(&self, _ctx: &mut PaintCtx<'_>, _canvas: &mut Pen<'_>) {}
671}
672
673pub(crate) type BoxedWidget<M> = Box<dyn Widget<M>>;
674
675#[cfg(test)]
676mod tests {
677    use super::*;
678
679    #[test]
680    fn visual_state_bits() {
681        let s = VisualState::HOVERED | VisualState::FOCUSED;
682        assert!(s.contains(VisualState::HOVERED));
683        assert!(!s.contains(VisualState::PRESSED));
684        assert!(
685            s.set(VisualState::HOVERED, false)
686                .contains(VisualState::FOCUSED)
687        );
688        assert!(VisualState::NONE.is_empty());
689    }
690}