Reworks state signalling

This commit is contained in:
Gregor Reitzenstein 2020-11-04 11:51:16 +01:00
parent 21550b411d
commit 3e7127f3df
3 changed files with 73 additions and 76 deletions

View File

@ -35,33 +35,28 @@ pub struct Sute<'a, S> {
// TODO: BE SMART. Inputs change the state, resize signals change the state, futures completing
// change the state.
state: SuteState<'a>,
state: Mutable<SuteState>,
signal: S,
inputs: Inputs,
api: Option<API>,
log: Logger,
drain: Arc<LogDrain<'a>>,
}
impl<'a, S: Unpin> Sute<'a, S> {
pub fn new(s: S, log: Logger, drain: Arc<LogDrain<'a>>, api: API) -> Self {
pub fn new(signal: S, log: Logger, drain: Arc<LogDrain<'a>>, api: Option<API>) -> Self {
let inputs = Inputs::new();
let state = SuteState::new(drain);
let state = Mutable::new(SuteState::new());
Self {
state: state,
signal: s,
inputs: inputs,
api: Some(api),
log: log,
}
Self { state, signal, inputs, api, log, drain, }
}
fn run_cmd(&mut self, cmdline: String) {
let mut words = cmdline.split_ascii_whitespace();
match words.next() {
Some("quit") => self.state.running = false,
Some("quit") => self.state.lock_mut().running = false,
Some("connect") => {
self.connect(cmdline)
},
@ -74,98 +69,85 @@ impl<'a, S: Unpin> Sute<'a, S> {
}
fn handle_resize(&mut self, new_size: (u16,u16)) {
self.state.size = new_size;
self.state.lock_mut().size = new_size;
}
pub fn get_state(&self) -> SuteState<'a> {
self.state.clone()
pub fn get_state(&self) -> SuteState {
self.state.get_cloned()
}
// We can make this more efficient by not cloning. But who cares?
pub fn signal(&self) -> impl Signal<Item=SuteState> {
self.state.signal_cloned()
}
pub fn handle_key(&mut self, key: Key) {
let mut state = self.state.lock_mut();
match key {
Key::Char('\n') => {
let cmd = mem::replace(&mut self.state.cmd_line, String::new());
let cmd = mem::replace(&mut state.cmd_line, String::new());
// drop the mutably borrowed state here so we can mutably re-borrow self afterwards
mem::drop(state);
self.run_cmd(cmd);
},
Key::Char(c) => {
self.state.cmd_line.push(c);
state.cmd_line.push(c);
},
Key::Backspace => {
self.state.cmd_line.pop();
state.cmd_line.pop();
},
_ => {}
}
}
}
impl<'a, S: Signal<Item=(u16,u16)> + Unpin> Signal for Sute<'a, S> {
type Item = SuteState<'a>;
fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
let mut dirty = false;
impl <'a, S: Unpin + Signal<Item=(u16,u16)>> Future for Sute<'a, S> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
// Return early if we're not running anymore
if ! self.state.lock_ref().running {
return Poll::Ready(());
}
if let Poll::Ready(Some(size)) = Pin::new(&mut self.signal).poll_change(cx) {
dirty = true;
self.handle_resize(size);
}
match ready!(Pin::new(&mut self.inputs).poll_next(cx)) {
Some(key) => {
dirty = true;
self.handle_key(key);
},
// If the input closes stop the program
None => {
self.state.running = false;
return Poll::Ready(None);
self.state.lock_mut().running = false;
return Poll::Ready(());
},
}
if dirty {
Poll::Ready(Some(self.get_state()))
} else {
Poll::Pending
}
}
}
impl <'a, S> Future for Sute<'a, S> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if self.state.running {
Poll::Pending
} else {
Poll::Ready(())
}
Poll::Pending
}
}
#[derive(Debug, Clone)]
// TODO: `Signal` struct changes using this struct?
// TODO: If so, procmacro here?
enum SuteStateChange {
active_win(Window),
}
#[derive(Debug, Clone)]
pub struct SuteState<'a> {
pub struct SuteState {
pub active_win: Window,
pub size: (u16,u16),
pub running: bool,
pub tick: usize,
pub server: Option<&'a str>,
pub log: Arc<LogDrain<'a>>,
pub server: Option<String>,
pub cmd_line: String,
pub tick_c: char,
// TODO: Figure out how to put log lines here signaled
}
impl<'a> SuteState<'a> {
pub fn new(log: Arc<LogDrain<'a>>) -> Self {
impl SuteState {
pub fn new() -> Self {
Self {
active_win: Window::Main,
size: (80,20),
running: true,
tick: 0,
server: None,
log: log,
cmd_line: String::new(),
tick_c: '|',
}

View File

@ -69,10 +69,11 @@ fn main() -> Result<(), io::Error> {
let (rpc_future, mut api) = schema::bootstrap(log.clone(), stream);
let app = app::Sute::new(resize, log.clone(), drain, api);
let app = app::Sute::new(resize, log.clone(), drain.clone(), Some(api));
let mut state = app.get_state();
let mut stream = app.to_stream();
let mut app_state = app.get_state();
let mut ui_state = ui::UIState::new(app_state, drain);
let mut stream = app.signal().to_stream();
let ui_future = async move {
let stdout = io::stdout().into_raw_mode()?;
@ -87,7 +88,7 @@ fn main() -> Result<(), io::Error> {
terminal.resize(tui::layout::Rect::new(0, 0, x,y)).unwrap();
}
terminal.draw(|f| ui::draw_ui(f, &mut state));
terminal.draw(|f| ui::draw_ui(f, &mut ui_state));
loop {
if let Some(mut state) = stream.next().await {
if !state.running {
@ -102,7 +103,8 @@ fn main() -> Result<(), io::Error> {
_ => '|',
};
state.tick_c = tick_c;
terminal.draw(|f| ui::draw_ui(f, &mut state))?;
ui_state.app_state = state;
terminal.draw(|f| ui::draw_ui(f, &mut ui_state))?;
} else {
break;
}
@ -114,9 +116,10 @@ fn main() -> Result<(), io::Error> {
Ok(())
};
//lex.spawn(rpc_future).detach();
let r: Result<(), io::Error> = smol::block_on(lex.run(ui_future));
//smol::block_on(lex.run(Box::pin(app)));
lex.spawn(rpc_future).detach();
let t: Task<Result<(), io::Error>> = lex.spawn(ui_future);
t.detach();
smol::block_on(lex.run(app));
Ok(())
}

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use tui::{
backend::Backend,
layout::{Layout, Direction, Constraint, Rect},
@ -5,9 +7,19 @@ use tui::{
Frame,
};
use crate::app::SuteState;
use crate::app::{SuteState, LogDrain};
pub fn draw_ui<B: Backend>(f: &mut Frame<B>, app: &mut SuteState) {
pub struct UIState<'a> {
pub app_state: SuteState,
pub loglines: Arc<LogDrain<'a>>,
}
impl<'a> UIState<'a> {
pub fn new(app_state: SuteState, loglines: Arc<LogDrain<'a>>) -> Self {
Self { app_state, loglines }
}
}
pub fn draw_ui<B: Backend>(f: &mut Frame<B>, state: &mut UIState) {
let outer_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
@ -19,32 +31,32 @@ pub fn draw_ui<B: Backend>(f: &mut Frame<B>, app: &mut SuteState) {
.split(f.size());
draw_header(f, app, outer_layout[0]);
draw_main(f, app, outer_layout[1]);
draw_command_line(f, app, outer_layout[2]);
draw_header(f, state, outer_layout[0]);
draw_main(f, state, outer_layout[1]);
draw_command_line(f, state, outer_layout[2]);
}
fn draw_header<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
fn draw_header<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
f.render_widget(Block::default()
.title("Status")
.borders(Borders::ALL), layout_chunk);
}
fn draw_main<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
fn draw_main<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
// let chunk = Layout::default()
// .direction(Direction::Horizontal)
// .constraints([Constraint::Percentage(20), Constraint::Percentage(80)].as_ref())
// .split(layout_chunk);
draw_logs(f, app, layout_chunk)
draw_logs(f, state, layout_chunk)
}
fn draw_logs<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
fn draw_logs<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
// TODO: Just use a signal.
let v = app.log.get_inner().clone();
f.render_widget(Paragraph::new(v.into_iter().collect::<Vec<tui::text::Spans>>()), layout_chunk);
let v = state.loglines.get_inner().clone();
f.render_widget(Paragraph::new(v), layout_chunk);
}
fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_chunk: Rect) {
fn draw_command_line<B: Backend>(f: &mut Frame<B>, state: &mut UIState, layout_chunk: Rect) {
let block = Block::default()
.title("Command line")
.borders(Borders::ALL);
@ -52,6 +64,6 @@ fn draw_command_line<B: Backend>(f: &mut Frame<B>, app: &mut SuteState, layout_c
f.render_widget(block, layout_chunk);
let cmdline = format!("{} > {}", app.tick_c, app.cmd_line);
let cmdline = format!("{} > {}", state.app_state.tick_c, state.app_state.cmd_line);
f.render_widget(Paragraph::new(&cmdline[..]), inner_rect);
}