This commit is contained in:
ClementTsang 2021-12-22 21:43:30 -05:00
parent 190615cd35
commit c1bbe7627d
6 changed files with 71 additions and 33 deletions

View File

@ -29,7 +29,7 @@ use frozen_state::FrozenState;
use crate::{ use crate::{
canvas::Painter, canvas::Painter,
constants, constants,
tuine::{Application, ComponentContext, Element, Flex}, tuine::{Application, Element, Flex, ViewContext},
units::data_units::DataUnit, units::data_units::DataUnit,
Pid, Pid,
}; };
@ -234,22 +234,22 @@ impl Application for AppState {
self.terminator.load(SeqCst) 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::FlexElement;
use crate::tuine::TextTable; use crate::tuine::TextTable;
Flex::column() Flex::column()
.with_flex_child( .with_flex_child(
Flex::row_with_children(vec![ Flex::row_with_children(vec![
FlexElement::new(TextTable::new(vec!["A", "B", "C"])), FlexElement::new(TextTable::new(ctx, vec!["A", "B", "C"])),
FlexElement::new(TextTable::new(vec!["D", "E", "F"])), FlexElement::new(TextTable::new(ctx, vec!["D", "E", "F"])),
]), ]),
1, 1,
) )
.with_flex_child( .with_flex_child(
Flex::row_with_children(vec![ Flex::row_with_children(vec![
FlexElement::new(TextTable::new(vec!["G", "H", "I", "J"])), FlexElement::new(TextTable::new(ctx, vec!["G", "H", "I", "J"])),
FlexElement::new(TextTable::new(vec!["K", "L", "M", "N"])), FlexElement::new(TextTable::new(ctx, vec!["K", "L", "M", "N"])),
]), ]),
2, 2,
) )

View File

@ -4,7 +4,7 @@ use tui::Terminal;
use super::{ use super::{
runtime::{self, RuntimeEvent}, runtime::{self, RuntimeEvent},
ComponentContext, Element, Event, Element, Event, ViewContext,
}; };
/// An alias to the [`tui::backend::CrosstermBackend`] writing to [`std::io::Stdout`]. /// An alias to the [`tui::backend::CrosstermBackend`] writing to [`std::io::Stdout`].
@ -21,7 +21,7 @@ pub trait Application: Sized {
/// always returning false. /// always returning false.
fn is_terminated(&self) -> bool; 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. /// To run upon stopping the application.
fn destroy(&mut self) {} fn destroy(&mut self) {}

View File

@ -14,7 +14,7 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::{ use crate::{
constants::TABLE_GAP_HEIGHT_LIMIT, constants::TABLE_GAP_HEIGHT_LIMIT,
tuine::{ComponentContext, DrawContext, Event, Status, TmpComponent}, tuine::{DrawContext, Event, Status, TmpComponent, ViewContext},
}; };
pub use self::table_column::{TextColumn, TextColumnConstraint}; pub use self::table_column::{TextColumn, TextColumnConstraint};
@ -29,6 +29,7 @@ pub struct StyleSheet {
/// A sortable, scrollable table for text data. /// A sortable, scrollable table for text data.
pub struct TextTable<'a, Message> { pub struct TextTable<'a, Message> {
test_state: &'a mut TextTableState,
state: TextTableState, state: TextTableState,
column_widths: Vec<u16>, column_widths: Vec<u16>,
columns: Vec<TextColumn>, columns: Vec<TextColumn>,
@ -44,11 +45,12 @@ pub struct TextTable<'a, Message> {
impl<'a, Message> TextTable<'a, Message> { impl<'a, Message> TextTable<'a, Message> {
#[track_caller] #[track_caller]
pub fn new<S: Into<Cow<'static, str>>>(columns: Vec<S>) -> Self { pub fn new<S: Into<Cow<'static, str>>>(ctx: &mut ViewContext<'_>, columns: Vec<S>) -> Self {
let state = TextTableState::default(); let test_state = ctx.state::<TextTableState>(Location::caller());
Self { Self {
state, test_state,
state: TextTableState::default(),
column_widths: vec![0; columns.len()], column_widths: vec![0; columns.len()],
columns: columns columns: columns
.into_iter() .into_iter()

View File

@ -1,4 +1,4 @@
use std::panic::Location; use std::{panic::Location, rc::Rc};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use tui::layout::Rect; use tui::layout::Rect;
@ -6,26 +6,41 @@ use tui::layout::Rect;
use super::{Key, LayoutNode, State}; use super::{Key, LayoutNode, State};
#[derive(Default)] #[derive(Default)]
pub struct ComponentContext { pub struct StateMap(FxHashMap<Key, (Rc<Box<dyn State>>, bool)>);
key_counter: usize,
state_map: FxHashMap<Key, Box<dyn State>>, impl StateMap {
stale_map: FxHashMap<Key, bool>, pub fn state<S: State + Default + 'static>(&mut self, key: Key) -> Rc<Box<dyn State>> {
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 struct ViewContext<'a> {
pub fn access_or_new<S: State + Default + 'static>( 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<S: State + Default + 'static>(
&mut self, location: &'static Location<'static>, &mut self, location: &'static Location<'static>,
) -> &mut Box<dyn State> { ) -> Rc<Box<dyn State>> {
let key = Key::new(location, self.key_counter); let key = Key::new(location, self.key_counter);
self.key_counter += 1; self.key_counter += 1;
self.state_map.state::<S>(key)
*(self.stale_map.entry(key).or_insert(true)) = true;
self.state_map
.entry(key)
.or_insert_with(|| Box::new(S::default()))
} }
pub fn cycle(&mut self) {}
} }
pub struct DrawContext<'a> { pub struct DrawContext<'a> {

View File

@ -1,10 +1,13 @@
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use rustc_hash::FxHashMap;
use tui::{backend::Backend, layout::Rect, Terminal}; use tui::{backend::Backend, layout::Rect, Terminal};
use crate::tuine::Status; 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)] #[derive(Clone, Copy, Debug)]
pub enum RuntimeEvent<Message> { pub enum RuntimeEvent<Message> {
@ -13,6 +16,11 @@ pub enum RuntimeEvent<Message> {
Custom(Message), Custom(Message),
} }
#[derive(Default)]
struct AppData {
state_map: StateMap,
}
pub(crate) fn launch<A, B>( pub(crate) fn launch<A, B>(
mut application: A, receiver: Receiver<RuntimeEvent<A::Message>>, terminal: &mut Terminal<B>, mut application: A, receiver: Receiver<RuntimeEvent<A::Message>>, terminal: &mut Terminal<B>,
) -> anyhow::Result<()> ) -> anyhow::Result<()>
@ -20,8 +28,13 @@ where
A: Application + 'static, A: Application + 'static,
B: Backend, B: Backend,
{ {
let mut user_interface = application.view(); let mut app_data = AppData::default();
draw(&mut user_interface, terminal)?; 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() { while !application.is_terminated() {
if let Ok(event) = receiver.recv() { if let Ok(event) = receiver.recv() {
@ -42,7 +55,8 @@ where
application.update(msg); 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)?; draw(&mut user_interface, terminal)?;
} }
RuntimeEvent::Custom(message) => { RuntimeEvent::Custom(message) => {
@ -52,7 +66,8 @@ where
width: _, width: _,
height: _, 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... // FIXME: Also nuke any cache and the like...
draw(&mut user_interface, terminal)?; draw(&mut user_interface, terminal)?;
} }

View File

@ -7,6 +7,8 @@ use std::any::Any;
pub trait State { pub trait State {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
fn are_equal(&self, other: &dyn State) -> bool; fn are_equal(&self, other: &dyn State) -> bool;
} }
@ -15,6 +17,10 @@ impl<S: PartialEq + 'static> State for S {
self self
} }
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
fn are_equal(&self, other: &dyn State) -> bool { fn are_equal(&self, other: &dyn State) -> bool {
other other
.as_any() .as_any()