From c1bbe7627d7c3583f67c714026d1836080d6bd94 Mon Sep 17 00:00:00 2001 From: ClementTsang Date: Wed, 22 Dec 2021 21:43:30 -0500 Subject: [PATCH] oh nyo --- src/app.rs | 12 +++---- src/tuine/application.rs | 4 +-- src/tuine/component/base/text_table.rs | 10 +++--- src/tuine/context.rs | 47 +++++++++++++++++--------- src/tuine/runtime.rs | 25 +++++++++++--- src/tuine/state.rs | 6 ++++ 6 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/app.rs b/src/app.rs index ed2238a3..e98ac670 100644 --- a/src/app.rs +++ b/src/app.rs @@ -29,7 +29,7 @@ use frozen_state::FrozenState; use crate::{ canvas::Painter, constants, - tuine::{Application, ComponentContext, Element, Flex}, + tuine::{Application, Element, Flex, ViewContext}, units::data_units::DataUnit, Pid, }; @@ -234,22 +234,22 @@ impl Application for AppState { self.terminator.load(SeqCst) } - fn view(&mut self) -> Element<'static, Self::Message> { + fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<'static, Self::Message> { use crate::tuine::FlexElement; use crate::tuine::TextTable; Flex::column() .with_flex_child( Flex::row_with_children(vec![ - FlexElement::new(TextTable::new(vec!["A", "B", "C"])), - FlexElement::new(TextTable::new(vec!["D", "E", "F"])), + FlexElement::new(TextTable::new(ctx, vec!["A", "B", "C"])), + FlexElement::new(TextTable::new(ctx, vec!["D", "E", "F"])), ]), 1, ) .with_flex_child( Flex::row_with_children(vec![ - FlexElement::new(TextTable::new(vec!["G", "H", "I", "J"])), - FlexElement::new(TextTable::new(vec!["K", "L", "M", "N"])), + FlexElement::new(TextTable::new(ctx, vec!["G", "H", "I", "J"])), + FlexElement::new(TextTable::new(ctx, vec!["K", "L", "M", "N"])), ]), 2, ) diff --git a/src/tuine/application.rs b/src/tuine/application.rs index c56d791d..adea9ce0 100644 --- a/src/tuine/application.rs +++ b/src/tuine/application.rs @@ -4,7 +4,7 @@ use tui::Terminal; use super::{ runtime::{self, RuntimeEvent}, - ComponentContext, Element, Event, + Element, Event, ViewContext, }; /// An alias to the [`tui::backend::CrosstermBackend`] writing to [`std::io::Stdout`]. @@ -21,7 +21,7 @@ pub trait Application: Sized { /// always returning false. fn is_terminated(&self) -> bool; - fn view(&mut self) -> Element<'static, Self::Message>; + fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<'static, Self::Message>; /// To run upon stopping the application. fn destroy(&mut self) {} diff --git a/src/tuine/component/base/text_table.rs b/src/tuine/component/base/text_table.rs index 78964df6..f7a4ce08 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::{ComponentContext, DrawContext, Event, Status, TmpComponent}, + tuine::{DrawContext, Event, Status, TmpComponent, ViewContext}, }; pub use self::table_column::{TextColumn, TextColumnConstraint}; @@ -29,6 +29,7 @@ pub struct StyleSheet { /// A sortable, scrollable table for text data. pub struct TextTable<'a, Message> { + test_state: &'a mut TextTableState, state: TextTableState, column_widths: Vec, columns: Vec, @@ -44,11 +45,12 @@ pub struct TextTable<'a, Message> { impl<'a, Message> TextTable<'a, Message> { #[track_caller] - pub fn new>>(columns: Vec) -> Self { - let state = TextTableState::default(); + pub fn new>>(ctx: &mut ViewContext<'_>, columns: Vec) -> Self { + let test_state = ctx.state::(Location::caller()); Self { - state, + test_state, + state: TextTableState::default(), column_widths: vec![0; columns.len()], columns: columns .into_iter() diff --git a/src/tuine/context.rs b/src/tuine/context.rs index 0772351e..2510e802 100644 --- a/src/tuine/context.rs +++ b/src/tuine/context.rs @@ -1,4 +1,4 @@ -use std::panic::Location; +use std::{panic::Location, rc::Rc}; use rustc_hash::FxHashMap; use tui::layout::Rect; @@ -6,26 +6,41 @@ use tui::layout::Rect; use super::{Key, LayoutNode, State}; #[derive(Default)] -pub struct ComponentContext { - key_counter: usize, - state_map: FxHashMap>, - stale_map: FxHashMap, +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() + } } -impl ComponentContext { - pub fn access_or_new( - &mut self, location: &'static Location<'static>, - ) -> &mut Box { - let key = Key::new(location, self.key_counter); - self.key_counter += 1; +pub struct ViewContext<'a> { + key_counter: usize, + state_map: &'a mut StateMap, +} - *(self.stale_map.entry(key).or_insert(true)) = true; - self.state_map - .entry(key) - .or_insert_with(|| Box::new(S::default())) +impl<'a> ViewContext<'a> { + pub fn new(state_map: &'a mut StateMap) -> Self { + Self { + key_counter: 0, + state_map, + } } - pub fn cycle(&mut self) {} + 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> { diff --git a/src/tuine/runtime.rs b/src/tuine/runtime.rs index 1062ea0b..0ac9e28f 100644 --- a/src/tuine/runtime.rs +++ b/src/tuine/runtime.rs @@ -1,10 +1,13 @@ use std::sync::mpsc::Receiver; +use rustc_hash::FxHashMap; use tui::{backend::Backend, layout::Rect, Terminal}; use crate::tuine::Status; -use super::{build_layout_tree, Application, ComponentContext, Element, Event, TmpComponent}; +use super::{ + build_layout_tree, Application, Element, Event, Key, State, StateMap, TmpComponent, ViewContext, +}; #[derive(Clone, Copy, Debug)] pub enum RuntimeEvent { @@ -13,6 +16,11 @@ pub enum RuntimeEvent { Custom(Message), } +#[derive(Default)] +struct AppData { + state_map: StateMap, +} + pub(crate) fn launch( mut application: A, receiver: Receiver>, terminal: &mut Terminal, ) -> anyhow::Result<()> @@ -20,8 +28,13 @@ where A: Application + 'static, B: Backend, { - let mut user_interface = application.view(); - draw(&mut user_interface, terminal)?; + let mut app_data = AppData::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)?; + ui + }; while !application.is_terminated() { if let Ok(event) = receiver.recv() { @@ -42,7 +55,8 @@ where application.update(msg); } - user_interface = application.view(); + let mut ctx = ViewContext::new(&mut app_data.state_map); + user_interface = application.view(&mut ctx); draw(&mut user_interface, terminal)?; } RuntimeEvent::Custom(message) => { @@ -52,7 +66,8 @@ where width: _, height: _, } => { - user_interface = application.view(); + let mut ctx = ViewContext::new(&mut app_data.state_map); + user_interface = application.view(&mut ctx); // FIXME: Also nuke any cache and the like... draw(&mut user_interface, terminal)?; } diff --git a/src/tuine/state.rs b/src/tuine/state.rs index 0228ab3a..db654eee 100644 --- a/src/tuine/state.rs +++ b/src/tuine/state.rs @@ -7,6 +7,8 @@ use std::any::Any; pub trait State { fn as_any(&self) -> &dyn Any; + fn as_mut_any(&mut self) -> &mut dyn Any; + fn are_equal(&self, other: &dyn State) -> bool; } @@ -15,6 +17,10 @@ impl State for S { self } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } + fn are_equal(&self, other: &dyn State) -> bool { other .as_any()