From e5eda52f43b538d62b62b9cfd293a708bf904f49 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 15 May 2024 09:31:12 +0200 Subject: [PATCH] lib: Improve widget base --- src/ui/widget.rs | 36 +++++++++++++++-------- src/ui/widget/container.rs | 59 +++++++++++++++++++------------------- src/ui/widget/input.rs | 16 +++++------ src/ui/widget/list.rs | 16 +++++------ src/ui/widget/text.rs | 16 +++++------ src/ui/widget/window.rs | 30 +++++++++---------- 6 files changed, 92 insertions(+), 81 deletions(-) diff --git a/src/ui/widget.rs b/src/ui/widget.rs index 798810c3..77f00d34 100644 --- a/src/ui/widget.rs +++ b/src/ui/widget.rs @@ -19,8 +19,9 @@ pub type BoxedWidget = Box>; pub type UpdateCallback = fn(&S) -> Box; pub type EventCallback = fn(&mut dyn Any); -/// A `View`s common fields. -pub struct BaseView { +/// A `WidgetBase` provides common functionality to a `Widget`. It's used to store +/// event and update callbacks as well sending messages to the UI's message channel. +pub struct WidgetBase { /// Message sender pub action_tx: UnboundedSender, /// Custom update handler @@ -29,7 +30,17 @@ pub struct BaseView { pub on_event: Option, } -impl BaseView { +impl WidgetBase { + /// Create a new `WidgetBase` with no callbacks set. + pub fn new(action_tx: UnboundedSender) -> Self { + Self { + action_tx: action_tx.clone(), + on_update: None, + on_event: None, + } + } + + /// Send a message to the internal channel. pub fn send(&self, action: A) -> Result<(), SendError> { self.action_tx.send(action) } @@ -107,8 +118,16 @@ pub trait Widget { /// Optional render props can be given. fn render(&self, frame: &mut Frame, props: RenderProps); - /// Return a mutable reference to this widgets' base view. - fn base_mut(&mut self) -> &mut BaseView; + /// Return a reference to this widgets' base. + fn base(&self) -> &WidgetBase; + + /// Return a mutable reference to this widgets' base. + fn base_mut(&mut self) -> &mut WidgetBase; + + /// Send a message to the widgets' base channel. + fn send(&self, action: Self::Action) -> Result<(), SendError> { + self.base().send(action) + } /// Should set the optional custom event handler. fn on_event(mut self, callback: EventCallback) -> Self @@ -135,13 +154,6 @@ pub trait Widget { { Box::new(self) } - - fn downcast_ref(any: &dyn Any) -> Option<&Self> - where - Self: Sized + 'static, - { - any.downcast_ref::() - } } /// Needs to be implemented for items that are supposed to be rendered in tables. diff --git a/src/ui/widget/container.rs b/src/ui/widget/container.rs index e8aaac30..fb24c838 100644 --- a/src/ui/widget/container.rs +++ b/src/ui/widget/container.rs @@ -10,7 +10,7 @@ use ratatui::widgets::{Block, BorderType, Borders, Row}; use crate::ui::ext::{FooterBlock, FooterBlockType, HeaderBlock}; use crate::ui::theme::style; -use super::{BaseView, BoxedAny, BoxedWidget, Properties, RenderProps, Widget}; +use super::{BoxedAny, BoxedWidget, Properties, RenderProps, Widget, WidgetBase}; #[derive(Clone, Debug)] pub struct Column<'a> { @@ -71,7 +71,7 @@ pub struct Header<'a: 'static, S, A> { /// Internal props props: HeaderProps<'a>, /// Internal base - base: BaseView, + base: WidgetBase, } impl<'a, S, A> Header<'a, S, A> { @@ -93,11 +93,7 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> { fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: HeaderProps::default(), } } @@ -170,7 +166,11 @@ impl<'a: 'static, S, A> Widget for Header<'a, S, A> { frame.render_widget(header, header_layout[0]); } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } @@ -212,7 +212,7 @@ pub struct Footer<'a, S, A> { /// Internal props props: FooterProps<'a>, /// Internal base - base: BaseView, + base: WidgetBase, } impl<'a, S, A> Footer<'a, S, A> { @@ -256,11 +256,7 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> { fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: FooterProps::default(), } } @@ -309,7 +305,11 @@ impl<'a: 'static, S, A> Widget for Footer<'a, S, A> { } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } @@ -331,7 +331,7 @@ impl BoxedAny for ContainerProps {} pub struct Container { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal props props: ContainerProps, /// Container header @@ -368,12 +368,7 @@ impl Widget for Container { Self: Sized, { Self { - base: BaseView { - action_tx: action_tx.clone(), - - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: ContainerProps::default(), header: None, content: None, @@ -451,7 +446,11 @@ impl Widget for Container { } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } @@ -482,7 +481,7 @@ impl BoxedAny for SectionGroupProps {} pub struct SectionGroup { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal table properties props: SectionGroupProps, /// All sections @@ -522,11 +521,7 @@ impl Widget for SectionGroup { fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: SectionGroupProps::default(), sections: vec![], state: SectionGroupState { focus: Some(0) }, @@ -584,7 +579,11 @@ impl Widget for SectionGroup { } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } diff --git a/src/ui/widget/input.rs b/src/ui/widget/input.rs index 3f2f8f1d..a9aa880a 100644 --- a/src/ui/widget/input.rs +++ b/src/ui/widget/input.rs @@ -6,7 +6,7 @@ use ratatui::layout::{Constraint, Layout}; use ratatui::style::Stylize; use ratatui::text::{Line, Span}; -use super::{BaseView, BoxedAny, Properties, RenderProps, Widget}; +use super::{BoxedAny, Properties, RenderProps, Widget, WidgetBase}; #[derive(Clone)] pub struct TextFieldProps { @@ -58,7 +58,7 @@ impl BoxedAny for TextFieldState {} pub struct TextField { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal props props: TextFieldProps, /// Internal state @@ -137,11 +137,7 @@ impl Widget for TextField { fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: TextFieldProps::default(), state: TextFieldState { text: None, @@ -238,7 +234,11 @@ impl Widget for TextField { } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } diff --git a/src/ui/widget/list.rs b/src/ui/widget/list.rs index ab3c3c19..cc335e4e 100644 --- a/src/ui/widget/list.rs +++ b/src/ui/widget/list.rs @@ -14,7 +14,7 @@ use crate::ui::theme::style; use crate::ui::{layout, span}; use super::BoxedAny; -use super::{container::Column, BaseView, Properties, RenderProps, ToRow, Widget}; +use super::{container::Column, Properties, RenderProps, ToRow, Widget, WidgetBase}; #[derive(Clone, Debug)] pub struct TableProps<'a, R, const W: usize> @@ -93,7 +93,7 @@ where R: ToRow, { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal table properties props: TableProps<'a, R, W>, /// Internal selection and offset state @@ -168,11 +168,7 @@ where fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: TableProps::default(), state: TableState::default().with_selected(Some(0)), } @@ -277,7 +273,11 @@ where } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } diff --git a/src/ui/widget/text.rs b/src/ui/widget/text.rs index 011a5e5a..1db0b97f 100644 --- a/src/ui/widget/text.rs +++ b/src/ui/widget/text.rs @@ -5,7 +5,7 @@ use termion::event::Key; use ratatui::layout::{Constraint, Layout}; use ratatui::text::Text; -use super::{BaseView, BoxedAny, Properties, RenderProps, Widget}; +use super::{BoxedAny, Properties, RenderProps, Widget, WidgetBase}; #[derive(Clone)] pub struct ParagraphProps<'a> { @@ -55,7 +55,7 @@ impl BoxedAny for ParagraphState {} pub struct Paragraph<'a, S, A> { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal props props: ParagraphProps<'a>, /// Internal state @@ -145,11 +145,7 @@ impl<'a: 'static, S: 'static, A: 'static> Widget for Paragraph<'a, S, A> { Self: Sized, { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: ParagraphProps::default(), state: ParagraphState { offset: 0, @@ -204,7 +200,11 @@ impl<'a: 'static, S: 'static, A: 'static> Widget for Paragraph<'a, S, A> { frame.render_widget(content, content_area); } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } diff --git a/src/ui/widget/window.rs b/src/ui/widget/window.rs index 44c9dc81..5472d670 100644 --- a/src/ui/widget/window.rs +++ b/src/ui/widget/window.rs @@ -12,7 +12,7 @@ use ratatui::widgets::Row; use crate::ui::theme::style; -use super::{BaseView, BoxedAny, BoxedWidget, Properties, RenderProps, Widget}; +use super::{BoxedAny, BoxedWidget, Properties, RenderProps, Widget, WidgetBase}; #[derive(Clone)] pub struct WindowProps { @@ -37,7 +37,7 @@ impl BoxedAny for WindowProps {} pub struct Window { /// Internal base - base: BaseView, + base: WidgetBase, /// Internal properties props: WindowProps, /// All pages known @@ -66,11 +66,7 @@ where Self: Sized, { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: WindowProps::default(), pages: HashMap::new(), } @@ -117,7 +113,11 @@ where } } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } } @@ -159,7 +159,7 @@ pub struct Shortcuts { /// Internal properties props: ShortcutsProps, /// Internal base - base: BaseView, + base: WidgetBase, } impl Shortcuts { @@ -185,11 +185,7 @@ impl Widget for Shortcuts { fn new(_state: &S, action_tx: UnboundedSender) -> Self { Self { - base: BaseView { - action_tx: action_tx.clone(), - on_update: None, - on_event: None, - }, + base: WidgetBase::new(action_tx.clone()), props: ShortcutsProps::default(), } } @@ -239,7 +235,11 @@ impl Widget for Shortcuts { frame.render_widget(table, props.area); } - fn base_mut(&mut self) -> &mut BaseView { + fn base(&self) -> &WidgetBase { + &self.base + } + + fn base_mut(&mut self) -> &mut WidgetBase { &mut self.base } }