wip: big flat pt.3, testing standalone tui

This commit is contained in:
🪞👃🪞 2024-12-30 18:20:36 +01:00
parent a5628fb663
commit cb680ab096
4 changed files with 189 additions and 139 deletions

View file

@ -74,30 +74,53 @@ impl Engine for Tui {
}
impl Tui {
/// Run the main loop.
pub fn run <R: Component<Tui> + Sized + 'static> (
state: Arc<RwLock<R>>
) -> Usually<Arc<RwLock<R>>> {
/// Construct a new TUI engine and wrap it for shared ownership.
pub fn new () -> Usually<Arc<RwLock<Self>>> {
let backend = CrosstermBackend::new(stdout());
let area = backend.size()?;
let engine = Self {
Ok(Arc::new(RwLock::new(Self {
exited: Arc::new(AtomicBool::new(false)),
buffer: Buffer::empty(area),
area: [area.x, area.y, area.width, area.height],
backend,
};
let engine = Arc::new(RwLock::new(engine));
let _input_thread = Self::spawn_input_thread(&engine, &state, Duration::from_millis(100));
engine.write().unwrap().setup()?;
let render_thread = Self::spawn_render_thread(&engine, &state, Duration::from_millis(10));
render_thread.join().expect("main thread failed");
engine.write().unwrap().teardown()?;
Ok(state)
})))
}
fn spawn_input_thread <R: Component<Tui> + Sized + 'static> (
engine: &Arc<RwLock<Self>>, state: &Arc<RwLock<R>>, poll: Duration
) -> JoinHandle<()> {
let exited = engine.read().unwrap().exited.clone();
/// Update the display buffer.
fn flip (&mut self, mut buffer: Buffer, size: ratatui::prelude::Rect) -> Buffer {
if self.buffer.area != size {
self.backend.clear_region(ClearType::All).unwrap();
self.buffer.resize(size);
self.buffer.reset();
}
let updates = self.buffer.diff(&buffer);
self.backend.draw(updates.into_iter()).expect("failed to render");
self.backend.flush().expect("failed to flush output buffer");
std::mem::swap(&mut self.buffer, &mut buffer);
buffer.reset();
buffer
}
}
pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
/// Run an app in the main loop.
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
/// Spawn the input thread.
fn run_input (&self, state: &Arc<RwLock<R>>, poll: Duration) -> JoinHandle<()>;
/// Spawn the output thread.
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
}
impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
let _input_thread = self.run_input(state, Duration::from_millis(100));
self.write().unwrap().setup()?;
let render_thread = self.run_output(state, Duration::from_millis(10));
render_thread.join().expect("main thread failed");
self.write().unwrap().teardown()?;
Ok(())
}
fn run_input (&self, state: &Arc<RwLock<T>>, poll: Duration) -> JoinHandle<()> {
let exited = self.read().unwrap().exited.clone();
let state = state.clone();
spawn(move || loop {
if exited.fetch_and(true, Relaxed) {
@ -119,11 +142,9 @@ impl Tui {
}
})
}
fn spawn_render_thread <R: Component<Tui> + Sized + 'static> (
engine: &Arc<RwLock<Self>>, state: &Arc<RwLock<R>>, sleep: Duration
) -> JoinHandle<()> {
let exited = engine.read().unwrap().exited.clone();
let engine = engine.clone();
fn run_output (&self, state: &Arc<RwLock<T>>, sleep: Duration) -> JoinHandle<()> {
let exited = self.read().unwrap().exited.clone();
let engine = self.clone();
let state = state.clone();
let size = engine.read().unwrap().backend.size().expect("get size failed");
let mut buffer = Buffer::empty(size);
@ -150,19 +171,6 @@ impl Tui {
std::thread::sleep(sleep);
})
}
fn flip (&mut self, mut buffer: Buffer, size: ratatui::prelude::Rect) -> Buffer {
if self.buffer.area != size {
self.backend.clear_region(ClearType::All).unwrap();
self.buffer.resize(size);
self.buffer.reset();
}
let updates = self.buffer.diff(&buffer);
self.backend.draw(updates.into_iter()).expect("failed to render");
self.backend.flush().expect("failed to flush output buffer");
std::mem::swap(&mut self.buffer, &mut buffer);
buffer.reset();
buffer
}
}
pub struct TuiInput {
@ -257,68 +265,6 @@ impl Input<Tui> for TuiInput {
};
}
/*
/// Define a key
pub const fn key (code: KeyCode) -> KeyEvent {
let modifiers = KeyModifiers::NONE;
let kind = KeyEventKind::Press;
let state = KeyEventState::NONE;
KeyEvent { code, modifiers, kind, state }
}
/// Add Ctrl modifier to key
pub const fn ctrl (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key }
}
/// Add Alt modifier to key
pub const fn alt (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key }
}
/// Add Shift modifier to key
pub const fn shift (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key }
}
/// Define a keymap
#[macro_export] macro_rules! keymap {
($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => {
&[
$((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),*
] as &'static [KeyBinding<$T>]
}
}
*/
/*
impl TuiInput {
// TODO remove
pub fn handle_keymap <T> (&self, state: &mut T, keymap: &KeyMap<T>) -> Usually<bool> {
match self.event() {
TuiEvent::Input(Key(event)) => {
for (code, modifiers, _, _, command) in keymap.iter() {
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
return command(state)
}
}
},
_ => {}
};
Ok(false)
}
}
pub type KeyHandler<T> = &'static dyn Fn(&mut T)->Usually<bool>;
pub type KeyBinding<T> = (KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler<T>);
pub type KeyMap<T> = [KeyBinding<T>];
*/
pub struct TuiOutput {
pub buffer: Buffer,
pub area: [u16;4]
@ -415,6 +361,18 @@ impl Render<Tui> for &str {
}
}
impl Render<Tui> for &String {
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
// TODO: line breaks
Ok(Some([self.chars().count() as u16, 1]))
}
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
let [x, y, ..] = to.area();
//let [w, h] = self.min_size(to.area().wh())?.unwrap();
Ok(to.blit(&self, x, y, None))
}
}
impl Render<Tui> for String {
fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> {
// TODO: line breaks
@ -438,3 +396,65 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C
}
}
}
/*
/// Define a key
pub const fn key (code: KeyCode) -> KeyEvent {
let modifiers = KeyModifiers::NONE;
let kind = KeyEventKind::Press;
let state = KeyEventState::NONE;
KeyEvent { code, modifiers, kind, state }
}
/// Add Ctrl modifier to key
pub const fn ctrl (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::CONTROL), ..key }
}
/// Add Alt modifier to key
pub const fn alt (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::ALT), ..key }
}
/// Add Shift modifier to key
pub const fn shift (key: KeyEvent) -> KeyEvent {
KeyEvent { modifiers: key.modifiers.union(KeyModifiers::SHIFT), ..key }
}
/// Define a keymap
#[macro_export] macro_rules! keymap {
($T:ty { $([$k:ident $(($char:literal))?, $m:ident, $n: literal, $d: literal, $f: expr]),* $(,)? }) => {
&[
$((KeyCode::$k $(($char))?, KeyModifiers::$m, $n, $d, &$f as KeyHandler<$T>)),*
] as &'static [KeyBinding<$T>]
}
}
*/
/*
impl TuiInput {
// TODO remove
pub fn handle_keymap <T> (&self, state: &mut T, keymap: &KeyMap<T>) -> Usually<bool> {
match self.event() {
TuiEvent::Input(Key(event)) => {
for (code, modifiers, _, _, command) in keymap.iter() {
if *code == event.code && modifiers.bits() == event.modifiers.bits() {
return command(state)
}
}
},
_ => {}
};
Ok(false)
}
}
pub type KeyHandler<T> = &'static dyn Fn(&mut T)->Usually<bool>;
pub type KeyBinding<T> = (KeyCode, KeyModifiers, &'static str, &'static str, KeyHandler<T>);
pub type KeyMap<T> = [KeyBinding<T>];
*/