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>>>>;