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}