mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-08-07 14:16:56 +02:00
moar flat
This commit is contained in:
parent
fc01fd6ad3
commit
a27dacff93
29 changed files with 649 additions and 582 deletions
|
|
@ -5,11 +5,12 @@ version = "0.15.0"
|
|||
description = "UI metaframework."
|
||||
|
||||
[features]
|
||||
default = ["lang", "sing", "midi", "draw", "play", "term", "text", "time", "rand", "okhsl", "eval"]
|
||||
default = ["lang", "sing", "midi", "draw", "play", "term", "text", "time", "rand", "okhsl", "eval", "exit"]
|
||||
bumpalo = ["dep:bumpalo"]
|
||||
draw = []
|
||||
gui = ["draw", "dep:winit"]
|
||||
eval = []
|
||||
exit = []
|
||||
lang = ["dep:dizzle"]
|
||||
midi = ["dep:midly"]
|
||||
okhsl = ["dep:palette"]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use ::tengri::{
|
||||
*,
|
||||
lang::*,
|
||||
dizzle::*,
|
||||
crossterm::event::{KeyEvent, Event::*, KeyCode::*},
|
||||
ratatui::style::Color,
|
||||
};
|
||||
|
|
@ -13,8 +13,8 @@ tui_app!(State {
|
|||
});
|
||||
|
||||
tui_view!(self: State {
|
||||
thunk(|to: &mut Tui|{
|
||||
let _ = "DEMO [MODE 00]".align_sw().draw(to);
|
||||
draw(|to: &mut Tui|{
|
||||
let _ = "Demo [Mode 00]".align_sw().draw(to);
|
||||
let _ = ShowSize.align_se().draw(to);
|
||||
let _ = format!("Counter:\n{}", self.counter).align_c().draw(to);
|
||||
Ok(Some(to.area()))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
//! Mode 01: Direct view, actions with history
|
||||
|
||||
use itertools::Itertools;
|
||||
use ::tengri::{
|
||||
*,
|
||||
lang::*,
|
||||
dizzle::{*, itertools::Itertools},
|
||||
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
||||
ratatui::style::Color,
|
||||
};
|
||||
|
|
@ -28,7 +27,7 @@ tui_keys!(self: State, input {
|
|||
});
|
||||
|
||||
tui_view!(self: State {
|
||||
let title = "Demo Mode 00";
|
||||
let title = "Demo [Mode 01]";
|
||||
let items = self.history.iter().take(10).map(|x|format!("{x:?}")).join("\n");
|
||||
let history = format!("History: {}\n{items}", self.history.len());
|
||||
let cursor = format!("Counter: {}", self.cursor);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use ::tengri::{
|
||||
*,
|
||||
lang::*,
|
||||
dizzle::*,
|
||||
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
||||
ratatui::style::Color,
|
||||
};
|
||||
|
|
@ -20,11 +20,15 @@ tui_view!(self: State {
|
|||
let index = self.cursor + 1;
|
||||
let wh = (self.size.w(), self.size.h());
|
||||
let src = VIEWS.get(self.cursor).unwrap_or(&"");
|
||||
let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh);
|
||||
let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_xy(2, 1)).exact_h(3);
|
||||
let code = bg(Color::Rgb(10, 60, 10), format!("Source:\n{}", src).align_n().push_xy(2, 1)).min_h(4);
|
||||
let widget = thunk(move|to: &mut Tui|self.interpret(to, &src));
|
||||
self.size.of(east(south(title, code).max_w(40), widget))
|
||||
let heading = format!("Demo [Mode 02]\nExample {}/{}\nSize {:?}", index, VIEWS.len(), &wh);
|
||||
let title = bg(Color::Rgb(60, 10, 10), heading);
|
||||
let code = bg(Color::Rgb(10, 60, 10), format!("Source:\n{}", src).max_h(10));
|
||||
let widget = draw(move|to: &mut Tui|self.interpret(to, &src)); // FIXME this forcefills
|
||||
let sidebar = bg(Color::Rgb(20, 20, 20), north(code, title)).max_w(40);
|
||||
let content = bg(Color::Rgb(64, 64, 64), widget);
|
||||
east(sidebar, content)
|
||||
//self.size.of(bg(Color::Rgb(10, 10, 10), south(east(sidebar, content), code)).full_w())
|
||||
//self.size.of(bg(Color::Rgb(10, 10, 10), east(sidebar, content)).full_w())
|
||||
});
|
||||
|
||||
impl Interpret<Tui, Option<XYWH<u16>>> for State {
|
||||
|
|
|
|||
40
src/coord.rs
40
src/coord.rs
|
|
@ -1,40 +0,0 @@
|
|||
use super::*;
|
||||
|
||||
/// A numeric type that can be used as coordinate.
|
||||
///
|
||||
/// FIXME: Replace with `num` crate?
|
||||
/// FIXME: Use AsRef/AsMut?
|
||||
///
|
||||
/// ```
|
||||
/// use tengri::*;
|
||||
/// let a: u16 = Coord::zero();
|
||||
/// let b: u16 = a.plus(1);
|
||||
/// let c: u16 = a.minus(2);
|
||||
/// let d = a.atomic();
|
||||
/// ```
|
||||
pub trait Coord: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Mul<Self, Output=Self>
|
||||
+ Div<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
+ Debug + Display + Default
|
||||
+ From<u16> + Into<u16>
|
||||
+ Into<usize>
|
||||
+ Into<f64>
|
||||
//+ std::iter::Step
|
||||
{
|
||||
/// Zero in own type.
|
||||
fn zero () -> Self { 0.into() }
|
||||
/// Addition.
|
||||
fn plus (self, other: Self) -> Self;
|
||||
/// Saturating subtraction.
|
||||
fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } }
|
||||
/// Convert to [AtomicUsize].
|
||||
fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) }
|
||||
}
|
||||
|
||||
/// TUI works in u16 coordinates.
|
||||
impl Coord for u16 {
|
||||
fn plus (self, other: Self) -> Self { self.saturating_add(other) }
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
use super::*;
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use crate::{*, layout::*, draw::*};
|
||||
use Azimuth::*;
|
||||
|
||||
impl<N: Coord, T: Xywh<N>> Lrtb<N> for T {}
|
||||
|
||||
pub trait Lrtb<N: Coord>: Xywh<N> {
|
||||
fn lrtb (&self) -> [N;4] {
|
||||
// FIXME: factor origin
|
||||
[self.x(), self.y(), self.x()+self.w(), self.y()+self.h()]
|
||||
}
|
||||
fn iter_x (&self) -> std::ops::Range<N> where Self: HasOrigin {
|
||||
self.x_west()..self.x_east()
|
||||
}
|
||||
fn x_west (&self) -> N where Self: HasOrigin {
|
||||
let w = self.w();
|
||||
let a = self.origin();
|
||||
let d = match a { NW|W|SW => 0.into(), N|X|C|Y|S => w/2.into(), NE|E|SE => w };
|
||||
self.x().minus(d)
|
||||
}
|
||||
fn x_east (&self) -> N where Self: HasOrigin {
|
||||
let w = self.w();
|
||||
let a = self.origin();
|
||||
let d = match a { NW|W|SW => w, N|X|C|Y|S => w/2.into(), NE|E|SE => 0.into() };
|
||||
self.x().plus(d)
|
||||
}
|
||||
fn x_center (&self) -> N where Self: HasOrigin {
|
||||
todo!()
|
||||
}
|
||||
fn iter_y (&self) -> std::ops::Range<N> where Self: HasOrigin {
|
||||
self.y_north()..self.y_south()
|
||||
}
|
||||
fn y_north (&self) -> N where Self: HasOrigin {
|
||||
let a = self.origin();
|
||||
let h = self.h();
|
||||
let d = match a { NW|N|NE => 0.into(), W|X|C|Y|E => h/2.into(), SW|S|SE => h };
|
||||
self.y().minus(d)
|
||||
}
|
||||
fn y_south (&self) -> N where Self: HasOrigin {
|
||||
let a = self.origin();
|
||||
let h = self.h();
|
||||
let d = match a { NW|N|NE => h, W|X|C|Y|E => h/2.into(), SW|S|SE => 0.into() };
|
||||
self.y().plus(d)
|
||||
}
|
||||
fn y_center (&self) -> N where Self: HasOrigin {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
use super::*;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
/// Uses [AtomicUsize] to measure size during\
|
||||
/// rendering (which is normally read-only).
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Sizer(
|
||||
/// Width
|
||||
pub Arc<AtomicUsize>,
|
||||
/// Height
|
||||
pub Arc<AtomicUsize>,
|
||||
);
|
||||
|
||||
impl Xy<u16> for Sizer {
|
||||
fn x (&self) -> u16 { self.0.load(Ordering::Relaxed) as u16 }
|
||||
fn y (&self) -> u16 { self.1.load(Ordering::Relaxed) as u16 }
|
||||
}
|
||||
impl Wide<u16> for Sizer { fn w (&self) -> u16 { self.0.load(Relaxed) as u16 } }
|
||||
impl Tall<u16> for Sizer { fn h (&self) -> u16 { self.1.load(Relaxed) as u16 } }
|
||||
impl PartialEq for Sizer { fn eq (&self, _: &Self) -> bool { todo!() } }
|
||||
|
||||
impl Sizer {
|
||||
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
|
||||
thunk(move|to: &mut T|{
|
||||
let area = of.draw(to)?;
|
||||
self.0.store(area.map(|a|a.w()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
||||
self.1.store(area.map(|a|a.h()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use crate::*;
|
||||
use super::*;
|
||||
|
||||
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
|
||||
pub struct Thunk<T: Screen, F>(pub F, std::marker::PhantomData<T>);
|
||||
|
||||
impl<T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> {
|
||||
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
||||
(self.0)(to)
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic [Draw]able closure.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// # fn test () -> impl Draw<Tui> {
|
||||
/// thunk(|to: &mut Tui|Ok(Some(to.1)))
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> (
|
||||
draw: F
|
||||
) -> Thunk<T, F> {
|
||||
Thunk(draw, std::marker::PhantomData)
|
||||
}
|
||||
|
||||
/// Only render when condition is true.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// # fn test () -> impl Draw<Tui> {
|
||||
/// when(true, "Yes")
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw<T> {
|
||||
thunk(move|to: &mut T|if condition { draw.draw(to) } else { Ok(Default::default()) })
|
||||
}
|
||||
|
||||
/// Render one thing if a condition is true and another false.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// # fn test () -> impl Draw<Tui> {
|
||||
/// either(true, "Yes", "No")
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||
thunk(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) })
|
||||
}
|
||||
115
src/draw/xywh.rs
115
src/draw/xywh.rs
|
|
@ -1,115 +0,0 @@
|
|||
use super::*;
|
||||
use Split::*;
|
||||
|
||||
pub trait Xy<N: Coord> {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
}
|
||||
|
||||
pub trait Wh<N: Coord>: Wide<N> + Tall<N> {
|
||||
fn wh (&self) -> [N;2];
|
||||
}
|
||||
|
||||
pub trait Xywh<N: Coord>: Xy<N> + Wh<N> {
|
||||
fn xywh (&self) -> XYWH<N> {
|
||||
XYWH(self.x(), self.y(), self.w(), self.h())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Wide<N: Coord>: Xy<N> {
|
||||
fn w (&self) -> N { N::zero() }
|
||||
fn w_min (&self) -> N { self.w() }
|
||||
fn w_max (&self) -> N { self.w() }
|
||||
}
|
||||
|
||||
pub trait Tall<N: Coord> {
|
||||
fn h (&self) -> N { N::zero() }
|
||||
fn h_min (&self) -> N { self.h() }
|
||||
fn h_max (&self) -> N { self.h() }
|
||||
}
|
||||
|
||||
/// Point with size.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// let xywh = XYWH(0u16, 0, 0, 0);
|
||||
/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), (20, 20));
|
||||
/// ```
|
||||
///
|
||||
/// * [ ] TODO: origin field (determines at which corner/side is X0 Y0)
|
||||
///
|
||||
#[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct XYWH<N: Coord>(pub N, pub N, pub N, pub N);
|
||||
|
||||
impl<N: Coord> Xy<N> for XYWH<N> {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
}
|
||||
|
||||
impl<N: Coord> Wide<N> for XYWH<N> { fn w (&self) -> N { self.2 } }
|
||||
|
||||
impl<N: Coord> Tall<N> for XYWH<N> { fn h (&self) -> N { self.3 } }
|
||||
|
||||
impl<N: Coord> XYWH<N> {
|
||||
|
||||
pub fn zero () -> Self {
|
||||
Self(0.into(), 0.into(), 0.into(), 0.into())
|
||||
}
|
||||
|
||||
pub fn center (&self) -> (N, N) {
|
||||
let Self(x, y, w, h) = *self;
|
||||
(x.plus(w/2.into()), y.plus(h/2.into()))
|
||||
}
|
||||
|
||||
pub fn centered (&self) -> (N, N) {
|
||||
let Self(x, y, w, h) = *self;
|
||||
(x.minus(w/2.into()), y.minus(h/2.into()))
|
||||
}
|
||||
|
||||
pub fn centered_x (&self, n: N) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||
let y_center = y.plus(h / 2.into());
|
||||
XYWH(x_center, y_center, n, 1.into())
|
||||
}
|
||||
|
||||
pub fn centered_y (&self, n: N) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = x.plus(w / 2.into());
|
||||
let y_corner = (y.plus(h / 2.into())).minus(n / 2.into());
|
||||
XYWH(x_center, y_corner, 1.into(), n)
|
||||
}
|
||||
|
||||
pub fn centered_xy (&self, [n, m]: [N;2]) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||
let y_corner = (y.plus(h / 2.into())).minus(m / 2.into());
|
||||
XYWH(x_center, y_corner, n, m)
|
||||
}
|
||||
|
||||
pub fn split_half (&self, direction: &Split) -> (Self, Self) {
|
||||
let XYWH(x, y, w, h) = self.xywh();
|
||||
match direction {
|
||||
South => (XYWH(x, y, w, h - h / 2.into()), XYWH(x, y + h / 2.into(), w, h / 2.into())),
|
||||
East => (XYWH(x, y, w - w / 2.into(), h), XYWH(x + w / 2.into(), y, w / 2.into(), h)),
|
||||
North => (XYWH(x, y + h / 2.into(), w, h - h / 2.into()), XYWH(x, y, w, h / 2.into())),
|
||||
West => (XYWH(x + w / 2.into(), y, w - w / 2.into(), h), XYWH(x, y, w / 2.into(), h)),
|
||||
Above | Below => (XYWH(x, y, w, h), XYWH(x, y, w, h))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl From<&ratatui::prelude::Rect> for XYWH<u16> {
|
||||
fn from (rect: &ratatui::prelude::Rect) -> Self {
|
||||
Self(rect.x, rect.y, rect.width, rect.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coord, T: Wide<N> + Tall<N>> Wh<N> for T {
|
||||
fn wh (&self) -> [N;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coord, T: Xy<N> + Wh<N>> Xywh<N> for T {}
|
||||
25
src/exit.rs
25
src/exit.rs
|
|
@ -1,25 +0,0 @@
|
|||
use crate::*;
|
||||
use std::sync::{Arc, atomic::AtomicBool};
|
||||
use crossterm::event::*;
|
||||
|
||||
#[derive(Clone)] pub struct Exit(Arc<AtomicBool>);
|
||||
|
||||
impl Exit {
|
||||
pub fn run <T> (run: impl FnOnce(Self)->Usually<T>) -> Usually<T> {
|
||||
run(Self(Arc::new(AtomicBool::new(false))))
|
||||
}
|
||||
pub fn is (event: &Event) -> bool {
|
||||
matches!(event, Event::Key(KeyEvent {
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
code: KeyCode::Char('c'),
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Arc<AtomicBool>> for Exit {
|
||||
fn as_ref (&self) -> &Arc<AtomicBool> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -8,11 +8,13 @@ pub struct Align<T>(
|
|||
|
||||
impl<S: Screen, T: Draw<S>> Draw<S> for Align<T> {
|
||||
fn layout (&self, area: XYWH<S::Unit>) -> Perhaps<XYWH<S::Unit>> {
|
||||
let XYWH(x0, y0, w0, h0) = area;
|
||||
Ok(align::<S>(area, self.1.layout(area)?, self.0))
|
||||
}
|
||||
fn draw (self, to: &mut S) -> Perhaps<XYWH<S::Unit>> {
|
||||
Ok(self.layout(to.area())?.map(|area|to.clip(area, |to|self.1.draw(to))).transpose()?.flatten())
|
||||
Ok(self.layout(to.area())?
|
||||
.map(|area|to.clip(area, |to|self.1.draw(to)))
|
||||
.transpose()?
|
||||
.flatten())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
/// ```
|
||||
/// use tengri::*;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
/// Set size of of drawing area.
|
||||
///
|
||||
|
|
@ -8,22 +8,42 @@ use crate::{*, draw::*};
|
|||
/// let _ = "".exact_h(1);
|
||||
/// let _ = "".exact_wh(1, 1);
|
||||
/// ```
|
||||
pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
|
||||
__(PhantomData<T>),
|
||||
pub enum Exact<S: Screen, I: Draw<S>, X: Into<Option<S::Unit>>> {
|
||||
__(PhantomData<S>),
|
||||
W(I, X),
|
||||
H(I, X),
|
||||
WH(I, X, X),
|
||||
}
|
||||
impl_draw!(<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>,>|self: Exact<T, I, X>, to: T|{
|
||||
let area: XYWH<T::Unit> = to.area();
|
||||
let (item, area) = match self {
|
||||
Self::W(item, w1) =>
|
||||
(item, XYWH(area.0, area.1, w1.into().unwrap_or(area.2), area.3)),
|
||||
Self::H(item, h1) =>
|
||||
(item, XYWH(area.0, area.1, area.2, h1.into().unwrap_or(area.3))),
|
||||
Self::WH(item, w1, h1) =>
|
||||
(item, XYWH(area.0, area.1, w1.into().unwrap_or(area.2), h1.into().unwrap_or(area.3))),
|
||||
_ => return Ok(None)
|
||||
|
||||
impl <S: Screen, I: Draw<S>, X: Into<Option<S::Unit>> + Copy> Draw<S> for Exact<S, I, X> {
|
||||
fn layout (&self, area: XYWH<S::Unit>) -> Perhaps<XYWH<S::Unit>> {
|
||||
Ok(Some(layout_exact(self, area)))
|
||||
}
|
||||
fn draw (self, to: &mut S) -> Perhaps<XYWH<S::Unit>> {
|
||||
let area = layout_exact(&self, to.area());
|
||||
let item = match self { Self::W(i, ..) => i, Self::H(i, ..) => i, Self::WH(i, ..) => i, _ => unreachable!() };
|
||||
to.clip(area, |to|item.draw(to))
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_exact <S: Screen, I: Draw<S>, X: Into<Option<S::Unit>> + Copy> (
|
||||
exact: &Exact<S, I, X>, area: XYWH<S::Unit>
|
||||
) -> XYWH<S::Unit> {
|
||||
let (w, h): (S::Unit, S::Unit) = match exact {
|
||||
Exact::W(_, w) => {
|
||||
let w: Option<S::Unit> = (*w).into();
|
||||
(w.unwrap_or(area.2), area.3)
|
||||
},
|
||||
Exact::H(_, h) => {
|
||||
let h: Option<S::Unit> = (*h).into();
|
||||
(area.2, h.unwrap_or(area.3))
|
||||
},
|
||||
Exact::WH(_, w, h) => {
|
||||
let w: Option<S::Unit> = (*w).into();
|
||||
let h: Option<S::Unit> = (*h).into();
|
||||
(w.unwrap_or(area.2), w.unwrap_or(area.3))
|
||||
},
|
||||
_ => unreachable!()
|
||||
};
|
||||
to.clip(area, |to|item.draw(to))
|
||||
});
|
||||
XYWH(area.0, area.1, w, h)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
/// Use whole drawing area along one or both axes.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
use super::*;
|
||||
use crate::layout::*;
|
||||
|
||||
/// Iterate over a collection of the same kind of [Draw]able:
|
||||
pub fn iter <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
/// Iterate over a collection of the same kind of [Draw]able:
|
||||
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
|
||||
_iter: impl Iterator<Item = D>, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
/// Iterate over a collection of various [Draw]ables:
|
||||
|
|
@ -23,43 +22,43 @@ pub fn iter_dyn <
|
|||
I: Iterator<Item = D>, // Type of the iterator
|
||||
F: Fn(&D, usize)->dyn Draw<S>, // Function that returns [Draw]able from iterator item
|
||||
> (_items: V, _cb: F) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_east_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_south <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_south_fixed <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
||||
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||
) -> impl Draw<S> {
|
||||
thunk(move|_to: &mut S|{ todo!() })
|
||||
draw(move|_to: &mut S|{ todo!() })
|
||||
}
|
||||
|
||||
pub fn row_south <S: Screen> (south: S::Unit, height: S::Unit, content: impl Draw<S>)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
/// Set maximum size of of drawing area.
|
||||
///
|
||||
|
|
@ -50,6 +50,6 @@ impl<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>> + Copy> Draw<T> for Max<T,
|
|||
)),
|
||||
_ => return Ok(None)
|
||||
};
|
||||
to.clip(area, draw(item))
|
||||
to.clip(area, |to|item.draw(to))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
/// Only draw content if area is above a certain size.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,58 +1,4 @@
|
|||
use super::{*, layout::*};
|
||||
|
||||
#[macro_export] macro_rules! north {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! south {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! east {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! west {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! above {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! below {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
|
||||
}
|
||||
|
||||
pub const fn east <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::East.half(a, b)
|
||||
}
|
||||
|
||||
pub const fn north <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::North.half(a, b)
|
||||
}
|
||||
|
||||
pub const fn west <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::West.half(a, b)
|
||||
}
|
||||
|
||||
pub const fn south <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::South.half(a, b)
|
||||
}
|
||||
|
||||
pub const fn above <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::Above.half(a, b)
|
||||
}
|
||||
|
||||
pub const fn below <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Split::Below.half(a, b)
|
||||
}
|
||||
use super::*;
|
||||
|
||||
/// Split along an axis. Direction determines order.
|
||||
#[cfg_attr(test, derive(Arbitrary))]
|
||||
|
|
@ -65,6 +11,28 @@ pub const fn below <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw
|
|||
#[default] Below
|
||||
}
|
||||
|
||||
pub struct Pair<S: Screen, A: Draw<S>, B: Draw<S>>(Split, A, B, PhantomData<S>);
|
||||
|
||||
pub fn split <S: Screen, A: Draw<S>, B: Draw<S>> (
|
||||
split: Split, a: A, b: B
|
||||
) -> Pair<S, A, B> {
|
||||
Pair(split, a, b, PhantomData)
|
||||
}
|
||||
|
||||
impl<S: Screen, A: Draw<S>, B: Draw<S>> Draw<S> for Pair<S, A, B> {
|
||||
fn layout (&self, to: XYWH<S::Unit>) -> Drawn<S::Unit> {
|
||||
let Self(split, a, b, ..) = self;
|
||||
let (area_a, area_b) = stack_areas(split, to, a, b)?;
|
||||
Ok(stack_drawn(split, area_a, area_b))
|
||||
}
|
||||
fn draw (self, to: &mut S) -> Drawn<S::Unit> {
|
||||
let Self(ref split, a, b, ..) = self;
|
||||
let (area_a, area_b) = stack_areas(split, to.area(), &a, &b)?;
|
||||
let (drawn_a, drawn_b) = draw_stacks(split, to, a, area_a, None, b, area_b, None)?;
|
||||
Ok(stack_drawn(split, drawn_a, drawn_b))
|
||||
}
|
||||
}
|
||||
|
||||
impl Split {
|
||||
|
||||
/// ```
|
||||
|
|
@ -77,11 +45,7 @@ impl Split {
|
|||
/// let _ = Split::West.stack("", "");
|
||||
/// ```
|
||||
pub const fn stack <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: A, b: B) -> impl Draw<S> {
|
||||
thunk(move|to: &mut S|{
|
||||
let (area_a, area_b) = stack_areas(self, to.area(), &a, &b)?;
|
||||
let (drawn_a, drawn_b) = draw_stacks(self, to, a, area_a, None, b, area_b, None)?;
|
||||
Ok(stack_drawn(self, drawn_a, drawn_b))
|
||||
})
|
||||
Pair(*self, a, b, PhantomData)
|
||||
}
|
||||
|
||||
/// ```
|
||||
|
|
@ -94,7 +58,7 @@ impl Split {
|
|||
/// let _ = Split::West.half("", "");
|
||||
/// ```
|
||||
pub const fn half <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: A, b: B) -> impl Draw<S> {
|
||||
thunk(move|to: &mut S|{
|
||||
draw(move|to: &mut S|{
|
||||
let (area_a, area_b) = to.xywh().split_half(self);
|
||||
let (origin_a, origin_b) = self.origins();
|
||||
let (drawn_a, drawn_b) = draw_stacks(self, to, a, area_a, origin_a, b, area_b, origin_b)?;
|
||||
|
|
@ -157,17 +121,22 @@ fn draw_stacks <S: Screen> (
|
|||
area_b: impl Into<Option<XYWH<S::Unit>>>,
|
||||
origin_b: impl Into<Option<Azimuth>>,
|
||||
) -> Usually<(Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
|
||||
Ok(match split {
|
||||
Split::Below => {
|
||||
let drawn_b = to.clip(area_b.into(), |to|b.align(origin_b.into()).draw(to))?;
|
||||
let drawn_a = to.clip(area_a.into(), |to|a.align(origin_a.into()).draw(to))?;
|
||||
(drawn_a, drawn_b)
|
||||
},
|
||||
_ => {
|
||||
let drawn_a = to.clip(area_a.into(), |to|a.align(origin_a.into()).draw(to))?;
|
||||
let drawn_b = to.clip(area_b.into(), |to|b.align(origin_b.into()).draw(to))?;
|
||||
(drawn_a, drawn_b)
|
||||
}
|
||||
let draw_a = |to: &mut S|Ok::<_, Box<dyn Error>>(if let Some(origin_a) = origin_a.into() {
|
||||
to.clip(area_a.into(), |to|a.align(origin_a).draw(to))?
|
||||
} else {
|
||||
to.clip(area_a.into(), |to|a.draw(to))?
|
||||
});
|
||||
let draw_b = |to: &mut S|Ok::<_, Box<dyn Error>>(if let Some(origin_b) = origin_b.into() {
|
||||
to.clip(area_b.into(), |to|b.align(origin_b).draw(to))?
|
||||
} else {
|
||||
to.clip(area_b.into(), |to|b.draw(to))?
|
||||
});
|
||||
Ok(if matches!(split, Split::Below) {
|
||||
let drawn_b = draw_b(to)?;
|
||||
let drawn_a = draw_a(to)?;
|
||||
(drawn_a, drawn_b)
|
||||
} else {
|
||||
(draw_a(to)?, draw_b(to)?)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +166,7 @@ pub fn stack_areas <S: Screen> (
|
|||
),
|
||||
Split::North => (
|
||||
if let Some(used) = area_a {
|
||||
Some(XYWH(used.x(), area.y() + used.h(), used.w(), used.h()))
|
||||
Some(XYWH(used.x(), (area.y() + area.h()).minus(used.h()), used.w(), used.h()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
|
@ -211,7 +180,7 @@ pub fn stack_areas <S: Screen> (
|
|||
),
|
||||
Split::West => (
|
||||
if let Some(used) = area_a {
|
||||
Some(XYWH(area.x() + used.w(), used.y(), used.w(), used.h()))
|
||||
Some(XYWH((area.x() + area.w()).minus(used.w()), used.y(), used.w(), used.h()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
|
@ -253,18 +222,94 @@ fn stack_drawn <S: Coord> (
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! north {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! south {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! east {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! west {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! above {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) };
|
||||
}
|
||||
|
||||
#[macro_export] macro_rules! below {
|
||||
($head:expr $(,)?) => { $head };
|
||||
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
|
||||
}
|
||||
|
||||
pub const fn east <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::East, a, b, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn north <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::North, a, b, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn west <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::West, a, b, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn south <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::South, a, b, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn above <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::Above, a, b, PhantomData)
|
||||
}
|
||||
|
||||
pub const fn below <S: Screen, A: Draw<S>, B: Draw<S>> (a: A, b: B) -> impl Draw<S> {
|
||||
Pair(Split::Below, a, b, PhantomData)
|
||||
}
|
||||
|
||||
#[cfg(test)] mod test {
|
||||
use crate::*;
|
||||
|
||||
#[test] fn test_stack_areas () -> Usually<()> {
|
||||
let area = XYWH(0u16, 0, 80, 25);
|
||||
|
||||
assert_eq!(stack_areas(&Split::East, area, &"foo", &"bar")?, (
|
||||
Some(XYWH(0u16, 0, 3, 1)),
|
||||
Some(XYWH(3u16, 0, 3, 1)),
|
||||
));
|
||||
|
||||
assert_eq!(stack_areas(&Split::South, area, &"foo", &"bar")?, (
|
||||
Some(XYWH(0u16, 0, 3, 1)),
|
||||
Some(XYWH(0u16, 1, 3, 1)),
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test] fn test_split_stack () -> Usually<()> {
|
||||
use tengri::{*, Split::*};
|
||||
|
||||
let stack = split(East, "foo", "bar");
|
||||
assert_eq!(stack.layout(XYWH(0, 0, 80, 25))?, Some(XYWH(0, 0, 6, 1)));
|
||||
|
||||
let stack = split(South, "foo", "bar");
|
||||
assert_eq!(stack.layout(XYWH(0, 0, 80, 25))?, Some(XYWH(0, 0, 3, 2)));
|
||||
|
||||
let stack = split(South, split(East, "foo", "bar"), "baz");
|
||||
assert_eq!(stack.layout(XYWH(0, 0, 80, 25))?, Some(XYWH(0, 0, 6, 2)));
|
||||
|
||||
let stack = split(East, split(South, "foo", "bar"), "baz");
|
||||
assert_eq!(stack.layout(XYWH(0, 0, 80, 25))?, Some(XYWH(0, 0, 6, 2)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
532
src/lib.rs
532
src/lib.rs
|
|
@ -46,7 +46,6 @@ macro_rules! features {
|
|||
|
||||
features! {
|
||||
"time": [ time ],
|
||||
"play": [ exit, task ],
|
||||
"sing": [ sing ]
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +191,7 @@ macro_rules! eval_xy (
|
|||
) => {{
|
||||
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
||||
let variant = $variant;
|
||||
let thunk = thunk(move|screen|$state.interpret(screen, &$arg));
|
||||
let thunk = draw(move|screen|$state.interpret(screen, &$arg));
|
||||
match variant {
|
||||
// X variant
|
||||
Some("x") => thunk.$x().draw($output),
|
||||
|
|
@ -215,7 +214,7 @@ macro_rules! eval_xy (
|
|||
) => {{
|
||||
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
||||
let variant = $variant;
|
||||
let thunk = thunk(move|screen|$state.interpret(screen, &match variant {
|
||||
let thunk = draw(move|screen|$state.interpret(screen, &match variant {
|
||||
Some("x") | Some("y") => $arg1,
|
||||
Some("xy") | None => $arg2,
|
||||
_ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name)
|
||||
|
|
@ -236,9 +235,109 @@ macro_rules! eval_xy (
|
|||
}};
|
||||
);
|
||||
|
||||
#[cfg(feature = "exit")] pub use self::exit::*;
|
||||
#[cfg(feature = "exit")] mod exit {
|
||||
use crate::*;
|
||||
use std::sync::{Arc, atomic::AtomicBool};
|
||||
use crossterm::event::*;
|
||||
|
||||
#[derive(Clone)] pub struct Exit(Arc<AtomicBool>);
|
||||
|
||||
impl Exit {
|
||||
pub fn run <T> (run: impl FnOnce(Self)->Usually<T>) -> Usually<T> {
|
||||
run(Self(Arc::new(AtomicBool::new(false))))
|
||||
}
|
||||
pub fn is (event: &Event) -> bool {
|
||||
matches!(event, Event::Key(KeyEvent {
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
code: KeyCode::Char('c'),
|
||||
kind: KeyEventKind::Press,
|
||||
state: KeyEventState::NONE
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Arc<AtomicBool>> for Exit {
|
||||
fn as_ref (&self) -> &Arc<AtomicBool> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "play")] pub use self::task::*;
|
||||
#[cfg(feature = "play")] mod task {
|
||||
use std::{
|
||||
time::Duration,
|
||||
sync::{Arc, atomic::{AtomicBool, Ordering::*}},
|
||||
thread::{Builder, JoinHandle, sleep},
|
||||
};
|
||||
#[cfg(feature = "term")] use ::crossterm::event::poll;
|
||||
use crate::time::PerfModel;
|
||||
|
||||
#[derive(Debug)] pub struct Task {
|
||||
/// Exit flag.
|
||||
pub exit: Arc<AtomicBool>,
|
||||
/// Performance counter.
|
||||
pub perf: Arc<PerfModel>,
|
||||
/// Use this to wait for the thread to finish.
|
||||
pub join: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
||||
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 {
|
||||
exit: exit.clone(),
|
||||
perf: perf.clone(),
|
||||
join: Builder::new().name("tengri tui output".into()).spawn(move || {
|
||||
while !exit.fetch_and(true, Relaxed) {
|
||||
let _ = perf.cycle(&mut call);
|
||||
}
|
||||
})?.into()
|
||||
})
|
||||
}
|
||||
|
||||
/// 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, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
let _ = call(perf);
|
||||
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, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
if poll(time).is_ok() {
|
||||
let _ = call(perf);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn join (self) -> Result<(), Box<dyn std::any::Any + Send>> {
|
||||
self.join.join()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "draw")] pub use self::draw::*;
|
||||
#[cfg(feature = "draw")] mod draw {
|
||||
use crate::*;
|
||||
use Azimuth::*;
|
||||
use Split::*;
|
||||
|
||||
/// Output target.
|
||||
///
|
||||
|
|
@ -338,9 +437,82 @@ macro_rules! eval_xy (
|
|||
}
|
||||
}
|
||||
|
||||
/// The opposite of [thunk]?
|
||||
pub fn draw <S: Screen, T: Draw<S>> (item: T) -> impl FnOnce(&mut S) -> Perhaps<XYWH<S::Unit>> {
|
||||
move|to: &mut S|item.draw(to)
|
||||
/// Emit a [Draw]able.
|
||||
///
|
||||
/// Speculative. How to avoid conflicts with [Draw] proper?
|
||||
pub trait View<T: Screen> {
|
||||
fn view (&self) -> impl Draw<T>;
|
||||
}
|
||||
|
||||
impl<T: Screen> View<T> for () {
|
||||
fn view (&self) -> impl Draw<T> {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [Draw]able.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// let _ = view::<Tui, _, _>(||"drawable");
|
||||
/// let _ = view::<Tui, _, _>(||Some("drawable"));
|
||||
/// ```
|
||||
pub const fn view <S: Screen, T: Draw<S>, F: Fn()->T> (view: F) -> impl View<S> {
|
||||
ViewThunk(view, PhantomData)
|
||||
}
|
||||
|
||||
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
|
||||
pub struct ViewThunk<S: Screen, F>(pub F, std::marker::PhantomData<S>);
|
||||
|
||||
impl<S: Screen, T: Draw<S>, F: Fn()->T> View<S> for ViewThunk<S, F> {
|
||||
fn view (&self) -> impl Draw<S> {
|
||||
self.0()
|
||||
}
|
||||
}
|
||||
|
||||
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
|
||||
pub struct DrawThunk<S: Screen, F>(pub F, std::marker::PhantomData<S>);
|
||||
|
||||
impl<T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> Draw<T> for DrawThunk<T, F> {
|
||||
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
||||
(self.0)(to)
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic [Draw]able closure.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// let _ = draw(|to: &mut Tui|Ok(Some(to.1)));
|
||||
/// ```
|
||||
pub const fn draw <T: Screen, F: FnOnce(&mut T)->Perhaps<XYWH<T::Unit>>> (
|
||||
item: F
|
||||
) -> DrawThunk<T, F> {
|
||||
DrawThunk(item, std::marker::PhantomData)
|
||||
}
|
||||
|
||||
/// Only render when condition is true.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// # fn test () -> impl Draw<Tui> {
|
||||
/// when(true, "Yes")
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn when <T: Screen> (condition: bool, item: impl Draw<T>) -> impl Draw<T> {
|
||||
draw(move|to: &mut T|if condition { item.draw(to) } else { Ok(Default::default()) })
|
||||
}
|
||||
|
||||
/// Render one thing if a condition is true and another false.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// # fn test () -> impl Draw<Tui> {
|
||||
/// either(true, "Yes", "No")
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||
draw(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) })
|
||||
}
|
||||
|
||||
pub type Drawn<U> = Perhaps<XYWH<U>>;
|
||||
|
|
@ -367,34 +539,168 @@ macro_rules! eval_xy (
|
|||
//}
|
||||
//}
|
||||
|
||||
/// Emit a [Draw]able.
|
||||
///
|
||||
/// Speculative. How to avoid conflicts with [Draw] proper?
|
||||
pub trait View<T: Screen> {
|
||||
fn view (&self) -> impl Draw<T>;
|
||||
}
|
||||
|
||||
impl<T: Screen> View<T> for () {
|
||||
fn view (&self) -> impl Draw<T> {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Screen, V: View<T>> Draw<T> for &V {
|
||||
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
||||
self.view().draw(to)
|
||||
}
|
||||
}
|
||||
|
||||
features! {
|
||||
"draw": [
|
||||
iter,
|
||||
lrtb,
|
||||
sizer,
|
||||
split,
|
||||
thunk,
|
||||
xywh
|
||||
]
|
||||
pub trait Xy<N: Coord> {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
}
|
||||
|
||||
pub trait Wh<N: Coord>: Wide<N> + Tall<N> {
|
||||
fn wh (&self) -> [N;2];
|
||||
}
|
||||
|
||||
pub trait Xywh<N: Coord>: Xy<N> + Wh<N> {
|
||||
fn xywh (&self) -> XYWH<N> {
|
||||
XYWH(self.x(), self.y(), self.w(), self.h())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Wide<N: Coord>: Xy<N> {
|
||||
fn w (&self) -> N { N::zero() }
|
||||
fn w_min (&self) -> N { self.w() }
|
||||
fn w_max (&self) -> N { self.w() }
|
||||
}
|
||||
|
||||
pub trait Tall<N: Coord> {
|
||||
fn h (&self) -> N { N::zero() }
|
||||
fn h_min (&self) -> N { self.h() }
|
||||
fn h_max (&self) -> N { self.h() }
|
||||
}
|
||||
|
||||
/// Point with size.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::*;
|
||||
/// let xywh = XYWH(0u16, 0, 0, 0);
|
||||
/// assert_eq!(XYWH(10u16, 10, 20, 20).center(), (20, 20));
|
||||
/// ```
|
||||
///
|
||||
/// * [ ] TODO: origin field (determines at which corner/side is X0 Y0)
|
||||
///
|
||||
#[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct XYWH<N: Coord>(pub N, pub N, pub N, pub N);
|
||||
|
||||
impl<N: Coord> Xy<N> for XYWH<N> {
|
||||
fn x (&self) -> N { self.0 }
|
||||
fn y (&self) -> N { self.1 }
|
||||
}
|
||||
|
||||
impl<N: Coord> Wide<N> for XYWH<N> { fn w (&self) -> N { self.2 } }
|
||||
|
||||
impl<N: Coord> Tall<N> for XYWH<N> { fn h (&self) -> N { self.3 } }
|
||||
|
||||
impl<N: Coord> XYWH<N> {
|
||||
|
||||
pub fn zero () -> Self {
|
||||
Self(0.into(), 0.into(), 0.into(), 0.into())
|
||||
}
|
||||
|
||||
pub fn center (&self) -> (N, N) {
|
||||
let Self(x, y, w, h) = *self;
|
||||
(x.plus(w/2.into()), y.plus(h/2.into()))
|
||||
}
|
||||
|
||||
pub fn centered (&self) -> (N, N) {
|
||||
let Self(x, y, w, h) = *self;
|
||||
(x.minus(w/2.into()), y.minus(h/2.into()))
|
||||
}
|
||||
|
||||
pub fn centered_x (&self, n: N) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||
let y_center = y.plus(h / 2.into());
|
||||
XYWH(x_center, y_center, n, 1.into())
|
||||
}
|
||||
|
||||
pub fn centered_y (&self, n: N) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = x.plus(w / 2.into());
|
||||
let y_corner = (y.plus(h / 2.into())).minus(n / 2.into());
|
||||
XYWH(x_center, y_corner, 1.into(), n)
|
||||
}
|
||||
|
||||
pub fn centered_xy (&self, [n, m]: [N;2]) -> Self {
|
||||
let Self(x, y, w, h) = *self;
|
||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||
let y_corner = (y.plus(h / 2.into())).minus(m / 2.into());
|
||||
XYWH(x_center, y_corner, n, m)
|
||||
}
|
||||
|
||||
pub fn split_half (&self, direction: &Split) -> (Self, Self) {
|
||||
let XYWH(x, y, w, h) = self.xywh();
|
||||
match direction {
|
||||
South => (XYWH(x, y, w, h - h / 2.into()), XYWH(x, y + h / 2.into(), w, h / 2.into())),
|
||||
East => (XYWH(x, y, w - w / 2.into(), h), XYWH(x + w / 2.into(), y, w / 2.into(), h)),
|
||||
North => (XYWH(x, y + h / 2.into(), w, h - h / 2.into()), XYWH(x, y, w, h / 2.into())),
|
||||
West => (XYWH(x + w / 2.into(), y, w - w / 2.into(), h), XYWH(x, y, w / 2.into(), h)),
|
||||
Above | Below => (XYWH(x, y, w, h), XYWH(x, y, w, h))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl From<&ratatui::prelude::Rect> for XYWH<u16> {
|
||||
fn from (rect: &ratatui::prelude::Rect) -> Self {
|
||||
Self(rect.x, rect.y, rect.width, rect.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coord, T: Wide<N> + Tall<N>> Wh<N> for T {
|
||||
fn wh (&self) -> [N;2] {
|
||||
[self.w(), self.h()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Coord, T: Xy<N> + Wh<N>> Xywh<N> for T {}
|
||||
|
||||
impl<N: Coord, T: Xywh<N>> Lrtb<N> for T {}
|
||||
|
||||
pub trait Lrtb<N: Coord>: Xywh<N> {
|
||||
fn lrtb (&self) -> [N;4] {
|
||||
// FIXME: factor origin
|
||||
[self.x(), self.y(), self.x()+self.w(), self.y()+self.h()]
|
||||
}
|
||||
fn iter_x (&self) -> std::ops::Range<N> where Self: HasOrigin {
|
||||
self.x_west()..self.x_east()
|
||||
}
|
||||
fn x_west (&self) -> N where Self: HasOrigin {
|
||||
let w = self.w();
|
||||
let a = self.origin();
|
||||
let d = match a { NW|W|SW => 0.into(), N|X|C|Y|S => w/2.into(), NE|E|SE => w };
|
||||
self.x().minus(d)
|
||||
}
|
||||
fn x_east (&self) -> N where Self: HasOrigin {
|
||||
let w = self.w();
|
||||
let a = self.origin();
|
||||
let d = match a { NW|W|SW => w, N|X|C|Y|S => w/2.into(), NE|E|SE => 0.into() };
|
||||
self.x().plus(d)
|
||||
}
|
||||
fn x_center (&self) -> N where Self: HasOrigin {
|
||||
todo!()
|
||||
}
|
||||
fn iter_y (&self) -> std::ops::Range<N> where Self: HasOrigin {
|
||||
self.y_north()..self.y_south()
|
||||
}
|
||||
fn y_north (&self) -> N where Self: HasOrigin {
|
||||
let a = self.origin();
|
||||
let h = self.h();
|
||||
let d = match a { NW|N|NE => 0.into(), W|X|C|Y|E => h/2.into(), SW|S|SE => h };
|
||||
self.y().minus(d)
|
||||
}
|
||||
fn y_south (&self) -> N where Self: HasOrigin {
|
||||
let a = self.origin();
|
||||
let h = self.h();
|
||||
let d = match a { NW|N|NE => h, W|X|C|Y|E => h/2.into(), SW|S|SE => 0.into() };
|
||||
self.y().plus(d)
|
||||
}
|
||||
fn y_center (&self) -> N where Self: HasOrigin {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +750,7 @@ macro_rules! eval_xy (
|
|||
|
||||
#[cfg(feature = "draw")] pub use self::layout::*;
|
||||
#[cfg(feature = "draw")] mod layout {
|
||||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
|
||||
impl<S: Screen, T: Draw<S>> Layout<S> for T {}
|
||||
|
||||
|
|
@ -613,21 +919,66 @@ macro_rules! eval_xy (
|
|||
#[default] C, X, Y, NW, N, NE, E, SE, S, SW, W
|
||||
}
|
||||
|
||||
/// Uses [AtomicUsize] to measure size during\
|
||||
/// rendering (which is normally read-only).
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Sizer(
|
||||
/// Width
|
||||
pub Arc<AtomicUsize>,
|
||||
/// Height
|
||||
pub Arc<AtomicUsize>,
|
||||
);
|
||||
|
||||
impl Xy<u16> for Sizer {
|
||||
fn x (&self) -> u16 {
|
||||
self.0.load(Relaxed) as u16
|
||||
}
|
||||
fn y (&self) -> u16 {
|
||||
self.1.load(Relaxed) as u16
|
||||
}
|
||||
}
|
||||
impl Wide<u16> for Sizer {
|
||||
fn w (&self) -> u16 {
|
||||
self.0.load(Relaxed) as u16
|
||||
}
|
||||
}
|
||||
impl Tall<u16> for Sizer {
|
||||
fn h (&self) -> u16 {
|
||||
self.1.load(Relaxed) as u16
|
||||
}
|
||||
}
|
||||
impl PartialEq for Sizer {
|
||||
fn eq (&self, _: &Self) -> bool { todo!() }
|
||||
}
|
||||
|
||||
impl Sizer {
|
||||
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
|
||||
draw(move|to: &mut T|{
|
||||
let area = of.draw(to)?;
|
||||
self.0.store(area.map(|a|a.w()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
||||
self.1.store(area.map(|a|a.h()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mod align; pub use self::align::*;
|
||||
mod area; pub use self::area::*;
|
||||
mod exact; pub use self::exact::*;
|
||||
mod full; pub use self::full::*;
|
||||
mod iter; pub use self::iter::*;
|
||||
mod max; pub use self::max::*;
|
||||
mod min; pub use self::min::*;
|
||||
mod origin; pub use self::origin::*;
|
||||
mod pad; pub use self::pad::*;
|
||||
mod pull; pub use self::pull::*;
|
||||
mod push; pub use self::push::*;
|
||||
mod split; pub use self::split::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "draw")] pub use self::color::*;
|
||||
#[cfg(feature = "draw")] mod color {
|
||||
use crate::{*, draw::*};
|
||||
use crate::*;
|
||||
use dizzle::LanguageError::*;
|
||||
use ::ratatui::style::Color;
|
||||
use ::rand::distributions::uniform::UniformSampler;
|
||||
|
|
@ -814,8 +1165,10 @@ macro_rules! eval_xy (
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
mod text {
|
||||
#[cfg(feature = "draw")] pub use self::text::*;
|
||||
#[cfg(feature = "text")] mod text {
|
||||
#![allow(unused)]
|
||||
|
||||
pub(crate) use ::unicode_width::*;
|
||||
|
||||
/// Displays an owned [str]-like with fixed maximum width.
|
||||
|
|
@ -878,10 +1231,7 @@ mod text {
|
|||
#[cfg(feature = "term")] pub use self::term::*;
|
||||
#[cfg(feature = "term")] mod term {
|
||||
use crate::*;
|
||||
use crate::draw::*;
|
||||
use crate::layout::*;
|
||||
use Color::*;
|
||||
#[cfg(feature = "text")] use crate::text::*;
|
||||
|
||||
#[macro_export] macro_rules! tui_app {
|
||||
($Struct:ident { $($fields:tt)* }) => {
|
||||
|
|
@ -982,6 +1332,7 @@ mod text {
|
|||
impl Tall<u16> for Tui { fn h (&self) -> u16 { self.1.3 } }
|
||||
impl HasOrigin for Tui { fn origin (&self) -> Azimuth { Azimuth::NW } }
|
||||
impl Xy<u16> for Tui { fn x (&self) -> u16 { self.1.0 } fn y (&self) -> u16 { self.1.1 } }
|
||||
|
||||
/// Terminal output.
|
||||
pub struct Tui(
|
||||
/// Ratatui buffer; area is screen size
|
||||
|
|
@ -989,6 +1340,7 @@ mod text {
|
|||
/// Current draw area
|
||||
pub XYWH<u16>
|
||||
);
|
||||
|
||||
impl Tui {
|
||||
pub fn setup_panic () {
|
||||
use ::std::panic::{set_hook, PanicHookInfo};
|
||||
|
|
@ -1151,7 +1503,7 @@ mod text {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn catcher <T: Draw<Tui>> (result: Usually<T>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|match result {
|
||||
draw(move|to: &mut Tui|match result {
|
||||
Ok(content) => content.draw(to),
|
||||
Err(e) => {
|
||||
let err_fg = Color::Rgb(255,224,244);
|
||||
|
|
@ -1166,7 +1518,7 @@ mod text {
|
|||
/// Interpret TUI-specific layout operation.
|
||||
///
|
||||
/// ```
|
||||
/// use tengri::{*, lang::*, ratatui::prelude::Color};
|
||||
/// use tengri::{*, dizzle::*, ratatui::prelude::Color};
|
||||
///
|
||||
/// #[namespace(bool)]
|
||||
/// #[namespace(u8)]
|
||||
|
|
@ -1233,10 +1585,7 @@ mod text {
|
|||
Some("fg") => {
|
||||
let arg0 = arg0?.expect("fg: expected arg 0 (color)");
|
||||
if let Some(color) = Namespace::namespace(state, arg0)? {
|
||||
fg(color, thunk(move|to: &mut Tui|{
|
||||
state.interpret(to, &arg1)?;
|
||||
Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
|
||||
})).draw(to)
|
||||
fg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(to)
|
||||
} else {
|
||||
return Err(format!("fg: {arg0:?}: not a color").into())
|
||||
}
|
||||
|
|
@ -1245,10 +1594,7 @@ mod text {
|
|||
Some("bg") => {
|
||||
let arg0 = arg0?.expect("bg: expected arg 0 (color)");
|
||||
if let Some(color) = Namespace::namespace(state, arg0)? {
|
||||
bg(color, thunk(move|to: &mut Tui|{
|
||||
state.interpret(to, &arg1)?;
|
||||
Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
|
||||
})).draw(to)
|
||||
bg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(to)
|
||||
} else {
|
||||
return Err(format!("bg: {arg0:?}: not a color").into())
|
||||
}
|
||||
|
|
@ -1310,6 +1656,7 @@ mod text {
|
|||
Ok(Some(XYWH(x0, y, string_width, 1)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Screen for Tui {
|
||||
type Unit = u16;
|
||||
/// Render drawable in subarea specified by `area`
|
||||
|
|
@ -1349,21 +1696,21 @@ mod text {
|
|||
}
|
||||
|
||||
pub const fn fill_char (c: char) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{
|
||||
draw(move|to: &mut Tui|Ok(Some(to.update(&|cell,_,_|{
|
||||
cell.set_char(c);
|
||||
}))))
|
||||
}
|
||||
|
||||
/// Draw contents with modifier applied.
|
||||
pub const fn modify (on: bool, modifier: Modifier, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
pub const fn modify (on: bool, modifier: Modifier, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
draw(move|to: &mut Tui|{
|
||||
fill_mod(on, modifier).draw(to)?;
|
||||
draw.draw(to)
|
||||
item.draw(to)
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|Ok(Some({
|
||||
draw(move|to: &mut Tui|Ok(Some({
|
||||
if on {
|
||||
to.update(&|cell,_,_|cell.modifier.insert(modifier))
|
||||
} else {
|
||||
|
|
@ -1373,8 +1720,8 @@ mod text {
|
|||
}
|
||||
|
||||
/// Draw contents with bold modifier applied.
|
||||
pub const fn bold (on: bool, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
modify(on, Modifier::BOLD, draw)
|
||||
pub const fn bold (on: bool, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
modify(on, Modifier::BOLD, item)
|
||||
}
|
||||
|
||||
#[cfg(feature = "text")]
|
||||
|
|
@ -1421,7 +1768,7 @@ mod text {
|
|||
layout_text_u16(self.as_ref(), area)
|
||||
}
|
||||
fn draw (self, to: &mut Tui) -> Drawn<u16> {
|
||||
let XYWH(x, y, w, ..) = to.1;
|
||||
let XYWH(x, y, w, ..) = to.area();
|
||||
let mut width: u16 = 1;
|
||||
let mut chars = self.1.as_ref().chars();
|
||||
while let Some(c) = chars.next() {
|
||||
|
|
@ -1443,14 +1790,14 @@ mod text {
|
|||
|
||||
#[cfg(feature = "text")]
|
||||
fn layout_text_u16 (text: &str, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
|
||||
let XYWH(x, y, ..) = area;
|
||||
let XYWH(x, y, w, h) = area;
|
||||
let mut max_w = 0u16;
|
||||
let mut max_h = 0u16;
|
||||
for line in text.split("\n") {
|
||||
max_h += 1;
|
||||
max_w = max_w.max(line.len() as u16);
|
||||
}
|
||||
Ok(Some(XYWH(x, y, max_w, max_h)))
|
||||
Ok(Some(XYWH(x, y, w.min(max_w), h.min(max_h))))
|
||||
}
|
||||
|
||||
pub struct ShowSize;
|
||||
|
|
@ -1467,31 +1814,56 @@ mod text {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ShowSizeOf<T>(pub T);
|
||||
|
||||
impl<T: Draw<Tui>> Draw<Tui> for ShowSizeOf<T> {
|
||||
fn layout (&self, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
|
||||
self.0.layout(area)
|
||||
}
|
||||
fn draw (self, to: &mut Tui) -> Drawn<u16> {
|
||||
Ok(self.0.draw(to)?.map(|used|{
|
||||
let _ = to.text(&format!("{used:?}"), used.0, used.1, u16::MAX);
|
||||
used
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply foreground color.
|
||||
pub const fn fg (fg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
pub const fn fg (fg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
draw(move|to: &mut Tui|{
|
||||
to.update(&|cell,_,_|{ cell.set_fg(fg); });
|
||||
draw.draw(to)
|
||||
item.draw(to)
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply background color.
|
||||
pub const fn bg (bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
to.update(&|cell,_,_|{ cell.set_bg(bg); });
|
||||
draw.draw(to)
|
||||
})
|
||||
pub const fn bg (bg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
Background(bg, item)
|
||||
}
|
||||
|
||||
pub const fn fg_bg (fg: Color, bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
pub struct Background<T>(Color, T);
|
||||
|
||||
impl<T: Draw<Tui>> Draw<Tui> for Background<T> {
|
||||
fn layout (&self, area: XYWH<u16>) -> Drawn<u16> {
|
||||
self.1.layout(area)
|
||||
}
|
||||
fn draw (self, to: &mut Tui) -> Drawn<u16> {
|
||||
self.layout(to.area()).map(|area|to.clip(area, |to|{
|
||||
to.update(&|cell,_,_|{ cell.set_bg(self.0); });
|
||||
self.1.draw(to)
|
||||
}))?
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn fg_bg (fg: Color, bg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
draw(move|to: &mut Tui|{
|
||||
to.update(&|cell,_,_|{ cell.set_fg(fg); cell.set_bg(bg); });
|
||||
draw.draw(to)
|
||||
item.draw(to)
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn fill_ul (color: Option<Color>) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|Ok(Some(if let Some(color) = color {
|
||||
draw(move|to: &mut Tui|Ok(Some(if let Some(color) = color {
|
||||
to.update(&|cell,_,_|{
|
||||
cell.modifier.insert(Modifier::UNDERLINED);
|
||||
cell.underline_color = color;
|
||||
|
|
@ -1532,7 +1904,6 @@ mod text {
|
|||
mod buffer; pub use self::buffer::*;
|
||||
mod repeat; pub use self::repeat::*;
|
||||
mod scroll; pub use self::scroll::*;
|
||||
mod colors; pub use self::colors::*;
|
||||
mod phat; pub use self::phat::*;
|
||||
mod button; pub use self::button::*;
|
||||
|
||||
|
|
@ -1553,7 +1924,7 @@ mod text {
|
|||
/// Interpret layout operation.
|
||||
///
|
||||
/// ```
|
||||
/// # use tengri::{*, lang::*};
|
||||
/// # use tengri::{*, dizzle::*};
|
||||
///
|
||||
/// struct State {/*app-specific*/}
|
||||
/// impl<'b> Namespace<'b, u16> for State {}
|
||||
|
|
@ -1598,29 +1969,28 @@ mod text {
|
|||
|
||||
Some("when") => when(
|
||||
state.namespace(arg0?)?.unwrap(),
|
||||
thunk(move|output: &mut O|{state.interpret(output, &arg1)})
|
||||
draw(move|output: &mut O|{state.interpret(output, &arg1)})
|
||||
).draw(output),
|
||||
|
||||
Some("either") => either(
|
||||
state.namespace(arg0?)?.unwrap(),
|
||||
thunk(move|output: &mut O|{state.interpret(output, &arg1)}),
|
||||
thunk(move|output: &mut O|{state.interpret(output, &arg2)}),
|
||||
draw(move|output: &mut O|{state.interpret(output, &arg1)}),
|
||||
draw(move|output: &mut O|{state.interpret(output, &arg2)}),
|
||||
).draw(output),
|
||||
|
||||
Some("bsp") => eval_enum!("bsp", output, state, frags.next(), arg0, Split {
|
||||
"n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below
|
||||
}).stack(
|
||||
thunk(move|output: &mut O|{state.interpret(output, &arg0)}),
|
||||
thunk(move|output: &mut O|{state.interpret(output, &arg1)}),
|
||||
draw(move|output: &mut O|{state.interpret(output, &arg0)}),
|
||||
draw(move|output: &mut O|{state.interpret(output, &arg1)}),
|
||||
).draw(output),
|
||||
|
||||
Some("align") => thunk(move|output: &mut O|{
|
||||
state.interpret(output, &arg0)
|
||||
}).align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth {
|
||||
"c" => C, "x" => X, "y" => Y,
|
||||
"n" => N, "s" => S, "e" => E, "w" => W,
|
||||
"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
|
||||
})).draw(output),
|
||||
Some("align") => draw(move|output: &mut O|{state.interpret(output, &arg0)})
|
||||
.align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth {
|
||||
"c" => C, "x" => X, "y" => Y,
|
||||
"n" => N, "s" => S, "e" => E, "w" => W,
|
||||
"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
|
||||
})).draw(output),
|
||||
|
||||
Some("exact") => eval_xy!(
|
||||
"exact" => expr, head, output, state, frags.next(), exact_wh, exact_w, exact_h, arg0, arg1, arg2,
|
||||
|
|
|
|||
65
src/task.rs
65
src/task.rs
|
|
@ -1,65 +0,0 @@
|
|||
use std::{
|
||||
time::Duration,
|
||||
sync::{Arc, atomic::{AtomicBool, Ordering::*}},
|
||||
thread::{Builder, JoinHandle, sleep},
|
||||
};
|
||||
#[cfg(feature = "term")] use ::crossterm::event::poll;
|
||||
use crate::time::PerfModel;
|
||||
|
||||
#[derive(Debug)] pub struct Task {
|
||||
/// Exit flag.
|
||||
pub exit: Arc<AtomicBool>,
|
||||
/// Performance counter.
|
||||
pub perf: Arc<PerfModel>,
|
||||
/// Use this to wait for the thread to finish.
|
||||
pub join: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
||||
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 {
|
||||
exit: exit.clone(),
|
||||
perf: perf.clone(),
|
||||
join: Builder::new().name("tengri tui output".into()).spawn(move || {
|
||||
while !exit.fetch_and(true, Relaxed) {
|
||||
let _ = perf.cycle(&mut call);
|
||||
}
|
||||
})?.into()
|
||||
})
|
||||
}
|
||||
|
||||
/// 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, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
let _ = call(perf);
|
||||
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, mut call: F
|
||||
) -> Result<Self, std::io::Error>
|
||||
where F: FnMut(&PerfModel)->() + Send + Sync + 'static
|
||||
{
|
||||
Self::new(exit, move |perf| {
|
||||
if poll(time).is_ok() {
|
||||
let _ = call(perf);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn join (self) -> Result<(), Box<dyn std::any::Any + Send>> {
|
||||
self.join.join()
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,9 @@ use super::{*, draw::*};
|
|||
/// ```
|
||||
/// /// TODO
|
||||
/// ```
|
||||
pub fn border (on: bool, style: impl BorderStyle, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
let content = draw.pad_wh(1, 1);
|
||||
let outline = when(on, thunk(move|to: &mut Tui|{
|
||||
pub fn border (on: bool, style: impl BorderStyle, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||
let content = item.pad_wh(1, 1);
|
||||
let outline = when(on, draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, w, h) = to.1;
|
||||
if w > 0 && h > 0 {
|
||||
to.blit(&style.border_nw(), x, y, style.style());
|
||||
|
|
@ -49,7 +49,7 @@ macro_rules! border {
|
|||
//impl Layout<Tui> for $T {}
|
||||
impl Draw<Tui> for $T {
|
||||
fn draw (self, to: &mut Tui) -> Perhaps<XYWH<u16>> {
|
||||
when(self.enabled(), thunk(|to: &mut Tui|BorderStyle::draw(self, to).map(Some))).draw(to)
|
||||
when(self.enabled(), draw(|to: &mut Tui|BorderStyle::draw(self, to).map(Some))).draw(to)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::{*, draw::*};
|
|||
use ratatui::{prelude::{Position}};
|
||||
|
||||
pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, w, _h) = to.xywh();
|
||||
for x in x..x+w {
|
||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
||||
|
|
@ -14,7 +14,7 @@ pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
|||
}
|
||||
|
||||
pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, _w, h) = to.xywh();
|
||||
for y in y..y+h {
|
||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
||||
|
|
@ -26,7 +26,7 @@ pub const fn y_repeat (c: &str) -> impl Draw<Tui> {
|
|||
}
|
||||
|
||||
pub const fn xy_repeat (c: &str) -> impl Draw<Tui> {
|
||||
thunk(move|to: &mut Tui|{
|
||||
draw(move|to: &mut Tui|{
|
||||
let XYWH(x, y, w, h) = to.xywh();
|
||||
let a = c.len();
|
||||
for (_v, y) in (y..y+h).enumerate() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub const ICON_DEC_H: &[char] = &[' ', '🞀', ' '];
|
|||
pub const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
|
||||
|
||||
pub fn x_scroll () -> impl Draw<Tui> {
|
||||
thunk(|Tui(buf, XYWH(x1, y1, w, _h)): &mut Tui|{
|
||||
draw(|Tui(buf, XYWH(x1, y1, w, _h)): &mut Tui|{
|
||||
let x2 = *x1 + *w;
|
||||
for (i, x) in (*x1..=x2).enumerate() {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) {
|
||||
|
|
@ -35,7 +35,7 @@ pub fn x_scroll () -> impl Draw<Tui> {
|
|||
}
|
||||
|
||||
pub fn y_scroll () -> impl Draw<Tui> {
|
||||
thunk(|Tui(buf, XYWH(x1, y1, _w, h)): &mut Tui|{
|
||||
draw(|Tui(buf, XYWH(x1, y1, _w, h)): &mut Tui|{
|
||||
let y2 = *y1 + *h;
|
||||
for (i, y) in (*y1..=y2).enumerate() {
|
||||
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue