From 1faf5bb6dfe5e481b528d65a59f46a10561eb351 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 5 Jan 2025 10:50:32 +0100 Subject: [PATCH] extract tui support code to tek_tui --- Cargo.lock | 22 +++-- Cargo.toml | 5 +- bin/lib.rs | 3 +- engine/Cargo.toml | 3 - engine/src/lib.rs | 12 ++- engine/src/output/coordinate.rs | 2 + engine/src/tui.rs | 92 -------------------- engine/src/tui/tui_input.rs | 105 ---------------------- engine/src/tui/tui_output.rs | 116 ------------------------- engine/src/tui/tui_run.rs | 75 ---------------- src/lib.rs | 66 +++++--------- tui/Cargo.toml | 18 ++++ tui/README.md | 3 + tui/src/lib.rs | 44 ++++++++++ src/border.rs => tui/src/tui_border.rs | 0 src/color.rs => tui/src/tui_color.rs | 0 tui/src/tui_engine.rs | 65 ++++++++++++++ tui/src/tui_input.rs | 106 ++++++++++++++++++++++ tui/src/tui_output.rs | 116 +++++++++++++++++++++++++ tui/src/tui_run.rs | 74 ++++++++++++++++ src/style.rs => tui/src/tui_style.rs | 0 src/theme.rs => tui/src/tui_theme.rs | 0 22 files changed, 477 insertions(+), 450 deletions(-) create mode 100644 tui/Cargo.toml create mode 100644 tui/README.md create mode 100644 tui/src/lib.rs rename src/border.rs => tui/src/tui_border.rs (100%) rename src/color.rs => tui/src/tui_color.rs (100%) create mode 100644 tui/src/tui_engine.rs create mode 100644 tui/src/tui_input.rs create mode 100644 tui/src/tui_output.rs create mode 100644 tui/src/tui_run.rs rename src/style.rs => tui/src/tui_style.rs (100%) rename src/theme.rs => tui/src/tui_theme.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 5befacd0..c4ceefaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1419,8 +1419,7 @@ dependencies = [ "quanta", "rand", "symphonia", - "tek_edn", - "tek_layout", + "tek_tui", "toml", "uuid", "wavers", @@ -1439,11 +1438,6 @@ dependencies = [ [[package]] name = "tek_engine" version = "0.2.0" -dependencies = [ - "better-panic", - "crossterm", - "ratatui", -] [[package]] name = "tek_layout" @@ -1452,6 +1446,20 @@ dependencies = [ "tek_engine", ] +[[package]] +name = "tek_tui" +version = "0.2.0" +dependencies = [ + "better-panic", + "crossterm", + "palette", + "rand", + "ratatui", + "tek_edn", + "tek_engine", + "tek_layout", +] + [[package]] name = "thiserror" version = "1.0.69" diff --git a/Cargo.toml b/Cargo.toml index 73cd11b9..2a87d0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,7 @@ edition = "2021" version = "0.2.0" [dependencies] -tek_layout = { path = "./layout" } -tek_edn = { optional = true, path = "./edn" } +tek_tui = { path = "./tui" } atomic_float = "1.0.0" backtrace = "0.3.72" @@ -29,7 +28,7 @@ wavers = "1.4.3" [features] default = ["edn"] -edn = ["tek_edn"] +edn = ["tek_tui/edn"] [[bin]] name = "tek_arranger" diff --git a/bin/lib.rs b/bin/lib.rs index 0819abb8..c44063d7 100644 --- a/bin/lib.rs +++ b/bin/lib.rs @@ -4,7 +4,8 @@ *, jack::*, tek_layout::Measure, - tek_engine::{Usually, tui::{Tui, TuiRun, ratatui::prelude::Color}} + tek_engine::Usually, + tek_tui::{Tui, TuiRun, ItemPalette, ItemColor, ratatui::prelude::Color} }; #[allow(unused)] diff --git a/engine/Cargo.toml b/engine/Cargo.toml index b72a07cb..f895f64b 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -4,6 +4,3 @@ edition = "2021" version = "0.2.0" [dependencies] -crossterm = "0.28.1" -ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] } -better-panic = "0.3.0" diff --git a/engine/src/lib.rs b/engine/src/lib.rs index bf8961d2..7548f5c2 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -5,8 +5,6 @@ mod engine; pub use self::engine::*; mod input; pub use self::input::*; mod output; pub use self::output::*; -pub mod tui; - pub use std::error::Error; /// Standard result type. @@ -15,6 +13,16 @@ pub type Usually = Result>; /// Standard optional result type. pub type Perhaps = Result, Box>; +/// Prototypal case of implementor macro. +/// Saves 4loc per data pats. +#[macro_export] macro_rules! from { + ($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => { + impl $(<$($lt),+>)? From<$Source> for $Target { + fn from ($state:$Source) -> Self { $cb } + } + }; +} + #[cfg(test)] #[test] fn test_dimensions () { assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); } diff --git a/engine/src/output/coordinate.rs b/engine/src/output/coordinate.rs index 5adf9385..4526a621 100644 --- a/engine/src/output/coordinate.rs +++ b/engine/src/output/coordinate.rs @@ -1,6 +1,8 @@ use std::fmt::{Debug, Display}; use std::ops::{Add, Sub, Mul, Div}; +impl Coordinate for u16 {} + /// A linear coordinate. pub trait Coordinate: Send + Sync + Copy + Add diff --git a/engine/src/tui.rs b/engine/src/tui.rs index c7579dd0..e69de29b 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -1,92 +0,0 @@ -mod tui_output; pub use self::tui_output::*; -mod tui_input; pub use self::tui_input::*; -mod tui_run; pub use self::tui_run::*; - -use crate::*; -use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; -use std::io::{stdout, Stdout}; - -pub use ::better_panic; -pub(crate) use better_panic::{Settings, Verbosity}; - -pub use ::crossterm; -pub(crate) use crossterm::{ - ExecutableCommand, - terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, - event::{KeyCode, KeyModifiers, KeyEvent, KeyEventKind, KeyEventState}, -}; - -pub use ::ratatui; -pub(crate) use ratatui::{ - prelude::{Color, Style, Buffer}, - style::Modifier, - backend::{Backend, CrosstermBackend, ClearType}, - layout::{Size, Rect}, - buffer::Cell -}; - -impl Coordinate for u16 {} - -pub struct Tui { - pub exited: Arc, - pub buffer: Buffer, - pub backend: CrosstermBackend, - pub area: [u16;4], // FIXME auto resize -} - -impl Engine for Tui { - type Unit = u16; - type Size = [Self::Unit;2]; - type Area = [Self::Unit;4]; - type Input = TuiIn; - type Handled = bool; - type Output = TuiOut; - fn exited (&self) -> bool { - self.exited.fetch_and(true, Relaxed) - } - fn setup (&mut self) -> Usually<()> { - let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler(); - std::panic::set_hook(Box::new(move |info: &std::panic::PanicHookInfo|{ - stdout().execute(LeaveAlternateScreen).unwrap(); - CrosstermBackend::new(stdout()).show_cursor().unwrap(); - disable_raw_mode().unwrap(); - better_panic_handler(info); - })); - stdout().execute(EnterAlternateScreen)?; - self.backend.hide_cursor()?; - enable_raw_mode().map_err(Into::into) - } - fn teardown (&mut self) -> Usually<()> { - stdout().execute(LeaveAlternateScreen)?; - self.backend.show_cursor()?; - disable_raw_mode().map_err(Into::into) - } -} - -impl Tui { - /// Construct a new TUI engine and wrap it for shared ownership. - pub fn new () -> Usually>> { - let backend = CrosstermBackend::new(stdout()); - let Size { width, height } = backend.size()?; - Ok(Arc::new(RwLock::new(Self { - exited: Arc::new(AtomicBool::new(false)), - buffer: Buffer::empty(Rect { x: 0, y: 0, width, height }), - area: [0, 0, width, height], - backend, - }))) - } - /// 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 - } -} diff --git a/engine/src/tui/tui_input.rs b/engine/src/tui/tui_input.rs index 9957fac1..e69de29b 100644 --- a/engine/src/tui/tui_input.rs +++ b/engine/src/tui/tui_input.rs @@ -1,105 +0,0 @@ -use crate::{*, tui::*}; -pub use crossterm::event::Event; -use Event as CrosstermEvent; - -#[derive(Debug, Clone)] -pub struct TuiIn(pub Arc, pub CrosstermEvent); - -impl Input for TuiIn { - type Event = Event; - fn event (&self) -> &CrosstermEvent { &self.1 } - fn is_done (&self) -> bool { self.0.fetch_and(true, Relaxed) } - fn done (&self) { self.0.store(true, Relaxed); } -} - -/// Define a key -pub const fn key (code: KeyCode) -> Event { - let modifiers = KeyModifiers::NONE; - let kind = KeyEventKind::Press; - let state = KeyEventState::NONE; - Event::Key(KeyEvent { code, modifiers, kind, state }) -} - -/// Add Ctrl modifier to key -pub const fn ctrl (event: Event) -> Event { - match event { - Event::Key(mut event) => { - event.modifiers = event.modifiers.union(KeyModifiers::CONTROL) - }, - _ => {} - } - event -} - -/// Add Alt modifier to key -pub const fn alt (event: Event) -> Event { - match event { - Event::Key(mut event) => { - event.modifiers = event.modifiers.union(KeyModifiers::ALT) - }, - _ => {} - } - event -} - -/// Add Shift modifier to key -pub const fn shift (event: Event) -> Event { - match event { - Event::Key(mut event) => { - event.modifiers = event.modifiers.union(KeyModifiers::SHIFT) - }, - _ => {} - } - event -} - -#[macro_export] macro_rules! kpat { - (Ctrl-Alt-$code:pat) => { kpat!($code, KeyModifiers::CONTROL | KeyModifiers::ALT) }; - (Ctrl-$code:pat) => { kpat!($code, KeyModifiers::CONTROL) }; - (Alt-$code:pat) => { kpat!($code, KeyModifiers::ALT) }; - (Shift-$code:pat) => { kpat!($code, KeyModifiers::SHIFT) }; - ($code:pat) => { - crossterm::event::Event::Key(KeyEvent { - code: $code, - modifiers: KeyModifiers::NONE, - kind: KeyEventKind::Press, - state: KeyEventState::NONE - }) - }; - ($code:pat, $modifiers: pat) => { - crossterm::event::Event::Key(KeyEvent { - code: $code, - modifiers: $modifiers, - kind: KeyEventKind::Press, - state: KeyEventState::NONE - }) - }; -} - -#[macro_export] macro_rules! kexp { - (Ctrl-Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::from_bits(0b0000_0110).unwrap()) }; - (Ctrl-$code:ident) => { key_event_expr!($code, KeyModifiers::CONTROL) }; - (Alt-$code:ident) => { key_event_expr!($code, KeyModifiers::ALT) }; - (Shift-$code:ident) => { key_event_expr!($code, KeyModifiers::SHIFT) }; - ($code:ident) => { key_event_expr!($code) }; - ($code:expr) => { key_event_expr!($code) }; -} - -#[macro_export] macro_rules! key_event_expr { - ($code:expr, $modifiers: expr) => { - crossterm::event::Event::Key(KeyEvent { - code: $code, - modifiers: $modifiers, - kind: KeyEventKind::Press, - state: KeyEventState::NONE - }) - }; - ($code:expr) => { - crossterm::event::Event::Key(KeyEvent { - code: $code, - modifiers: KeyModifiers::NONE, - kind: KeyEventKind::Press, - state: KeyEventState::NONE - }) - }; -} diff --git a/engine/src/tui/tui_output.rs b/engine/src/tui/tui_output.rs index 43382fb7..e69de29b 100644 --- a/engine/src/tui/tui_output.rs +++ b/engine/src/tui/tui_output.rs @@ -1,116 +0,0 @@ -use crate::{*, tui::*}; - -pub struct TuiOut { - pub buffer: Buffer, - pub area: [u16;4] -} - -impl Output for TuiOut { - #[inline] fn area (&self) -> [u16;4] { self.area } - #[inline] fn area_mut (&mut self) -> &mut [u16;4] { &mut self.area } - #[inline] fn place (&mut self, area: [u16;4], content: &impl Render) { - let last = self.area(); - *self.area_mut() = area; - content.render(self); - *self.area_mut() = last; - } -} - -impl TuiOut { - pub fn buffer_update (&mut self, area: [u16;4], callback: &impl Fn(&mut Cell, u16, u16)) { - buffer_update(&mut self.buffer, area, callback); - } - pub fn fill_bold (&mut self, area: [u16;4], on: bool) { - if on { - self.buffer_update(area, &|cell,_,_|cell.modifier.insert(Modifier::BOLD)) - } else { - self.buffer_update(area, &|cell,_,_|cell.modifier.remove(Modifier::BOLD)) - } - } - pub fn fill_bg (&mut self, area: [u16;4], color: Color) { - self.buffer_update(area, &|cell,_,_|{cell.set_bg(color);}) - } - pub fn fill_fg (&mut self, area: [u16;4], color: Color) { - self.buffer_update(area, &|cell,_,_|{cell.set_fg(color);}) - } - pub fn fill_ul (&mut self, area: [u16;4], color: Color) { - self.buffer_update(area, &|cell,_,_|{ - cell.modifier = ratatui::prelude::Modifier::UNDERLINED; - cell.underline_color = color; - }) - } - pub fn fill_char (&mut self, area: [u16;4], c: char) { - self.buffer_update(area, &|cell,_,_|{cell.set_char(c);}) - } - pub fn make_dim (&mut self) { - for cell in self.buffer.content.iter_mut() { - cell.bg = ratatui::style::Color::Rgb(30,30,30); - cell.fg = ratatui::style::Color::Rgb(100,100,100); - cell.modifier = ratatui::style::Modifier::DIM; - } - } - pub fn blit ( - &mut self, text: &impl AsRef, x: u16, y: u16, style: Option