Switch back to dyn for now

This commit is contained in:
ClementTsang 2021-12-16 22:58:09 -05:00
parent c002a3fa3a
commit 40a92e2562
14 changed files with 142 additions and 87 deletions

View File

@ -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<std::io::Stdout>;
pub(crate) type CrosstermBackend = tui::backend::CrosstermBackend<std::io::Stdout>;
#[allow(unused_variables)]
pub trait Application: Sized {
pub trait Application<B = CrosstermBackend>: 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<A, B>(
application: A, receiver: Receiver<RuntimeEvent<A::Message>>, terminal: &mut Terminal<B>,
) -> anyhow::Result<()>
where
A: Application + 'static,
B: tui::backend::Backend,
A: Application<B> + 'static,
B: Backend,
{
runtime::launch(application, receiver, terminal)
}

View File

@ -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<Message> {
pub trait Component<Message, Backend>
where
Backend: tui::backend::Backend,
{
/// Draws the component.
fn draw<Backend>(&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`].
///

View File

@ -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<Message> TmpComponent<Message> for Block {
fn draw<B>(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>)
impl<Message, B: Backend> Component<Message, B> for Block {
fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>)
where
B: Backend,
{

View File

@ -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<Message> TmpComponent<Message> for Carousel {
fn draw<B>(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>)
where
B: Backend,
{
impl<Message, B: Backend> Component<Message, B> for Carousel {
fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) {
todo!()
}

View File

@ -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<Message> for Container<'a, Message> {
fn draw<B>(&mut self, context: DrawContext<'_>, _frame: &mut Frame<'_, B>)
where
B: Backend,
{
impl<'a, Message, B: Backend> Component<Message, B> for Container<'a, Message> {
fn draw(&mut self, context: DrawContext<'_>, _frame: &mut Frame<'_, B>) {
todo!()
}

View File

@ -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<FlexElement<'a, Message>>,
pub struct Flex<'a, Message, B: Backend> {
children: Vec<FlexElement<'a, Message, B>>,
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<C>(children: Vec<C>) -> Self
where
C: Into<FlexElement<'a, Message>>,
C: Into<FlexElement<'a, Message, B>>,
{
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<C>(children: Vec<C>) -> Self
where
C: Into<FlexElement<'a, Message>>,
C: Into<FlexElement<'a, Message, B>>,
{
Self {
children: children.into_iter().map(Into::into).collect(),
@ -67,7 +67,7 @@ impl<'a, Message> Flex<'a, Message> {
pub fn with_child<E>(mut self, child: E) -> Self
where
E: Into<Element<'a, Message>>,
E: Into<Element<'a, Message, B>>,
{
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<E>(mut self, child: E, flex: u16) -> Self
where
E: Into<Element<'a, Message>>,
E: Into<Element<'a, Message, B>>,
{
self.children
.push(FlexElement::with_flex(child.into(), flex));
@ -83,11 +83,8 @@ impl<'a, Message> Flex<'a, Message> {
}
}
impl<'a, Message> TmpComponent<Message> for Flex<'a, Message> {
fn draw<B>(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>)
where
B: Backend,
{
impl<'a, Message, B: Backend> Component<Message, B> 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<Message> for Flex<'a, Message> {
current_size
}
}
impl<'a, Message, B: Backend> From<Flex<'a, Message, B>> for Element<'a, Message, B>
where
Message: 'a,
B: 'a,
{
fn from(flex: Flex<'a, Message, B>) -> Self {
Element::new(flex)
}
}

View File

@ -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<I: Into<Element<'a, Message>>>(element: I) -> Self {
impl<'a, Message, B: Backend> FlexElement<'a, Message, B> {
pub fn new<I: Into<Element<'a, Message, B>>>(element: I) -> Self {
Self {
flex: 1,
element: element.into(),
}
}
pub fn with_flex<I: Into<Element<'a, Message>>>(element: I, flex: u16) -> Self {
pub fn with_flex<I: Into<Element<'a, Message, B>>>(element: I, flex: u16) -> Self {
Self {
flex,
element: element.into(),
}
}
pub fn with_no_flex<I: Into<Element<'a, Message>>>(element: I) -> Self {
pub fn with_no_flex<I: Into<Element<'a, Message, B>>>(element: I) -> Self {
Self {
flex: 0,
element: element.into(),
@ -37,10 +37,7 @@ impl<'a, Message> FlexElement<'a, Message> {
self
}
pub(crate) fn draw<B>(&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<Element<'a, Message>> for FlexElement<'a, Message> {
fn from(element: Element<'a, Message>) -> Self {
impl<'a, Message, B: Backend> From<Element<'a, Message, B>> for FlexElement<'a, Message, B> {
fn from(element: Element<'a, Message, B>) -> Self {
Self { flex: 0, element }
}
}

View File

@ -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;

View File

@ -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<Message> TmpComponent<Message> for Shortcut {
fn draw<B>(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>)
where
B: Backend,
{
impl<Message, B: Backend> Component<Message, B> for Shortcut {
fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) {
todo!()
}

View File

@ -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<Message> for TextTable<'a, Message> {
fn draw<B>(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>)
where
B: Backend,
{
impl<'a, Message, B: Backend> Component<Message, B> 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<Message> for TextTable<'a, Message> {
}
}
impl<'a, Message, B: Backend> From<TextTable<'a, Message>> 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 {}

View File

@ -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,
}

View File

@ -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<Message>)]
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<Stdout>>
where
B: Backend,
{
component: Box<dyn Component<Message, B> + 'a>,
}
impl<'a, Message, B> Element<'a, Message, B>
where
B: Backend,
{
pub fn new<C: Component<Message, B> + '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<Message>) -> 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)
}
}

View File

@ -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<Message>(rect: Rect, root: &Element<'_, Message>) -> LayoutNode {
pub fn build_layout_tree<Message, B: Backend>(
rect: Rect, root: &Element<'_, Message, B>,
) -> LayoutNode {
let mut root_layout_node = LayoutNode::from_rect(rect);
let bounds = Bounds {
min_width: 0,

View File

@ -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<Message> {
@ -17,7 +17,7 @@ pub(crate) fn launch<A, B>(
mut application: A, receiver: Receiver<RuntimeEvent<A::Message>>, terminal: &mut Terminal<B>,
) -> anyhow::Result<()>
where
A: Application + 'static,
A: Application<B> + 'static,
B: Backend,
{
let mut user_interface = application.view();
@ -67,7 +67,9 @@ where
Ok(())
}
fn draw<M, B>(user_interface: &mut Element<'_, M>, terminal: &mut Terminal<B>) -> anyhow::Result<()>
fn draw<M, B>(
user_interface: &mut Element<'_, M, B>, terminal: &mut Terminal<B>,
) -> anyhow::Result<()>
where
B: Backend,
{