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