mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-04-03 13:30:44 +02:00
compiles, once again.
now 616 errors downstream...
This commit is contained in:
parent
eb899906f9
commit
cdc513060d
8 changed files with 196 additions and 201 deletions
27
src/draw.rs
27
src/draw.rs
|
|
@ -18,31 +18,16 @@ use crate::{*, lang::*, color::*, space::*};
|
|||
/// TestWidget(false).draw(&mut screen);
|
||||
/// ```
|
||||
pub trait Draw<T: Screen> {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>>;
|
||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
|
||||
}
|
||||
impl<T: Screen> Draw<T> for () {
|
||||
fn draw (&self, __: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
(*self).draw(to)
|
||||
}
|
||||
}
|
||||
impl<T: Screen, D: Draw<T>> Draw<T> for Arc<D> {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
(**self).draw(to)
|
||||
}
|
||||
}
|
||||
impl<T: Screen, D: Draw<T>> Draw<T> for RwLock<D> {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
self.read().unwrap().draw(to)
|
||||
}
|
||||
}
|
||||
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
Ok(self.as_ref().map(|draw|draw.draw(to)).transpose()?.unwrap_or_default())
|
||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
Ok(self.map(|draw|draw.draw(to)).transpose()?.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,8 +40,8 @@ pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> (draw:
|
|||
Thunk(draw, std::marker::PhantomData)
|
||||
}
|
||||
impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> {
|
||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
(&self.0)(to)
|
||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||
(self.0)(to)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
110
src/keys.rs
Normal file
110
src/keys.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use crate::play::Thread;
|
||||
|
||||
use ::std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}};
|
||||
use ::std::time::Duration;
|
||||
use ::dizzle::{Usually, Do, impl_from};
|
||||
use ::crossterm::event::{
|
||||
read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState
|
||||
};
|
||||
|
||||
/// Spawn the TUI input thread which reads keys from the terminal.
|
||||
pub fn tui_input <T: Do<TuiEvent, Usually<T>> + Send + Sync + 'static> (
|
||||
exited: &Arc<AtomicBool>, state: &Arc<RwLock<T>>, poll: Duration
|
||||
) -> Result<Thread, std::io::Error> {
|
||||
let exited = exited.clone();
|
||||
let state = state.clone();
|
||||
Thread::new_poll(exited.clone(), poll, move |_| {
|
||||
let event = read().unwrap();
|
||||
match event {
|
||||
|
||||
// Hardcoded exit.
|
||||
Event::Key(KeyEvent {
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
code: KeyCode::Char('c'),
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE
|
||||
}) => { exited.store(true, Relaxed); },
|
||||
|
||||
// Handle all other events by the state:
|
||||
event => {
|
||||
if let Err(e) = state.write().unwrap().apply(&TuiEvent(event)) {
|
||||
panic!("{e}")
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// TUI input loop event.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiEvent(pub Event);
|
||||
impl_from!(TuiEvent: |e: Event| TuiEvent(e));
|
||||
impl_from!(TuiEvent: |c: char| TuiEvent(Event::Key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE))));
|
||||
impl Ord for TuiEvent {
|
||||
fn cmp (&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other)
|
||||
.unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf
|
||||
}
|
||||
}
|
||||
|
||||
/// TUI key spec.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiKey(pub Option<KeyCode>, pub KeyModifiers);
|
||||
impl TuiKey {
|
||||
const SPLIT: char = '/';
|
||||
pub fn from_crossterm (event: KeyEvent) -> Self {
|
||||
Self(Some(event.code), event.modifiers)
|
||||
}
|
||||
pub fn to_crossterm (&self) -> Option<Event> {
|
||||
self.0.map(|code|Event::Key(KeyEvent {
|
||||
code,
|
||||
modifiers: self.1,
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE,
|
||||
}))
|
||||
}
|
||||
pub fn named (token: &str) -> Option<KeyCode> {
|
||||
use KeyCode::*;
|
||||
Some(match token {
|
||||
"up" => Up,
|
||||
"down" => Down,
|
||||
"left" => Left,
|
||||
"right" => Right,
|
||||
"esc" | "escape" => Esc,
|
||||
"enter" | "return" => Enter,
|
||||
"delete" | "del" => Delete,
|
||||
"backspace" => Backspace,
|
||||
"tab" => Tab,
|
||||
"space" => Char(' '),
|
||||
"comma" => Char(','),
|
||||
"period" => Char('.'),
|
||||
"plus" => Char('+'),
|
||||
"minus" | "dash" => Char('-'),
|
||||
"equal" | "equals" => Char('='),
|
||||
"underscore" => Char('_'),
|
||||
"backtick" => Char('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ pub(crate) use ::{
|
|||
#[cfg(feature = "draw")] pub mod color;
|
||||
#[cfg(feature = "text")] pub mod text;
|
||||
#[cfg(feature = "term")] pub mod term;
|
||||
#[cfg(feature = "term")] pub mod keys;
|
||||
#[cfg(feature = "term")] pub extern crate ratatui;
|
||||
#[cfg(feature = "term")] pub extern crate crossterm;
|
||||
|
||||
|
|
|
|||
16
src/play.rs
16
src/play.rs
|
|
@ -21,8 +21,8 @@ impl Exit {
|
|||
|
||||
impl Thread {
|
||||
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
||||
pub fn new <F> (exit: Arc<AtomicBool>, call: F) -> Result<Self, std::io::Error>
|
||||
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
||||
pub fn new <F> (exit: Arc<AtomicBool>, mut call: F) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
let perf = Arc::new(PerfModel::default());
|
||||
Ok(Self {
|
||||
|
|
@ -30,7 +30,7 @@ impl Thread {
|
|||
perf: perf.clone(),
|
||||
join: std::thread::Builder::new().name("tengri tui output".into()).spawn(move || {
|
||||
while !exit.fetch_and(true, Relaxed) {
|
||||
let _ = perf.cycle(&call);
|
||||
let _ = perf.cycle(&mut call);
|
||||
}
|
||||
})?.into()
|
||||
})
|
||||
|
|
@ -39,19 +39,19 @@ impl Thread {
|
|||
/// Spawn a thread that runs `call` least one, then repeats
|
||||
/// until `exit`, sleeping for `time` msec after every iteration.
|
||||
pub fn new_sleep <F> (
|
||||
exit: Arc<AtomicBool>, time: Duration, call: F
|
||||
exit: Arc<AtomicBool>, time: Duration, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| { let _ = call(perf); std::thread::sleep(time); })
|
||||
}
|
||||
|
||||
/// Spawn a thread that uses [crossterm::event::poll]
|
||||
/// to run `call` every `time` msec.
|
||||
#[cfg(feature = "term")]pub fn new_poll <F> (
|
||||
exit: Arc<AtomicBool>, time: Duration, call: F
|
||||
#[cfg(feature = "term")] pub fn new_poll <F> (
|
||||
exit: Arc<AtomicBool>, time: Duration, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } })
|
||||
}
|
||||
|
|
|
|||
10
src/space.rs
10
src/space.rs
|
|
@ -161,14 +161,16 @@ impl Split {
|
|||
thunk(move|to: &mut T|{
|
||||
let (area_a, area_b) = to.xywh().split_half(self);
|
||||
let (origin_a, origin_b) = self.origins();
|
||||
let a = origin_a.align(a);
|
||||
let b = origin_b.align(b);
|
||||
match self {
|
||||
Self::Below => {
|
||||
to.place(&origin_b.align(b), Some(area_b));
|
||||
to.place(&origin_a.align(a), Some(area_b));
|
||||
to.place(&b, Some(area_b));
|
||||
to.place(&a, Some(area_b));
|
||||
},
|
||||
_ => {
|
||||
to.place(&origin_a.align(a), Some(area_a));
|
||||
to.place(&origin_b.align(b), Some(area_a));
|
||||
to.place(&a, Some(area_a));
|
||||
to.place(&b, Some(area_a));
|
||||
}
|
||||
}
|
||||
Ok(to.xywh()) // FIXME: compute and return actually used area
|
||||
|
|
|
|||
206
src/term.rs
206
src/term.rs
|
|
@ -131,26 +131,10 @@ impl Coord for u16 {
|
|||
}
|
||||
|
||||
impl Draw<Tui> for u64 {
|
||||
fn draw (&self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
||||
fn draw (self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
||||
}
|
||||
impl Draw<Tui> for f64 {
|
||||
fn draw (&self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
||||
}
|
||||
impl Draw<Tui> for &str {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
let XYWH(x, y, w, ..) = to.1.centered_xy([width_chars_max(to.w(), self), 1]);
|
||||
to.text(&self, x, y, w)
|
||||
}
|
||||
}
|
||||
impl Draw<Tui> for String {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
self.as_str().draw(to)
|
||||
}
|
||||
}
|
||||
impl Draw<Tui> for Arc<str> {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
self.as_ref().draw(to)
|
||||
}
|
||||
fn draw (self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
||||
}
|
||||
|
||||
mod phat {
|
||||
|
|
@ -223,7 +207,7 @@ pub const fn button_2 <'a> (key: impl Draw<Tui>, label: impl Draw<Tui>, hide: bo
|
|||
let c2 = tui_g(0);
|
||||
let c3 = tui_g(96);
|
||||
let c4 = tui_g(255);
|
||||
bold(true, fg_bg(c1, c2, east(fg(c2, east(key, fg(c3, &"▐"))), when(!hide, fg_bg(c4, c3, label)))))
|
||||
bold(true, fg_bg(c1, c2, east(fg(c2, east(key, fg(c3, "▐"))), when(!hide, fg_bg(c4, c3, label)))))
|
||||
}
|
||||
|
||||
/// ```
|
||||
|
|
@ -235,10 +219,10 @@ pub const fn button_3 <'a> (
|
|||
) -> impl Draw<Tui> {
|
||||
bold(true, east(
|
||||
fg_bg(tui_orange(), tui_g(0),
|
||||
east(fg(tui_g(0), &"▐"), east(key, fg(if editing { tui_g(128) } else { tui_g(96) }, "▐")))),
|
||||
east(fg(tui_g(0), "▐"), east(key, fg(if editing { tui_g(128) } else { tui_g(96) }, "▐")))),
|
||||
east(
|
||||
when(!editing, east(fg_bg(tui_g(255), tui_g(96), label), fg_bg(tui_g(128), tui_g(96), &"▐"),)),
|
||||
east(fg_bg(tui_g(224), tui_g(128), value), fg_bg(tui_g(128), Reset, &"▌"), ))))
|
||||
when(!editing, east(fg_bg(tui_g(255), tui_g(96), label), fg_bg(tui_g(128), tui_g(96), "▐"),)),
|
||||
east(fg_bg(tui_g(224), tui_g(128), value), fg_bg(tui_g(128), Reset, "▌"), ))))
|
||||
}
|
||||
|
||||
macro_rules! border {
|
||||
|
|
@ -261,7 +245,7 @@ macro_rules! border {
|
|||
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
||||
//impl Layout<Tui> for $T {}
|
||||
impl Draw<Tui> for $T {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
when(self.enabled(), thunk(|to: &mut Tui|BorderStyle::draw(self, to))).draw(to)
|
||||
}
|
||||
}
|
||||
|
|
@ -385,7 +369,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
fn border_sw (&self) -> &str { Self::SW }
|
||||
fn border_se (&self) -> &str { Self::SE }
|
||||
|
||||
#[inline] fn draw <'a> (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
#[inline] fn draw <'a> (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
if self.enabled() {
|
||||
self.draw_h(to, None)?;
|
||||
self.draw_v(to, None)?;
|
||||
|
|
@ -393,7 +377,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
}
|
||||
Ok(to.1)
|
||||
}
|
||||
#[inline] fn draw_h (&self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
#[inline] fn draw_h (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let style = style.or_else(||self.style_horizontal());
|
||||
let y1 = to.y_north();
|
||||
let y2 = to.y_south().saturating_sub(1);
|
||||
|
|
@ -403,7 +387,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
}
|
||||
Ok(to.xywh())
|
||||
}
|
||||
#[inline] fn draw_v (&self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
#[inline] fn draw_v (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let area = to.1;
|
||||
let style = style.or_else(||self.style_vertical());
|
||||
let [x, x2, y, y2] = area.lrtb();
|
||||
|
|
@ -419,7 +403,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
|||
}
|
||||
Ok(area)
|
||||
}
|
||||
#[inline] fn draw_c (&self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
#[inline] fn draw_c (self, to: &mut Tui, style: Option<Style>) -> Usually<XYWH<u16>> {
|
||||
let area = to.1;
|
||||
let style = style.or_else(||self.style_corners());
|
||||
let XYWH(x, y, w, h) = area;
|
||||
|
|
@ -520,52 +504,27 @@ fn y_scroll () -> impl Draw<Tui> {
|
|||
}
|
||||
|
||||
/// Spawn the TUI output thread which writes colored characters to the terminal.
|
||||
pub fn tui_output <W: Write + Send + Sync, T: Draw<Tui> + Send + Sync + 'static> (
|
||||
pub fn tui_output <W: Write + Send + Sync + 'static, T: Draw<Tui> + Send + Sync + 'static> (
|
||||
output: W,
|
||||
exited: &Arc<AtomicBool>,
|
||||
state: &Arc<RwLock<T>>,
|
||||
sleep: Duration
|
||||
) -> Result<Thread, std::io::Error> {
|
||||
) -> Usually<Thread> {
|
||||
let state = state.clone();
|
||||
tui_setup(&mut output)?;
|
||||
tui_setup()?;
|
||||
let mut backend = CrosstermBackend::new(output);
|
||||
let Size { width, height } = backend.size().expect("get size failed");
|
||||
let mut buffer_a = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
||||
let mut buffer_b = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
||||
Thread::new_sleep(exited.clone(), sleep, move |perf| {
|
||||
let size = backend.size().expect("get size failed");
|
||||
Ok(Thread::new_sleep(exited.clone(), sleep, move |perf| {
|
||||
let Size { width, height } = backend.size().expect("get size failed");
|
||||
if let Ok(state) = state.try_read() {
|
||||
tui_resize(&mut backend, &mut buffer_a, size);
|
||||
buffer_a = tui_redraw(&mut backend, &mut buffer_a, &mut buffer_b);
|
||||
tui_resize(&mut backend, &mut buffer_a, Rect { x: 0, y: 0, width, height });
|
||||
tui_redraw(&mut backend, &mut buffer_a, &mut buffer_b);
|
||||
}
|
||||
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
|
||||
buffer_a.set_string(0, 0, &timer, Style::default());
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tui_setup <T: Write + Send + Sync> (output: &mut T) -> Usually<()> {
|
||||
let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler();
|
||||
std::panic::set_hook(Box::new(move |info: &std::panic::PanicHookInfo|{
|
||||
output.execute(LeaveAlternateScreen).unwrap();
|
||||
CrosstermBackend::new(output).show_cursor().unwrap();
|
||||
disable_raw_mode().unwrap();
|
||||
better_panic_handler(info);
|
||||
}));
|
||||
output.execute(EnterAlternateScreen)?;
|
||||
CrosstermBackend::new(output).hide_cursor()?;
|
||||
enable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn tui_resize <W: Write> (
|
||||
backend: &mut CrosstermBackend<W>,
|
||||
buffer: &mut Buffer,
|
||||
size: XYWH<u16>
|
||||
) {
|
||||
if buffer.1 != size {
|
||||
backend.clear_region(ClearType::All).unwrap();
|
||||
buffer.resize(size);
|
||||
buffer.reset();
|
||||
}
|
||||
})?)
|
||||
}
|
||||
|
||||
pub fn tui_redraw <'b, W: Write> (
|
||||
|
|
@ -578,7 +537,31 @@ pub fn tui_redraw <'b, W: Write> (
|
|||
Backend::flush(backend).expect("failed to flush output new_buffer");
|
||||
std::mem::swap(&mut prev_buffer, &mut next_buffer);
|
||||
next_buffer.reset();
|
||||
next_buffer
|
||||
}
|
||||
|
||||
pub fn tui_setup () -> 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)?;
|
||||
CrosstermBackend::new(stdout()).hide_cursor()?;
|
||||
enable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn tui_resize <W: Write> (
|
||||
backend: &mut CrosstermBackend<W>,
|
||||
buffer: &mut Buffer,
|
||||
size: Rect
|
||||
) {
|
||||
if buffer.area != size {
|
||||
backend.clear_region(ClearType::All).unwrap();
|
||||
buffer.resize(size);
|
||||
buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tui_teardown <W: Write> (backend: &mut CrosstermBackend<W>) -> Usually<()> {
|
||||
|
|
@ -628,7 +611,7 @@ pub const fn border <T, S: BorderStyle> (on: bool, style: S, draw: impl Draw<Tui
|
|||
/// let _ = tengri::tui::catcher(Err("draw fail".into()));
|
||||
/// ```
|
||||
pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|match result.as_ref() {
|
||||
thunk(move|to: &mut Tui|match result {
|
||||
Ok(content) => content.draw(to),
|
||||
Err(e) => {
|
||||
let err_fg = Color::Rgb(255,224,244);
|
||||
|
|
@ -690,102 +673,3 @@ use self::colors::*; mod colors {
|
|||
pub const fn tui_title_fg (f: bool) -> Color { if f { tui_ti1() } else { tui_ti2() } }
|
||||
pub const fn tui_yellow () -> Color { Color::Rgb(255,255,0) }
|
||||
}
|
||||
|
||||
/// Spawn the TUI input thread which reads keys from the terminal.
|
||||
pub fn tui_input <T: Do<TuiEvent, T> + Send + Sync + 'static> (
|
||||
exited: &Arc<AtomicBool>, state: &Arc<RwLock<T>>, poll: Duration
|
||||
) -> Result<Thread, std::io::Error> {
|
||||
let exited = exited.clone();
|
||||
let state = state.clone();
|
||||
Thread::new_poll(exited.clone(), poll, move |_| {
|
||||
let event = read().unwrap();
|
||||
match event {
|
||||
|
||||
// Hardcoded exit.
|
||||
Event::Key(KeyEvent {
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
code: KeyCode::Char('c'),
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE
|
||||
}) => { exited.store(true, Relaxed); },
|
||||
|
||||
// Handle all other events by the state:
|
||||
_ => {
|
||||
let event = TuiEvent(TuiKey::from_crossterm(event));
|
||||
if let Err(e) = state.write().unwrap().apply(&event) {
|
||||
panic!("{e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// TUI input loop event.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiEvent(pub Event);
|
||||
impl_from!(TuiEvent: |e: Event| TuiEvent(e));
|
||||
impl_from!(TuiEvent: |c: char| TuiEvent(Event::Key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE))));
|
||||
impl Ord for TuiEvent {
|
||||
fn cmp (&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other)
|
||||
.unwrap_or_else(||format!("{:?}", self).cmp(&format!("{other:?}"))) // FIXME perf
|
||||
}
|
||||
}
|
||||
|
||||
/// TUI key spec.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
|
||||
pub struct TuiKey(pub Option<KeyCode>, pub KeyModifiers);
|
||||
impl TuiKey {
|
||||
const SPLIT: char = '/';
|
||||
pub fn to_crossterm (&self) -> Option<Event> {
|
||||
self.0.map(|code|Event::Key(KeyEvent {
|
||||
code,
|
||||
modifiers: self.1,
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE,
|
||||
}))
|
||||
}
|
||||
pub fn named (token: &str) -> Option<KeyCode> {
|
||||
use KeyCode::*;
|
||||
Some(match token {
|
||||
"up" => Up,
|
||||
"down" => Down,
|
||||
"left" => Left,
|
||||
"right" => Right,
|
||||
"esc" | "escape" => Esc,
|
||||
"enter" | "return" => Enter,
|
||||
"delete" | "del" => Delete,
|
||||
"backspace" => Backspace,
|
||||
"tab" => Tab,
|
||||
"space" => Char(' '),
|
||||
"comma" => Char(','),
|
||||
"period" => Char('.'),
|
||||
"plus" => Char('+'),
|
||||
"minus" | "dash" => Char('-'),
|
||||
"equal" | "equals" => Char('='),
|
||||
"underscore" => Char('_'),
|
||||
"backtick" => Char('`'),
|
||||
"lt" => Char('<'),
|
||||
"gt" => Char('>'),
|
||||
"cbopen" | "openbrace" => Char('{'),
|
||||
"cbclose" | "closebrace" => Char('}'),
|
||||
"bropen" | "openbracket" => Char('['),
|
||||
"brclose" | "closebracket" => Char(']'),
|
||||
"pgup" | "pageup" => PageUp,
|
||||
"pgdn" | "pagedown" => PageDown,
|
||||
"f1" => F(1),
|
||||
"f2" => F(2),
|
||||
"f3" => F(3),
|
||||
"f4" => F(4),
|
||||
"f5" => F(5),
|
||||
"f6" => F(6),
|
||||
"f7" => F(7),
|
||||
"f8" => F(8),
|
||||
"f9" => F(9),
|
||||
"f10" => F(10),
|
||||
"f11" => F(11),
|
||||
"f12" => F(12),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
src/text.rs
21
src/text.rs
|
|
@ -6,16 +6,29 @@ pub(crate) use ::unicode_width::*;
|
|||
use crate::term::Tui;
|
||||
use ratatui::prelude::Position;
|
||||
|
||||
impl Draw<Tui> for str {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
||||
impl Draw<Tui> for &str {
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
let XYWH(x, y, w, ..) = to.1.centered_xy([width_chars_max(to.w(), self), 1]);
|
||||
to.text(&self, x, y, w)
|
||||
}
|
||||
}
|
||||
impl Draw<Tui> for String {
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
self.as_str().draw(to)
|
||||
}
|
||||
}
|
||||
impl Draw<Tui> for std::sync::Arc<str> {
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
self.as_ref().draw(to)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Draw<Tui> for TrimString<T> {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> { self.as_ref().draw(to) }
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> { self.as_ref().draw(to) }
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Draw<Tui> for TrimStringRef<'_, T> {
|
||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||
let XYWH(x, y, w, ..) = to.1;
|
||||
let mut width: u16 = 1;
|
||||
let mut chars = self.1.as_ref().chars();
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl PerfModel {
|
|||
None
|
||||
}
|
||||
}
|
||||
pub fn cycle <F: Fn(&Self)->T, T> (&self, call: &F) -> T {
|
||||
pub fn cycle <F: FnMut(&Self)->T, T> (&self, call: &mut F) -> T {
|
||||
let t0 = self.get_t0();
|
||||
let result = call(self);
|
||||
let _t1 = self.get_t1(t0).unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue