mirror of
https://seed.radicle.dev/rad:z39mP9rQAaGmERfUMPULfPUi473tY.git
synced 2026-11-16 00:10:38 +08:00
lib: Improve widget base
This commit is contained in:
+24
-12
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user