duat_core/ui/
widget.rs

1//! APIs for the construction of widgets, and a few common ones.
2//!
3//! This module has the [`Widget`] trait, which is a region on the
4//! window containing a [`Text`], and may be modified by user mode
5//! (but not necessarily).
6//!
7//! With the exception of the [`File`], these widgets will show up in
8//! one of three contexts:
9//!
10//! - Being pushed to a [`File`] via the hook [`OnFileOpen`];
11//! - Being pushed to the outer edges via [`OnWindowOpen`];
12//! - Being pushed to popup widgets via `OnPopupOpen` (TODO);
13//!
14//! These widgets can be pushed to all 4 sides of other widgets,
15//! through the use of [`PushSpecs`]. When pushing widgets, you can
16//! also include [`Constraint`] in order to get a specific size on the
17//! screen for the widget.
18//!
19//! ```rust
20//! # use duat_core::ui::PushSpecs;
21//! let specs = PushSpecs::left().with_hor_min(10.0).with_ver_len(2.0);
22//! ```
23//!
24//! When pushing a widget with these `specs` to another widget, Duat
25//! will put it on the left, and _try_ to give it a minimum width of
26//! `10.0`, and a height of `2.0`.
27//!
28//! The module also provides 4 native widgets, [`File`] and
29//! [`PromptLine`], which can receive user mode, and [`StatusLine`]
30//! and [`LineNumbers`] which are not supposed to.
31//!
32//! These 4 widgets are supposed to be universal, not needing a
33//! specific [`Ui`] implementation to work. In contrast, you can
34//! create widgets for specific [`Ui`]s. As an example, the
35//! [`duat-term`] crate, which is a terminal [`Ui`] implementation for
36//! Duat, defines the [`VertRule`] widget, which is a separator that
37//! only makes sense in the context of a terminal.
38//!
39//! This module also describes a [`WidgetCfg`], which is used in
40//! widget construction.
41//!
42//! [`PromptLine`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.PromptLine.html
43//! [`LineNumbers`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.LineNumbers.html
44//! [`StatusLine`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.StatusLine.html
45//! [`duat-term`]: https://docs.rs/duat-term/latest/duat_term/
46//! [`VertRule`]: https://docs.rs/duat-term/latest/duat_term/struct.VertRule.html
47//! [`OnFileOpen`]: crate::hook::OnFileOpen
48//! [`OnWindowOpen`]: crate::hook::OnWindowOpen
49//! [`Constraint`]: crate::ui::Constraint
50use std::{any::TypeId, cell::Cell, rc::Rc};
51
52use crate::{
53    cfg::PrintCfg,
54    context::{FileHandle, FileParts, Handle},
55    data::{Pass, RwData},
56    file::File,
57    form::{self, Painter},
58    hook::{self, FocusedOn, UnfocusedFrom},
59    text::Text,
60    ui::{PushSpecs, RawArea, Ui},
61};
62
63/// An area where [`Text`] will be printed to the screen
64///
65/// Most widgets are supposed to be passive widgets, that simply show
66/// information about the current state of Duat. In order to
67/// show that information, widgets make use of [`Text`], which can
68/// show stylized text, buttons, and all sorts of other stuff. (For
69/// widgets that react to input, see the documentation for[`Mode`]).
70///
71/// For a demonstration on how to create a widget, I will create a
72/// widget that shows the uptime, in seconds, for Duat.
73///
74/// ```rust
75/// # use duat_core::{data::PeriodicChecker, text::Text};
76/// struct UpTime(Text, PeriodicChecker);
77/// ```
78///
79/// In order to be a proper widget, it must have a [`Text`] to
80/// display. The [`PeriodicChecker`] will be explained later. Next, I
81/// implement [`Widget`] on `UpTime`:
82///
83/// ```rust
84/// # use std::{marker::PhantomData, sync::OnceLock, time::{Duration, Instant}};
85/// # use duat_core::{data::PeriodicChecker, prelude::*};
86/// # struct UpTime(Text, PeriodicChecker);
87/// # struct UpTimeCfg<U>(PhantomData<U>);
88/// # impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
89/// #     type Widget = UpTime;
90/// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) { todo!() }
91/// # }
92/// impl<U: Ui> Widget<U> for UpTime {
93///     type Cfg = UpTimeCfg<U>;
94///     fn cfg() -> Self::Cfg {
95///         UpTimeCfg(PhantomData)
96///     }
97///     // more methods remain below
98/// #     fn text(&self) -> &Text { &self.0 }
99/// #     fn text_mut(&mut self) -> &mut Text { &mut self.0 }
100/// #     fn once() -> Result<(), Text> { Ok(()) }
101/// #     fn update(_: &mut Pass, handle: Handle<Self, U>) {}
102/// #     fn needs_update(&self) -> bool { todo!(); }
103/// }
104/// ```
105///
106/// Notice the `UpTimeCfg` defined as the `Widget::Cfg` for `UpTime`.
107/// [`WidgetCfg`]s  exist to let users push [`Widget`]s to [`File`]s
108/// and the window through the [`OnFileOpen`] and [`OnWindowOpen`]
109/// [hooks]. It lets users configure widgets through methods defined
110/// by the widget author.
111///
112/// ```rust
113/// # use std::{marker::PhantomData, sync::OnceLock, time::{Duration, Instant}};
114/// # use duat_core::{data::PeriodicChecker, prelude::*};
115/// # struct UpTime(Text, PeriodicChecker);
116/// struct UpTimeCfg<U>(PhantomData<U>);
117///
118/// impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
119///     type Widget = UpTime;
120///
121///     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) {
122///         let checker = PeriodicChecker::new(std::time::Duration::from_secs(1));
123///         let widget = UpTime(Text::new(), checker);
124///         let specs = PushSpecs::below().with_ver_len(1.0);
125///
126///         (widget, specs)
127///     }
128/// }
129/// # impl<U: Ui> Widget<U> for UpTime {
130/// #     type Cfg = UpTimeCfg<U>;
131/// #     fn cfg() -> Self::Cfg { UpTimeCfg(PhantomData) }
132/// #     fn text(&self) -> &Text { &self.0 }
133/// #     fn text_mut(&mut self) -> &mut Text{ &mut self.0 }
134/// #     fn once() -> Result<(), Text> { Ok(()) }
135/// #     fn update(_: &mut Pass, handle: Handle<Self, U>) {}
136/// #     fn needs_update(&self) -> bool { todo!(); }
137/// # }
138/// ```
139///
140/// The [`build`] method should return 3 objects:
141///
142/// * The widget itself.
143/// * [How] to push the widget into the [`File`]/window.
144///
145/// Also, note that `UpTimeCfg` includes a [`PhantomData<U>`]. This is
146/// done so that the end user does not need to specify a [`Ui`] when
147/// using [`WidgetCfg`]s.
148///
149/// Now, there are some other methods from [`Widget`] that need
150/// to be implemented for this to work. First of all, there needs to
151/// be a starting [`Instant`] to compare with the current moment in
152/// time.
153///
154/// The best time to do something like this is after Duat is done with
155/// initial setup. This happens when the [`ConfigLoaded`] hook is
156/// triggered.
157///
158/// ```rust
159/// # use duat_core::doc_duat as duat;
160/// setup_duat!(setup);
161/// use std::{sync::OnceLock, time::Instant};
162///
163/// use duat::prelude::*;
164///
165/// fn setup() {
166///     static START_TIME: OnceLock<Instant> = OnceLock::new();
167///     hook::add::<ConfigLoaded>(|_, _| START_TIME.set(Instant::now()).unwrap());
168/// }
169/// ```
170///
171/// This should be added to the `setup` function in the `config`
172/// crate. Obviously, requiring that the end user adds a [hook] for
173/// your [`Widget`] to work is poor UX design, so this should be
174/// placed inside of a [`Plugin`] instead.
175///
176/// Next I'm going to implement two other [`Widget`] functions:
177/// [`once`] and [`update`]. The [`once`] function will do things that
178/// should only happen once, even if multiple of a given [`Widget`]
179/// are spawned. The [`update`] function is where the [`Text`] should
180/// be updated:
181///
182/// ```rust
183/// # use std::{marker::PhantomData, sync::OnceLock, time::Instant};
184/// # use duat_core::{prelude::*, data::PeriodicChecker};
185/// # struct UpTime(Text, PeriodicChecker);
186/// # struct UpTimeCfg<U>(PhantomData<U>);
187/// # impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
188/// #     type Widget = UpTime;
189/// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) { todo!() }
190/// # }
191/// // This was set during the `setup` function
192/// static START_TIME: OnceLock<Instant> = OnceLock::new();
193/// impl<U: Ui> Widget<U> for UpTime {
194/// #     type Cfg = UpTimeCfg<U>;
195/// #     fn cfg() -> Self::Cfg { UpTimeCfg(PhantomData) }
196/// #     fn text(&self) -> &Text { &self.0 }
197/// #     fn text_mut(&mut self) -> &mut Text { &mut self.0 }
198/// #     fn needs_update(&self) -> bool { todo!(); }
199///     // ...
200///     fn update(pa: &mut Pass, handle: Handle<Self, U>) {
201///         let start = START_TIME.get().unwrap();
202///         let elapsed = start.elapsed();
203///         let mins = elapsed.as_secs() / 60;
204///         let secs = elapsed.as_secs() % 60;
205///
206///         handle.widget().replace_text::<U>(
207///             pa,
208///             txt!("[uptime.mins]{mins}m [uptime.secs]{secs}s")
209///         );
210///     }
211///
212///     fn once() -> Result<(), Text> {
213///         form::set_weak("uptime.mins", Form::new().green());
214///         form::set_weak("uptime.secs", Form::new().green());
215///         Ok(())
216///     }
217/// }
218/// ```
219///
220/// In the [`once`] function, I am setting the `"UpTime"` [`Form`],
221/// which is going to be used on the `UpTime`'s [`Text`]. Finally, the
222/// only thing that remains to be done is a function to check for
223/// updates: [`Widget::needs_update`]. That's where the
224/// [`PeriodicChecker`] comes in:
225///
226/// ```rust
227/// # use std::{marker::PhantomData, sync::OnceLock, time::{Duration, Instant}};
228/// # use duat_core::{data::PeriodicChecker, prelude::*};
229/// // This was set during the `setup` function
230/// static START_TIME: OnceLock<Instant> = OnceLock::new();
231///
232/// struct UpTime(Text, PeriodicChecker);
233///
234/// impl<U: Ui> Widget<U> for UpTime {
235///     type Cfg = UpTimeCfg<U>;
236///
237///     fn needs_update(&self) -> bool {
238///         // Returns `true` once per second
239///         self.1.check()
240///     }
241///
242///     fn update(pa: &mut Pass, handle: Handle<Self, U>) {
243///         let start = START_TIME.get().unwrap();
244///         let elapsed = start.elapsed();
245///         let mins = elapsed.as_secs() / 60;
246///         let secs = elapsed.as_secs() % 60;
247///
248///         handle
249///             .widget()
250///             .replace_text::<U>(pa, txt!("[uptime.mins]{mins}m [uptime.secs]{secs}s"));
251///     }
252///
253///     fn cfg() -> Self::Cfg {
254///         UpTimeCfg(PhantomData)
255///     }
256///
257///     // Some methods used in Duat
258///     fn text(&self) -> &Text {
259///         &self.0
260///     }
261///
262///     fn text_mut(&mut self) -> &mut Text {
263///         &mut self.0
264///     }
265///
266///     fn once() -> Result<(), Text> {
267///         form::set_weak("uptime.mins", Form::new().green());
268///         form::set_weak("uptime.secs", Form::new().green());
269///         Ok(())
270///     }
271/// }
272///
273/// struct UpTimeCfg<U>(PhantomData<U>);
274///
275/// impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
276///     type Widget = UpTime;
277///
278///     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) {
279///         // You could imagine how a method on `UpTimeCfg` could
280///         // change the periodicity
281///         let checker = PeriodicChecker::new(Duration::from_secs(1));
282///         let widget = UpTime(Text::new(), checker);
283///         let specs = PushSpecs::below().with_ver_len(1.0);
284///
285///         (widget, specs)
286///     }
287/// }
288/// ```
289///
290/// [`Mode`]: crate::mode::Mode
291/// [`cfg`]: Widget::cfg
292/// [`build`]: WidgetCfg::build
293/// [How]: PushSpecs
294/// [`PeriodicChecker`]: crate::data::PeriodicChecker
295/// [`OnFileOpen`]: crate::hook::OnFileOpen
296/// [`OnWindowOpen`]: crate::hook::OnWindowOpen\
297/// [hooks]: crate::hook
298/// [`PhantomData<U>`]: std::marker::PhantomData
299/// [`Instant`]: std::time::Instant
300/// [`ConfigLoaded`]: crate::hook::ConfigLoaded
301/// [`once`]: Widget::once
302/// [`update`]: Widget::update
303/// [`Form`]: crate::form::Form
304/// [`form::set_weak*`]: crate::form::set_weak
305/// [`txt!`]: crate::text::txt
306/// [`Plugin`]: crate::Plugin
307pub trait Widget<U: Ui>: 'static {
308    /// The configuration type
309    type Cfg: WidgetCfg<U, Widget = Self>
310    where
311        Self: Sized;
312
313    /// Returns a [`WidgetCfg`], for use in layout construction
314    ///
315    /// This function exists primarily so the [`WidgetCfg`]s
316    /// themselves don't need to be in scope. You will want to use
317    /// these in [hooks] like [`OnFileOpen`]:
318    ///
319    /// ```rust
320    /// # use duat_core::doc_duat as duat;
321    /// setup_duat!(setup);
322    /// use duat::prelude::*;
323    ///
324    /// fn setup() {
325    ///     hook::remove("FileWidgets");
326    ///     hook::add::<OnFileOpen>(|pa, builder| {
327    ///         // Screw it, LineNumbers on both sides.
328    ///         builder.push(pa, LineNumbers::cfg());
329    ///         builder.push(pa, LineNumbers::cfg().on_the_right().align_right());
330    ///     });
331    /// }
332    /// ```
333    ///
334    /// [hooks]: crate::hook
335    /// [`OnFileOpen`]: crate::hook::OnFileOpen
336    fn cfg() -> Self::Cfg
337    where
338        Self: Sized;
339
340    ////////// Stateful functions
341
342    /// Updates the widget, allowing the modification of its
343    /// [`RawArea`]
344    ///
345    /// This function will be triggered when Duat deems that a change
346    /// has happened to this [`Widget`], which is when
347    /// [`RwData<Self>::has_changed`] returns `true`. This can happen
348    /// from many places, like [hooks], [commands], editing by
349    /// [`Mode`]s, etc.
350    ///
351    /// Importantly, [`update`] should be able to handle any number of
352    /// changes that have taken place in this [`Widget`], so you
353    /// should avoid depending on state which may become
354    /// desynchronized.
355    ///
356    /// [hooks]: crate::hook
357    /// [commands]: crate::cmd
358    /// [`Mode`]: crate::mode::Mode
359    /// [`update`]: Widget::update
360    #[allow(unused)]
361    fn update(pa: &mut Pass, handle: Handle<Self, U>)
362    where
363        Self: Sized;
364
365    /// Actions to do whenever this [`Widget`] is focused
366    #[allow(unused)]
367    fn on_focus(pa: &mut Pass, handle: Handle<Self, U>)
368    where
369        Self: Sized,
370    {
371    }
372
373    /// Actions to do whenever this [`Widget`] is unfocused
374    #[allow(unused)]
375    fn on_unfocus(pa: &mut Pass, handle: Handle<Self, U>)
376    where
377        Self: Sized,
378    {
379    }
380
381    /// Tells Duat that this [`Widget`] should be updated
382    ///
383    /// Determining wether a [`Widget`] should be updated, for a good
384    /// chunk of them, will require some code like this:
385    ///
386    /// ```rust
387    /// # use duat_core::prelude::*;
388    /// # struct Cfg;
389    /// # impl<U: Ui> WidgetCfg<U> for Cfg {
390    /// #     type Widget = MyWidget<U>;
391    /// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs) {
392    /// #         todo!();
393    /// #     }
394    /// # }
395    /// struct MyWidget<U: Ui>(FileHandle<U>);
396    ///
397    /// impl<U: Ui> Widget<U> for MyWidget<U> {
398    /// #   type Cfg = Cfg;
399    /// #   fn cfg() -> Self::Cfg { todo!() }
400    /// #   fn update(_: &mut Pass, handle: Handle<Self, U>) { todo!() }
401    /// #   fn text(&self) -> &Text { todo!() }
402    /// #   fn text_mut(&mut self) -> &mut Text { todo!() }
403    /// #   fn once() -> Result<(), Text> { todo!() }
404    ///     // ...
405    ///     fn needs_update(&self) -> bool {
406    ///         self.0.has_changed()
407    ///     }
408    /// }
409    /// ```
410    ///
411    /// In this case, `MyWidget` is telling Duat that it should be
412    /// updated whenever the file in the [`FileHandle`] gets changed.
413    ///
414    /// One exception to this is the [`StatusLine`], which can be
415    /// altered if any of its parts get changed, some of them depend
416    /// on a [`FileHandle`], but a lot of others depend on checking
417    /// functions which might need to be triggered.
418    ///
419    /// [`FileHandle`]: crate::context::FileHandle
420    /// [`StatusLine`]: https://docs.rs/duat-core/latest/duat_utils/widgets/struct.StatusLine.html
421    fn needs_update(&self) -> bool;
422
423    /// The text that this widget prints out
424    fn text(&self) -> &Text;
425
426    /// A mutable reference to the [`Text`] that is printed
427    fn text_mut(&mut self) -> &mut Text;
428
429    /// The [configuration] for how to print [`Text`]
430    ///
431    /// The default configuration, used when `print_cfg` is not
432    /// implemented,can be found at [`PrintCfg::new`].
433    ///
434    /// [configuration]: PrintCfg
435    fn print_cfg(&self) -> PrintCfg {
436        PrintCfg::new()
437    }
438
439    /// Prints the widget
440    ///
441    /// Very rarely shouuld you actually implement this method, one
442    /// example of where this is actually implemented is in
443    /// [`File::print`], where [`RawArea::print_with`] is called in
444    /// order to simultaneously update the list of lines numbers,
445    /// for widgets like [`LineNumbers`] to read.
446    ///
447    /// [`LineNumbers`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.LineNumbers.html
448    fn print(&mut self, painter: Painter, area: &U::Area) {
449        let cfg = self.print_cfg();
450        area.print(self.text_mut(), cfg, painter)
451    }
452
453    /// Actions taken when this widget opens for the first time
454    ///
455    /// Examples of things that should go in here are [`form`]
456    /// functions, [hooks], [commands] you want executed only once
457    ///
458    /// [hooks]: crate::hook
459    /// [commands]: crate::cmd
460    fn once() -> Result<(), Text>
461    where
462        Self: Sized;
463}
464
465/// A configuration struct for a [`Widget`]
466///
467/// This configuration is used to make adjustments on how a widget
468/// will be added to a file or a window. These adjustments are
469/// primarily configurations for the widget itself, and to what
470/// direction it will be pushed:
471///
472/// ```rust
473/// # use duat_core::doc_duat as duat;
474/// setup_duat!(setup);
475/// use duat::prelude::*;
476///
477/// fn setup() {
478///     hook::add::<OnFileOpen>(|pa, builder| {
479///         // Change pushing direction to the right.
480///         let cfg = LineNumbers::cfg().on_the_right();
481///         // Changes to where the numbers will be placed.
482///         let cfg = cfg.align_right().align_main_left();
483///
484///         builder.push(pa, cfg);
485///     });
486/// }
487/// ```
488pub trait WidgetCfg<U: Ui>: Sized {
489    /// The [`Widget`] that will be created by this [`WidgetCfg`]
490    type Widget: Widget<U, Cfg = Self>;
491
492    /// Builds the [`Widget`] alongside [`PushSpecs`]
493    ///
494    /// The [`PushSpecs`] are determined by the [`WidgetCfg`] itself,
495    /// and the end user is meant to change it by public facing
496    /// functions in the [`WidgetCfg`]. This is to prevent nonsensical
497    /// [`Widget`] pushing, like [`LineNumbers`] on the bottom of a
498    /// [`File`], for example.
499    ///
500    /// [`LineNumbers`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.LineNumbers.html
501    fn build(self, pa: &mut Pass, handle: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs);
502}
503
504/// Elements related to the [`Widget`]s
505#[derive(Clone)]
506pub(crate) struct Node<U: Ui> {
507    widget: RwData<dyn Widget<U>>,
508    area: U::Area,
509    mask: Rc<Cell<&'static str>>,
510    related_widgets: Related<U>,
511    update: fn(&Self, &mut Pass),
512    print: fn(&Self, &mut Pass),
513    on_focus: fn(&Self, &mut Pass, Handle<dyn Widget<U>, U>),
514    on_unfocus: fn(&Self, &mut Pass, Handle<dyn Widget<U>, U>),
515}
516
517impl<U: Ui> Node<U> {
518    /// Returns a new [`Node`]
519    pub(crate) fn new<W: Widget<U>>(widget: RwData<dyn Widget<U>>, area: U::Area) -> Self {
520        let related_widgets = (TypeId::of::<W>() == TypeId::of::<File<U>>()).then(RwData::default);
521
522        Self {
523            widget,
524            area,
525            mask: Rc::new(Cell::new("")),
526            related_widgets,
527            update: Self::update_fn::<W>,
528            print: Self::print_fn::<W>,
529            on_focus: Self::on_focus_fn::<W>,
530            on_unfocus: Self::on_unfocus_fn::<W>,
531        }
532    }
533
534    ////////// Reading and parts acquisition
535
536    pub(crate) fn read_as<W: 'static, Ret>(
537        &self,
538        pa: &Pass,
539        f: impl FnOnce(&W) -> Ret,
540    ) -> Option<Ret> {
541        self.widget.clone().read_as(pa, f)
542    }
543
544    /// The [`Widget`] of this [`Node`]
545    pub(crate) fn widget(&self) -> &RwData<dyn Widget<U>> {
546        &self.widget
547    }
548
549    pub(crate) fn area(&self) -> &U::Area {
550        &self.area
551    }
552
553    /// Returns the downcast ref of this [`Widget`].
554    pub(crate) fn try_downcast<W: 'static>(&self) -> Option<RwData<W>> {
555        self.widget.try_downcast()
556    }
557
558    pub(crate) fn parts(
559        &self,
560    ) -> (
561        &RwData<dyn Widget<U>>,
562        &<U as Ui>::Area,
563        &Rc<Cell<&'static str>>,
564        &Related<U>,
565    ) {
566        (&self.widget, &self.area, &self.mask, &self.related_widgets)
567    }
568
569    pub(crate) fn as_file(&self) -> Option<FileParts<U>> {
570        self.widget.try_downcast().map(|file: RwData<File<U>>| {
571            (
572                Handle::from_parts(file, self.area.clone(), self.mask.clone()),
573                self.related_widgets.clone().unwrap(),
574            )
575        })
576    }
577
578    pub(crate) fn related_widgets(&self) -> Option<&RwData<Vec<Node<U>>>> {
579        self.related_widgets.as_ref()
580    }
581
582    ////////// Querying functions
583
584    /// Wether the value within is `W`
585    pub(crate) fn data_is<W: 'static>(&self) -> bool {
586        self.widget.data_is::<W>()
587    }
588
589    /// Wether this and [`RwData`] point to the same value
590    pub(crate) fn ptr_eq<W: ?Sized>(&self, other: &RwData<W>) -> bool {
591        self.widget.ptr_eq(other)
592    }
593
594    /// Wether this [`Widget`] needs to be updated
595    pub(crate) fn needs_update(&self, pa: &Pass) -> bool {
596        self.area.has_changed()
597            || self.widget.has_changed()
598            || self.widget.read(pa, |w| w.needs_update())
599    }
600
601    ////////// Eventful functions
602
603    /// Updates and prints this [`Node`]
604    pub(crate) fn update_and_print(&self, pa: &mut Pass) {
605        (self.update)(self, pa);
606
607        {
608            let mut widget = self.widget.acquire_mut(pa);
609            let cfg = widget.print_cfg();
610            widget.text_mut().add_selections(&self.area, cfg);
611
612            if self.area.print_info() != <U::Area as RawArea>::PrintInfo::default() {
613                widget.text_mut().update_bounds();
614            }
615        };
616
617        (self.print)(self, pa);
618
619        let mut widget = self.widget.acquire_mut(pa);
620        let cfg = widget.print_cfg();
621        widget.text_mut().remove_selections(&self.area, cfg);
622    }
623
624    /// What to do when focusing
625    pub(crate) fn on_focus(&self, pa: &mut Pass, old: Handle<dyn Widget<U>, U>) {
626        self.area.set_as_active();
627        (self.on_focus)(self, pa, old)
628    }
629
630    /// What to do when unfocusing
631    pub(crate) fn on_unfocus(&self, pa: &mut Pass, new: Handle<dyn Widget<U>, U>) {
632        (self.on_unfocus)(self, pa, new)
633    }
634
635    /// Static dispatch inner update function
636    fn update_fn<W: Widget<U>>(&self, pa: &mut Pass) {
637        let widget = self.widget.try_downcast::<W>().unwrap();
638        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
639        Widget::update(pa, handle);
640    }
641
642    /// Static dispatch inner print function
643    fn print_fn<W: Widget<U>>(&self, pa: &mut Pass) {
644        let painter = form::painter_with_mask::<W>(self.mask.get());
645        let mut widget = self.widget.acquire_mut(pa);
646
647        widget.print(painter, &self.area);
648    }
649
650    /// Static dispatch inner update on_focus
651    fn on_focus_fn<W: Widget<U>>(&self, pa: &mut Pass, old: Handle<dyn Widget<U>, U>) {
652        self.area.set_as_active();
653        let widget: RwData<W> = self.widget.try_downcast().unwrap();
654
655        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
656        hook::trigger(pa, FocusedOn((old, handle.clone())));
657
658        Widget::on_focus(pa, handle);
659    }
660
661    /// Static dispatch inner update on_unfocus
662    fn on_unfocus_fn<W: Widget<U>>(&self, pa: &mut Pass, new: Handle<dyn Widget<U>, U>) {
663        let widget: RwData<W> = self.widget.try_downcast().unwrap();
664
665        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
666        hook::trigger(pa, UnfocusedFrom((handle.clone(), new)));
667
668        Widget::on_unfocus(pa, handle);
669    }
670}
671
672impl<U: Ui> PartialEq for Node<U> {
673    fn eq(&self, other: &Self) -> bool {
674        self.widget.ptr_eq(&other.widget)
675    }
676}
677
678impl<U: Ui> Eq for Node<U> {}
679
680/// [`Widget`]s related to another [`Widget`]
681pub(crate) type Related<U> = Option<RwData<Vec<Node<U>>>>;