mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: big flat pt.3, testing standalone tui
This commit is contained in:
parent
a5628fb663
commit
cb680ab096
4 changed files with 189 additions and 139 deletions
|
|
@ -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>];
|
||||
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue