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
29
src/draw.rs
29
src/draw.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{*, lang::*, color::*, space::*};
|
use crate::{*, lang::*, color::*, space::*};
|
||||||
|
|
||||||
/// Drawable that supports dynamic dispatch.
|
/// Drawable that supports dynamic dispatch.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::draw::*;
|
/// use tengri::draw::*;
|
||||||
/// struct TestScreen(bool);
|
/// struct TestScreen(bool);
|
||||||
|
|
@ -18,31 +18,16 @@ use crate::{*, lang::*, color::*, space::*};
|
||||||
/// TestWidget(false).draw(&mut screen);
|
/// TestWidget(false).draw(&mut screen);
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Draw<T: 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 () {
|
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())
|
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> {
|
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
|
||||||
fn draw (&self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||||
Ok(self.as_ref().map(|draw|draw.draw(to)).transpose()?.unwrap_or_default())
|
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)
|
Thunk(draw, std::marker::PhantomData)
|
||||||
}
|
}
|
||||||
impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> {
|
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>> {
|
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||||
(&self.0)(to)
|
(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 = "draw")] pub mod color;
|
||||||
#[cfg(feature = "text")] pub mod text;
|
#[cfg(feature = "text")] pub mod text;
|
||||||
#[cfg(feature = "term")] pub mod term;
|
#[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 ratatui;
|
||||||
#[cfg(feature = "term")] pub extern crate crossterm;
|
#[cfg(feature = "term")] pub extern crate crossterm;
|
||||||
|
|
||||||
|
|
|
||||||
16
src/play.rs
16
src/play.rs
|
|
@ -21,8 +21,8 @@ impl Exit {
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
/// 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>
|
pub fn new <F> (exit: Arc<AtomicBool>, mut call: F) -> Result<Self, std::io::Error>
|
||||||
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||||
{
|
{
|
||||||
let perf = Arc::new(PerfModel::default());
|
let perf = Arc::new(PerfModel::default());
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -30,7 +30,7 @@ impl Thread {
|
||||||
perf: perf.clone(),
|
perf: perf.clone(),
|
||||||
join: std::thread::Builder::new().name("tengri tui output".into()).spawn(move || {
|
join: std::thread::Builder::new().name("tengri tui output".into()).spawn(move || {
|
||||||
while !exit.fetch_and(true, Relaxed) {
|
while !exit.fetch_and(true, Relaxed) {
|
||||||
let _ = perf.cycle(&call);
|
let _ = perf.cycle(&mut call);
|
||||||
}
|
}
|
||||||
})?.into()
|
})?.into()
|
||||||
})
|
})
|
||||||
|
|
@ -39,19 +39,19 @@ impl Thread {
|
||||||
/// Spawn a thread that runs `call` least one, then repeats
|
/// Spawn a thread that runs `call` least one, then repeats
|
||||||
/// until `exit`, sleeping for `time` msec after every iteration.
|
/// until `exit`, sleeping for `time` msec after every iteration.
|
||||||
pub fn new_sleep <F> (
|
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>
|
) -> 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); })
|
Self::new(exit, move |perf| { let _ = call(perf); std::thread::sleep(time); })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a thread that uses [crossterm::event::poll]
|
/// Spawn a thread that uses [crossterm::event::poll]
|
||||||
/// to run `call` every `time` msec.
|
/// to run `call` every `time` msec.
|
||||||
#[cfg(feature = "term")]pub fn new_poll <F> (
|
#[cfg(feature = "term")] pub fn new_poll <F> (
|
||||||
exit: Arc<AtomicBool>, time: Duration, call: F
|
exit: Arc<AtomicBool>, time: Duration, mut call: F
|
||||||
) -> Result<Self, std::io::Error>
|
) -> 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); } })
|
Self::new(exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/space.rs
12
src/space.rs
|
|
@ -158,17 +158,19 @@ impl Split {
|
||||||
/// let _ = West.bsp((), ());
|
/// let _ = West.bsp((), ());
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
thunk(move|to: &mut T|{
|
thunk(move|to: &mut T|{
|
||||||
let (area_a, area_b) = to.xywh().split_half(self);
|
let (area_a, area_b) = to.xywh().split_half(self);
|
||||||
let (origin_a, origin_b) = self.origins();
|
let (origin_a, origin_b) = self.origins();
|
||||||
|
let a = origin_a.align(a);
|
||||||
|
let b = origin_b.align(b);
|
||||||
match self {
|
match self {
|
||||||
Self::Below => {
|
Self::Below => {
|
||||||
to.place(&origin_b.align(b), Some(area_b));
|
to.place(&b, Some(area_b));
|
||||||
to.place(&origin_a.align(a), Some(area_b));
|
to.place(&a, Some(area_b));
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
to.place(&origin_a.align(a), Some(area_a));
|
to.place(&a, Some(area_a));
|
||||||
to.place(&origin_b.align(b), Some(area_a));
|
to.place(&b, Some(area_a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(to.xywh()) // FIXME: compute and return actually used area
|
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 {
|
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 {
|
impl Draw<Tui> for f64 {
|
||||||
fn draw (&self, _to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod phat {
|
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 c2 = tui_g(0);
|
||||||
let c3 = tui_g(96);
|
let c3 = tui_g(96);
|
||||||
let c4 = tui_g(255);
|
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> {
|
) -> impl Draw<Tui> {
|
||||||
bold(true, east(
|
bold(true, east(
|
||||||
fg_bg(tui_orange(), tui_g(0),
|
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(
|
east(
|
||||||
when(!editing, east(fg_bg(tui_g(255), tui_g(96), label), fg_bg(tui_g(128), tui_g(96), &"▐"),)),
|
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, &"▌"), ))))
|
east(fg_bg(tui_g(224), tui_g(128), value), fg_bg(tui_g(128), Reset, "▌"), ))))
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! border {
|
macro_rules! border {
|
||||||
|
|
@ -261,7 +245,7 @@ macro_rules! border {
|
||||||
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
||||||
//impl Layout<Tui> for $T {}
|
//impl Layout<Tui> for $T {}
|
||||||
impl Draw<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)
|
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_sw (&self) -> &str { Self::SW }
|
||||||
fn border_se (&self) -> &str { Self::SE }
|
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() {
|
if self.enabled() {
|
||||||
self.draw_h(to, None)?;
|
self.draw_h(to, None)?;
|
||||||
self.draw_v(to, None)?;
|
self.draw_v(to, None)?;
|
||||||
|
|
@ -393,7 +377,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
||||||
}
|
}
|
||||||
Ok(to.1)
|
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 style = style.or_else(||self.style_horizontal());
|
||||||
let y1 = to.y_north();
|
let y1 = to.y_north();
|
||||||
let y2 = to.y_south().saturating_sub(1);
|
let y2 = to.y_south().saturating_sub(1);
|
||||||
|
|
@ -403,7 +387,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
||||||
}
|
}
|
||||||
Ok(to.xywh())
|
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 area = to.1;
|
||||||
let style = style.or_else(||self.style_vertical());
|
let style = style.or_else(||self.style_vertical());
|
||||||
let [x, x2, y, y2] = area.lrtb();
|
let [x, x2, y, y2] = area.lrtb();
|
||||||
|
|
@ -419,7 +403,7 @@ pub trait BorderStyle: Draw<Tui> + Copy {
|
||||||
}
|
}
|
||||||
Ok(area)
|
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 area = to.1;
|
||||||
let style = style.or_else(||self.style_corners());
|
let style = style.or_else(||self.style_corners());
|
||||||
let XYWH(x, y, w, h) = area;
|
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.
|
/// 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,
|
output: W,
|
||||||
exited: &Arc<AtomicBool>,
|
exited: &Arc<AtomicBool>,
|
||||||
state: &Arc<RwLock<T>>,
|
state: &Arc<RwLock<T>>,
|
||||||
sleep: Duration
|
sleep: Duration
|
||||||
) -> Result<Thread, std::io::Error> {
|
) -> Usually<Thread> {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
tui_setup(&mut output)?;
|
tui_setup()?;
|
||||||
let mut backend = CrosstermBackend::new(output);
|
let mut backend = CrosstermBackend::new(output);
|
||||||
let Size { width, height } = backend.size().expect("get size failed");
|
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_a = Buffer::empty(Rect { x: 0, y: 0, width, height });
|
||||||
let mut buffer_b = 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| {
|
Ok(Thread::new_sleep(exited.clone(), sleep, move |perf| {
|
||||||
let size = backend.size().expect("get size failed");
|
let Size { width, height } = backend.size().expect("get size failed");
|
||||||
if let Ok(state) = state.try_read() {
|
if let Ok(state) = state.try_read() {
|
||||||
tui_resize(&mut backend, &mut buffer_a, size);
|
tui_resize(&mut backend, &mut buffer_a, Rect { x: 0, y: 0, width, height });
|
||||||
buffer_a = tui_redraw(&mut backend, &mut buffer_a, &mut buffer_b);
|
tui_redraw(&mut backend, &mut buffer_a, &mut buffer_b);
|
||||||
}
|
}
|
||||||
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
|
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
|
||||||
buffer_a.set_string(0, 0, &timer, Style::default());
|
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> (
|
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");
|
Backend::flush(backend).expect("failed to flush output new_buffer");
|
||||||
std::mem::swap(&mut prev_buffer, &mut next_buffer);
|
std::mem::swap(&mut prev_buffer, &mut next_buffer);
|
||||||
next_buffer.reset();
|
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<()> {
|
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()));
|
/// let _ = tengri::tui::catcher(Err("draw fail".into()));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
|
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),
|
Ok(content) => content.draw(to),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let err_fg = Color::Rgb(255,224,244);
|
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_title_fg (f: bool) -> Color { if f { tui_ti1() } else { tui_ti2() } }
|
||||||
pub const fn tui_yellow () -> Color { Color::Rgb(255,255,0) }
|
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 crate::term::Tui;
|
||||||
use ratatui::prelude::Position;
|
use ratatui::prelude::Position;
|
||||||
|
|
||||||
impl Draw<Tui> for str {
|
impl Draw<Tui> for &str {
|
||||||
fn draw (&self, to: &mut Tui) -> Usually<XYWH<u16>> { todo!() }
|
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> {
|
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> {
|
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 XYWH(x, y, w, ..) = to.1;
|
||||||
let mut width: u16 = 1;
|
let mut width: u16 = 1;
|
||||||
let mut chars = self.1.as_ref().chars();
|
let mut chars = self.1.as_ref().chars();
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ impl PerfModel {
|
||||||
None
|
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 t0 = self.get_t0();
|
||||||
let result = call(self);
|
let result = call(self);
|
||||||
let _t1 = self.get_t1(t0).unwrap();
|
let _t1 = self.get_t1(t0).unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue