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."
|
description = "UI metaframework."
|
||||||
|
|
||||||
[features]
|
[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"]
|
bumpalo = ["dep:bumpalo"]
|
||||||
draw = []
|
draw = []
|
||||||
gui = ["draw", "dep:winit"]
|
gui = ["draw", "dep:winit"]
|
||||||
eval = []
|
eval = []
|
||||||
|
exit = []
|
||||||
lang = ["dep:dizzle"]
|
lang = ["dep:dizzle"]
|
||||||
midi = ["dep:midly"]
|
midi = ["dep:midly"]
|
||||||
okhsl = ["dep:palette"]
|
okhsl = ["dep:palette"]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use ::tengri::{
|
use ::tengri::{
|
||||||
*,
|
*,
|
||||||
lang::*,
|
dizzle::*,
|
||||||
crossterm::event::{KeyEvent, Event::*, KeyCode::*},
|
crossterm::event::{KeyEvent, Event::*, KeyCode::*},
|
||||||
ratatui::style::Color,
|
ratatui::style::Color,
|
||||||
};
|
};
|
||||||
|
|
@ -13,8 +13,8 @@ tui_app!(State {
|
||||||
});
|
});
|
||||||
|
|
||||||
tui_view!(self: State {
|
tui_view!(self: State {
|
||||||
thunk(|to: &mut Tui|{
|
draw(|to: &mut Tui|{
|
||||||
let _ = "DEMO [MODE 00]".align_sw().draw(to);
|
let _ = "Demo [Mode 00]".align_sw().draw(to);
|
||||||
let _ = ShowSize.align_se().draw(to);
|
let _ = ShowSize.align_se().draw(to);
|
||||||
let _ = format!("Counter:\n{}", self.counter).align_c().draw(to);
|
let _ = format!("Counter:\n{}", self.counter).align_c().draw(to);
|
||||||
Ok(Some(to.area()))
|
Ok(Some(to.area()))
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
//! Mode 01: Direct view, actions with history
|
//! Mode 01: Direct view, actions with history
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use ::tengri::{
|
use ::tengri::{
|
||||||
*,
|
*,
|
||||||
lang::*,
|
dizzle::{*, itertools::Itertools},
|
||||||
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
||||||
ratatui::style::Color,
|
ratatui::style::Color,
|
||||||
};
|
};
|
||||||
|
|
@ -28,7 +27,7 @@ tui_keys!(self: State, input {
|
||||||
});
|
});
|
||||||
|
|
||||||
tui_view!(self: State {
|
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 items = self.history.iter().take(10).map(|x|format!("{x:?}")).join("\n");
|
||||||
let history = format!("History: {}\n{items}", self.history.len());
|
let history = format!("History: {}\n{items}", self.history.len());
|
||||||
let cursor = format!("Counter: {}", self.cursor);
|
let cursor = format!("Counter: {}", self.cursor);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use ::tengri::{
|
use ::tengri::{
|
||||||
*,
|
*,
|
||||||
lang::*,
|
dizzle::*,
|
||||||
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
crossterm::event::{Event::*, KeyEvent, KeyCode::*},
|
||||||
ratatui::style::Color,
|
ratatui::style::Color,
|
||||||
};
|
};
|
||||||
|
|
@ -20,11 +20,15 @@ tui_view!(self: State {
|
||||||
let index = self.cursor + 1;
|
let index = self.cursor + 1;
|
||||||
let wh = (self.size.w(), self.size.h());
|
let wh = (self.size.w(), self.size.h());
|
||||||
let src = VIEWS.get(self.cursor).unwrap_or(&"");
|
let src = VIEWS.get(self.cursor).unwrap_or(&"");
|
||||||
let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh);
|
let heading = format!("Demo [Mode 02]\nExample {}/{}\nSize {:?}", index, VIEWS.len(), &wh);
|
||||||
let title = bg(Color::Rgb(60, 10, 10), heading.align_n().push_xy(2, 1)).exact_h(3);
|
let title = bg(Color::Rgb(60, 10, 10), heading);
|
||||||
let code = bg(Color::Rgb(10, 60, 10), format!("Source:\n{}", src).align_n().push_xy(2, 1)).min_h(4);
|
let code = bg(Color::Rgb(10, 60, 10), format!("Source:\n{}", src).max_h(10));
|
||||||
let widget = thunk(move|to: &mut Tui|self.interpret(to, &src));
|
let widget = draw(move|to: &mut Tui|self.interpret(to, &src)); // FIXME this forcefills
|
||||||
self.size.of(east(south(title, code).max_w(40), widget))
|
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 {
|
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> {
|
impl<S: Screen, T: Draw<S>> Draw<S> for Align<T> {
|
||||||
fn layout (&self, area: XYWH<S::Unit>) -> Perhaps<XYWH<S::Unit>> {
|
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))
|
Ok(align::<S>(area, self.1.layout(area)?, self.0))
|
||||||
}
|
}
|
||||||
fn draw (self, to: &mut S) -> Perhaps<XYWH<S::Unit>> {
|
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::*;
|
/// use tengri::*;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{*, draw::*};
|
use crate::*;
|
||||||
|
|
||||||
/// Set size of of drawing area.
|
/// Set size of of drawing area.
|
||||||
///
|
///
|
||||||
|
|
@ -8,22 +8,42 @@ use crate::{*, draw::*};
|
||||||
/// let _ = "".exact_h(1);
|
/// let _ = "".exact_h(1);
|
||||||
/// let _ = "".exact_wh(1, 1);
|
/// let _ = "".exact_wh(1, 1);
|
||||||
/// ```
|
/// ```
|
||||||
pub enum Exact<T: Screen, I: Draw<T>, X: Into<Option<T::Unit>>> {
|
pub enum Exact<S: Screen, I: Draw<S>, X: Into<Option<S::Unit>>> {
|
||||||
__(PhantomData<T>),
|
__(PhantomData<S>),
|
||||||
W(I, X),
|
W(I, X),
|
||||||
H(I, X),
|
H(I, X),
|
||||||
WH(I, X, 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();
|
impl <S: Screen, I: Draw<S>, X: Into<Option<S::Unit>> + Copy> Draw<S> for Exact<S, I, X> {
|
||||||
let (item, area) = match self {
|
fn layout (&self, area: XYWH<S::Unit>) -> Perhaps<XYWH<S::Unit>> {
|
||||||
Self::W(item, w1) =>
|
Ok(Some(layout_exact(self, area)))
|
||||||
(item, XYWH(area.0, area.1, w1.into().unwrap_or(area.2), area.3)),
|
}
|
||||||
Self::H(item, h1) =>
|
fn draw (self, to: &mut S) -> Perhaps<XYWH<S::Unit>> {
|
||||||
(item, XYWH(area.0, area.1, area.2, h1.into().unwrap_or(area.3))),
|
let area = layout_exact(&self, to.area());
|
||||||
Self::WH(item, w1, h1) =>
|
let item = match self { Self::W(i, ..) => i, Self::H(i, ..) => i, Self::WH(i, ..) => i, _ => unreachable!() };
|
||||||
(item, XYWH(area.0, area.1, w1.into().unwrap_or(area.2), h1.into().unwrap_or(area.3))),
|
to.clip(area, |to|item.draw(to))
|
||||||
_ => return Ok(None)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
/// Use whole drawing area along one or both axes.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::layout::*;
|
|
||||||
|
|
||||||
/// Iterate over a collection of the same kind of [Draw]able:
|
/// 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>> (
|
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,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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:
|
/// Iterate over a collection of the same kind of [Draw]able:
|
||||||
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
|
pub fn iter_once <'a, S: Screen, D: 'a, U: Draw<S>> (
|
||||||
_iter: impl Iterator<Item = D>, _draw: impl Fn(D, usize)->U,
|
_iter: impl Iterator<Item = D>, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> impl Draw<S> {
|
||||||
thunk(move|_to: &mut S|{ todo!() })
|
draw(move|_to: &mut S|{ todo!() })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over a collection of various [Draw]ables:
|
/// Iterate over a collection of various [Draw]ables:
|
||||||
|
|
@ -23,43 +22,43 @@ pub fn iter_dyn <
|
||||||
I: Iterator<Item = D>, // Type of the iterator
|
I: Iterator<Item = D>, // Type of the iterator
|
||||||
F: Fn(&D, usize)->dyn Draw<S>, // Function that returns [Draw]able from iterator item
|
F: Fn(&D, usize)->dyn Draw<S>, // Function that returns [Draw]able from iterator item
|
||||||
> (_items: V, _cb: F) -> impl Draw<S> {
|
> (_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>> (
|
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,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>> (
|
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,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>> (
|
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,
|
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>> (
|
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,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>> (
|
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,
|
_height: S::Unit, _iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>> (
|
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,
|
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
|
||||||
) -> impl Draw<S> {
|
) -> 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>)
|
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.
|
/// 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)
|
_ => 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.
|
/// Only draw content if area is above a certain size.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,4 @@
|
||||||
use super::{*, layout::*};
|
use super::*;
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split along an axis. Direction determines order.
|
/// Split along an axis. Direction determines order.
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
#[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
|
#[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 {
|
impl Split {
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -77,11 +45,7 @@ impl Split {
|
||||||
/// let _ = Split::West.stack("", "");
|
/// let _ = Split::West.stack("", "");
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn stack <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: A, b: B) -> impl Draw<S> {
|
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|{
|
Pair(*self, a, b, PhantomData)
|
||||||
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))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -94,7 +58,7 @@ impl Split {
|
||||||
/// let _ = Split::West.half("", "");
|
/// let _ = Split::West.half("", "");
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn half <S: Screen, A: Draw<S>, B: Draw<S>> (&self, a: A, b: B) -> impl Draw<S> {
|
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 (area_a, area_b) = to.xywh().split_half(self);
|
||||||
let (origin_a, origin_b) = self.origins();
|
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)?;
|
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>>>,
|
area_b: impl Into<Option<XYWH<S::Unit>>>,
|
||||||
origin_b: impl Into<Option<Azimuth>>,
|
origin_b: impl Into<Option<Azimuth>>,
|
||||||
) -> Usually<(Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
|
) -> Usually<(Option<XYWH<S::Unit>>, Option<XYWH<S::Unit>>)> {
|
||||||
Ok(match split {
|
let draw_a = |to: &mut S|Ok::<_, Box<dyn Error>>(if let Some(origin_a) = origin_a.into() {
|
||||||
Split::Below => {
|
to.clip(area_a.into(), |to|a.align(origin_a).draw(to))?
|
||||||
let drawn_b = to.clip(area_b.into(), |to|b.align(origin_b.into()).draw(to))?;
|
} else {
|
||||||
let drawn_a = to.clip(area_a.into(), |to|a.align(origin_a.into()).draw(to))?;
|
to.clip(area_a.into(), |to|a.draw(to))?
|
||||||
(drawn_a, drawn_b)
|
});
|
||||||
},
|
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))?
|
||||||
let drawn_a = to.clip(area_a.into(), |to|a.align(origin_a.into()).draw(to))?;
|
} else {
|
||||||
let drawn_b = to.clip(area_b.into(), |to|b.align(origin_b.into()).draw(to))?;
|
to.clip(area_b.into(), |to|b.draw(to))?
|
||||||
(drawn_a, drawn_b)
|
});
|
||||||
}
|
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 => (
|
Split::North => (
|
||||||
if let Some(used) = area_a {
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
@ -211,7 +180,7 @@ pub fn stack_areas <S: Screen> (
|
||||||
),
|
),
|
||||||
Split::West => (
|
Split::West => (
|
||||||
if let Some(used) = area_a {
|
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 {
|
} else {
|
||||||
None
|
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 {
|
#[cfg(test)] mod test {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[test] fn test_stack_areas () -> Usually<()> {
|
#[test] fn test_stack_areas () -> Usually<()> {
|
||||||
let area = XYWH(0u16, 0, 80, 25);
|
let area = XYWH(0u16, 0, 80, 25);
|
||||||
|
|
||||||
assert_eq!(stack_areas(&Split::East, area, &"foo", &"bar")?, (
|
assert_eq!(stack_areas(&Split::East, area, &"foo", &"bar")?, (
|
||||||
Some(XYWH(0u16, 0, 3, 1)),
|
Some(XYWH(0u16, 0, 3, 1)),
|
||||||
Some(XYWH(3u16, 0, 3, 1)),
|
Some(XYWH(3u16, 0, 3, 1)),
|
||||||
));
|
));
|
||||||
|
|
||||||
assert_eq!(stack_areas(&Split::South, area, &"foo", &"bar")?, (
|
assert_eq!(stack_areas(&Split::South, area, &"foo", &"bar")?, (
|
||||||
Some(XYWH(0u16, 0, 3, 1)),
|
Some(XYWH(0u16, 0, 3, 1)),
|
||||||
Some(XYWH(0u16, 1, 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
532
src/lib.rs
532
src/lib.rs
|
|
@ -46,7 +46,6 @@ macro_rules! features {
|
||||||
|
|
||||||
features! {
|
features! {
|
||||||
"time": [ time ],
|
"time": [ time ],
|
||||||
"play": [ exit, task ],
|
|
||||||
"sing": [ sing ]
|
"sing": [ sing ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +191,7 @@ macro_rules! eval_xy (
|
||||||
) => {{
|
) => {{
|
||||||
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
||||||
let variant = $variant;
|
let variant = $variant;
|
||||||
let thunk = thunk(move|screen|$state.interpret(screen, &$arg));
|
let thunk = draw(move|screen|$state.interpret(screen, &$arg));
|
||||||
match variant {
|
match variant {
|
||||||
// X variant
|
// X variant
|
||||||
Some("x") => thunk.$x().draw($output),
|
Some("x") => thunk.$x().draw($output),
|
||||||
|
|
@ -215,7 +214,7 @@ macro_rules! eval_xy (
|
||||||
) => {{
|
) => {{
|
||||||
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
||||||
let variant = $variant;
|
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("x") | Some("y") => $arg1,
|
||||||
Some("xy") | None => $arg2,
|
Some("xy") | None => $arg2,
|
||||||
_ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name)
|
_ => 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")] pub use self::draw::*;
|
||||||
#[cfg(feature = "draw")] mod draw {
|
#[cfg(feature = "draw")] mod draw {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use Azimuth::*;
|
||||||
|
use Split::*;
|
||||||
|
|
||||||
/// Output target.
|
/// Output target.
|
||||||
///
|
///
|
||||||
|
|
@ -338,9 +437,82 @@ macro_rules! eval_xy (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The opposite of [thunk]?
|
/// Emit a [Draw]able.
|
||||||
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)
|
/// 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>>;
|
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 {
|
impl<T: Screen, V: View<T>> Draw<T> for &V {
|
||||||
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
fn draw (self, to: &mut T) -> Perhaps<XYWH<T::Unit>> {
|
||||||
self.view().draw(to)
|
self.view().draw(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
features! {
|
pub trait Xy<N: Coord> {
|
||||||
"draw": [
|
fn x (&self) -> N;
|
||||||
iter,
|
fn y (&self) -> N;
|
||||||
lrtb,
|
}
|
||||||
sizer,
|
|
||||||
split,
|
pub trait Wh<N: Coord>: Wide<N> + Tall<N> {
|
||||||
thunk,
|
fn wh (&self) -> [N;2];
|
||||||
xywh
|
}
|
||||||
]
|
|
||||||
|
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")] pub use self::layout::*;
|
||||||
#[cfg(feature = "draw")] mod layout {
|
#[cfg(feature = "draw")] mod layout {
|
||||||
use crate::{*, draw::*};
|
use crate::*;
|
||||||
|
|
||||||
impl<S: Screen, T: Draw<S>> Layout<S> for T {}
|
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
|
#[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 align; pub use self::align::*;
|
||||||
mod area; pub use self::area::*;
|
mod area; pub use self::area::*;
|
||||||
mod exact; pub use self::exact::*;
|
mod exact; pub use self::exact::*;
|
||||||
mod full; pub use self::full::*;
|
mod full; pub use self::full::*;
|
||||||
|
mod iter; pub use self::iter::*;
|
||||||
mod max; pub use self::max::*;
|
mod max; pub use self::max::*;
|
||||||
mod min; pub use self::min::*;
|
mod min; pub use self::min::*;
|
||||||
mod origin; pub use self::origin::*;
|
mod origin; pub use self::origin::*;
|
||||||
mod pad; pub use self::pad::*;
|
mod pad; pub use self::pad::*;
|
||||||
mod pull; pub use self::pull::*;
|
mod pull; pub use self::pull::*;
|
||||||
mod push; pub use self::push::*;
|
mod push; pub use self::push::*;
|
||||||
|
mod split; pub use self::split::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "draw")] pub use self::color::*;
|
#[cfg(feature = "draw")] pub use self::color::*;
|
||||||
#[cfg(feature = "draw")] mod color {
|
#[cfg(feature = "draw")] mod color {
|
||||||
use crate::{*, draw::*};
|
use crate::*;
|
||||||
use dizzle::LanguageError::*;
|
use dizzle::LanguageError::*;
|
||||||
use ::ratatui::style::Color;
|
use ::ratatui::style::Color;
|
||||||
use ::rand::distributions::uniform::UniformSampler;
|
use ::rand::distributions::uniform::UniformSampler;
|
||||||
|
|
@ -814,8 +1165,10 @@ macro_rules! eval_xy (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "text")]
|
#[cfg(feature = "draw")] pub use self::text::*;
|
||||||
mod text {
|
#[cfg(feature = "text")] mod text {
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
pub(crate) use ::unicode_width::*;
|
pub(crate) use ::unicode_width::*;
|
||||||
|
|
||||||
/// Displays an owned [str]-like with fixed maximum 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")] pub use self::term::*;
|
||||||
#[cfg(feature = "term")] mod term {
|
#[cfg(feature = "term")] mod term {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::draw::*;
|
|
||||||
use crate::layout::*;
|
|
||||||
use Color::*;
|
use Color::*;
|
||||||
#[cfg(feature = "text")] use crate::text::*;
|
|
||||||
|
|
||||||
#[macro_export] macro_rules! tui_app {
|
#[macro_export] macro_rules! tui_app {
|
||||||
($Struct:ident { $($fields:tt)* }) => {
|
($Struct:ident { $($fields:tt)* }) => {
|
||||||
|
|
@ -982,6 +1332,7 @@ mod text {
|
||||||
impl Tall<u16> for Tui { fn h (&self) -> u16 { self.1.3 } }
|
impl Tall<u16> for Tui { fn h (&self) -> u16 { self.1.3 } }
|
||||||
impl HasOrigin for Tui { fn origin (&self) -> Azimuth { Azimuth::NW } }
|
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 } }
|
impl Xy<u16> for Tui { fn x (&self) -> u16 { self.1.0 } fn y (&self) -> u16 { self.1.1 } }
|
||||||
|
|
||||||
/// Terminal output.
|
/// Terminal output.
|
||||||
pub struct Tui(
|
pub struct Tui(
|
||||||
/// Ratatui buffer; area is screen size
|
/// Ratatui buffer; area is screen size
|
||||||
|
|
@ -989,6 +1340,7 @@ mod text {
|
||||||
/// Current draw area
|
/// Current draw area
|
||||||
pub XYWH<u16>
|
pub XYWH<u16>
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Tui {
|
impl Tui {
|
||||||
pub fn setup_panic () {
|
pub fn setup_panic () {
|
||||||
use ::std::panic::{set_hook, PanicHookInfo};
|
use ::std::panic::{set_hook, PanicHookInfo};
|
||||||
|
|
@ -1151,7 +1503,7 @@ mod text {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
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 {
|
draw(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);
|
||||||
|
|
@ -1166,7 +1518,7 @@ mod text {
|
||||||
/// Interpret TUI-specific layout operation.
|
/// Interpret TUI-specific layout operation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::{*, lang::*, ratatui::prelude::Color};
|
/// use tengri::{*, dizzle::*, ratatui::prelude::Color};
|
||||||
///
|
///
|
||||||
/// #[namespace(bool)]
|
/// #[namespace(bool)]
|
||||||
/// #[namespace(u8)]
|
/// #[namespace(u8)]
|
||||||
|
|
@ -1233,10 +1585,7 @@ mod text {
|
||||||
Some("fg") => {
|
Some("fg") => {
|
||||||
let arg0 = arg0?.expect("fg: expected arg 0 (color)");
|
let arg0 = arg0?.expect("fg: expected arg 0 (color)");
|
||||||
if let Some(color) = Namespace::namespace(state, arg0)? {
|
if let Some(color) = Namespace::namespace(state, arg0)? {
|
||||||
fg(color, thunk(move|to: &mut Tui|{
|
fg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(to)
|
||||||
state.interpret(to, &arg1)?;
|
|
||||||
Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
|
|
||||||
})).draw(to)
|
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("fg: {arg0:?}: not a color").into())
|
return Err(format!("fg: {arg0:?}: not a color").into())
|
||||||
}
|
}
|
||||||
|
|
@ -1245,10 +1594,7 @@ mod text {
|
||||||
Some("bg") => {
|
Some("bg") => {
|
||||||
let arg0 = arg0?.expect("bg: expected arg 0 (color)");
|
let arg0 = arg0?.expect("bg: expected arg 0 (color)");
|
||||||
if let Some(color) = Namespace::namespace(state, arg0)? {
|
if let Some(color) = Namespace::namespace(state, arg0)? {
|
||||||
bg(color, thunk(move|to: &mut Tui|{
|
bg(color, draw(move|to: &mut Tui|state.interpret(to, &arg1))).draw(to)
|
||||||
state.interpret(to, &arg1)?;
|
|
||||||
Ok(Some(to.area().into())) // FIXME?: don't max out the used area?
|
|
||||||
})).draw(to)
|
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("bg: {arg0:?}: not a color").into())
|
return Err(format!("bg: {arg0:?}: not a color").into())
|
||||||
}
|
}
|
||||||
|
|
@ -1310,6 +1656,7 @@ mod text {
|
||||||
Ok(Some(XYWH(x0, y, string_width, 1)))
|
Ok(Some(XYWH(x0, y, string_width, 1)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen for Tui {
|
impl Screen for Tui {
|
||||||
type Unit = u16;
|
type Unit = u16;
|
||||||
/// Render drawable in subarea specified by `area`
|
/// Render drawable in subarea specified by `area`
|
||||||
|
|
@ -1349,21 +1696,21 @@ mod text {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn fill_char (c: char) -> impl Draw<Tui> {
|
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);
|
cell.set_char(c);
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw contents with modifier applied.
|
/// Draw contents with modifier applied.
|
||||||
pub const fn modify (on: bool, modifier: Modifier, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub const fn modify (on: bool, modifier: Modifier, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||||
thunk(move|to: &mut Tui|{
|
draw(move|to: &mut Tui|{
|
||||||
fill_mod(on, modifier).draw(to)?;
|
fill_mod(on, modifier).draw(to)?;
|
||||||
draw.draw(to)
|
item.draw(to)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn fill_mod (on: bool, modifier: Modifier) -> impl Draw<Tui> {
|
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 {
|
if on {
|
||||||
to.update(&|cell,_,_|cell.modifier.insert(modifier))
|
to.update(&|cell,_,_|cell.modifier.insert(modifier))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1373,8 +1720,8 @@ mod text {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw contents with bold modifier applied.
|
/// Draw contents with bold modifier applied.
|
||||||
pub const fn bold (on: bool, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub const fn bold (on: bool, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||||
modify(on, Modifier::BOLD, draw)
|
modify(on, Modifier::BOLD, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "text")]
|
#[cfg(feature = "text")]
|
||||||
|
|
@ -1421,7 +1768,7 @@ mod text {
|
||||||
layout_text_u16(self.as_ref(), area)
|
layout_text_u16(self.as_ref(), area)
|
||||||
}
|
}
|
||||||
fn draw (self, to: &mut Tui) -> Drawn<u16> {
|
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 width: u16 = 1;
|
||||||
let mut chars = self.1.as_ref().chars();
|
let mut chars = self.1.as_ref().chars();
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
|
|
@ -1443,14 +1790,14 @@ mod text {
|
||||||
|
|
||||||
#[cfg(feature = "text")]
|
#[cfg(feature = "text")]
|
||||||
fn layout_text_u16 (text: &str, area: XYWH<u16>) -> Perhaps<XYWH<u16>> {
|
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_w = 0u16;
|
||||||
let mut max_h = 0u16;
|
let mut max_h = 0u16;
|
||||||
for line in text.split("\n") {
|
for line in text.split("\n") {
|
||||||
max_h += 1;
|
max_h += 1;
|
||||||
max_w = max_w.max(line.len() as u16);
|
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;
|
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.
|
/// Apply foreground color.
|
||||||
pub const fn fg (fg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub const fn fg (fg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||||
thunk(move|to: &mut Tui|{
|
draw(move|to: &mut Tui|{
|
||||||
to.update(&|cell,_,_|{ cell.set_fg(fg); });
|
to.update(&|cell,_,_|{ cell.set_fg(fg); });
|
||||||
draw.draw(to)
|
item.draw(to)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply background color.
|
/// Apply background color.
|
||||||
pub const fn bg (bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub const fn bg (bg: Color, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||||
thunk(move|to: &mut Tui|{
|
Background(bg, item)
|
||||||
to.update(&|cell,_,_|{ cell.set_bg(bg); });
|
|
||||||
draw.draw(to)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn fg_bg (fg: Color, bg: Color, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub struct Background<T>(Color, T);
|
||||||
thunk(move|to: &mut Tui|{
|
|
||||||
|
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); });
|
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> {
|
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,_,_|{
|
to.update(&|cell,_,_|{
|
||||||
cell.modifier.insert(Modifier::UNDERLINED);
|
cell.modifier.insert(Modifier::UNDERLINED);
|
||||||
cell.underline_color = color;
|
cell.underline_color = color;
|
||||||
|
|
@ -1532,7 +1904,6 @@ mod text {
|
||||||
mod buffer; pub use self::buffer::*;
|
mod buffer; pub use self::buffer::*;
|
||||||
mod repeat; pub use self::repeat::*;
|
mod repeat; pub use self::repeat::*;
|
||||||
mod scroll; pub use self::scroll::*;
|
mod scroll; pub use self::scroll::*;
|
||||||
mod colors; pub use self::colors::*;
|
|
||||||
mod phat; pub use self::phat::*;
|
mod phat; pub use self::phat::*;
|
||||||
mod button; pub use self::button::*;
|
mod button; pub use self::button::*;
|
||||||
|
|
||||||
|
|
@ -1553,7 +1924,7 @@ mod text {
|
||||||
/// Interpret layout operation.
|
/// Interpret layout operation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use tengri::{*, lang::*};
|
/// # use tengri::{*, dizzle::*};
|
||||||
///
|
///
|
||||||
/// struct State {/*app-specific*/}
|
/// struct State {/*app-specific*/}
|
||||||
/// impl<'b> Namespace<'b, u16> for State {}
|
/// impl<'b> Namespace<'b, u16> for State {}
|
||||||
|
|
@ -1598,29 +1969,28 @@ mod text {
|
||||||
|
|
||||||
Some("when") => when(
|
Some("when") => when(
|
||||||
state.namespace(arg0?)?.unwrap(),
|
state.namespace(arg0?)?.unwrap(),
|
||||||
thunk(move|output: &mut O|{state.interpret(output, &arg1)})
|
draw(move|output: &mut O|{state.interpret(output, &arg1)})
|
||||||
).draw(output),
|
).draw(output),
|
||||||
|
|
||||||
Some("either") => either(
|
Some("either") => either(
|
||||||
state.namespace(arg0?)?.unwrap(),
|
state.namespace(arg0?)?.unwrap(),
|
||||||
thunk(move|output: &mut O|{state.interpret(output, &arg1)}),
|
draw(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, &arg2)}),
|
||||||
).draw(output),
|
).draw(output),
|
||||||
|
|
||||||
Some("bsp") => eval_enum!("bsp", output, state, frags.next(), arg0, Split {
|
Some("bsp") => eval_enum!("bsp", output, state, frags.next(), arg0, Split {
|
||||||
"n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below
|
"n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below
|
||||||
}).stack(
|
}).stack(
|
||||||
thunk(move|output: &mut O|{state.interpret(output, &arg0)}),
|
draw(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, &arg1)}),
|
||||||
).draw(output),
|
).draw(output),
|
||||||
|
|
||||||
Some("align") => thunk(move|output: &mut O|{
|
Some("align") => draw(move|output: &mut O|{state.interpret(output, &arg0)})
|
||||||
state.interpret(output, &arg0)
|
.align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth {
|
||||||
}).align(eval_enum!("align", output, state, frags.next(), arg0, Azimuth {
|
"c" => C, "x" => X, "y" => Y,
|
||||||
"c" => C, "x" => X, "y" => Y,
|
"n" => N, "s" => S, "e" => E, "w" => W,
|
||||||
"n" => N, "s" => S, "e" => E, "w" => W,
|
"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
|
||||||
"nw" => NW, "sw" => SW, "ne" => NE, "se" => SE,
|
})).draw(output),
|
||||||
})).draw(output),
|
|
||||||
|
|
||||||
Some("exact") => eval_xy!(
|
Some("exact") => eval_xy!(
|
||||||
"exact" => expr, head, output, state, frags.next(), exact_wh, exact_w, exact_h, arg0, arg1, arg2,
|
"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
|
/// /// TODO
|
||||||
/// ```
|
/// ```
|
||||||
pub fn border (on: bool, style: impl BorderStyle, draw: impl Draw<Tui>) -> impl Draw<Tui> {
|
pub fn border (on: bool, style: impl BorderStyle, item: impl Draw<Tui>) -> impl Draw<Tui> {
|
||||||
let content = draw.pad_wh(1, 1);
|
let content = item.pad_wh(1, 1);
|
||||||
let outline = when(on, thunk(move|to: &mut Tui|{
|
let outline = when(on, draw(move|to: &mut Tui|{
|
||||||
let XYWH(x, y, w, h) = to.1;
|
let XYWH(x, y, w, h) = to.1;
|
||||||
if w > 0 && h > 0 {
|
if w > 0 && h > 0 {
|
||||||
to.blit(&style.border_nw(), x, y, style.style());
|
to.blit(&style.border_nw(), x, y, style.style());
|
||||||
|
|
@ -49,7 +49,7 @@ macro_rules! border {
|
||||||
//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) -> Perhaps<XYWH<u16>> {
|
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}};
|
use ratatui::{prelude::{Position}};
|
||||||
|
|
||||||
pub const fn x_repeat (c: &str) -> impl Draw<Tui> {
|
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();
|
let XYWH(x, y, w, _h) = to.xywh();
|
||||||
for x in x..x+w {
|
for x in x..x+w {
|
||||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
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> {
|
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();
|
let XYWH(x, y, _w, h) = to.xywh();
|
||||||
for y in y..y+h {
|
for y in y..y+h {
|
||||||
if let Some(cell) = to.0.cell_mut(Position::from((x, y))) {
|
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> {
|
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 XYWH(x, y, w, h) = to.xywh();
|
||||||
let a = c.len();
|
let a = c.len();
|
||||||
for (_v, y) in (y..y+h).enumerate() {
|
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 const ICON_INC_H: &[char] = &[' ', '🞂', ' '];
|
||||||
|
|
||||||
pub fn x_scroll () -> impl Draw<Tui> {
|
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;
|
let x2 = *x1 + *w;
|
||||||
for (i, x) in (*x1..=x2).enumerate() {
|
for (i, x) in (*x1..=x2).enumerate() {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((x, *y1))) {
|
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> {
|
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;
|
let y2 = *y1 + *h;
|
||||||
for (i, y) in (*y1..=y2).enumerate() {
|
for (i, y) in (*y1..=y2).enumerate() {
|
||||||
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {
|
if let Some(cell) = buf.cell_mut(Position::from((*x1, y))) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue