lib: Improve widget base

This commit is contained in:
Erik Kundt
2024-05-15 11:55:29 +02:00
parent 0d43859bcf
commit e5eda52f43
6 changed files with 92 additions and 81 deletions
+24 -12
View File
@@ -19,8 +19,9 @@ pub type BoxedWidget<S, A> = Box<dyn Widget<State = S, Action = A>>;
pub type UpdateCallback<S> = fn(&S) -> Box<dyn Any>;
pub type EventCallback = fn(&mut dyn Any);
/// A `View`s common fields.
pub struct BaseView<S, A> {
/// 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<S, A> {
/// Message sender
pub action_tx: UnboundedSender<A>,
/// Custom update handler
@@ -29,7 +30,17 @@ pub struct BaseView<S, A> {
pub on_event: Option<EventCallback>,
}
impl<S, A> BaseView<S, A> {
impl<S, A> WidgetBase<S, A> {
/// Create a new `WidgetBase` with no callbacks set.
pub fn new(action_tx: UnboundedSender<A>) -> 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<A>> {
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<Self::State, Self::Action>;
/// Return a reference to this widgets' base.
fn base(&self) -> &WidgetBase<Self::State, Self::Action>;
/// Return a mutable reference to this widgets' base.
fn base_mut(&mut self) -> &mut WidgetBase<Self::State, Self::Action>;
/// Send a message to the widgets' base channel.
fn send(&self, action: Self::Action) -> Result<(), SendError<Self::Action>> {
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::<Self>()
}
}
/// Needs to be implemented for items that are supposed to be rendered in tables.
+29 -30
View File
@@ -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<S, A>,
base: WidgetBase<S, A>,
}
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<A>) -> 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<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
@@ -212,7 +212,7 @@ pub struct Footer<'a, S, A> {
/// Internal props
props: FooterProps<'a>,
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
}
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<A>) -> 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<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
@@ -331,7 +331,7 @@ impl BoxedAny for ContainerProps {}
pub struct Container<S, A> {
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
/// Internal props
props: ContainerProps,
/// Container header
@@ -368,12 +368,7 @@ impl<S, A> Widget for Container<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: ContainerProps::default(),
header: None,
content: None,
@@ -451,7 +446,11 @@ impl<S, A> Widget for Container<S, A> {
}
}
fn base_mut(&mut self) -> &mut BaseView<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
@@ -482,7 +481,7 @@ impl BoxedAny for SectionGroupProps {}
pub struct SectionGroup<S, A> {
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
/// Internal table properties
props: SectionGroupProps,
/// All sections
@@ -522,11 +521,7 @@ impl<S: 'static, A: 'static> Widget for SectionGroup<S, A> {
fn new(_state: &S, action_tx: UnboundedSender<A>) -> 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<S: 'static, A: 'static> Widget for SectionGroup<S, A> {
}
}
fn base_mut(&mut self) -> &mut BaseView<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
+8 -8
View File
@@ -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<S, A> {
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
/// Internal props
props: TextFieldProps,
/// Internal state
@@ -137,11 +137,7 @@ impl<S: 'static, A: 'static> Widget for TextField<S, A> {
fn new(_state: &S, action_tx: UnboundedSender<A>) -> 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<S: 'static, A: 'static> Widget for TextField<S, A> {
}
}
fn base_mut(&mut self) -> &mut BaseView<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
+8 -8
View File
@@ -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<W>,
{
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
/// 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<A>) -> 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<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
+8 -8
View File
@@ -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<S, A>,
base: WidgetBase<S, A>,
/// 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<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
+15 -15
View File
@@ -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<Id> {
@@ -37,7 +37,7 @@ impl<Id> BoxedAny for WindowProps<Id> {}
pub struct Window<S, A, Id> {
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
/// Internal properties
props: WindowProps<Id>,
/// 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<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}
@@ -159,7 +159,7 @@ pub struct Shortcuts<S, A> {
/// Internal properties
props: ShortcutsProps,
/// Internal base
base: BaseView<S, A>,
base: WidgetBase<S, A>,
}
impl<S, A> Shortcuts<S, A> {
@@ -185,11 +185,7 @@ impl<S, A> Widget for Shortcuts<S, A> {
fn new(_state: &S, action_tx: UnboundedSender<A>) -> 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<S, A> Widget for Shortcuts<S, A> {
frame.render_widget(table, props.area);
}
fn base_mut(&mut self) -> &mut BaseView<S, A> {
fn base(&self) -> &WidgetBase<S, A> {
&self.base
}
fn base_mut(&mut self) -> &mut WidgetBase<S, A> {
&mut self.base
}
}