From fd291575a618b10bb4e730ae0522f104d94e93d3 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Wed, 15 Dec 2021 23:30:18 -0500 Subject: Switch back to dyn for now --- src/tuice/application.rs | 15 ++++---- src/tuice/component.rs | 11 +++--- src/tuice/component/base/block.rs | 6 ++-- src/tuice/component/base/carousel.rs | 9 ++--- src/tuice/component/base/container.rs | 9 ++--- src/tuice/component/base/flex.rs | 33 ++++++++++------- src/tuice/component/base/flex/flex_element.rs | 23 ++++++------ src/tuice/component/base/mod.rs | 2 +- src/tuice/component/base/shortcut.rs | 9 ++--- src/tuice/component/base/text_table.rs | 26 ++++++++++---- src/tuice/component/properties.rs | 19 +++++++++- src/tuice/element.rs | 51 ++++++++++++++++++++------- src/tuice/layout/build_layout.rs | 8 +++-- src/tuice/runtime.rs | 8 +++-- 14 files changed, 142 insertions(+), 87 deletions(-) diff --git a/src/tuice/application.rs b/src/tuice/application.rs index 540c8a3c..68457e6f 100644 --- a/src/tuice/application.rs +++ b/src/tuice/application.rs @@ -1,6 +1,6 @@ use std::{fmt::Debug, sync::mpsc::Receiver}; -use tui::Terminal; +use tui::{backend::Backend, Terminal}; use super::{ runtime::{self, RuntimeEvent}, @@ -8,10 +8,13 @@ use super::{ }; /// An alias to the [`tui::backend::CrosstermBackend`] writing to [`std::io::Stdout`]. -pub type CrosstermBackend = tui::backend::CrosstermBackend; +pub(crate) type CrosstermBackend = tui::backend::CrosstermBackend; #[allow(unused_variables)] -pub trait Application: Sized { +pub trait Application: Sized +where + B: Backend, +{ type Message: Debug; /// Determines how to handle a given message. @@ -21,7 +24,7 @@ pub trait Application: Sized { /// always returning false. fn is_terminated(&self) -> bool; - fn view(&mut self) -> Element<'static, Self::Message>; + fn view(&mut self) -> Element<'static, Self::Message, B>; /// To run upon stopping the application. fn destroy(&mut self) {} @@ -39,8 +42,8 @@ pub fn launch_with_application( application: A, receiver: Receiver>, terminal: &mut Terminal, ) -> anyhow::Result<()> where - A: Application + 'static, - B: tui::backend::Backend, + A: Application + 'static, + B: Backend, { runtime::launch(application, receiver, terminal) } diff --git a/src/tuice/component.rs b/src/tuice/component.rs index 753f36ba..d479a188 100644 --- a/src/tuice/component.rs +++ b/src/tuice/component.rs @@ -7,19 +7,18 @@ pub use widget::*; pub mod properties; pub use properties::*; -use enum_dispatch::enum_dispatch; use tui::{layout::Rect, Frame}; use super::{Bounds, DrawContext, Event, LayoutNode, Size, Status}; /// A component displays information and can be interacted with. #[allow(unused_variables)] -#[enum_dispatch] -pub trait TmpComponent { +pub trait Component +where + Backend: tui::backend::Backend, +{ /// Draws the component. - fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, Backend>) - where - Backend: tui::backend::Backend; + fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, Backend>); /// How a component should react to an [`Event`]. /// diff --git a/src/tuice/component/base/block.rs b/src/tuice/component/base/block.rs index d1a7e2b9..c9386112 100644 --- a/src/tuice/component/base/block.rs +++ b/src/tuice/component/base/block.rs @@ -1,11 +1,11 @@ use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{DrawContext, Event, Status, TmpComponent}; +use crate::tuice::{Component, DrawContext, Event, Status}; pub struct Block {} -impl TmpComponent for Block { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) +impl Component for Block { + fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) where B: Backend, { diff --git a/src/tuice/component/base/carousel.rs b/src/tuice/component/base/carousel.rs index 2790e372..468774fc 100644 --- a/src/tuice/component/base/carousel.rs +++ b/src/tuice/component/base/carousel.rs @@ -1,14 +1,11 @@ use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{DrawContext, Event, Status, TmpComponent}; +use crate::tuice::{Component, DrawContext, Event, Status}; pub struct Carousel {} -impl TmpComponent for Carousel { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where - B: Backend, - { +impl Component for Carousel { + fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) { todo!() } diff --git a/src/tuice/component/base/container.rs b/src/tuice/component/base/container.rs index f30937ca..67872d8e 100644 --- a/src/tuice/component/base/container.rs +++ b/src/tuice/component/base/container.rs @@ -1,6 +1,6 @@ use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuice::{Bounds, Component, DrawContext, Element, Event, LayoutNode, Size, Status}; /// A [`Container`] just contains a child, as well as being able to be sized. /// @@ -37,11 +37,8 @@ impl<'a, Message> Container<'a, Message> { } } -impl<'a, Message> TmpComponent for Container<'a, Message> { - fn draw(&mut self, context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where - B: Backend, - { +impl<'a, Message, B: Backend> Component for Container<'a, Message> { + fn draw(&mut self, context: DrawContext<'_>, _frame: &mut Frame<'_, B>) { todo!() } diff --git a/src/tuice/component/base/flex.rs b/src/tuice/component/base/flex.rs index 1aefca0e..98172955 100644 --- a/src/tuice/component/base/flex.rs +++ b/src/tuice/component/base/flex.rs @@ -3,7 +3,7 @@ use tui::{backend::Backend, layout::Rect, Frame}; pub mod flex_element; pub use flex_element::FlexElement; -use crate::tuice::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuice::{Bounds, Component, DrawContext, Element, Event, LayoutNode, Size, Status}; #[derive(Clone, Copy, Debug)] pub enum Axis { @@ -14,12 +14,12 @@ pub enum Axis { Vertical, } -pub struct Flex<'a, Message> { - children: Vec>, +pub struct Flex<'a, Message, B: Backend> { + children: Vec>, alignment: Axis, } -impl<'a, Message> Flex<'a, Message> { +impl<'a, Message, B: Backend> Flex<'a, Message, B> { pub fn new(alignment: Axis) -> Self { Self { children: vec![], @@ -38,7 +38,7 @@ impl<'a, Message> Flex<'a, Message> { /// Creates a new [`Flex`] with a horizontal alignment with the given children. pub fn row_with_children(children: Vec) -> Self where - C: Into>, + C: Into>, { Self { children: children.into_iter().map(Into::into).collect(), @@ -57,7 +57,7 @@ impl<'a, Message> Flex<'a, Message> { /// Creates a new [`Flex`] with a vertical alignment with the given children. pub fn column_with_children(children: Vec) -> Self where - C: Into>, + C: Into>, { Self { children: children.into_iter().map(Into::into).collect(), @@ -67,7 +67,7 @@ impl<'a, Message> Flex<'a, Message> { pub fn with_child(mut self, child: E) -> Self where - E: Into>, + E: Into>, { self.children.push(FlexElement::with_no_flex(child.into())); self @@ -75,7 +75,7 @@ impl<'a, Message> Flex<'a, Message> { pub fn with_flex_child(mut self, child: E, flex: u16) -> Self where - E: Into>, + E: Into>, { self.children .push(FlexElement::with_flex(child.into(), flex)); @@ -83,11 +83,8 @@ impl<'a, Message> Flex<'a, Message> { } } -impl<'a, Message> TmpComponent for Flex<'a, Message> { - fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) - where - B: Backend, - { +impl<'a, Message, B: Backend> Component for Flex<'a, Message, B> { + fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) { self.children .iter_mut() .zip(context.children()) @@ -194,3 +191,13 @@ impl<'a, Message> TmpComponent for Flex<'a, Message> { current_size } } + +impl<'a, Message, B: Backend> From> for Element<'a, Message, B> +where + Message: 'a, + B: 'a, +{ + fn from(flex: Flex<'a, Message, B>) -> Self { + Element::new(flex) + } +} diff --git a/src/tuice/component/base/flex/flex_element.rs b/src/tuice/component/base/flex/flex_element.rs index 43982f56..b961f281 100644 --- a/src/tuice/component/base/flex/flex_element.rs +++ b/src/tuice/component/base/flex/flex_element.rs @@ -1,31 +1,31 @@ use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuice::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status}; use super::Axis; -pub struct FlexElement<'a, Message> { +pub struct FlexElement<'a, Message, B: Backend> { /// Represents a ratio with other [`FlexElement`]s on how far to expand. pub flex: u16, - element: Element<'a, Message>, + element: Element<'a, Message, B>, } -impl<'a, Message> FlexElement<'a, Message> { - pub fn new>>(element: I) -> Self { +impl<'a, Message, B: Backend> FlexElement<'a, Message, B> { + pub fn new>>(element: I) -> Self { Self { flex: 1, element: element.into(), } } - pub fn with_flex>>(element: I, flex: u16) -> Self { + pub fn with_flex>>(element: I, flex: u16) -> Self { Self { flex, element: element.into(), } } - pub fn with_no_flex>>(element: I) -> Self { + pub fn with_no_flex>>(element: I) -> Self { Self { flex: 0, element: element.into(), @@ -37,10 +37,7 @@ impl<'a, Message> FlexElement<'a, Message> { self } - pub(crate) fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) - where - B: Backend, - { + pub(crate) fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) { self.element.draw(context, frame) } @@ -81,8 +78,8 @@ impl<'a, Message> FlexElement<'a, Message> { } } -impl<'a, Message> From> for FlexElement<'a, Message> { - fn from(element: Element<'a, Message>) -> Self { +impl<'a, Message, B: Backend> From> for FlexElement<'a, Message, B> { + fn from(element: Element<'a, Message, B>) -> Self { Self { flex: 0, element } } } diff --git a/src/tuice/component/base/mod.rs b/src/tuice/component/base/mod.rs index 7e7932c4..a72b4a39 100644 --- a/src/tuice/component/base/mod.rs +++ b/src/tuice/component/base/mod.rs @@ -1,5 +1,5 @@ pub mod text_table; -pub use text_table::{TextColumn, TextColumnConstraint, TextTable}; +pub use text_table::{TextColumn, TextColumnConstraint, TextTable, TextTableProps}; pub mod shortcut; pub use shortcut::Shortcut; diff --git a/src/tuice/component/base/shortcut.rs b/src/tuice/component/base/shortcut.rs index 661c45d7..7354f609 100644 --- a/src/tuice/component/base/shortcut.rs +++ b/src/tuice/component/base/shortcut.rs @@ -1,17 +1,14 @@ use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{DrawContext, Event, Status, TmpComponent}; +use crate::tuice::{Component, DrawContext, Event, Status}; /// 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 {} -impl TmpComponent for Shortcut { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where - B: Backend, - { +impl Component for Shortcut { + fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) { todo!() } diff --git a/src/tuice/component/base/text_table.rs b/src/tuice/component/base/text_table.rs index b6d04a77..782313b9 100644 --- a/src/tuice/component/base/text_table.rs +++ b/src/tuice/component/base/text_table.rs @@ -14,12 +14,13 @@ use unicode_segmentation::UnicodeSegmentation; use crate::{ constants::TABLE_GAP_HEIGHT_LIMIT, - tuice::{DrawContext, Event, Status, TmpComponent}, + tuice::{Component, DrawContext, Element, Event, Properties, Status}, }; pub use self::table_column::{TextColumn, TextColumnConstraint}; use self::table_scroll_state::ScrollState as TextTableState; +/// Styles for a [`TextTable`]. #[derive(Clone, Debug, Default)] pub struct StyleSheet { text: Style, @@ -27,7 +28,11 @@ pub struct StyleSheet { table_header: Style, } -pub enum TextTableMsg {} +/// Properties for a [`TextTable`]. +#[derive(PartialEq, Clone, Debug)] +pub struct TextTableProps {} + +impl Properties for TextTableProps {} /// A sortable, scrollable table for text data. pub struct TextTable<'a, Message> { @@ -165,11 +170,8 @@ impl<'a, Message> TextTable<'a, Message> { } } -impl<'a, Message> TmpComponent for TextTable<'a, Message> { - fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) - where - B: Backend, - { +impl<'a, Message, B: Backend> Component for TextTable<'a, Message> { + fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) { let rect = context.rect(); self.table_gap = if !self.show_gap @@ -272,5 +274,15 @@ impl<'a, Message> TmpComponent for TextTable<'a, Message> { } } +impl<'a, Message, B: Backend> From> for Element<'a, Message, B> +where + Message: 'a, + B: 'a, +{ + fn from(text_table: TextTable<'a, Message>) -> Self { + Element::new(text_table) + } +} + #[cfg(test)] mod tests {} diff --git a/src/tuice/component/properties.rs b/src/tuice/component/properties.rs index d44ab368..5b6d6fc1 100644 --- a/src/tuice/component/properties.rs +++ b/src/tuice/component/properties.rs @@ -1,3 +1,20 @@ +use enum_dispatch::enum_dispatch; + +use crate::tuice::*; + /// A trait that the properties of a [`Component`](super::Component) /// should implement. -pub trait Properties: PartialEq {} +#[enum_dispatch] +pub trait Properties: PartialEq + Clone {} + +#[derive(PartialEq, Clone, Debug)] +pub struct DefaultProp; + +impl Properties for DefaultProp {} + +#[enum_dispatch(Properties)] +#[derive(PartialEq, Clone)] +pub enum Props { + DefaultProp, + TextTableProps, +} diff --git a/src/tuice/element.rs b/src/tuice/element.rs index fe859e79..d7c1b432 100644 --- a/src/tuice/element.rs +++ b/src/tuice/element.rs @@ -1,18 +1,43 @@ -use enum_dispatch::enum_dispatch; -use tui::{layout::Rect, Frame}; +use std::io::Stdout; -use super::{ - Block, Bounds, Carousel, Container, DrawContext, Event, Flex, LayoutNode, Shortcut, Size, - Status, TextTable, TmpComponent, +use tui::{ + backend::{Backend, CrosstermBackend}, + layout::Rect, + Frame, }; +use super::*; + /// An [`Element`] is an instantiated [`Component`]. -#[enum_dispatch(TmpComponent)] -pub enum Element<'a, Message> { - Block, - Carousel, - Container(Container<'a, Message>), - Flex(Flex<'a, Message>), - Shortcut, - TextTable(TextTable<'a, Message>), +pub struct Element<'a, Message, B = CrosstermBackend> +where + B: Backend, +{ + component: Box + 'a>, +} + +impl<'a, Message, B> Element<'a, Message, B> +where + B: Backend, +{ + pub fn new + 'a>(component: C) -> Self { + Self { + component: Box::new(component), + } + } + + /// Draws the element. + pub fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) { + self.component.draw(context, frame) + } + + /// How an element should react to an [`Event`]. + pub fn on_event(&mut self, area: Rect, event: Event, messages: &mut Vec) -> Status { + self.component.on_event(area, event, messages) + } + + /// How an element should size itself and its children, given some [`Bounds`]. + pub fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size { + self.component.layout(bounds, node) + } } diff --git a/src/tuice/layout/build_layout.rs b/src/tuice/layout/build_layout.rs index d9fdddae..0002b0fc 100644 --- a/src/tuice/layout/build_layout.rs +++ b/src/tuice/layout/build_layout.rs @@ -1,8 +1,10 @@ -use tui::layout::Rect; +use tui::{backend::Backend, layout::Rect}; -use crate::tuice::{Bounds, Element, LayoutNode, TmpComponent}; +use crate::tuice::{Bounds, Element, LayoutNode}; -pub fn build_layout_tree(rect: Rect, root: &Element<'_, Message>) -> LayoutNode { +pub fn build_layout_tree( + rect: Rect, root: &Element<'_, Message, B>, +) -> LayoutNode { let mut root_layout_node = LayoutNode::from_rect(rect); let bounds = Bounds { min_width: 0, diff --git a/src/tuice/runtime.rs b/src/tuice/runtime.rs index 1666c21d..c592dba7 100644 --- a/src/tuice/runtime.rs +++ b/src/tuice/runtime.rs @@ -4,7 +4,7 @@ use tui::{backend::Backend, layout::Rect, Terminal}; use crate::tuice::Status; -use super::{build_layout_tree, Application, Element, Event, TmpComponent}; +use super::{build_layout_tree, Application, Element, Event}; #[derive(Clone, Copy, Debug)] pub enum RuntimeEvent { @@ -17,7 +17,7 @@ pub(crate) fn launch( mut application: A, receiver: Receiver>, terminal: &mut Terminal, ) -> anyhow::Result<()> where - A: Application + 'static, + A: Application + 'static, B: Backend, { let mut user_interface = application.view(); @@ -67,7 +67,9 @@ where Ok(()) } -fn draw(user_interface: &mut Element<'_, M>, terminal: &mut Terminal) -> anyhow::Result<()> +fn draw( + user_interface: &mut Element<'_, M, B>, terminal: &mut Terminal, +) -> anyhow::Result<()> where B: Backend, { -- cgit v1.2.3