diff --git a/src/app.rs b/src/app.rs index b8581381..00d3e415 100644 --- a/src/app.rs +++ b/src/app.rs @@ -29,7 +29,7 @@ use frozen_state::FrozenState; use crate::{ canvas::Painter, constants, - tuine::{Application, Element, Flex, ViewContext}, + tuine::{Application, Element, Flex, Shortcut, ViewContext}, units::data_units::DataUnit, Pid, }; @@ -241,7 +241,10 @@ impl Application for AppState { Flex::column() .with_flex_child( Flex::row_with_children(vec![ - FlexElement::new(TextTable::new(ctx, vec!["A", "B", "C"])), + FlexElement::new(Shortcut::with_child(TextTable::new( + ctx, + vec!["A", "B", "C"], + ))), FlexElement::new(TextTable::new(ctx, vec!["D", "E", "F"])), ]), 1, diff --git a/src/tuine/component.rs b/src/tuine/component.rs index 2cea82ec..71699bb6 100644 --- a/src/tuine/component.rs +++ b/src/tuine/component.rs @@ -15,7 +15,7 @@ use super::{Bounds, DrawContext, Event, LayoutNode, Size, StateContext, Status}; pub trait TmpComponent { /// Draws the component. fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, Backend>, ) where Backend: tui::backend::Backend; @@ -24,7 +24,7 @@ pub trait TmpComponent { /// /// Defaults to just ignoring the event. fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { Status::Ignored diff --git a/src/tuine/component/base/block.rs b/src/tuine/component/base/block.rs index 86df5537..fd94cec1 100644 --- a/src/tuine/component/base/block.rs +++ b/src/tuine/component/base/block.rs @@ -6,7 +6,7 @@ pub struct Block {} impl TmpComponent for Block { fn draw( - &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: &DrawContext<'_>, _frame: &mut Frame<'_, B>, ) where B: Backend, @@ -15,7 +15,7 @@ impl TmpComponent for Block { } fn on_event( - &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, _event: Event, + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: &DrawContext<'_>, _event: Event, _messages: &mut Vec, ) -> Status { Status::Ignored diff --git a/src/tuine/component/base/carousel.rs b/src/tuine/component/base/carousel.rs index 0c27967d..ea63d096 100644 --- a/src/tuine/component/base/carousel.rs +++ b/src/tuine/component/base/carousel.rs @@ -6,7 +6,7 @@ pub struct Carousel {} impl TmpComponent for Carousel { fn draw( - &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: &DrawContext<'_>, _frame: &mut Frame<'_, B>, ) where B: Backend, @@ -15,7 +15,7 @@ impl TmpComponent for Carousel { } fn on_event( - &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, _event: Event, + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: &DrawContext<'_>, _event: Event, _messages: &mut Vec, ) -> Status { Status::Ignored diff --git a/src/tuine/component/base/container.rs b/src/tuine/component/base/container.rs index afb86be3..5e12572d 100644 --- a/src/tuine/component/base/container.rs +++ b/src/tuine/component/base/container.rs @@ -1,4 +1,4 @@ -use tui::{backend::Backend, Frame}; +use tui::{backend::Backend, layout::Rect, Frame}; use crate::tuine::{ Bounds, DrawContext, Element, Event, LayoutNode, Size, StateContext, Status, TmpComponent, @@ -15,16 +15,22 @@ pub struct Container<'a, Message> { } impl<'a, Message> Container<'a, Message> { - pub fn with_child(child: Element<'a, Message>) -> Self { + pub fn with_child(child: C) -> Self + where + C: Into>, + { Self { width: None, height: None, - child: Some(child.into()), + child: Some(Box::new(child.into())), } } - pub fn child(mut self, child: Option>) -> Self { - self.child = child.map(|c| c.into()); + pub fn child(mut self, child: Option) -> Self + where + C: Into>, + { + self.child = child.map(|c| Box::new(c.into())); self } @@ -41,25 +47,25 @@ impl<'a, Message> Container<'a, Message> { impl<'a, Message> TmpComponent for Container<'a, Message> { fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, B>, ) where B: Backend, { if let Some(child) = &mut self.child { if let Some(child_draw_ctx) = draw_ctx.children().next() { - child.draw(state_ctx, child_draw_ctx, frame) + child.draw(state_ctx, &child_draw_ctx, frame) } } } fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { if let Some(child) = &mut self.child { if let Some(child_draw_ctx) = draw_ctx.children().next() { - return child.on_event(state_ctx, child_draw_ctx, event, messages); + return child.on_event(state_ctx, &child_draw_ctx, event, messages); } } @@ -94,6 +100,7 @@ impl<'a, Message> TmpComponent for Container<'a, Message> { }; let child_size = child.layout(child_bounds, &mut child_node); + child_node.rect = Rect::new(0, 0, child_size.width, child_size.height); node.children = vec![child_node]; // Note that this is implicitly bounded by our above calculations, diff --git a/src/tuine/component/base/empty.rs b/src/tuine/component/base/empty.rs new file mode 100644 index 00000000..ff026b24 --- /dev/null +++ b/src/tuine/component/base/empty.rs @@ -0,0 +1,16 @@ +use tui::{backend::Backend, Frame}; + +use crate::tuine::{DrawContext, StateContext, TmpComponent}; + +#[derive(Default)] +pub struct Empty {} + +impl TmpComponent for Empty { + fn draw( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: &DrawContext<'_>, + _frame: &mut Frame<'_, B>, + ) where + B: Backend, + { + } +} diff --git a/src/tuine/component/base/flex.rs b/src/tuine/component/base/flex.rs index 4fb0d505..e07c83df 100644 --- a/src/tuine/component/base/flex.rs +++ b/src/tuine/component/base/flex.rs @@ -87,7 +87,7 @@ impl<'a, Message> Flex<'a, Message> { impl<'a, Message> TmpComponent for Flex<'a, Message> { fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, B>, ) where B: Backend, @@ -97,17 +97,17 @@ impl<'a, Message> TmpComponent for Flex<'a, Message> { .zip(draw_ctx.children()) .for_each(|(child, child_draw_ctx)| { if child_draw_ctx.should_draw() { - child.draw(state_ctx, child_draw_ctx, frame); + child.draw(state_ctx, &child_draw_ctx, frame); } }); } fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { for (child, child_draw_ctx) in self.children.iter_mut().zip(draw_ctx.children()) { - match child.on_event(state_ctx, child_draw_ctx, event, messages) { + match child.on_event(state_ctx, &child_draw_ctx, event, messages) { Status::Captured => { return Status::Captured; } diff --git a/src/tuine/component/base/flex/flex_element.rs b/src/tuine/component/base/flex/flex_element.rs index a5797760..baad1bda 100644 --- a/src/tuine/component/base/flex/flex_element.rs +++ b/src/tuine/component/base/flex/flex_element.rs @@ -40,7 +40,7 @@ impl<'a, Message> FlexElement<'a, Message> { } pub(crate) fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, B>, ) where B: Backend, @@ -49,7 +49,7 @@ impl<'a, Message> FlexElement<'a, Message> { } pub(crate) fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { self.element.on_event(state_ctx, draw_ctx, event, messages) diff --git a/src/tuine/component/base/mod.rs b/src/tuine/component/base/mod.rs index 7e7932c4..1255cf1c 100644 --- a/src/tuine/component/base/mod.rs +++ b/src/tuine/component/base/mod.rs @@ -15,3 +15,6 @@ pub use carousel::Carousel; pub mod container; pub use container::Container; + +pub mod empty; +pub use empty::Empty; diff --git a/src/tuine/component/base/shortcut.rs b/src/tuine/component/base/shortcut.rs index 63fc49e7..f6d29317 100644 --- a/src/tuine/component/base/shortcut.rs +++ b/src/tuine/component/base/shortcut.rs @@ -1,6 +1,5 @@ -use crossterm::event::KeyEvent; use rustc_hash::FxHashMap; -use tui::{backend::Backend, Frame}; +use tui::{backend::Backend, layout::Rect, Frame}; use crate::tuine::{ Bounds, DrawContext, Element, Event, LayoutNode, Size, StateContext, Status, TmpComponent, @@ -18,7 +17,7 @@ pub struct Shortcut<'a, Message> { dyn Fn( &mut Element<'a, Message>, &mut StateContext<'_>, - DrawContext<'_>, + &DrawContext<'_>, Event, &mut Vec, ) -> Status, @@ -27,41 +26,73 @@ pub struct Shortcut<'a, Message> { } impl<'a, Message> Shortcut<'a, Message> { - pub fn with_child(child: Element<'a, Message>) -> Self { + pub fn with_child(child: C) -> Self + where + C: Into>, + { Self { - child: Some(child.into()), + child: Some(Box::new(child.into())), shortcuts: Default::default(), } } + + pub fn child(mut self, child: Option) -> Self + where + C: Into>, + { + self.child = child.map(|c| Box::new(c.into())); + self + } + + pub fn shortcut( + mut self, event: Event, + f: Box< + dyn Fn( + &mut Element<'a, Message>, + &mut StateContext<'_>, + &DrawContext<'_>, + Event, + &mut Vec, + ) -> Status, + >, + ) -> Self { + self.shortcuts.insert(event, f); + self + } + + pub fn remove_shortcut(mut self, event: &Event) -> Self { + self.shortcuts.remove(event); + self + } } impl<'a, Message> TmpComponent for Shortcut<'a, Message> { fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, B>, ) where B: Backend, { if let Some(child) = &mut self.child { if let Some(child_draw_ctx) = draw_ctx.children().next() { - child.draw(state_ctx, child_draw_ctx, frame) + child.draw(state_ctx, &child_draw_ctx, frame) } } } fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { if let Some(child_draw_ctx) = draw_ctx.children().next() { if let Some(child) = &mut self.child { - match child.on_event(state_ctx, child_draw_ctx, event, messages) { + match child.on_event(state_ctx, &child_draw_ctx, event, messages) { Status::Captured => { return Status::Captured; } Status::Ignored => { if let Some(f) = self.shortcuts.get(&event) { - return f(child, state_ctx, child_draw_ctx, event, messages); + return f(child, state_ctx, &child_draw_ctx, event, messages); } } } @@ -72,6 +103,19 @@ impl<'a, Message> TmpComponent for Shortcut<'a, Message> { } fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size { - todo!() + if let Some(child) = &self.child { + let mut child_node = LayoutNode::default(); + let child_size = child.layout(bounds, &mut child_node); + + child_node.rect = Rect::new(0, 0, child_size.width, child_size.height); + node.children = vec![child_node]; + + child_size + } else { + Size { + width: 0, + height: 0, + } + } } } diff --git a/src/tuine/component/base/text_table.rs b/src/tuine/component/base/text_table.rs index 91f7e8f6..57fe7f68 100644 --- a/src/tuine/component/base/text_table.rs +++ b/src/tuine/component/base/text_table.rs @@ -166,7 +166,7 @@ impl<'a, Message> TextTable<'a, Message> { impl<'a, Message> TmpComponent for TextTable<'a, Message> { fn draw( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, frame: &mut Frame<'_, B>, ) where B: Backend, @@ -220,7 +220,7 @@ impl<'a, Message> TmpComponent for TextTable<'a, Message> { } fn on_event( - &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, event: Event, messages: &mut Vec, ) -> Status { use crate::tuine::MouseBoundIntersect; diff --git a/src/tuine/element.rs b/src/tuine/element.rs index 55abed35..ec976d76 100644 --- a/src/tuine/element.rs +++ b/src/tuine/element.rs @@ -2,8 +2,8 @@ use enum_dispatch::enum_dispatch; use tui::Frame; use super::{ - Block, Bounds, Carousel, Container, DrawContext, Event, Flex, LayoutNode, Shortcut, Size, - StateContext, Status, TextTable, TmpComponent, + Block, Bounds, Carousel, Container, DrawContext, Empty, Event, Flex, LayoutNode, Shortcut, + Size, StateContext, Status, TextTable, TmpComponent, }; /// An [`Element`] is an instantiated [`Component`]. @@ -15,4 +15,5 @@ pub enum Element<'a, Message> { Flex(Flex<'a, Message>), Shortcut(Shortcut<'a, Message>), TextTable(TextTable<'a, Message>), + Empty, } diff --git a/src/tuine/runtime.rs b/src/tuine/runtime.rs index cbe11da4..80ab84c2 100644 --- a/src/tuine/runtime.rs +++ b/src/tuine/runtime.rs @@ -84,7 +84,7 @@ fn on_event( let mut state_ctx = StateContext::new(&mut app_data.state_map); let draw_ctx = DrawContext::root(&layout); - match user_interface.on_event(&mut state_ctx, draw_ctx, event, &mut messages) { + match user_interface.on_event(&mut state_ctx, &draw_ctx, event, &mut messages) { Status::Captured => { // TODO: What to do on capture? } @@ -124,7 +124,7 @@ where let mut state_ctx = StateContext::new(&mut app_data.state_map); let draw_ctx = DrawContext::root(&layout); - user_interface.draw(&mut state_ctx, draw_ctx, frame); + user_interface.draw(&mut state_ctx, &draw_ctx, frame); })?; Ok(())