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