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//! [`WidgetOpened`]: crate::hook::WidgetOpened
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, config_address_space_ui_setup, ui_is},
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 /// Put this `Widget` _inside_, rather than outside
325 ///
326 /// A consequence of this is that no repositioning will ever be
327 /// done, since there is no spatial difference between placing the
328 /// [`Widget`] above the bottom or below the top.
329 ///
330 /// This is `false` by default.
331 pub inside: bool,
332}
333
334impl DynSpawnSpecs {
335 /// The constraints on a given [`Axis`]
336 pub const fn len_on(&self, axis: Axis) -> Option<f32> {
337 match axis {
338 Axis::Horizontal => self.width,
339 Axis::Vertical => self.height,
340 }
341 }
342}
343
344/// Information about how a [`Widget`] should be spawned statically
345///
346/// Statically spawned `Widget`s are those that are placed in a
347/// [`Coord`] on screen via [`Window::spawn`] and don't change
348/// location.
349///
350/// This is in contrast with [`DynSpawnSpecs`], which are allowed to
351/// be moved automatically, due to being spawned on [`Handle`]s or
352/// [`Text`], which are allowed to change.
353///
354/// [`Text`]: crate::text::Text
355#[derive(Debug, Clone, Copy, PartialEq)]
356pub struct StaticSpawnSpecs {
357 /// The top left corner where the [`Widget`] will be spawned
358 pub top_left: Coord,
359 /// The size of the [`Widget`], represented by a [`Coord`]
360 pub size: Coord,
361 /// Hide this [`Widget`] by default
362 ///
363 /// You can call [`Area::hide`] or [`Area::reveal`] to toggle
364 /// this property.
365 pub hidden: bool,
366 /// Reposition the [`Widget`] in case the [`Window`] resizes
367 ///
368 /// Normally, this is [`None`], which means no repositioning is
369 /// done unless the `Widget` would be clipped by the `Window`, in
370 /// which case it will be "dragged up and to the left" until it no
371 /// longer clips the window (or not, depending on the Ui you're
372 /// using :P)
373 ///
374 /// However, if this is [`Some`], then the `Widget` will be
375 /// repositioned. If it is `Some(false)`, then it will keep an
376 /// absolute distance from the bottom right corner. This is useful
377 /// for, for example, notifications which you'd want to be located
378 /// "near the bottom right".
379 ///
380 /// If it is `Some(true)`, then the repositioning will be
381 /// _fractional_. What this means is that, if it was placed
382 /// centrally, it will remain centered.
383 pub fractional_repositioning: Option<bool>,
384}
385
386impl StaticSpawnSpecs {
387 /// The constraints on a given [`Axis`]
388 pub const fn len_on(&self, axis: Axis) -> f32 {
389 match axis {
390 Axis::Horizontal => self.size.x,
391 Axis::Vertical => self.size.y,
392 }
393 }
394}
395
396impl Default for StaticSpawnSpecs {
397 fn default() -> Self {
398 Self {
399 top_left: Coord { x: 0.0, y: 0.0 },
400 size: Coord { x: 50.0, y: 5.0 },
401 hidden: false,
402 fractional_repositioning: None,
403 }
404 }
405}
406
407/// A direction, where a [`Widget`] will be placed in relation to
408/// another.
409#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
410pub enum Side {
411 /// Put the [`Widget`] above another
412 Above,
413 /// Put the [`Widget`] on the right
414 #[default]
415 Right,
416 /// Put the [`Widget`] on the left
417 Below,
418 /// Put the [`Widget`] below another
419 Left,
420}
421
422impl Side {
423 /// Which [`Axis`] this [`Side`] belongs to
424 pub fn axis(&self) -> Axis {
425 match self {
426 Side::Above | Side::Below => Axis::Vertical,
427 Side::Right | Side::Left => Axis::Horizontal,
428 }
429 }
430}
431
432/// Where to place a spawned [`Widget`]
433///
434/// The `Orientation` has 3 components of positioning, which follow
435/// priorities in order to relocate the `Widget` in case there isn't
436/// enough space. Respectively, they are the following:
437///
438/// - An axis to align the `Widget`.
439/// - How to align said `Widget` on said axis.
440/// - Which side of the parent should be prioritized.
441///
442/// For example, [`Orientation::HorTopLeft`] means: Spawn this
443/// `Widget` horizontally, trying to align its top edge with the top
444/// edge of the parent, prioritizing the left side. Visually speaking,
445/// it will try to spawn a `Widget` like this:
446///
447/// ```text
448/// ╭─────────┬────────╮
449/// │ │ Parent │
450/// │ Spawned ├────────╯
451/// │ │
452/// ╰─────────╯
453/// ```
454///
455/// Notice that their tops are aligned, the edges connect on the
456/// horizontal axis, and it is on the left side. However, if there is
457/// not enough space, (e.g. the parent is very close to the bottom
458/// left edge of the screen) it might try to spawn it like this:
459///
460/// ```text
461/// ╭─────────╮ ╭─────────╮
462/// │ ├────────╮ │ │
463/// │ Spawned │ Parent │, or even like ╭────────┤ Spawned │
464/// │ ├────────╯ │ Parent │ │
465/// ╰─────────╯ ╰────────┴─────────╯
466/// ```
467///
468/// This prioritization gives more flexibility to the spawning of
469/// `Widget`s, which usually follows patterns of where to spawn and
470/// how to place things, mostly to prevent obscuring information. The
471/// most notable example of this are completion lists. For obvious
472/// reasons, those should only be placed above or below (`Ver`),
473/// alignment should try to be on the left edge (`VerLeft`), and
474/// ideally below the cursor ([`Orientation::VerLeftBelow`]).
475/// Likewise, these completion lists are sometimes accompanied by
476/// description panels, which should ideally follow a
477/// [`HorCenterRight`] or [`HorBottomRight`] orientation.
478///
479/// [`HorCenterRight`]: Orientation::HorCenterRight
480/// [`HorBottomRight`]: Orientation::HorBottomRight
481#[derive(Default, Debug, Clone, Copy)]
482pub enum Orientation {
483 /// Place the [`Widget`] vertically, prioritizing the left edge
484 /// above
485 VerLeftAbove,
486 /// Place the [`Widget`] vertically, prioritizing centering above
487 VerCenterAbove,
488 /// Place the [`Widget`] vertically, prioritizing the right edge
489 /// above
490 VerRightAbove,
491 /// Place the [`Widget`] vertically, prioritizing the left edge
492 /// below
493 #[default]
494 VerLeftBelow,
495 /// Place the [`Widget`] vertically, prioritizing centering below
496 VerCenterBelow,
497 /// Place the [`Widget`] vertically, prioritizing the right edge
498 /// below
499 VerRightBelow,
500 /// Place the [`Widget`] horizontally, prioritizing the top edge
501 /// on the left
502 HorTopLeft,
503 /// Place the [`Widget`] horizontally, prioritizing centering
504 /// on the left
505 HorCenterLeft,
506 /// Place the [`Widget`] horizontally, prioritizing the right edge
507 /// on the left
508 HorBottomLeft,
509 /// Place the [`Widget`] horizontally, prioritizing the top edge
510 /// on the right
511 HorTopRight,
512 /// Place the [`Widget`] horizontally, prioritizing centering
513 /// on the right
514 HorCenterRight,
515 /// Place the [`Widget`] horizontally, prioritizing the bottom
516 /// edge on the right
517 HorBottomRight,
518}
519
520impl Orientation {
521 /// The [`Axis`] to which this `Orientation` pushes
522 pub fn axis(&self) -> Axis {
523 match self {
524 Orientation::VerLeftAbove
525 | Orientation::VerCenterAbove
526 | Orientation::VerRightAbove
527 | Orientation::VerLeftBelow
528 | Orientation::VerCenterBelow
529 | Orientation::VerRightBelow => Axis::Vertical,
530 Orientation::HorTopLeft
531 | Orientation::HorCenterLeft
532 | Orientation::HorBottomLeft
533 | Orientation::HorTopRight
534 | Orientation::HorCenterRight
535 | Orientation::HorBottomRight => Axis::Horizontal,
536 }
537 }
538
539 /// Wether this should prefer being pushed before (left or above)
540 pub fn prefers_before(&self) -> bool {
541 match self {
542 Orientation::VerLeftAbove
543 | Orientation::VerCenterAbove
544 | Orientation::VerRightAbove
545 | Orientation::HorTopLeft
546 | Orientation::HorCenterLeft
547 | Orientation::HorBottomLeft => true,
548 Orientation::VerLeftBelow
549 | Orientation::VerCenterBelow
550 | Orientation::VerRightBelow
551 | Orientation::HorTopRight
552 | Orientation::HorCenterRight
553 | Orientation::HorBottomRight => false,
554 }
555 }
556}
557
558/// A struct representing a "visual position" on the screen
559///
560/// This position differs from a [`VPoint`] in the sense that it
561/// represents three properties of a printed character:
562///
563/// - The x position in which it was printed;
564/// - The amount of horizontal space it occupies;
565/// - Wether this character is the first on the line (i.e. it wraps)
566///
567/// [`VPoint`]: crate::mode::VPoint
568#[derive(Debug, Clone, Copy)]
569pub struct Caret {
570 /// The horizontal position in which a character was printed
571 pub x: u32,
572 /// The horizontal space it occupied
573 pub len: u32,
574 /// Wether it is the first character in the line
575 pub wrap: bool,
576}
577
578impl Caret {
579 /// Returns a new [`Caret`]
580 #[inline(always)]
581 pub fn new(x: u32, len: u32, wrap: bool) -> Self {
582 Self { x, len, wrap }
583 }
584}
585
586/// A target for pushing [`Widget`]s to
587///
588/// This can either be a [`Handle`], which will push around a `Widget`
589/// or a [`Window`], which will push around the window.
590///
591/// This trait is useful if you wish to let your [`Widget`] both be
592/// pushed around other `Widget`s and also around the window with the
593/// [`Window`]. One example of this is the [`StatusLine`] widget,
594/// which behaves differently depending on if it was pushed to a
595/// [`Handle<Buffer>`].
596///
597/// [`StatusLine`]: https://docs.rs/duat/duat/latest/widgets/struct.StatusLine.html
598pub trait PushTarget {
599 /// Pushes a [`Widget`] around `self`
600 ///
601 /// If `self` is a [`Handle`], this will push around the
602 /// `Handle`'s own [`Area`]. If this is a [`Window`],
603 /// this will push around the master `Area` of the central
604 /// region of buffers.
605 ///
606 /// This `Widget` will be placed internally, i.e., around the
607 /// [`Area`] of `self`. This is in contrast to
608 /// [`Handle::push_outer_widget`], which will push around the
609 /// "cluster master" of `self`.
610 ///
611 /// A cluster master is the collection of every `Widget` that was
612 /// pushed around a central one with [`PushSpecs::cluster`] set to
613 /// `true`.
614 ///
615 /// Both of these functions behave identically in the situation
616 /// where no other [`Widget`]s were pushed around `self`.
617 ///
618 /// However, if, for example, a `Widget` was previously pushed
619 /// below `self`, when pushing to the left, the following would
620 /// happen:
621 ///
622 /// ```text
623 /// ╭────────────────╮ ╭─────┬──────────╮
624 /// │ │ │ │ │
625 /// │ self │ │ new │ self │
626 /// │ │ -> │ │ │
627 /// ├────────────────┤ ├─────┴──────────┤
628 /// │ old │ │ old │
629 /// ╰────────────────╯ ╰────────────────╯
630 /// ```
631 ///
632 /// While in [`Handle::push_outer_widget`], this happens instead:
633 ///
634 /// ```text
635 /// ╭────────────────╮ ╭─────┬──────────╮
636 /// │ │ │ │ │
637 /// │ self │ │ │ self │
638 /// │ │ -> │ new │ │
639 /// ├────────────────┤ │ ├──────────┤
640 /// │ old │ │ │ old │
641 /// ╰────────────────╯ ╰─────┴──────────╯
642 /// ```
643 ///
644 /// Note that `new` was pushed _around_ other clustered widgets in
645 /// the second case, not just around `self`.
646 fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW>;
647
648 /// Pushes a [`Widget`] around the "master region" of `self`
649 ///
650 /// If `self` is a [`Handle`], this will push its "cluster
651 /// master". If this is a [`Window`], this will push the
652 /// `Widget` to the edges of the window.
653 ///
654 /// A cluster master is the collection of every `Widget` that was
655 /// pushed around a central one with [`PushSpecs::cluster`] set to
656 /// `true`.
657 ///
658 /// This [`Widget`] will be placed externally, i.e., around every
659 /// other `Widget` that was pushed around `self`. This is in
660 /// contrast to [`Handle::push_inner_widget`], which will push
661 /// only around `self`.
662 ///
663 /// Both of these functions behave identically in the situation
664 /// where no other [`Widget`]s were pushed around `self`.
665 ///
666 /// However, if, for example, a `Widget` was previously pushed
667 /// to the left of `self`, when pushing to the left again, the
668 /// following would happen:
669 ///
670 /// ```text
671 /// ╭──────┬──────────╮ ╭─────┬─────┬──────╮
672 /// │ │ │ │ │ │ │
673 /// │ │ │ │ │ │ │
674 /// │ old │ self │ -> │ new │ old │ self │
675 /// │ │ │ │ │ │ │
676 /// │ │ │ │ │ │ │
677 /// ╰──────┴──────────╯ ╰─────┴─────┴──────╯
678 /// ```
679 ///
680 /// While in [`Handle::push_inner_widget`], this happens instead:
681 ///
682 /// ```text
683 /// ╭──────┬──────────╮ ╭─────┬─────┬──────╮
684 /// │ │ │ │ │ │ │
685 /// │ │ │ │ │ │ │
686 /// │ old │ self │ -> │ old │ new │ self │
687 /// │ │ │ │ │ │ │
688 /// │ │ │ │ │ │ │
689 /// ╰──────┴──────────╯ ╰─────┴─────┴──────╯
690 /// ```
691 ///
692 /// Note that `new` was pushed _around_ other clustered widgets in
693 /// the first case, not just around `self`.
694 fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW>;
695
696 /// Tries to downcast to a [`Handle`] of some `W`
697 fn try_downcast<W: Widget>(&self) -> Option<Handle<W>>;
698}
699
700impl<W: Widget + ?Sized> PushTarget for Handle<W> {
701 #[doc(hidden)]
702 fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
703 self.push_inner_widget(pa, widget, specs)
704 }
705
706 #[doc(hidden)]
707 fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
708 self.push_outer_widget(pa, widget, specs)
709 }
710
711 fn try_downcast<DW: Widget>(&self) -> Option<Handle<DW>> {
712 self.try_downcast()
713 }
714}
715
716impl PushTarget for Window {
717 #[doc(hidden)]
718 fn push_inner<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
719 Window::push_inner(self, pa, widget, specs)
720 }
721
722 #[doc(hidden)]
723 fn push_outer<PW: Widget>(&self, pa: &mut Pass, widget: PW, specs: PushSpecs) -> Handle<PW> {
724 Window::push_outer(self, pa, widget, specs)
725 }
726
727 fn try_downcast<W: Widget>(&self) -> Option<Handle<W>> {
728 None
729 }
730}
731
732/// The id of a spawned [`Widget`]
733///
734/// [`Widget`]: crate::ui::Widget
735#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
736pub struct SpawnId(u16);
737
738impl SpawnId {
739 /// Creates a new [`SpawnId`]
740 #[allow(clippy::new_without_default)]
741 pub(crate) fn new() -> Self {
742 static SPAWN_COUNT: AtomicU16 = AtomicU16::new(0);
743 Self(SPAWN_COUNT.fetch_add(1, Ordering::Relaxed))
744 }
745}
746
747impl std::fmt::Debug for SpawnId {
748 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
749 write!(f, "SpawnId({})", self.0)
750 }
751}
752
753/// Information about a line that was printed
754#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
755pub struct PrintedLine {
756 /// The line's number
757 pub number: usize,
758 /// Wether the line is wrapped
759 pub is_wrapped: bool,
760}