This commit is contained in:
ClementTsang 2021-11-29 23:21:48 -05:00
parent 63dcaae8e8
commit 7029412929
6 changed files with 65 additions and 24 deletions

View File

@ -8,11 +8,17 @@ use tui::{backend::Backend, layout::Rect, Frame};
use super::{Event, Status}; use super::{Event, Status};
/// A [`Component`] is an element that displays information and can be interacted with. pub type ShouldRender = bool;
/// A is an element that displays information and can be interacted with.
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait Component { pub trait Component {
/// How to inform a component after some event takes place. Typically some enum.
type Message: 'static; type Message: 'static;
/// Information passed to the component from its parent.
type Properties;
/// Handles an [`Event`]. Defaults to just ignoring the event. /// Handles an [`Event`]. Defaults to just ignoring the event.
fn on_event( fn on_event(
&mut self, bounds: Rect, event: Event, messages: &mut Vec<Self::Message>, &mut self, bounds: Rect, event: Event, messages: &mut Vec<Self::Message>,
@ -20,8 +26,16 @@ pub trait Component {
Status::Ignored Status::Ignored
} }
fn update(&mut self, message: Self::Message) {} /// How the component should handle a [`Self::Message`]. Defaults to doing nothing.
fn update(&mut self, message: Self::Message) -> ShouldRender {
false
}
/// Draws the [`Component`]. /// How the component should handle an update to its properties. Defaults to doing nothing.
fn change(&mut self, props: Self::Properties) -> ShouldRender {
false
}
/// Draws the component.
fn draw<B: Backend>(&mut self, bounds: Rect, frame: &mut Frame<'_, B>); fn draw<B: Backend>(&mut self, bounds: Rect, frame: &mut Frame<'_, B>);
} }

View File

View File

View File

View File

@ -0,0 +1,35 @@
use super::Component;
/// A [`Component`] to handle keyboard shortcuts and assign actions to them.
///
/// Inspired by [Flutter's approach](https://docs.flutter.dev/development/ui/advanced/actions_and_shortcuts).
pub struct Shortcut<Msg: 'static> {
_p: std::marker::PhantomData<Msg>,
}
impl<Msg> Component for Shortcut<Msg> {
type Message = Msg;
type Properties = ();
fn on_event(
&mut self, bounds: tui::layout::Rect, event: crate::tuine::Event,
messages: &mut Vec<Self::Message>,
) -> crate::tuine::Status {
crate::tuine::Status::Ignored
}
fn update(&mut self, message: Self::Message) -> super::ShouldRender {
false
}
fn change(&mut self, props: Self::Properties) -> super::ShouldRender {
false
}
fn draw<B: tui::backend::Backend>(
&mut self, bounds: tui::layout::Rect, frame: &mut tui::Frame<'_, B>,
) {
todo!()
}
}

View File

@ -20,7 +20,7 @@ use crate::{
pub use self::table_column::{TextColumn, TextColumnConstraint}; pub use self::table_column::{TextColumn, TextColumnConstraint};
use self::table_scroll_state::ScrollState as TextTableState; use self::table_scroll_state::ScrollState as TextTableState;
use super::Component; use super::{Component, ShouldRender};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct StyleSheet { pub struct StyleSheet {
@ -29,7 +29,7 @@ pub struct StyleSheet {
table_header: Style, table_header: Style,
} }
struct TextTableMsg {} pub enum TextTableMsg {}
/// A sortable, scrollable table for text data. /// A sortable, scrollable table for text data.
pub struct TextTable<'a> { pub struct TextTable<'a> {
@ -59,8 +59,6 @@ impl<'a> TextTable<'a> {
style_sheet: StyleSheet::default(), style_sheet: StyleSheet::default(),
sortable: false, sortable: false,
table_gap: 0, table_gap: 0,
on_select: None,
on_select_click: None,
} }
} }
@ -89,22 +87,6 @@ impl<'a> TextTable<'a> {
self self
} }
/// What [`Message`] to send when a row is selected.
///
/// Defaults to `None` (doing nothing).
pub fn on_select(mut self, on_select: Option<Message>) -> Self {
self.on_select = on_select;
self
}
/// What [`Message`] to send if a selected row is clicked on.
///
/// Defaults to `None` (doing nothing).
pub fn on_select_click(mut self, on_select_click: Option<Message>) -> Self {
self.on_select_click = on_select_click;
self
}
fn update_column_widths(&mut self, bounds: Rect) { fn update_column_widths(&mut self, bounds: Rect) {
let total_width = bounds.width; let total_width = bounds.width;
let mut width_remaining = bounds.width; let mut width_remaining = bounds.width;
@ -157,7 +139,11 @@ impl<'a> TextTable<'a> {
impl<'a> Component for TextTable<'a> { impl<'a> Component for TextTable<'a> {
type Message = TextTableMsg; type Message = TextTableMsg;
fn on_event(&mut self, bounds: Rect, event: Event, messages: &mut Vec<Message>) -> Status { type Properties = ();
fn on_event(
&mut self, bounds: Rect, event: Event, messages: &mut Vec<Self::Message>,
) -> Status {
use crate::tuine::MouseBoundIntersect; use crate::tuine::MouseBoundIntersect;
use crossterm::event::{MouseButton, MouseEventKind}; use crossterm::event::{MouseButton, MouseEventKind};
@ -190,6 +176,12 @@ impl<'a> Component for TextTable<'a> {
} }
} }
fn update(&mut self, message: Self::Message) -> ShouldRender {
match message {}
true
}
fn draw<B: Backend>(&mut self, bounds: Rect, frame: &mut Frame<'_, B>) { fn draw<B: Backend>(&mut self, bounds: Rect, frame: &mut Frame<'_, B>) {
self.table_gap = if !self.show_gap self.table_gap = if !self.show_gap
|| (self.data.len() + 2 > bounds.height.into() || (self.data.len() + 2 > bounds.height.into()