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 std::{sync::OnceLock, time::Instant};
160/// # use duat_core::{hook::{self, ConfigLoaded}, ui::Ui};
161/// # fn test<U: Ui>() {
162/// static START_TIME: OnceLock<Instant> = OnceLock::new();
163/// hook::add::<ConfigLoaded>(|_, _| START_TIME.set(Instant::now()).unwrap());
164/// # }
165/// ```
166///
167/// This should be added to the `setup` function in the `config`
168/// crate. Obviously, requiring that the end user adds a [hook] for
169/// your [`Widget`] to work is poor UX design, so this should be
170/// placed inside of a [`Plugin`] instead.
171///
172/// Next I'm going to implement two other [`Widget`] functions:
173/// [`once`] and [`update`]. The [`once`] function will do things that
174/// should only happen once, even if multiple of a given [`Widget`]
175/// are spawned. The [`update`] function is where the [`Text`] should
176/// be updated:
177///
178/// ```rust
179/// # use std::{marker::PhantomData, sync::OnceLock, time::Instant};
180/// # use duat_core::{prelude::*, data::PeriodicChecker};
181/// # struct UpTime(Text, PeriodicChecker);
182/// # struct UpTimeCfg<U>(PhantomData<U>);
183/// # impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
184/// #     type Widget = UpTime;
185/// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) { todo!() }
186/// # }
187/// // This was set during the `setup` function
188/// static START_TIME: OnceLock<Instant> = OnceLock::new();
189/// impl<U: Ui> Widget<U> for UpTime {
190/// #     type Cfg = UpTimeCfg<U>;
191/// #     fn cfg() -> Self::Cfg { UpTimeCfg(PhantomData) }
192/// #     fn text(&self) -> &Text { &self.0 }
193/// #     fn text_mut(&mut self) -> &mut Text { &mut self.0 }
194/// #     fn needs_update(&self) -> bool { todo!(); }
195///     // ...
196///     fn update(pa: &mut Pass, handle: Handle<Self, U>) {
197///         let start = START_TIME.get().unwrap();
198///         let elapsed = start.elapsed();
199///         let mins = elapsed.as_secs() / 60;
200///         let secs = elapsed.as_secs() % 60;
201///
202///         handle.widget().replace_text::<U>(
203///             pa,
204///             txt!("[uptime.mins]{mins}m [uptime.secs]{secs}s")
205///         );
206///     }
207///
208///     fn once() -> Result<(), Text> {
209///         form::set_weak("uptime.mins", Form::new().green());
210///         form::set_weak("uptime.secs", Form::new().green());
211///         Ok(())
212///     }
213/// }
214/// ```
215///
216/// In the [`once`] function, I am setting the `"UpTime"` [`Form`],
217/// which is going to be used on the `UpTime`'s [`Text`]. Finally, the
218/// only thing that remains to be done is a function to check for
219/// updates: [`Widget::needs_update`]. That's where the
220/// [`PeriodicChecker`] comes in:
221///
222/// ```rust
223/// # use std::{marker::PhantomData, sync::OnceLock, time::{Duration, Instant}};
224/// # use duat_core::{data::PeriodicChecker, prelude::*};
225/// // This was set during the `setup` function
226/// static START_TIME: OnceLock<Instant> = OnceLock::new();
227///
228/// struct UpTime(Text, PeriodicChecker);
229///
230/// impl<U: Ui> Widget<U> for UpTime {
231///     type Cfg = UpTimeCfg<U>;
232///
233///     fn needs_update(&self) -> bool {
234///         // Returns `true` once per second
235///         self.1.check()
236///     }
237///
238///     fn update(pa: &mut Pass, handle: Handle<Self, U>) {
239///         let start = START_TIME.get().unwrap();
240///         let elapsed = start.elapsed();
241///         let mins = elapsed.as_secs() / 60;
242///         let secs = elapsed.as_secs() % 60;
243///
244///         handle
245///             .widget()
246///             .replace_text::<U>(pa, txt!("[uptime.mins]{mins}m [uptime.secs]{secs}s"));
247///     }
248///
249///     fn cfg() -> Self::Cfg {
250///         UpTimeCfg(PhantomData)
251///     }
252///
253///     // Some methods used in Duat
254///     fn text(&self) -> &Text {
255///         &self.0
256///     }
257///
258///     fn text_mut(&mut self) -> &mut Text {
259///         &mut self.0
260///     }
261///
262///     fn once() -> Result<(), Text> {
263///         form::set_weak("uptime.mins", Form::new().green());
264///         form::set_weak("uptime.secs", Form::new().green());
265///         Ok(())
266///     }
267/// }
268///
269/// struct UpTimeCfg<U>(PhantomData<U>);
270///
271/// impl<U: Ui> WidgetCfg<U> for UpTimeCfg<U> {
272///     type Widget = UpTime;
273///
274///     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (UpTime, PushSpecs) {
275///         // You could imagine how a method on `UpTimeCfg` could
276///         // change the periodicity
277///         let checker = PeriodicChecker::new(Duration::from_secs(1));
278///         let widget = UpTime(Text::new(), checker);
279///         let specs = PushSpecs::below().with_ver_len(1.0);
280///
281///         (widget, specs)
282///     }
283/// }
284/// ```
285///
286/// [`Mode`]: crate::mode::Mode
287/// [`cfg`]: Widget::cfg
288/// [`build`]: WidgetCfg::build
289/// [How]: PushSpecs
290/// [`PeriodicChecker`]: crate::data::PeriodicChecker
291/// [`OnFileOpen`]: crate::hook::OnFileOpen
292/// [`OnWindowOpen`]: crate::hook::OnWindowOpen\
293/// [hooks]: crate::hook
294/// [`PhantomData<U>`]: std::marker::PhantomData
295/// [`Instant`]: std::time::Instant
296/// [`ConfigLoaded`]: crate::hook::ConfigLoaded
297/// [`once`]: Widget::once
298/// [`update`]: Widget::update
299/// [`Form`]: crate::form::Form
300/// [`form::set_weak*`]: crate::form::set_weak
301/// [`txt!`]: crate::text::txt
302/// [`Plugin`]: crate::Plugin
303pub trait Widget<U: Ui>: 'static {
304    /// The configuration type
305    type Cfg: WidgetCfg<U, Widget = Self>
306    where
307        Self: Sized;
308
309    ////////// Stateful functions
310
311    /// Updates the widget, allowing the modification of its
312    /// [`RawArea`]
313    ///
314    /// This function will be triggered when Duat deems that a change
315    /// has happened to this [`Widget`], which is when
316    /// [`RwData<Self>::has_changed`] returns `true`. This can happen
317    /// from many places, like [hooks], [commands], editing by
318    /// [`Mode`]s, etc.
319    ///
320    /// Importantly, [`update`] should be able to handle any number of
321    /// changes that have taken place in this [`Widget`], so you
322    /// should avoid depending on state which may become
323    /// desynchronized.
324    ///
325    /// [hooks]: crate::hook
326    /// [commands]: crate::cmd
327    /// [`Mode`]: crate::mode::Mode
328    /// [`update`]: Widget::update
329    #[allow(unused)]
330    fn update(pa: &mut Pass, handle: Handle<Self, U>)
331    where
332        Self: Sized;
333
334    /// Actions to do whenever this [`Widget`] is focused
335    #[allow(unused)]
336    fn on_focus(pa: &mut Pass, handle: Handle<Self, U>)
337    where
338        Self: Sized,
339    {
340    }
341
342    /// Actions to do whenever this [`Widget`] is unfocused
343    #[allow(unused)]
344    fn on_unfocus(pa: &mut Pass, handle: Handle<Self, U>)
345    where
346        Self: Sized,
347    {
348    }
349
350    /// Tells Duat that this [`Widget`] should be updated
351    ///
352    /// Determining wether a [`Widget`] should be updated, for a good
353    /// chunk of them, will require some code like this:
354    ///
355    /// ```rust
356    /// # use duat_core::prelude::*;
357    /// # struct Cfg;
358    /// # impl<U: Ui> WidgetCfg<U> for Cfg {
359    /// #     type Widget = MyWidget<U>;
360    /// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs) {
361    /// #         todo!();
362    /// #     }
363    /// # }
364    /// struct MyWidget<U: Ui>(FileHandle<U>);
365    ///
366    /// impl<U: Ui> Widget<U> for MyWidget<U> {
367    /// #   type Cfg = Cfg;
368    /// #   fn cfg() -> Self::Cfg { todo!() }
369    /// #   fn update(_: &mut Pass, handle: Handle<Self, U>) { todo!() }
370    /// #   fn text(&self) -> &Text { todo!() }
371    /// #   fn text_mut(&mut self) -> &mut Text { todo!() }
372    /// #   fn once() -> Result<(), Text> { todo!() }
373    ///     // ...
374    ///     fn needs_update(&self) -> bool {
375    ///         self.0.has_changed()
376    ///     }
377    /// }
378    /// ```
379    ///
380    /// In this case, `MyWidget` is telling Duat that it should be
381    /// updated whenever the file in the [`FileHandle`] gets changed.
382    ///
383    /// One exception to this is the [`StatusLine`], which can be
384    /// altered if any of its parts get changed, some of them depend
385    /// on a [`FileHandle`], but a lot of others depend on checking
386    /// functions which might need to be triggered.
387    ///
388    /// [`FileHandle`]: crate::context::FileHandle
389    /// [`StatusLine`]: https://docs.rs/duat-core/latest/duat_utils/widgets/struct.StatusLine.html
390    fn needs_update(&self) -> bool;
391
392    /// Returns a [`WidgetCfg`], for use in layout construction
393    ///
394    /// This function exists primarily so the [`WidgetCfg`]s
395    /// themselves don't need to be in scope. You will want to use
396    /// these in [hooks] like [`OnFileOpen`]:
397    ///
398    /// ```rust
399    /// # use duat_core::{hook::OnFileOpen, prelude::*};
400    /// # struct LineNumbers<U: Ui>(std::marker::PhantomData<U>);
401    /// # impl<U: Ui> Widget<U> for LineNumbers<U> {
402    /// #     type Cfg = LineNumbersOptions<U>;
403    /// #     fn update(_: &mut Pass, handle: Handle<Self, U>) { todo!() }
404    /// #     fn needs_update(&self) -> bool { todo!(); }
405    /// #     fn cfg() -> Self::Cfg { todo!() }
406    /// #     fn text(&self) -> &Text { todo!(); }
407    /// #     fn text_mut(&mut self) -> &mut Text { todo!(); }
408    /// #     fn once() -> Result<(), Text> { Ok(()) }
409    /// # }
410    /// # struct LineNumbersOptions<U>(std::marker::PhantomData<U>);
411    /// # impl<U> LineNumbersOptions<U> {
412    /// #     pub fn align_right(self) -> Self { todo!(); }
413    /// #     pub fn align_main_left(self) -> Self { todo!(); }
414    /// #     pub fn on_the_right(self) -> Self { todo!(); }
415    /// # }
416    /// # impl<U: Ui> WidgetCfg<U> for LineNumbersOptions<U> {
417    /// #     type Widget = LineNumbers<U>;
418    /// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs) {
419    /// #         todo!();
420    /// #     }
421    /// # }
422    /// # fn test<U: Ui>() {
423    /// hook::remove("FileWidgets");
424    /// hook::add::<OnFileOpen<U>>(|pa, builder| {
425    ///     // Screw it, LineNumbers on both sides.
426    ///     builder.push(pa, LineNumbers::cfg());
427    ///     builder.push(pa, LineNumbers::cfg().on_the_right().align_right());
428    /// });
429    /// # }
430    /// ```
431    ///
432    /// [hooks]: crate::hook
433    /// [`OnFileOpen`]: crate::hook::OnFileOpen
434    fn cfg() -> Self::Cfg
435    where
436        Self: Sized;
437
438    /// The text that this widget prints out
439    fn text(&self) -> &Text;
440
441    /// A mutable reference to the [`Text`] that is printed
442    fn text_mut(&mut self) -> &mut Text;
443
444    /// The [configuration] for how to print [`Text`]
445    ///
446    /// The default configuration, used when `print_cfg` is not
447    /// implemented,can be found at [`PrintCfg::new`].
448    ///
449    /// [configuration]: PrintCfg
450    fn print_cfg(&self) -> PrintCfg {
451        PrintCfg::new()
452    }
453
454    /// Prints the widget
455    ///
456    /// Very rarely shouuld you actually implement this method, one
457    /// example of where this is actually implemented is in
458    /// [`File::print`], where [`RawArea::print_with`] is called in
459    /// order to simultaneously update the list of lines numbers,
460    /// for widgets like [`LineNumbers`] to read.
461    ///
462    /// [`LineNumbers`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.LineNumbers.html
463    fn print(&mut self, painter: Painter, area: &U::Area) {
464        let cfg = self.print_cfg();
465        area.print(self.text_mut(), cfg, painter)
466    }
467
468    /// Actions taken when this widget opens for the first time
469    ///
470    /// Examples of things that should go in here are [`form`]
471    /// functions, [hooks], [commands] you want executed only once
472    ///
473    /// [hooks]: crate::hook
474    /// [commands]: crate::cmd
475    fn once() -> Result<(), Text>
476    where
477        Self: Sized;
478}
479
480/// A configuration struct for a [`Widget`]
481///
482/// This configuration is used to make adjustments on how a widget
483/// will be added to a file or a window. These adjustments are
484/// primarily configurations for the widget itself, and to what
485/// direction it will be pushed:
486///
487/// ```rust
488/// # use duat_core::{hook::OnFileOpen, prelude::*};
489/// # struct LineNumbers<U: Ui> {
490/// #     _ghost: std::marker::PhantomData<U>,
491/// # }
492/// # impl<U: Ui> Widget<U> for LineNumbers<U> {
493/// #     type Cfg = LineNumbersOptions<U>;
494/// #     fn update(_: &mut Pass, handle: Handle<Self, U>) {}
495/// #     fn needs_update(&self) -> bool { todo!(); }
496/// #     fn cfg() -> Self::Cfg { todo!(); }
497/// #     fn text(&self) -> &Text { todo!(); }
498/// #     fn text_mut(&mut self) -> &mut Text { todo!(); }
499/// #     fn once() -> Result<(), Text> { Ok(()) }
500/// # }
501/// # struct LineNumbersOptions<U> {
502/// #     _ghost: std::marker::PhantomData<U>,
503/// # }
504/// # impl<U> LineNumbersOptions<U> {
505/// #     pub fn align_right(self) -> Self { todo!(); }
506/// #     pub fn align_main_left(self) -> Self { todo!(); }
507/// #     pub fn on_the_right(self) -> Self { todo!(); }
508/// # }
509/// # impl<U: Ui> WidgetCfg<U> for LineNumbersOptions<U> {
510/// #     type Widget = LineNumbers<U>;
511/// #     fn build(self, _: &mut Pass, _: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs) {
512/// #         todo!();
513/// #     }
514/// # }
515/// # fn test<U: Ui>() {
516/// hook::add::<OnFileOpen<U>>(|pa, builder| {
517///     // Change pushing direction to the right.
518///     let cfg = LineNumbers::cfg().on_the_right();
519///     // Changes to where the numbers will be placed.
520///     let cfg = cfg.align_right().align_main_left();
521///
522///     builder.push(pa, cfg);
523/// });
524/// # }
525/// ```
526pub trait WidgetCfg<U: Ui>: Sized {
527    /// The [`Widget`] that will be created by this [`WidgetCfg`]
528    type Widget: Widget<U, Cfg = Self>;
529
530    /// Builds the [`Widget`] alongside [`PushSpecs`]
531    ///
532    /// The [`PushSpecs`] are determined by the [`WidgetCfg`] itself,
533    /// and the end user is meant to change it by public facing
534    /// functions in the [`WidgetCfg`]. This is to prevent nonsensical
535    /// [`Widget`] pushing, like [`LineNumbers`] on the bottom of a
536    /// [`File`], for example.
537    ///
538    /// [`LineNumbers`]: docs.rs/duat-utils/latest/duat_utils/widgets/struct.LineNumbers.html
539    /// 
540    fn build(self, pa: &mut Pass, handle: Option<FileHandle<U>>) -> (Self::Widget, PushSpecs);
541}
542
543/// Elements related to the [`Widget`]s
544#[derive(Clone)]
545pub(crate) struct Node<U: Ui> {
546    widget: RwData<dyn Widget<U>>,
547    area: U::Area,
548    mask: Rc<Cell<&'static str>>,
549    related_widgets: Related<U>,
550    update: fn(&Self, &mut Pass),
551    print: fn(&Self, &mut Pass),
552    on_focus: fn(&Self, &mut Pass),
553    on_unfocus: fn(&Self, &mut Pass),
554}
555
556impl<U: Ui> Node<U> {
557    /// Returns a new [`Node`]
558    pub(crate) fn new<W: Widget<U>>(widget: RwData<dyn Widget<U>>, area: U::Area) -> Self {
559        let related_widgets = (TypeId::of::<W>() == TypeId::of::<File<U>>()).then(RwData::default);
560
561        Self {
562            widget,
563            area,
564            mask: Rc::new(Cell::new("")),
565            related_widgets,
566            update: Self::update_fn::<W>,
567            print: Self::print_fn::<W>,
568            on_focus: Self::on_focus_fn::<W>,
569            on_unfocus: Self::on_unfocus_fn::<W>,
570        }
571    }
572
573    ////////// Reading and parts acquisition
574
575    pub(crate) fn read_as<W: 'static, Ret>(
576        &self,
577        pa: &Pass,
578        f: impl FnOnce(&W) -> Ret,
579    ) -> Option<Ret> {
580        self.widget.clone().read_as(pa, f)
581    }
582
583    /// The [`Widget`] of this [`Node`]
584    pub(crate) fn widget(&self) -> &RwData<dyn Widget<U>> {
585        &self.widget
586    }
587
588    pub(crate) fn area(&self) -> &U::Area {
589        &self.area
590    }
591
592    /// Returns the downcast ref of this [`Widget`].
593    pub(crate) fn try_downcast<W: 'static>(&self) -> Option<RwData<W>> {
594        self.widget.try_downcast()
595    }
596
597    pub(crate) fn parts(
598        &self,
599    ) -> (
600        &RwData<dyn Widget<U>>,
601        &<U as Ui>::Area,
602        &Rc<Cell<&'static str>>,
603        &Related<U>,
604    ) {
605        (&self.widget, &self.area, &self.mask, &self.related_widgets)
606    }
607
608    pub(crate) fn as_file(&self) -> Option<FileParts<U>> {
609        self.widget.try_downcast().map(|file: RwData<File<U>>| {
610            (
611                Handle::from_parts(file, self.area.clone(), self.mask.clone()),
612                self.related_widgets.clone().unwrap(),
613            )
614        })
615    }
616
617    pub(crate) fn related_widgets(&self) -> Option<&RwData<Vec<Node<U>>>> {
618        self.related_widgets.as_ref()
619    }
620
621    ////////// Querying functions
622
623    /// Wether the value within is `W`
624    pub(crate) fn data_is<W: 'static>(&self) -> bool {
625        self.widget.data_is::<W>()
626    }
627
628    /// Wether this and [`RwData`] point to the same value
629    pub(crate) fn ptr_eq<W: ?Sized>(&self, other: &RwData<W>) -> bool {
630        self.widget.ptr_eq(other)
631    }
632
633    /// Wether this [`Widget`] needs to be updated
634    pub(crate) fn needs_update(&self, pa: &Pass) -> bool {
635        self.area.has_changed()
636            || self.widget.has_changed()
637            || self.widget.read(pa, |w| w.needs_update())
638    }
639
640    ////////// Eventful functions
641
642    /// Updates and prints this [`Node`]
643    pub(crate) fn update_and_print(&self, pa: &mut Pass) {
644        (self.update)(self, pa);
645
646        {
647            let mut widget = self.widget.acquire_mut(pa);
648            let cfg = widget.print_cfg();
649            widget.text_mut().add_selections(&self.area, cfg);
650
651            if self.area.print_info() != <U::Area as RawArea>::PrintInfo::default() {
652                widget.text_mut().update_bounds();
653            }
654        };
655
656        (self.print)(self, pa);
657
658        let mut widget = self.widget.acquire_mut(pa);
659        let cfg = widget.print_cfg();
660        widget.text_mut().remove_selections(&self.area, cfg);
661    }
662
663    /// What to do when focusing
664    pub(crate) fn on_focus(&self, pa: &mut Pass) {
665        self.area.set_as_active();
666        (self.on_focus)(self, pa)
667    }
668
669    /// What to do when unfocusing
670    pub(crate) fn on_unfocus(&self, pa: &mut Pass) {
671        (self.on_unfocus)(self, pa)
672    }
673
674    /// Static dispatch inner update function
675    fn update_fn<W: Widget<U>>(&self, pa: &mut Pass) {
676        let widget = self.widget.try_downcast::<W>().unwrap();
677        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
678        Widget::update(pa, handle);
679    }
680
681    /// Static dispatch inner print function
682    fn print_fn<W: Widget<U>>(&self, pa: &mut Pass) {
683        let painter = form::painter_with_mask::<W>(self.mask.get());
684        let mut widget = self.widget.acquire_mut(pa);
685
686        widget.print(painter, &self.area);
687    }
688
689    /// Static dispatch inner update on_focus
690    fn on_focus_fn<W: Widget<U>>(&self, pa: &mut Pass) {
691        self.area.set_as_active();
692        let widget: RwData<W> = self.widget.try_downcast().unwrap();
693
694        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
695        hook::trigger(pa, FocusedOn(handle.clone()));
696
697        Widget::on_focus(pa, handle);
698    }
699
700    /// Static dispatch inner update on_unfocus
701    fn on_unfocus_fn<W: Widget<U>>(&self, pa: &mut Pass) {
702        let widget: RwData<W> = self.widget.try_downcast().unwrap();
703
704        let handle = Handle::from_parts(widget, self.area.clone(), self.mask.clone());
705        hook::trigger(pa, UnfocusedFrom(handle.clone()));
706
707        Widget::on_unfocus(pa, handle);
708    }
709}
710
711impl<U: Ui> PartialEq for Node<U> {
712    fn eq(&self, other: &Self) -> bool {
713        self.widget.ptr_eq(&other.widget)
714    }
715}
716
717impl<U: Ui> Eq for Node<U> {}
718
719/// [`Widget`]s related to another [`Widget`]
720pub(crate) type Related<U> = Option<RwData<Vec<Node<U>>>>;