diff --git a/src/app.rs b/src/app.rs index e98ac670..8ec79085 100644 --- a/src/app.rs +++ b/src/app.rs @@ -256,7 +256,7 @@ impl Application for AppState { .into() } - fn destroy(&mut self) { + fn destructor(&mut self) { // TODO: Eventually move some thread logic into the app creation, and destroy here? } diff --git a/src/tuine/application.rs b/src/tuine/application.rs index adea9ce0..e2bb5815 100644 --- a/src/tuine/application.rs +++ b/src/tuine/application.rs @@ -24,7 +24,7 @@ pub trait Application: Sized { fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<'static, Self::Message>; /// To run upon stopping the application. - fn destroy(&mut self) {} + fn destructor(&mut self) {} /// An optional event handler, intended for use with global shortcuts or events. /// This will be run *after* trying to send the events into the user interface, and diff --git a/src/tuine/component.rs b/src/tuine/component.rs index 42cc7d53..2cea82ec 100644 --- a/src/tuine/component.rs +++ b/src/tuine/component.rs @@ -5,23 +5,28 @@ pub mod widget; pub use widget::*; use enum_dispatch::enum_dispatch; -use tui::{layout::Rect, Frame}; +use tui::Frame; -use super::{Bounds, DrawContext, Event, LayoutNode, Size, Status}; +use super::{Bounds, DrawContext, Event, LayoutNode, Size, StateContext, Status}; /// A component displays information and can be interacted with. #[allow(unused_variables)] #[enum_dispatch] pub trait TmpComponent { /// Draws the component. - fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, Backend>) - where + fn draw( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + frame: &mut Frame<'_, Backend>, + ) where Backend: tui::backend::Backend; /// How a component should react to an [`Event`]. /// /// Defaults to just ignoring the event. - fn on_event(&mut self, area: Rect, event: Event, messages: &mut Vec) -> Status { + fn on_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 209dadf5..86df5537 100644 --- a/src/tuine/component/base/block.rs +++ b/src/tuine/component/base/block.rs @@ -1,18 +1,23 @@ -use tui::{backend::Backend, layout::Rect, Frame}; +use tui::{backend::Backend, Frame}; -use crate::tuine::{DrawContext, Event, Status, TmpComponent}; +use crate::tuine::{DrawContext, Event, StateContext, Status, TmpComponent}; pub struct Block {} impl TmpComponent for Block { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where + fn draw( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + _frame: &mut Frame<'_, B>, + ) where B: Backend, { todo!() } - fn on_event(&mut self, _area: Rect, _event: Event, _messages: &mut Vec) -> Status { + fn on_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 a129681f..0c27967d 100644 --- a/src/tuine/component/base/carousel.rs +++ b/src/tuine/component/base/carousel.rs @@ -1,18 +1,23 @@ -use tui::{backend::Backend, layout::Rect, Frame}; +use tui::{backend::Backend, Frame}; -use crate::tuine::{DrawContext, Event, Status, TmpComponent}; +use crate::tuine::{DrawContext, Event, StateContext, Status, TmpComponent}; pub struct Carousel {} impl TmpComponent for Carousel { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where + fn draw( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + _frame: &mut Frame<'_, B>, + ) where B: Backend, { todo!() } - fn on_event(&mut self, _area: Rect, _event: Event, _messages: &mut Vec) -> Status { + fn on_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 82c3cd3e..d3702f8a 100644 --- a/src/tuine/component/base/container.rs +++ b/src/tuine/component/base/container.rs @@ -1,6 +1,8 @@ -use tui::{backend::Backend, layout::Rect, Frame}; +use tui::{backend::Backend, Frame}; -use crate::tuine::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuine::{ + Bounds, DrawContext, Element, Event, LayoutNode, Size, StateContext, Status, TmpComponent, +}; /// A [`Container`] just contains a child, as well as being able to be sized. /// @@ -38,14 +40,19 @@ 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 + fn draw( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + _frame: &mut Frame<'_, B>, + ) where B: Backend, { todo!() } - fn on_event(&mut self, _area: Rect, _event: Event, _messages: &mut Vec) -> Status { + fn on_event( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, _event: Event, + _messages: &mut Vec, + ) -> Status { todo!() } diff --git a/src/tuine/component/base/flex.rs b/src/tuine/component/base/flex.rs index 608d75d5..043c5f3e 100644 --- a/src/tuine/component/base/flex.rs +++ b/src/tuine/component/base/flex.rs @@ -3,7 +3,9 @@ use tui::{backend::Backend, layout::Rect, Frame}; pub mod flex_element; pub use flex_element::FlexElement; -use crate::tuine::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuine::{ + Bounds, DrawContext, Element, Event, LayoutNode, Size, StateContext, Status, TmpComponent, +}; #[derive(Clone, Copy, Debug)] pub enum Axis { @@ -84,21 +86,26 @@ 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 + fn draw( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + frame: &mut Frame<'_, B>, + ) where B: Backend, { self.children .iter_mut() - .zip(context.children()) + .zip(draw_ctx.children()) .for_each(|(child, child_node)| { if child_node.should_draw() { - child.draw(child_node, frame); + child.draw(state_ctx, child_node, frame); } }); } - fn on_event(&mut self, area: Rect, event: Event, messages: &mut Vec) -> Status { + fn on_event( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, event: Event, + messages: &mut Vec, + ) -> Status { // FIXME: On event for flex Status::Ignored diff --git a/src/tuine/component/base/flex/flex_element.rs b/src/tuine/component/base/flex/flex_element.rs index 4972a351..a5797760 100644 --- a/src/tuine/component/base/flex/flex_element.rs +++ b/src/tuine/component/base/flex/flex_element.rs @@ -1,6 +1,8 @@ -use tui::{backend::Backend, layout::Rect, Frame}; +use tui::{backend::Backend, Frame}; -use crate::tuine::{Bounds, DrawContext, Element, Event, LayoutNode, Size, Status, TmpComponent}; +use crate::tuine::{ + Bounds, DrawContext, Element, Event, LayoutNode, Size, StateContext, Status, TmpComponent, +}; use super::Axis; @@ -37,17 +39,20 @@ impl<'a, Message> FlexElement<'a, Message> { self } - pub(crate) fn draw(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) - where + pub(crate) fn draw( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + frame: &mut Frame<'_, B>, + ) where B: Backend, { - self.element.draw(context, frame) + self.element.draw(state_ctx, draw_ctx, frame) } pub(crate) fn on_event( - &mut self, area: Rect, event: Event, messages: &mut Vec, + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + messages: &mut Vec, ) -> Status { - self.element.on_event(area, event, messages) + self.element.on_event(state_ctx, draw_ctx, event, messages) } /// Assumes the flex is 0. Just calls layout on its child. diff --git a/src/tuine/component/base/shortcut.rs b/src/tuine/component/base/shortcut.rs index cb93b00f..0dd964c8 100644 --- a/src/tuine/component/base/shortcut.rs +++ b/src/tuine/component/base/shortcut.rs @@ -1,6 +1,6 @@ -use tui::{backend::Backend, layout::Rect, Frame}; +use tui::{backend::Backend, Frame}; -use crate::tuine::{DrawContext, Event, Status, TmpComponent}; +use crate::tuine::{DrawContext, Event, StateContext, Status, TmpComponent}; /// A [`Component`] to handle keyboard shortcuts and assign actions to them. /// @@ -8,14 +8,19 @@ use crate::tuine::{DrawContext, Event, Status, TmpComponent}; pub struct Shortcut {} impl TmpComponent for Shortcut { - fn draw(&mut self, _context: DrawContext<'_>, _frame: &mut Frame<'_, B>) - where + fn draw( + &mut self, _state_ctx: &mut StateContext<'_>, _draw_ctx: DrawContext<'_>, + _frame: &mut Frame<'_, B>, + ) where B: Backend, { todo!() } - fn on_event(&mut self, _area: Rect, _event: Event, _messages: &mut Vec) -> Status { + fn on_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/text_table.rs b/src/tuine/component/base/text_table.rs index f7a4ce08..60464b84 100644 --- a/src/tuine/component/base/text_table.rs +++ b/src/tuine/component/base/text_table.rs @@ -14,7 +14,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::{ constants::TABLE_GAP_HEIGHT_LIMIT, - tuine::{DrawContext, Event, Status, TmpComponent, ViewContext}, + tuine::{DrawContext, Event, Key, StateContext, Status, TmpComponent, ViewContext}, }; pub use self::table_column::{TextColumn, TextColumnConstraint}; @@ -29,7 +29,7 @@ pub struct StyleSheet { /// A sortable, scrollable table for text data. pub struct TextTable<'a, Message> { - test_state: &'a mut TextTableState, + key: Key, state: TextTableState, column_widths: Vec, columns: Vec, @@ -46,10 +46,8 @@ pub struct TextTable<'a, Message> { impl<'a, Message> TextTable<'a, Message> { #[track_caller] pub fn new>>(ctx: &mut ViewContext<'_>, columns: Vec) -> Self { - let test_state = ctx.state::(Location::caller()); - Self { - test_state, + key: ctx.register_component(Location::caller()), state: TextTableState::default(), column_widths: vec![0; columns.len()], columns: columns @@ -169,11 +167,14 @@ 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 + fn draw( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, + frame: &mut Frame<'_, B>, + ) where B: Backend, { - let rect = context.rect(); + let rect = draw_ctx.rect(); + let state = state_ctx.mut_state::(self.key); self.table_gap = if !self.show_gap || (self.rows.len() + 2 > rect.height.into() && rect.height < TABLE_GAP_HEIGHT_LIMIT) @@ -198,10 +199,8 @@ impl<'a, Message> TmpComponent for TextTable<'a, Message> { // as well as truncating some entries based on available width. let data_slice = { // Note: `get_list_start` already ensures `start` is within the bounds of the number of items, so no need to check! - let start = self - .state - .display_start_index(rect, scrollable_height as usize); - let end = min(self.state.num_items(), start + scrollable_height as usize); + let start = state.display_start_index(rect, scrollable_height as usize); + let end = min(state.num_items(), start + scrollable_height as usize); self.rows[start..end].to_vec() }; @@ -219,13 +218,19 @@ impl<'a, Message> TmpComponent for TextTable<'a, Message> { table = table.highlight_style(self.style_sheet.selected_text); } - frame.render_stateful_widget(table.widths(&widths), rect, self.state.tui_state()); + frame.render_stateful_widget(table.widths(&widths), rect, state.tui_state()); } - fn on_event(&mut self, area: Rect, event: Event, messages: &mut Vec) -> Status { + fn on_event( + &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: DrawContext<'_>, event: Event, + messages: &mut Vec, + ) -> Status { use crate::tuine::MouseBoundIntersect; use crossterm::event::{MouseButton, MouseEventKind}; + let rect = draw_ctx.rect(); + let state = state_ctx.mut_state::(self.key); + match event { Event::Keyboard(key_event) => { if key_event.modifiers.is_empty() { @@ -237,10 +242,10 @@ impl<'a, Message> TmpComponent for TextTable<'a, Message> { } } Event::Mouse(mouse_event) => { - if mouse_event.does_mouse_intersect_bounds(area) { + if mouse_event.does_mouse_intersect_bounds(rect) { match mouse_event.kind { MouseEventKind::Down(MouseButton::Left) => { - let y = mouse_event.row - area.top(); + let y = mouse_event.row - rect.top(); if self.sortable && y == 0 { todo!() diff --git a/src/tuine/context.rs b/src/tuine/context.rs deleted file mode 100644 index 2510e802..00000000 --- a/src/tuine/context.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::{panic::Location, rc::Rc}; - -use rustc_hash::FxHashMap; -use tui::layout::Rect; - -use super::{Key, LayoutNode, State}; - -#[derive(Default)] -pub struct StateMap(FxHashMap>, bool)>); - -impl StateMap { - pub fn state(&mut self, key: Key) -> Rc> { - let state = self - .0 - .entry(key) - .or_insert_with(|| (Rc::new(Box::new(S::default())), true)); - - state.1 = true; - - state.0.clone() - } -} - -pub struct ViewContext<'a> { - key_counter: usize, - state_map: &'a mut StateMap, -} - -impl<'a> ViewContext<'a> { - pub fn new(state_map: &'a mut StateMap) -> Self { - Self { - key_counter: 0, - state_map, - } - } - - pub fn state( - &mut self, location: &'static Location<'static>, - ) -> Rc> { - let key = Key::new(location, self.key_counter); - self.key_counter += 1; - self.state_map.state::(key) - } -} - -pub struct DrawContext<'a> { - current_node: &'a LayoutNode, - current_offset: (u16, u16), -} - -impl<'a> DrawContext<'_> { - /// Creates a new [`DrawContext`], with the offset set to `(0, 0)`. - pub(crate) fn root(root: &'a LayoutNode) -> DrawContext<'a> { - DrawContext { - current_node: root, - current_offset: (0, 0), - } - } - - pub(crate) fn rect(&self) -> Rect { - let mut rect = self.current_node.rect; - rect.x += self.current_offset.0; - rect.y += self.current_offset.1; - - rect - } - - pub(crate) fn should_draw(&self) -> bool { - self.current_node.rect.area() != 0 - } - - pub(crate) fn children(&self) -> impl Iterator> { - let new_offset = ( - self.current_offset.0 + self.current_node.rect.x, - self.current_offset.1 + self.current_node.rect.y, - ); - - self.current_node - .children - .iter() - .map(move |layout_node| DrawContext { - current_node: layout_node, - current_offset: new_offset, - }) - } -} diff --git a/src/tuine/context/draw_context.rs b/src/tuine/context/draw_context.rs new file mode 100644 index 00000000..97c885dc --- /dev/null +++ b/src/tuine/context/draw_context.rs @@ -0,0 +1,45 @@ +use tui::layout::Rect; + +use crate::tuine::LayoutNode; + +pub struct DrawContext<'a> { + current_node: &'a LayoutNode, + current_offset: (u16, u16), +} + +impl<'a> DrawContext<'a> { + /// Creates a new [`DrawContext`], with the offset set to `(0, 0)`. + pub(crate) fn root(root: &'a LayoutNode) -> DrawContext<'a> { + DrawContext { + current_node: root, + current_offset: (0, 0), + } + } + + pub(crate) fn rect(&self) -> Rect { + let mut rect = self.current_node.rect; + rect.x += self.current_offset.0; + rect.y += self.current_offset.1; + + rect + } + + pub(crate) fn should_draw(&self) -> bool { + self.current_node.rect.area() != 0 + } + + pub(crate) fn children(&'a self) -> impl Iterator> { + let new_offset = ( + self.current_offset.0 + self.current_node.rect.x, + self.current_offset.1 + self.current_node.rect.y, + ); + + self.current_node + .children + .iter() + .map(move |layout_node| DrawContext { + current_node: layout_node, + current_offset: new_offset, + }) + } +} diff --git a/src/tuine/context/mod.rs b/src/tuine/context/mod.rs new file mode 100644 index 00000000..e4fc90e8 --- /dev/null +++ b/src/tuine/context/mod.rs @@ -0,0 +1,11 @@ +pub mod state_map; +pub use state_map::StateMap; + +pub mod draw_context; +pub use draw_context::DrawContext; + +pub mod view_context; +pub use view_context::ViewContext; + +pub mod state_context; +pub use state_context::StateContext; diff --git a/src/tuine/context/state_context.rs b/src/tuine/context/state_context.rs new file mode 100644 index 00000000..8ee1dd4f --- /dev/null +++ b/src/tuine/context/state_context.rs @@ -0,0 +1,19 @@ +use crate::tuine::{Key, State, StateMap}; + +pub struct StateContext<'a> { + state_map: &'a mut StateMap, +} + +impl<'a> StateContext<'a> { + pub fn new(state_map: &'a mut StateMap) -> Self { + Self { state_map } + } + + pub fn state(&mut self, key: Key) -> &S { + self.state_map.state::(key) + } + + pub fn mut_state(&mut self, key: Key) -> &mut S { + self.state_map.mut_state::(key) + } +} diff --git a/src/tuine/context/state_map.rs b/src/tuine/context/state_map.rs new file mode 100644 index 00000000..8d0cb241 --- /dev/null +++ b/src/tuine/context/state_map.rs @@ -0,0 +1,38 @@ +use rustc_hash::FxHashMap; + +use crate::tuine::{Key, State}; + +#[derive(Default)] +pub struct StateMap(FxHashMap, bool)>); + +impl StateMap { + pub fn state(&mut self, key: Key) -> &S { + let state = self + .0 + .entry(key) + .or_insert_with(|| (Box::new(S::default()), true)); + + state.1 = true; + + state + .0 + .as_any() + .downcast_ref() + .expect("Successful downcast of state.") + } + + pub fn mut_state(&mut self, key: Key) -> &mut S { + let state = self + .0 + .entry(key) + .or_insert_with(|| (Box::new(S::default()), true)); + + state.1 = true; + + state + .0 + .as_mut_any() + .downcast_mut() + .expect("Successful downcast of state.") + } +} diff --git a/src/tuine/context/view_context.rs b/src/tuine/context/view_context.rs new file mode 100644 index 00000000..fccea752 --- /dev/null +++ b/src/tuine/context/view_context.rs @@ -0,0 +1,30 @@ +use crate::tuine::{Caller, Key, State, StateMap}; + +use super::StateContext; + +pub struct ViewContext<'a> { + key_counter: usize, + state_context: StateContext<'a>, +} + +impl<'a> ViewContext<'a> { + pub fn new(state_map: &'a mut StateMap) -> Self { + Self { + key_counter: 0, + state_context: StateContext::new(state_map), + } + } + + pub fn register_component>(&mut self, caller: C) -> Key { + self.key_counter += 1; + Key::new(caller.into(), self.key_counter) + } + + pub fn state(&mut self, key: Key) -> &S { + self.state_context.state(key) + } + + pub fn mut_state(&mut self, key: Key) -> &mut S { + self.state_context.mut_state(key) + } +} diff --git a/src/tuine/element.rs b/src/tuine/element.rs index fe859e79..3f30940b 100644 --- a/src/tuine/element.rs +++ b/src/tuine/element.rs @@ -1,9 +1,9 @@ use enum_dispatch::enum_dispatch; -use tui::{layout::Rect, Frame}; +use tui::Frame; use super::{ Block, Bounds, Carousel, Container, DrawContext, Event, Flex, LayoutNode, Shortcut, Size, - Status, TextTable, TmpComponent, + StateContext, Status, TextTable, TmpComponent, }; /// An [`Element`] is an instantiated [`Component`]. diff --git a/src/tuine/runtime.rs b/src/tuine/runtime.rs index 0ac9e28f..452f8adc 100644 --- a/src/tuine/runtime.rs +++ b/src/tuine/runtime.rs @@ -1,12 +1,12 @@ use std::sync::mpsc::Receiver; -use rustc_hash::FxHashMap; -use tui::{backend::Backend, layout::Rect, Terminal}; +use tui::{backend::Backend, Terminal}; use crate::tuine::Status; use super::{ - build_layout_tree, Application, Element, Event, Key, State, StateMap, TmpComponent, ViewContext, + build_layout_tree, Application, DrawContext, Element, Event, LayoutNode, StateContext, + StateMap, TmpComponent, ViewContext, }; #[derive(Clone, Copy, Debug)] @@ -29,10 +29,11 @@ where B: Backend, { let mut app_data = AppData::default(); + let mut layout: LayoutNode = LayoutNode::default(); + let mut user_interface = { - let mut ctx = ViewContext::new(&mut app_data.state_map); - let mut ui = application.view(&mut ctx); - draw(&mut ui, terminal)?; + let mut ui = new_user_interface(&mut application, &mut app_data); + draw(&mut ui, terminal, &mut app_data, &mut layout)?; ui }; @@ -40,24 +41,15 @@ where if let Ok(event) = receiver.recv() { match event { RuntimeEvent::UserInterface(event) => { - let mut messages = vec![]; - - let rect = Rect::default(); // FIXME: TEMP - match user_interface.on_event(rect, event, &mut messages) { - Status::Captured => {} - Status::Ignored => { - application.global_event_handler(event, &mut messages); - } - } - - for msg in messages { - debug!("Message: {:?}", msg); // FIXME: Remove this debug line! - application.update(msg); - } - - let mut ctx = ViewContext::new(&mut app_data.state_map); - user_interface = application.view(&mut ctx); - draw(&mut user_interface, terminal)?; + on_event( + &mut application, + &mut user_interface, + &mut app_data, + &mut layout, + event, + ); + user_interface = new_user_interface(&mut application, &mut app_data); + draw(&mut user_interface, terminal, &mut app_data, &mut layout)?; } RuntimeEvent::Custom(message) => { application.update(message); @@ -66,10 +58,9 @@ where width: _, height: _, } => { - let mut ctx = ViewContext::new(&mut app_data.state_map); - user_interface = application.view(&mut ctx); + user_interface = new_user_interface(&mut application, &mut app_data); // FIXME: Also nuke any cache and the like... - draw(&mut user_interface, terminal)?; + draw(&mut user_interface, terminal, &mut app_data, &mut layout)?; } } } else { @@ -77,21 +68,60 @@ where } } - application.destroy(); + application.destructor(); Ok(()) } -fn draw(user_interface: &mut Element<'_, M>, terminal: &mut Terminal) -> anyhow::Result<()> +fn on_event( + application: &mut A, user_interface: &mut Element<'_, A::Message>, app_data: &mut AppData, + layout: &mut LayoutNode, event: Event, +) where + A: Application + 'static, +{ + let mut messages = vec![]; + 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) { + Status::Captured => { + // TODO: What to do on capture? + } + Status::Ignored => { + application.global_event_handler(event, &mut messages); + } + } + + for msg in messages { + debug!("Message: {:?}", msg); // FIXME: Remove this debug line! + application.update(msg); + } +} + +fn new_user_interface( + application: &mut A, app_data: &mut AppData, +) -> Element<'static, A::Message> +where + A: Application + 'static, +{ + let mut ctx = ViewContext::new(&mut app_data.state_map); + application.view(&mut ctx) +} + +fn draw( + user_interface: &mut Element<'_, M>, terminal: &mut Terminal, app_data: &mut AppData, + layout: &mut LayoutNode, +) -> anyhow::Result<()> where B: Backend, { terminal.draw(|frame| { let rect = frame.size(); - let layout = build_layout_tree(rect, &user_interface); - let context = super::DrawContext::root(&layout); + *layout = build_layout_tree(rect, &user_interface); + let mut state_ctx = StateContext::new(&mut app_data.state_map); + let draw_ctx = DrawContext::root(&layout); - user_interface.draw(context, frame); + user_interface.draw(&mut state_ctx, draw_ctx, frame); })?; Ok(())