duat_core/ui/
mod.rs

1//! [Ui] structs and functions
2//!
3//! Although there is only a terminal [Ui] implemented at the
4//! moment, Duat is supposed to be Ui agnostic, and I plan to create a
5//! GUI app (probably in `gpui` or something), and a web app as well,
6//! which is honestly more of an excuse for me to become more well
7//! versed on javascript.
8//!
9//! Each [Ui] is essentially a screen separated by a bunch of
10//! [`Ui::Area`]s. This happens by splitting a main `Ui::Area`
11//! continuously, by pushing [`Widget`]s on other `Widget`s. When a
12//! `Widget` is pushed to another, the area of the prior `Widget`
13//! is split in half, with [`PushSpecs`] defining information about
14//! the new area.
15//!
16//! Additionally, [`Widget`]s may be spawned via various methods, such
17//! as [on `Handle`]s, [on `Text`], or even [around the `Window`]
18//!
19//! Duat also supports multiple [`Window`]s in a [`Windows`] struct,
20//! each of which is defined by a main [`Ui::Area`] that was split
21//! many times over. This `Windows` struct is accessible in
22//! [`context::windows`], and you are free to inspect and mutate
23//! whatever state is in there.
24//!
25//! The [Ui] also supports the concept of "clustering", that is,
26//! when you push a [`Widget`] to a [`Buffer`], it gets "clustered" to
27//! that `Buffer`. This means a few things. For one, if you close that
28//! `Buffer`, all of its clustered `Widget`s will also close. If
29//! you swap two `Buffer`s, what you will actually swap is the
30//! [`Ui::Area`] that contains the `Buffer` and all of its clustered
31//! `Widget`.
32//!
33//! Additionally, on the terminal [Ui], clustering is used to
34//! determine where to draw borders between [`Ui::Area`]s, and it
35//! should be used like that in other [Ui] implementations as well.
36//!
37//! [`hook`]: crate::hook
38//! [`Buffer`]: crate::buffer::Buffer
39//! [`WidgetCreated`]: crate::hook::WidgetCreated
40//! [Ui]: traits::RawUi
41//! [`Ui::Area`]: traits::RawUi::Area
42//! [on `Handle`]: Handle::spawn_widget
43//! [on `Text`]: crate::text::SpawnTag
44//! [`context::windows`]: crate::context::windows
45use std::{
46    fmt::Debug,
47    sync::atomic::{AtomicU16, Ordering},
48};
49
50pub(crate) use self::widget::Node;
51pub use self::{
52    type_erased::{Area, PrintInfo, RwArea, Ui},
53    widget::Widget,
54    window::{Window, Windows},
55};
56use crate::{context::Handle, data::Pass};
57
58pub mod layout;
59pub mod traits;
60mod type_erased;
61mod widget;
62mod window;
63
64/// A coordinate on screen
65///
66/// An integer value should represent the size of a monospaced font
67/// cell. So, for example, in a terminal, x should represent the top
68/// left corner of a column, and y represents the top left corner of a
69/// row.
70///
71/// For non terminal GUIs, an integer should have the same
72/// representation, but fractional values should be permitted as well.
73#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
74pub struct Coord {
75    /// The x value of this coordinate. In a terminal cell, it would
76    /// be the top left corner.
77    pub x: f32,
78    /// The y value of this coordinate. In a terminal cell, it would
79    /// be the top left corner.
80    pub y: f32,
81}
82
83impl Coord {
84    /// Returns a new `Coord` from an `x` and a `y` values,
85    /// respectively
86    pub const fn new(x: f32, y: f32) -> Self {
87        Self { x, y }
88    }
89}
90
91impl std::ops::Add for Coord {
92    type Output = Self;
93
94    fn add(self, rhs: Self) -> Self::Output {
95        Self { x: self.x + rhs.x, y: self.y + rhs.y }
96    }
97}
98
99impl std::ops::Sub for Coord {
100    type Output = Self;
101
102    fn sub(self, rhs: Self) -> Self::Output {
103        Self { x: self.x - rhs.x, y: self.y - rhs.y }
104    }
105}
106
107impl std::ops::Mul<f32> for Coord {
108    type Output = Self;
109
110    fn mul(self, rhs: f32) -> Self::Output {
111        Self { x: rhs * self.x, y: rhs * self.y }
112    }
113}
114
115impl std::ops::Mul<Coord> for f32 {
116    type Output = Coord;
117
118    fn mul(self, rhs: Coord) -> Self::Output {
119        Coord { x: self * rhs.x, y: self * rhs.y }
120    }
121}
122
123impl std::ops::Div<f32> for Coord {
124    type Output = Self;
125
126    fn div(self, rhs: f32) -> Self::Output {
127        Self { x: self.x / rhs, y: self.y / rhs }
128    }
129}
130
131/// A dimension on screen, can either be horizontal or vertical
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum Axis {
134    /// The horizontal axis
135    Horizontal,
136    /// The vertical axis
137    Vertical,
138}
139
140impl Axis {
141    /// The [`Axis`] perpendicular to this one
142    pub fn perp(&self) -> Self {
143        match self {
144            Axis::Horizontal => Axis::Vertical,
145            Axis::Vertical => Axis::Horizontal,
146        }
147    }
148
149    /// Returns `true` if the axis is [`Horizontal`].
150    ///
151    /// [`Horizontal`]: Axis::Horizontal
152    #[must_use]
153    pub fn is_hor(&self) -> bool {
154        matches!(self, Self::Horizontal)
155    }
156
157    /// Returns `true` if the axis is [`Vertical`].
158    ///
159    /// [`Vertical`]: Axis::Vertical
160    #[must_use]
161    pub fn is_ver(&self) -> bool {
162        matches!(self, Self::Vertical)
163    }
164}
165
166impl From<PushSpecs> for Axis {
167    fn from(value: PushSpecs) -> Self {
168        match value.side {
169            Side::Above | Side::Below => Axis::Vertical,
170            _ => Axis::Horizontal,
171        }
172    }
173}
174
175/// Information on how a [`Widget`] should be pushed onto another
176///
177/// This information is composed of four parts:
178///
179/// * A side to push;
180/// * An optional width;
181/// * An optional height;
182/// * Wether to hide it by default;
183/// * wether to cluster the [`Widget`]
184///
185/// Constraints are demands that must be met by the widget's
186/// [`Area`], on a best effort basis.
187///
188/// So, for example, if the [`PushSpecs`] are:
189///
190/// ```rust
191/// # duat_core::doc_duat!(duat);
192/// use duat::prelude::*;
193/// let specs = ui::PushSpecs {
194///     side: ui::Side::Left,
195///     width: Some(3.0),
196///     height: None,
197///     hidden: false,
198///     cluster: true,
199/// };
200/// ```
201///
202/// Then the widget should be pushed to the left, with a width of 3,
203/// an unspecified height, _not_ hidden by default and clustered if
204/// possible. Note that you can shorten the definition above:
205///
206/// ```rust
207/// # duat_core::doc_duat!(duat);
208/// use duat::prelude::*;
209/// let specs = ui::PushSpecs {
210///     side: ui::Side::Left,
211///     width: Some(3.0),
212///     ..Default::default()
213/// };
214/// ```
215///
216/// Since the remaining values are the default.
217#[derive(Clone, Copy, Debug)]
218pub struct PushSpecs {
219    /// Which [`Side`] to push the [`Widget`] to
220    pub side: Side,
221    /// A width (in character cells) for this `Widget`
222    ///
223    /// Note that this may be ignored if it is not possible to
224    /// create an area big (or small) enough.
225    pub width: Option<f32>,
226    /// A height (in lines) for this `Widget`
227    ///
228    /// Note that this may be ignored if it is not possible to
229    /// create an area big (or small) enough.
230    pub height: Option<f32>,
231    /// Hide this `Widget` by default
232    ///
233    /// You can call [`Area::hide`] or [`Area::reveal`] to toggle
234    /// this property.
235    pub hidden: bool,
236    /// Cluster this `Widget` when pushing
237    ///
238    /// This makes it so, if the main `Widget` is moved or deleted,
239    /// then this one will follow. Useful for things like
240    /// [`LineNumbers`], since they should follow their [`Buffer`]
241    /// around.
242    ///
243    /// [`LineNumbers`]: https://docs.rs/duat/latest/duat/widgets/struct.LineNumbers.html
244    /// [`Buffer`]: crate::buffer::Buffer
245    pub cluster: bool,
246}
247
248impl Default for PushSpecs {
249    fn default() -> Self {
250        Self {
251            side: Side::Right,
252            width: None,
253            height: None,
254            hidden: false,
255            cluster: true,
256        }
257    }
258}
259
260impl PushSpecs {
261    /// The [`Axis`] where it will be pushed
262    ///
263    /// - left/right: [`Axis::Horizontal`]
264    /// - above/below: [`Axis::Vertical`]
265    pub const fn axis(&self) -> Axis {
266        match self.side {
267            Side::Above | Side::Below => Axis::Vertical,
268            Side::Right | Side::Left => Axis::Horizontal,
269        }
270    }
271
272    /// Wether this "comes earlier" on the screen
273    ///
274    /// This returns true if `self.side() == Side::Left || self.side()
275    /// == Side::Above`, since that is considered "earlier" on
276    /// screens.
277    pub const fn comes_earlier(&self) -> bool {
278        matches!(self.side, Side::Left | Side::Above)
279    }
280
281    /// The constraints on a given [`Axis`]
282    pub fn len_on(&self, axis: Axis) -> Option<f32> {
283        match axis {
284            Axis::Horizontal => self.width,
285            Axis::Vertical => self.height,
286        }
287    }
288}
289
290/// Information about how a [`Widget`] should be spawned dynamically
291///
292/// Dynamically spawned `Widget`s are those that are spawned on
293/// [`Handle`]s or [`Text`]. They are called dynamic because their
294/// spawning location can change automatically, either by the widget
295/// they are spawned on resizing, or the `Text` changing, etc.
296///
297/// This is in contrast with [`StaticSpawnSpecs`], which are not
298/// spawned on a `Handle` or `Text`, and are instead placed in a
299/// [`Coord`] on screen.
300///
301/// [`Handle`]: Handle::push_outer_widget
302/// [`Text`]: crate::text::SpawnTag
303#[derive(Default, Debug, Clone, Copy)]
304pub struct DynSpawnSpecs {
305    /// The orientation to place this [`Widget`] in
306    ///
307    /// May receive some reworks in the future.
308    pub orientation: Orientation,
309    /// A width (in character cells) for this `Widget`
310    ///
311    /// Note that this may be ignored if it is not possible to
312    /// create an area big (or small) enough.
313    pub width: Option<f32>,
314    /// A height (in lines) for this `Widget`
315    ///
316    /// Note that this may be ignored if it is not possible to
317    /// create an area big (or small) enough.
318    pub height: Option<f32>,
319    /// Hide this `Widget` by default
320    ///
321    /// You can call [`Area::hide`] or [`Area::reveal`] to toggle
322    /// this property.
323    pub hidden: bool,
324}
325
326impl DynSpawnSpecs {
327    /// The constraints on a given [`Axis`]
328    pub const fn len_on(&self, axis: Axis) -> Option<f32> {
329        match axis {
330            Axis::Horizontal => self.width,
331            Axis::Vertical => self.height,
332        }
333    }
334}
335
336/// Information about how a [`Widget`] should be spawned statically
337///
338/// Statically spawned `Widget`s are those that are placed in a
339/// [`Coord`] on screen via [`Window::spawn`] and don't change
340/// location.
341///
342/// This is in contrast with [`DynSpawnSpecs`], which are allowed to
343/// be moved automatically, due to being spawned on [`Handle`]s or
344/// [`Text`], which are allowed to change.
345///
346/// [`Text`]: crate::text::Text
347#[derive(Debug, Clone, Copy, PartialEq)]
348pub struct StaticSpawnSpecs {
349    /// The top left corner where the [`Widget`] will be spawned
350    pub top_left: Coord,
351    /// The size of the [`Widget`], represented by a [`Coord`]
352    pub size: Coord,
353    /// Hide this [`Widget`] by default
354    ///
355    /// You can call [`Area::hide`] or [`Area::reveal`] to toggle
356    /// this property.
357    pub hidden: bool,
358    /// Reposition the [`Widget`] in case the [`Window`] resizes
359    ///
360    /// Normally, this is [`None`], which means no repositioning is
361    /// done unless the `Widget` would be clipped by the `Window`, in
362    /// which case it will be "dragged up and to the left" until it no
363    /// longer clips the window (or not, depending on the Ui you're
364    /// using :P)
365    ///
366    /// However, if this is [`Some`], then the `Widget` will be
367    /// repositioned. If it is `Some(false)`, then it will keep an
368    /// absolute distance from the bottom right corner. This is useful
369    /// for, for example, notifications which you'd want to be located
370    /// "near the bottom right".
371    ///
372    /// If it is `Some(true)`, then the repositioning will be
373    /// _fractional_. What this means is that, if it was placed
374    /// centrally, it will remain centered.
375    pub fractional_repositioning: Option<bool>,
376}
377
378impl StaticSpawnSpecs {
379    /// The constraints on a given [`Axis`]
380    pub const fn len_on(&self, axis: Axis) -> f32 {
381        match axis {
382            Axis::Horizontal => self.size.x,
383            Axis::Vertical => self.size.y,
384        }
385    }
386}
387
388impl Default for StaticSpawnSpecs {
389    fn default() -> Self {
390        Self {
391            top_left: Coord { x: 0.0, y: 0.0 },
392            size: Coord { x: 50.0, y: 5.0 },
393            hidden: false,
394            fractional_repositioning: None,
395        }
396    }
397}
398
399/// A direction, where a [`Widget`] will be placed in relation to
400/// another.
401#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
402pub enum Side {
403    /// Put the [`Widget`] above another
404    Above,
405    /// Put the [`Widget`] on the right
406    #[default]
407    Right,
408    /// Put the [`Widget`] on the left
409    Below,
410    /// Put the [`Widget`] below another
411    Left,
412}
413
414impl Side {
415    /// Which [`Axis`] this [`Side`] belongs to
416    pub fn axis(&self) -> Axis {
417        match self {
418            Side::Above | Side::Below => Axis::Vertical,
419            Side::Right | Side::Left => Axis::Horizontal,
420        }
421    }
422}
423
424/// Where to place a spawned [`Widget`]
425///
426/// The `Orientation` has 3 components of positioning, which follow
427/// priorities in order to relocate the `Widget` in case there isn't
428/// enough space. Respectively, they are the following:
429///
430/// - An axis to align the `Widget`.
431/// - How to align said `Widget` on said axis.
432/// - Which side of the parent should be prioritized.
433///
434/// For example, [`Orientation::HorTopLeft`] means: Spawn this
435/// `Widget` horizontally, trying to align its top edge with the top
436/// edge of the parent, prioritizing the left side. Visually speaking,
437/// it will try to spawn a `Widget` like this:
438///
439/// ```text
440/// ╭─────────┬────────╮
441/// │         │ Parent │
442/// │ Spawned ├────────╯
443/// │         │
444/// ╰─────────╯
445/// ```
446///
447/// Notice that their tops are aligned, the edges connect on the
448/// horizontal axis, and it is on the left side. However, if there is
449/// not enough space, (e.g. the parent is very close to the bottom
450/// left edge of the screen) it might try to spawn it like this:
451///
452/// ```text
453/// ╭─────────╮                                 ╭─────────╮
454/// │         ├────────╮                        │         │
455/// │ Spawned │ Parent │, or even like ╭────────┤ Spawned │
456/// │         ├────────╯               │ Parent │         │
457/// ╰─────────╯                        ╰────────┴─────────╯
458/// ```
459///
460/// This prioritization gives more flexibility to the spawning of
461/// `Widget`s, which usually follows patterns of where to spawn and
462/// how to place things, mostly to prevent obscuring information. The
463/// most notable example of this are completion lists. For obvious
464/// reasons, those should only be placed above or below (`Ver`),
465/// alignment should try to be on the left edge (`VerLeft`), and
466/// ideally below the cursor ([`Orientation::VerLeftBelow`]).
467/// Likewise, these completion lists are sometimes accompanied by
468/// description panels, which should ideally follow a
469/// [`HorCenterRight`] or [`HorBottomRight`] orientation.
470///
471/// [`HorCenterRight`]: Orientation::HorCenterRight
472/// [`HorBottomRight`]: Orientation::HorBottomRight
473#[derive(Default, Debug, Clone, Copy)]
474pub enum Orientation {
475    /// Place the [`Widget`] vertically, prioritizing the left edge
476    /// above
477    VerLeftAbove,
478    /// Place the [`Widget`] vertically, prioritizing centering above
479    VerCenterAbove,
480    /// Place the [`Widget`] vertically, prioritizing the right edge
481    /// above
482    VerRightAbove,
483    /// Place the [`Widget`] vertically, prioritizing the left edge
484    /// below
485    #[default]
486    VerLeftBelow,
487    /// Place the [`Widget`] vertically, prioritizing centering below
488    VerCenterBelow,
489    /// Place the [`Widget`] vertically, prioritizing the right edge
490    /// below
491    VerRightBelow,
492    /// Place the [`Widget`] horizontally, prioritizing the top edge
493    /// on the left
494    HorTopLeft,
495    /// Place the [`Widget`] horizontally, prioritizing centering
496    /// on the left
497    HorCenterLeft,
498    /// Place the [`Widget`] horizontally, prioritizing the right edge
499    /// on the left
500    HorBottomLeft,
501    /// Place the [`Widget`] horizontally, prioritizing the top edge
502    /// on the right
503    HorTopRight,
504    /// Place the [`Widget`] horizontally, prioritizing centering
505    /// on the right
506    HorCenterRight,
507    /// Place the [`Widget`] horizontally, prioritizing the bottom
508    /// edge on the right
509    HorBottomRight,
510}
511
512impl Orientation {
513    /// The [`Axis`] to which this `Orientation` pushes
514    pub fn axis(&self) -> Axis {
515        match self {
516            Orientation::VerLeftAbove
517            | Orientation::VerCenterAbove
518            | Orientation::VerRightAbove
519            | Orientation::VerLeftBelow
520            | Orientation::VerCenterBelow
521            | Orientation::VerRightBelow => Axis::Vertical,
522            Orientation::HorTopLeft
523            | Orientation::HorCenterLeft
524            | Orientation::HorBottomLeft
525            | Orientation::HorTopRight
526            | Orientation::HorCenterRight
527            | Orientation::HorBottomRight => Axis::Horizontal,
528        }
529    }
530
531    /// Wether this should prefer being pushed before (left or above)
532    pub fn prefers_before(&self) -> bool {
533        match self {
534            Orientation::VerLeftAbove
535            | Orientation::VerCenterAbove
536            | Orientation::VerRightAbove
537            | Orientation::HorTopLeft
538            | Orientation::HorCenterLeft
539            | Orientation::HorBottomLeft => true,
540            Orientation::VerLeftBelow
541            | Orientation::VerCenterBelow
542            | Orientation::VerRightBelow
543            | Orientation::HorTopRight
544            | Orientation::HorCenterRight
545            | Orientation::HorBottomRight => false,
546        }
547    }
548}
549
550/// A struct representing a "visual position" on the screen
551///
552/// This position differs from a [`VPoint`] in the sense that it
553/// represents three properties of a printed character:
554///
555/// - The x position in which it was printed;
556/// - The amount of horizontal space it occupies;
557/// - Wether this character is the first on the line (i.e. it wraps)
558///
559/// [`VPoint`]: crate::mode::VPoint
560#[derive(Debug, Clone, Copy)]
561pub struct Caret {
562    /// The horizontal position in which a character was printed
563    pub x: u32,
564    /// The horizontal space it occupied
565    pub len: u32,
566    /// Wether it is the first character in the line
567    pub wrap: bool,
568}
569
570impl Caret {
571    /// Returns a new [`Caret`]
572    #[inline(always)]
573    pub fn new(x: u32, len: u32, wrap: bool) -> Self {
574        Self { x, len, wrap }
575    }
576}
577
578/// A target for pushing [`Widget`]s to
579///
580/// This can either be a [`Handle`], which will push around a `Widget`
581/// or a [`Window`], which will push around the window.
582///
583/// This trait is useful if you wish to let your [`Widget`] both be
584/// pushed around other `Widget`s and also around the window with the
585/// [`Window`]. One example of this is the [`StatusLine`] widget,
586/// which behaves differently depending on if it was pushed to a
587/// [`Handle<Buffer>`].
588///
589/// [`StatusLine`]: https://docs.rs/duat/duat/latest/widgets/struct.StatusLine.html
590pub trait PushTarget {
591    /// Pushes a [`Widget`] around `self`
592    ///
593    /// If `self` is a [`Handle`], this will push around the
594    /// `Handle`'s own [`Area`]. If this is a [`Window`],
595    /// this will push around the master `Area` of the central
596    /// region of buffers.
597    ///
598    /// This `Widget` will be placed internally, i.e., around the
599    /// [`Area`] of `self`. This is in contrast to
600    /// [`Handle::push_outer_widget`], which will push around the
601    /// "cluster master" of `self`.
602    ///
603    /// A cluster master is the collection of every `Widget` that was
604    /// pushed around a central one with [`PushSpecs::cluster`] set to
605    /// `true`.
606    ///
607    /// Both of these functions behave identically in the situation
608    /// where no other [`Widget`]s were pushed around `self`.
609    ///
610    /// However, if, for example, a `Widget` was previously pushed
611    /// below `self`, when pushing to the left, the following would
612    /// happen:
613    ///
614    /// ```text
615    /// ╭────────────────╮    ╭─────┬──────────╮
616    /// │                │    │     │          │
617    /// │      self      │    │ new │   self   │
618    /// │                │ -> │     │          │
619    /// ├────────────────┤    ├─────┴──────────┤
620    /// │      old       │    │      old       │
621    /// ╰────────────────╯    ╰────────────────╯
622    /// ```
623    ///
624    /// While in [`Handle::push_outer_widget`], this happens instead:
625    ///
626    /// ```text
627    /// ╭────────────────╮    ╭─────┬──────────╮
628    /// │                │    │     │          │
629    /// │      self      │    │     │   self   │
630    /// │                │ -> │ new │          │
631    /// ├────────────────┤    │     ├──────────┤
632    /// │      old       │    │     │   old    │
633    /// ╰────────────────╯    ╰─────┴──────────╯
634    /// ```
635    ///
636    /// Note that `new` was pushed _around_ other clustered widgets in
637    /// the second case, not just around `self`.
638    fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW>;
639
640    /// Pushes a [`Widget`] around the "master region" of `self`
641    ///
642    /// If `self` is a [`Handle`], this will push its "cluster
643    /// master". If this is a [`Window`], this will push the
644    /// `Widget` to the edges of the window.
645    ///
646    /// A cluster master is the collection of every `Widget` that was
647    /// pushed around a central one with [`PushSpecs::cluster`] set to
648    /// `true`.
649    ///
650    /// This [`Widget`] will be placed externally, i.e., around every
651    /// other `Widget` that was pushed around `self`. This is in
652    /// contrast to [`Handle::push_inner_widget`], which will push
653    /// only around `self`.
654    ///
655    /// Both of these functions behave identically in the situation
656    /// where no other [`Widget`]s were pushed around `self`.
657    ///
658    /// However, if, for example, a `Widget` was previously pushed
659    /// to the left of `self`, when pushing to the left again, the
660    /// following would happen:
661    ///
662    /// ```text
663    /// ╭──────┬──────────╮    ╭─────┬─────┬──────╮
664    /// │      │          │    │     │     │      │
665    /// │      │          │    │     │     │      │
666    /// │  old │   self   │ -> │ new │ old │ self │
667    /// │      │          │    │     │     │      │
668    /// │      │          │    │     │     │      │
669    /// ╰──────┴──────────╯    ╰─────┴─────┴──────╯
670    /// ```
671    ///
672    /// While in [`Handle::push_inner_widget`], this happens instead:
673    ///
674    /// ```text
675    /// ╭──────┬──────────╮    ╭─────┬─────┬──────╮
676    /// │      │          │    │     │     │      │
677    /// │      │          │    │     │     │      │
678    /// │  old │   self   │ -> │ old │ new │ self │
679    /// │      │          │    │     │     │      │
680    /// │      │          │    │     │     │      │
681    /// ╰──────┴──────────╯    ╰─────┴─────┴──────╯
682    /// ```
683    ///
684    /// Note that `new` was pushed _around_ other clustered widgets in
685    /// the first case, not just around `self`.
686    fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW>;
687
688    /// Tries to downcast to a [`Handle`] of some `W`
689    fn try_downcast<W: Widget>(&self) -> Option<Handle<W>>;
690}
691
692impl<W: Widget + ?Sized> PushTarget for Handle<W> {
693    #[doc(hidden)]
694    fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
695        self.push_inner_widget(pa, widget, specs)
696    }
697
698    #[doc(hidden)]
699    fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
700        self.push_outer_widget(pa, widget, specs)
701    }
702
703    fn try_downcast<DW: Widget>(&self) -> Option<Handle<DW>> {
704        self.try_downcast()
705    }
706}
707
708impl PushTarget for Window {
709    #[doc(hidden)]
710    fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
711        Window::push_inner(self, pa, widget, specs)
712    }
713
714    #[doc(hidden)]
715    fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
716        Window::push_outer(self, pa, widget, specs)
717    }
718
719    fn try_downcast<W: Widget>(&self) -> Option<Handle<W>> {
720        None
721    }
722}
723
724/// The id of a spawned [`Widget`]
725///
726/// [`Widget`]: crate::ui::Widget
727#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
728pub struct SpawnId(u16);
729
730impl SpawnId {
731    /// Creates a new [`SpawnId`]
732    #[allow(clippy::new_without_default)]
733    pub(crate) fn new() -> Self {
734        static SPAWN_COUNT: AtomicU16 = AtomicU16::new(0);
735        Self(SPAWN_COUNT.fetch_add(1, Ordering::Relaxed))
736    }
737}
738
739impl std::fmt::Debug for SpawnId {
740    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
741        write!(f, "SpawnId({})", self.0)
742    }
743}