mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-02-21 10:39:03 +01:00
This commit is contained in:
parent
f3fcf663a8
commit
501782f8fe
18 changed files with 841 additions and 908 deletions
|
|
@ -36,9 +36,9 @@ need_stdout = false
|
||||||
on_success = "back" # so that we don't open the browser at each change
|
on_success = "back" # so that we don't open the browser at each change
|
||||||
|
|
||||||
[skin]
|
[skin]
|
||||||
status_fg = 251
|
status_fg = 15
|
||||||
status_bg = 200
|
status_bg = 16
|
||||||
key_fg = 11
|
key_fg = 11
|
||||||
status_key_fg = 11
|
status_key_fg = 11
|
||||||
project_name_badge_fg = 11
|
project_name_badge_fg = 42
|
||||||
project_name_badge_bg = 69
|
project_name_badge_bg = 16
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
mod layout_align; pub use self::layout_align::*;
|
|
||||||
mod layout_bsp; pub use self::layout_bsp::*;
|
|
||||||
mod layout_cond; pub use self::layout_cond::*;
|
mod layout_cond; pub use self::layout_cond::*;
|
||||||
mod layout_map; pub use self::layout_map::*;
|
mod layout_map; pub use self::layout_map::*;
|
||||||
mod layout_pad; pub use self::layout_pad::*;
|
mod layout_pad; pub use self::layout_pad::*;
|
||||||
|
|
@ -9,153 +5,195 @@ mod layout_move; pub use self::layout_move::*;
|
||||||
mod layout_size; pub use self::layout_size::*;
|
mod layout_size; pub use self::layout_size::*;
|
||||||
mod layout_stack; //pub use self::layout_stack::*;
|
mod layout_stack; //pub use self::layout_stack::*;
|
||||||
|
|
||||||
/// Drawable area of display.
|
/// Stack things on top of each other,
|
||||||
pub trait Layout<O: Out> {
|
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
|
||||||
|
/// Stack southward.
|
||||||
|
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
|
||||||
|
/// Stack northward.
|
||||||
|
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
|
||||||
|
/// Stack eastward.
|
||||||
|
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});
|
||||||
|
|
||||||
|
pub use self::align::*; mod align {
|
||||||
|
use crate::*;
|
||||||
|
use Alignment::*;
|
||||||
|
|
||||||
|
/// 9th of area to place.
|
||||||
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
|
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
||||||
|
|
||||||
|
/// Align position of inner area to middle, side, or corner of outer area.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ::tengri::{output::*, tui::*};
|
||||||
|
/// let area: [u16;4] = [10, 10, 20, 20];
|
||||||
|
/// fn test (area: [u16;4], item: &impl Draw<TuiOut>, expected: [u16;4]) {
|
||||||
|
/// assert_eq!(Content::layout(item, area), expected);
|
||||||
|
/// assert_eq!(Draw::layout(item, area), expected);
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let four = ||Fixed::XY(4, 4, "");
|
||||||
|
/// test(area, &Align::nw(four()), [10, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::n(four()), [18, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::ne(four()), [26, 10, 4, 4]);
|
||||||
|
/// test(area, &Align::e(four()), [26, 18, 4, 4]);
|
||||||
|
/// test(area, &Align::se(four()), [26, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::s(four()), [18, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::sw(four()), [10, 26, 4, 4]);
|
||||||
|
/// test(area, &Align::w(four()), [10, 18, 4, 4]);
|
||||||
|
///
|
||||||
|
/// let two_by_four = ||Fixed::XY(4, 2, "");
|
||||||
|
/// test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
|
||||||
|
/// test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
|
||||||
|
/// test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
|
||||||
|
/// test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
||||||
|
/// ```
|
||||||
|
pub struct Align<T>(Alignment, T);
|
||||||
|
impl<T> Align<T> {
|
||||||
|
#[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) }
|
||||||
|
#[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) }
|
||||||
|
#[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) }
|
||||||
|
#[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) }
|
||||||
|
#[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) }
|
||||||
|
#[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) }
|
||||||
|
#[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) }
|
||||||
|
#[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) }
|
||||||
|
#[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) }
|
||||||
|
#[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) }
|
||||||
|
#[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Content<O>> Draw<O> for Align<T> {
|
||||||
|
fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Layout<O>> Layout<O> for Align<T> {
|
||||||
fn x (&self, to: O::Area) -> O::Unit {
|
fn x (&self, to: O::Area) -> O::Unit {
|
||||||
to.x()
|
match self.0 {
|
||||||
|
NW | W | SW => to.x(),
|
||||||
|
N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()),
|
||||||
|
NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn y (&self, to: O::Area) -> O::Unit {
|
fn y (&self, to: O::Area) -> O::Unit {
|
||||||
to.y()
|
match self.0 {
|
||||||
|
NW | N | NE => to.y(),
|
||||||
|
W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()),
|
||||||
|
SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)),
|
||||||
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
fn min_w (&self, _to: O::Area) -> O::Unit {
|
|
||||||
0.into()
|
|
||||||
}
|
|
||||||
fn max_w (&self, to: O::Area) -> O::Unit {
|
|
||||||
to.w()
|
|
||||||
}
|
|
||||||
fn w (&self, to: O::Area) -> O::Unit {
|
|
||||||
to.w().max(self.min_w(to)).min(self.max_w(to))
|
|
||||||
}
|
|
||||||
fn min_h (&self, _to: O::Area) -> O::Unit {
|
|
||||||
0.into()
|
|
||||||
}
|
|
||||||
fn max_h (&self, to: O::Area) -> O::Unit {
|
|
||||||
to.h()
|
|
||||||
}
|
|
||||||
fn h (&self, to: O::Area) -> O::Unit {
|
|
||||||
to.h().max(self.min_h(to)).min(self.max_h(to))
|
|
||||||
}
|
|
||||||
fn layout (&self, to: O::Area) -> O::Area {
|
|
||||||
[self.x(to), self.y(to), self.w(to), self.h(to)].into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out> Layout<O> for () {
|
|
||||||
fn x (&self, a: O::Area) -> O::Unit { a.x() }
|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { a.y() }
|
|
||||||
fn w (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn min_w (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn max_w (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn h (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn min_h (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn max_h (&self, _: O::Area) -> O::Unit { 0.into() }
|
|
||||||
fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for &L {
|
pub use self::bsp::*; mod bsp {
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) }
|
use crate::*;
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) }
|
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) }
|
/// A binary split or layer.
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) }
|
pub struct Bsp<Head, Tail>(
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) }
|
pub(crate) Direction,
|
||||||
fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) }
|
/// First element.
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) }
|
pub(crate) Head,
|
||||||
fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) }
|
/// Second element.
|
||||||
fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) }
|
pub(crate) Tail,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Head, Tail> Bsp<Head, Tail> {
|
||||||
|
#[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) }
|
||||||
|
#[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) }
|
||||||
|
#[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) }
|
||||||
|
#[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) }
|
||||||
|
#[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) }
|
||||||
|
#[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for &mut L {
|
impl<O: Out, Head: Content<O>, Tail: Content<O>> Draw<O> for Bsp<Head, Tail> {
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
|
||||||
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
|
||||||
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
|
||||||
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for Arc<L> {
|
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
|
||||||
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
|
||||||
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
|
||||||
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> Layout<O> for Box<dyn Layout<O>> {
|
|
||||||
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
|
||||||
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
|
||||||
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
|
||||||
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
|
||||||
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for RwLock<L> {
|
|
||||||
fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) }
|
|
||||||
fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) }
|
|
||||||
fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) }
|
|
||||||
fn min_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_w(a) }
|
|
||||||
fn max_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_w(a) }
|
|
||||||
fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) }
|
|
||||||
fn min_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_h(a) }
|
|
||||||
fn max_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_h(a) }
|
|
||||||
fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, L: Layout<O>> Layout<O> for Option<L> {
|
|
||||||
fn x (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.x(to)).unwrap_or(to.x())
|
|
||||||
}
|
|
||||||
fn y (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.y(to)).unwrap_or(to.y())
|
|
||||||
}
|
|
||||||
fn min_w (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn max_w (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn w (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.w(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn min_h (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn max_h (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn h (&self, to: O::Area) -> O::Unit {
|
|
||||||
self.as_ref().map(|c|c.h(to)).unwrap_or(0.into())
|
|
||||||
}
|
|
||||||
fn layout (&self, to: O::Area) -> O::Area {
|
|
||||||
self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into()))
|
|
||||||
.unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Bounded<O: Out, D>(pub O::Area, pub D);
|
|
||||||
|
|
||||||
impl<O: Out, D: Content<O>> HasContent<O> for Bounded<O, D> {
|
|
||||||
fn content (&self) -> impl Content<O> {
|
|
||||||
&self.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, T: Draw<O>> Draw<O> for Bounded<O, T> {
|
|
||||||
fn draw (&self, to: &mut O) {
|
fn draw (&self, to: &mut O) {
|
||||||
let area = to.area();
|
match self.0 {
|
||||||
*to.area_mut() = self.0;
|
South => {
|
||||||
self.1.draw(to);
|
//panic!("{}", self.1.h(to.area()));
|
||||||
*to.area_mut() = area;
|
let area_1 = self.1.layout(to.area());
|
||||||
|
let area_2 = self.2.layout([
|
||||||
|
to.area().x(),
|
||||||
|
to.area().y().plus(area_1.h()),
|
||||||
|
to.area().w(),
|
||||||
|
to.area().h().minus(area_1.h())
|
||||||
|
].into());
|
||||||
|
//panic!("{area_1:?} {area_2:?}");
|
||||||
|
to.place_at(area_1, &self.1);
|
||||||
|
to.place_at(area_2, &self.2);
|
||||||
|
},
|
||||||
|
_ => todo!("{:?}", self.0)
|
||||||
|
}
|
||||||
|
//let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
|
||||||
|
//panic!("{a:?} {b:?}");
|
||||||
|
//if self.0 == Below {
|
||||||
|
//to.place_at(a, &self.1);
|
||||||
|
//to.place_at(b, &self.2);
|
||||||
|
//} else {
|
||||||
|
//to.place_at(b, &self.2);
|
||||||
|
//to.place_at(a, &self.1);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out, Head: Layout<O>, Tail: Layout<O>> Layout<O> for Bsp<Head, Tail> {
|
||||||
|
fn w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w(area).max(self.2.w(area)), East | West => self.1.min_w(area).plus(self.2.w(area)), } }
|
||||||
|
fn min_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.min_w(area).max(self.2.min_w(area)), East | West => self.1.min_w(area).plus(self.2.min_w(area)), } }
|
||||||
|
fn max_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_w(area).max(self.2.max_w(area)), East | West => self.1.max_w(area).plus(self.2.max_w(area)), } }
|
||||||
|
fn h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.h(area).max(self.2.h(area)), North | South => self.1.h(area).plus(self.2.h(area)), } }
|
||||||
|
fn min_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.min_h(area).max(self.2.min_h(area)), North | South => self.1.min_h(area).plus(self.2.min_h(area)), } }
|
||||||
|
fn max_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_h(area).max(self.2.max_h(area)), East | West => self.1.max_h(area).plus(self.2.max_h(area)), } }
|
||||||
|
fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bsp_areas <O: Out, A: Layout<O>, B: Layout<O>> (area: O::Area, direction: Direction, a: &A, b: &B,) -> [O::Area;3] {
|
||||||
|
let [x, y, w, h] = area.xywh();
|
||||||
|
let [aw, ah] = a.layout(area).wh();
|
||||||
|
let [bw, bh] = b.layout(match direction {
|
||||||
|
Above | Below => area,
|
||||||
|
South => [x, y + ah, w, h.minus(ah)].into(),
|
||||||
|
North => [x, y, w, h.minus(ah)].into(),
|
||||||
|
East => [x + aw, y, w.minus(aw), h].into(),
|
||||||
|
West => [x, y, w.minus(aw), h].into(),
|
||||||
|
}).wh();
|
||||||
|
match direction {
|
||||||
|
Above | Below => {
|
||||||
|
let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]);
|
||||||
|
let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
||||||
|
let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
||||||
|
[a.into(), b.into(), [x, y, w, h].into()]
|
||||||
|
},
|
||||||
|
South => {
|
||||||
|
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
|
||||||
|
let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah];
|
||||||
|
let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh];
|
||||||
|
[a.into(), b.into(), [x, y, w, h].into()]
|
||||||
|
},
|
||||||
|
North => {
|
||||||
|
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
|
||||||
|
let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah];
|
||||||
|
let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh];
|
||||||
|
[a.into(), b.into(), [x, y, w, h].into()]
|
||||||
|
},
|
||||||
|
East => {
|
||||||
|
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
|
||||||
|
let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
||||||
|
let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
||||||
|
[a.into(), b.into(), [x, y, w, h].into()]
|
||||||
|
},
|
||||||
|
West => {
|
||||||
|
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
|
||||||
|
let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
||||||
|
let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
||||||
|
[a.into(), b.into(), [x, y, w, h].into()]
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
//! ```
|
|
||||||
//! use ::tengri::{output::*, tui::*};
|
|
||||||
//! let area: [u16;4] = [10, 10, 20, 20];
|
|
||||||
//! fn test (area: [u16;4], item: &impl Draw<TuiOut>, expected: [u16;4]) {
|
|
||||||
//! assert_eq!(Content::layout(item, area), expected);
|
|
||||||
//! assert_eq!(Draw::layout(item, area), expected);
|
|
||||||
//! };
|
|
||||||
//!
|
|
||||||
//! let four = ||Fixed::XY(4, 4, "");
|
|
||||||
//! test(area, &Align::nw(four()), [10, 10, 4, 4]);
|
|
||||||
//! test(area, &Align::n(four()), [18, 10, 4, 4]);
|
|
||||||
//! test(area, &Align::ne(four()), [26, 10, 4, 4]);
|
|
||||||
//! test(area, &Align::e(four()), [26, 18, 4, 4]);
|
|
||||||
//! test(area, &Align::se(four()), [26, 26, 4, 4]);
|
|
||||||
//! test(area, &Align::s(four()), [18, 26, 4, 4]);
|
|
||||||
//! test(area, &Align::sw(four()), [10, 26, 4, 4]);
|
|
||||||
//! test(area, &Align::w(four()), [10, 18, 4, 4]);
|
|
||||||
//!
|
|
||||||
//! let two_by_four = ||Fixed::XY(4, 2, "");
|
|
||||||
//! test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
|
|
||||||
//! test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
|
|
||||||
//! test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
|
|
||||||
//! test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
|
|
||||||
//! test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
|
|
||||||
//! test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
|
|
||||||
//! test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
|
|
||||||
//! test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
|
|
||||||
//! ```
|
|
||||||
use crate::*;
|
|
||||||
use Alignment::*;
|
|
||||||
|
|
||||||
/// 9th of area to place.
|
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
|
||||||
pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
|
|
||||||
pub struct Align<T>(Alignment, T);
|
|
||||||
impl<T> Align<T> {
|
|
||||||
#[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) }
|
|
||||||
#[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) }
|
|
||||||
#[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) }
|
|
||||||
#[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) }
|
|
||||||
#[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) }
|
|
||||||
#[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) }
|
|
||||||
#[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) }
|
|
||||||
#[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) }
|
|
||||||
#[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) }
|
|
||||||
#[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) }
|
|
||||||
#[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Content<O>> Draw<O> for Align<T> {
|
|
||||||
fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) }
|
|
||||||
}
|
|
||||||
impl<O: Out, T: Layout<O>> Layout<O> for Align<T> {
|
|
||||||
fn x (&self, to: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
NW | W | SW => to.x(),
|
|
||||||
N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()),
|
|
||||||
NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)),
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn y (&self, to: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
NW | N | NE => to.y(),
|
|
||||||
W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()),
|
|
||||||
SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)),
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// A binary split or layer.
|
|
||||||
pub struct Bsp<Head, Tail>(
|
|
||||||
pub(crate) Direction,
|
|
||||||
/// First element.
|
|
||||||
pub(crate) Head,
|
|
||||||
/// Second element.
|
|
||||||
pub(crate) Tail,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<Head, Tail> Bsp<Head, Tail> {
|
|
||||||
#[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) }
|
|
||||||
#[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) }
|
|
||||||
#[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) }
|
|
||||||
#[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) }
|
|
||||||
#[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) }
|
|
||||||
#[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out, Head: Content<O>, Tail: Content<O>> Draw<O> for Bsp<Head, Tail> {
|
|
||||||
fn draw (&self, to: &mut O) {
|
|
||||||
match self.0 {
|
|
||||||
South => {
|
|
||||||
//panic!("{}", self.1.h(to.area()));
|
|
||||||
let area_1 = self.1.layout(to.area());
|
|
||||||
let area_2 = self.2.layout([
|
|
||||||
to.area().x(),
|
|
||||||
to.area().y().plus(area_1.h()),
|
|
||||||
to.area().w(),
|
|
||||||
to.area().h().minus(area_1.h())
|
|
||||||
].into());
|
|
||||||
//panic!("{area_1:?} {area_2:?}");
|
|
||||||
to.place_at(area_1, &self.1);
|
|
||||||
to.place_at(area_2, &self.2);
|
|
||||||
},
|
|
||||||
_ => todo!("{:?}", self.0)
|
|
||||||
}
|
|
||||||
//let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
|
|
||||||
//panic!("{a:?} {b:?}");
|
|
||||||
//if self.0 == Below {
|
|
||||||
//to.place_at(a, &self.1);
|
|
||||||
//to.place_at(b, &self.2);
|
|
||||||
//} else {
|
|
||||||
//to.place_at(b, &self.2);
|
|
||||||
//to.place_at(a, &self.1);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<O: Out, Head: Layout<O>, Tail: Layout<O>> Layout<O> for Bsp<Head, Tail> {
|
|
||||||
fn w (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
North | South | Above | Below => self.1.w(area).max(self.2.w(area)),
|
|
||||||
East | West => self.1.min_w(area).plus(self.2.w(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn min_w (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
North | South | Above | Below => self.1.min_w(area).max(self.2.min_w(area)),
|
|
||||||
East | West => self.1.min_w(area).plus(self.2.min_w(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn max_w (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
North | South | Above | Below => self.1.max_w(area).max(self.2.max_w(area)),
|
|
||||||
East | West => self.1.max_w(area).plus(self.2.max_w(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn h (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
East | West | Above | Below => self.1.h(area).max(self.2.h(area)),
|
|
||||||
North | South => self.1.h(area).plus(self.2.h(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn min_h (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
East | West | Above | Below => self.1.min_h(area).max(self.2.min_h(area)),
|
|
||||||
North | South => self.1.min_h(area).plus(self.2.min_h(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn max_h (&self, area: O::Area) -> O::Unit {
|
|
||||||
match self.0 {
|
|
||||||
North | South | Above | Below => self.1.max_h(area).max(self.2.max_h(area)),
|
|
||||||
East | West => self.1.max_h(area).plus(self.2.max_h(area)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn layout (&self, area: O::Area) -> O::Area {
|
|
||||||
bsp_areas(area, self.0, &self.1, &self.2)[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bsp_areas <O: Out, A: Layout<O>, B: Layout<O>> (
|
|
||||||
area: O::Area, direction: Direction, a: &A, b: &B,
|
|
||||||
) -> [O::Area;3] {
|
|
||||||
let [x, y, w, h] = area.xywh();
|
|
||||||
let [aw, ah] = a.layout(area).wh();
|
|
||||||
let [bw, bh] = b.layout(match direction {
|
|
||||||
Above | Below => area,
|
|
||||||
South => [x, y + ah, w, h.minus(ah)].into(),
|
|
||||||
North => [x, y, w, h.minus(ah)].into(),
|
|
||||||
East => [x + aw, y, w.minus(aw), h].into(),
|
|
||||||
West => [x, y, w.minus(aw), h].into(),
|
|
||||||
}).wh();
|
|
||||||
match direction {
|
|
||||||
Above | Below => {
|
|
||||||
let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]);
|
|
||||||
let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
|
||||||
let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
|
||||||
[a.into(), b.into(), [x, y, w, h].into()]
|
|
||||||
},
|
|
||||||
South => {
|
|
||||||
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
|
|
||||||
let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah];
|
|
||||||
let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh];
|
|
||||||
[a.into(), b.into(), [x, y, w, h].into()]
|
|
||||||
},
|
|
||||||
North => {
|
|
||||||
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
|
|
||||||
let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah];
|
|
||||||
let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh];
|
|
||||||
[a.into(), b.into(), [x, y, w, h].into()]
|
|
||||||
},
|
|
||||||
East => {
|
|
||||||
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
|
|
||||||
let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
|
||||||
let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
|
||||||
[a.into(), b.into(), [x, y, w, h].into()]
|
|
||||||
},
|
|
||||||
West => {
|
|
||||||
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
|
|
||||||
let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah];
|
|
||||||
let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh];
|
|
||||||
[a.into(), b.into(), [x, y, w, h].into()]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stack things on top of each other,
|
|
||||||
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) =>
|
|
||||||
{{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
|
|
||||||
|
|
||||||
/// Stack southward.
|
|
||||||
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) =>
|
|
||||||
{{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
|
|
||||||
|
|
||||||
/// Stack northward.
|
|
||||||
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) =>
|
|
||||||
{{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
|
|
||||||
|
|
||||||
/// Stack eastward.
|
|
||||||
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) =>
|
|
||||||
{{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});
|
|
||||||
|
|
@ -30,76 +30,6 @@ mod widget; pub use self::widget::*;
|
||||||
use crate::{*, Direction::*};
|
use crate::{*, Direction::*};
|
||||||
//use proptest_derive::Arbitrary;
|
//use proptest_derive::Arbitrary;
|
||||||
use proptest::{prelude::*, option::of};
|
use proptest::{prelude::*, option::of};
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#[test] fn proptest_direction (
|
|
||||||
d in prop_oneof![
|
|
||||||
Just(North), Just(South),
|
|
||||||
Just(East), Just(West),
|
|
||||||
Just(Above), Just(Below)
|
|
||||||
],
|
|
||||||
x in u16::MIN..u16::MAX,
|
|
||||||
y in u16::MIN..u16::MAX,
|
|
||||||
w in u16::MIN..u16::MAX,
|
|
||||||
h in u16::MIN..u16::MAX,
|
|
||||||
a in u16::MIN..u16::MAX,
|
|
||||||
) {
|
|
||||||
let _ = d.split_fixed([x, y, w, h], a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#[test] fn proptest_size (
|
|
||||||
x in u16::MIN..u16::MAX,
|
|
||||||
y in u16::MIN..u16::MAX,
|
|
||||||
a in u16::MIN..u16::MAX,
|
|
||||||
b in u16::MIN..u16::MAX,
|
|
||||||
) {
|
|
||||||
let size = [x, y];
|
|
||||||
let _ = size.w();
|
|
||||||
let _ = size.h();
|
|
||||||
let _ = size.wh();
|
|
||||||
let _ = size.clip_w(a);
|
|
||||||
let _ = size.clip_h(b);
|
|
||||||
let _ = size.expect_min(a, b);
|
|
||||||
let _ = size.to_area_pos();
|
|
||||||
let _ = size.to_area_size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
#[test] fn proptest_area (
|
|
||||||
x in u16::MIN..u16::MAX,
|
|
||||||
y in u16::MIN..u16::MAX,
|
|
||||||
w in u16::MIN..u16::MAX,
|
|
||||||
h in u16::MIN..u16::MAX,
|
|
||||||
a in u16::MIN..u16::MAX,
|
|
||||||
b in u16::MIN..u16::MAX,
|
|
||||||
) {
|
|
||||||
let _: [u16;4] = <[u16;4] as Area<u16>>::zero();
|
|
||||||
let _: [u16;4] = <[u16;4] as Area<u16>>::from_position([a, b]);
|
|
||||||
let _: [u16;4] = <[u16;4] as Area<u16>>::from_size([a, b]);
|
|
||||||
let area: [u16;4] = [x, y, w, h];
|
|
||||||
let _ = area.expect_min(a, b);
|
|
||||||
let _ = area.xy();
|
|
||||||
let _ = area.wh();
|
|
||||||
let _ = area.xywh();
|
|
||||||
let _ = area.clip_h(a);
|
|
||||||
let _ = area.clip_w(b);
|
|
||||||
let _ = area.clip([a, b]);
|
|
||||||
let _ = area.set_w(a);
|
|
||||||
let _ = area.set_h(b);
|
|
||||||
let _ = area.x2();
|
|
||||||
let _ = area.y2();
|
|
||||||
let _ = area.lrtb();
|
|
||||||
let _ = area.center();
|
|
||||||
let _ = area.center_x(a);
|
|
||||||
let _ = area.center_y(b);
|
|
||||||
let _ = area.center_xy([a, b]);
|
|
||||||
let _ = area.centered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! test_op_transform {
|
macro_rules! test_op_transform {
|
||||||
($fn:ident, $Op:ident) => {
|
($fn:ident, $Op:ident) => {
|
||||||
proptest! {
|
proptest! {
|
||||||
|
|
@ -115,7 +45,7 @@ mod widget; pub use self::widget::*;
|
||||||
if let Some(op) = match (op_x, op_y) {
|
if let Some(op) = match (op_x, op_y) {
|
||||||
(Some(x), Some(y)) => Some($Op::XY(x, y, content)),
|
(Some(x), Some(y)) => Some($Op::XY(x, y, content)),
|
||||||
(Some(x), None) => Some($Op::X(x, content)),
|
(Some(x), None) => Some($Op::X(x, content)),
|
||||||
(None, Some(y)) => Some($Op::y(y, content)),
|
(None, Some(y)) => Some($Op::Y(y, content)),
|
||||||
_ => None
|
_ => None
|
||||||
} {
|
} {
|
||||||
//assert_eq!(Content::layout(&op, [x, y, w, h]),
|
//assert_eq!(Content::layout(&op, [x, y, w, h]),
|
||||||
|
|
@ -125,7 +55,6 @@ mod widget; pub use self::widget::*;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test_op_transform!(proptest_op_fixed, Fixed);
|
test_op_transform!(proptest_op_fixed, Fixed);
|
||||||
test_op_transform!(proptest_op_min, Min);
|
test_op_transform!(proptest_op_min, Min);
|
||||||
test_op_transform!(proptest_op_max, Max);
|
test_op_transform!(proptest_op_max, Max);
|
||||||
|
|
@ -157,44 +86,13 @@ mod widget; pub use self::widget::*;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test] fn test_stub_output () -> Usually<()> {
|
//#[test] fn test_iter_map () {
|
||||||
use crate::*;
|
//struct Foo;
|
||||||
struct TestOut([u16;4]);
|
//impl<T: Out> Content<T> for Foo {}
|
||||||
impl Out for TestOut {
|
//fn _make_map <T: Out, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Draw<T> {
|
||||||
type Unit = u16;
|
//Map::new(||data.iter(), |_foo, _index|{})
|
||||||
type Size = [u16;2];
|
//}
|
||||||
type Area = [u16;4];
|
//let _data = vec![Foo, Foo, Foo];
|
||||||
fn area (&self) -> [u16;4] {
|
////let map = make_map(&data);
|
||||||
self.0
|
//}
|
||||||
}
|
|
||||||
fn area_mut (&mut self) -> &mut [u16;4] {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], _: &T) {
|
|
||||||
println!("place_at: {area:?}");
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Draw<TestOut> for String {
|
|
||||||
fn draw (&self, to: &mut TestOut) {
|
|
||||||
to.area_mut().set_w(self.len() as u16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] fn test_space () {
|
|
||||||
use crate::*;
|
|
||||||
assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test] fn test_iter_map () {
|
|
||||||
struct Foo;
|
|
||||||
impl<T: Out> Content<T> for Foo {}
|
|
||||||
fn _make_map <T: Out, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Draw<T> {
|
|
||||||
Map::new(||data.iter(), |_foo, _index|{})
|
|
||||||
}
|
|
||||||
let _data = vec![Foo, Foo, Foo];
|
|
||||||
//let map = make_map(&data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,29 @@ pub trait Out: Send + Sync + Sized {
|
||||||
/// Mutable pointer to area.
|
/// Mutable pointer to area.
|
||||||
fn area_mut (&mut self) -> &mut Self::Area;
|
fn area_mut (&mut self) -> &mut Self::Area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)] #[test] fn test_stub_output () -> Usually<()> {
|
||||||
|
use crate::*;
|
||||||
|
struct TestOut([u16;4]);
|
||||||
|
impl Out for TestOut {
|
||||||
|
type Unit = u16;
|
||||||
|
type Size = [u16;2];
|
||||||
|
type Area = [u16;4];
|
||||||
|
fn area (&self) -> [u16;4] {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
fn area_mut (&mut self) -> &mut [u16;4] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], _: &T) {
|
||||||
|
println!("place_at: {area:?}");
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Draw<TestOut> for String {
|
||||||
|
fn draw (&self, to: &mut TestOut) {
|
||||||
|
to.area_mut().set_w(self.len() as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,441 @@
|
||||||
mod space_area; pub use self::space_area::*;
|
pub(self) use crate::*;
|
||||||
mod space_coordinate; pub use self::space_coordinate::*;
|
#[cfg(test)] use proptest::{prelude::*, option::of};
|
||||||
mod space_direction; pub use self::space_direction::*;
|
|
||||||
mod space_measure; pub use self::space_measure::*;
|
pub use self::direction::*; mod direction {
|
||||||
mod space_size; pub use self::space_size::*;
|
use super::*;
|
||||||
|
/// A cardinal direction.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
pub enum Direction { North, South, East, West, Above, Below }
|
||||||
|
impl Direction {
|
||||||
|
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
|
||||||
|
let [x, y, w, h] = area.xywh();
|
||||||
|
match self {
|
||||||
|
North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]),
|
||||||
|
South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]),
|
||||||
|
East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]),
|
||||||
|
West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]),
|
||||||
|
Above | Below => (area.xywh(), area.xywh())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)] proptest! {
|
||||||
|
#[test] fn proptest_direction (
|
||||||
|
d in prop_oneof![
|
||||||
|
Just(North), Just(South),
|
||||||
|
Just(East), Just(West),
|
||||||
|
Just(Above), Just(Below)
|
||||||
|
],
|
||||||
|
x in u16::MIN..u16::MAX,
|
||||||
|
y in u16::MIN..u16::MAX,
|
||||||
|
w in u16::MIN..u16::MAX,
|
||||||
|
h in u16::MIN..u16::MAX,
|
||||||
|
a in u16::MIN..u16::MAX,
|
||||||
|
) {
|
||||||
|
let _ = d.split_fixed([x, y, w, h], a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::coordinate::*; mod coordinate {
|
||||||
|
use super::*;
|
||||||
|
/// A linear coordinate.
|
||||||
|
pub trait Coordinate: 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>
|
||||||
|
{
|
||||||
|
fn zero () -> Self { 0.into() }
|
||||||
|
fn plus (self, other: Self) -> Self;
|
||||||
|
fn minus (self, other: Self) -> Self {
|
||||||
|
if self >= other {
|
||||||
|
self - other
|
||||||
|
} else {
|
||||||
|
0.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Coordinate for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::area::*; mod area {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait Area<N: Coordinate>: From<[N;4]> + Debug + Copy {
|
||||||
|
fn x (&self) -> N;
|
||||||
|
fn y (&self) -> N;
|
||||||
|
fn w (&self) -> N;
|
||||||
|
fn h (&self) -> N;
|
||||||
|
fn zero () -> [N;4] {
|
||||||
|
[N::zero(), N::zero(), N::zero(), N::zero()]
|
||||||
|
}
|
||||||
|
fn from_position (pos: impl Size<N>) -> [N;4] {
|
||||||
|
let [x, y] = pos.wh();
|
||||||
|
[x, y, 0.into(), 0.into()]
|
||||||
|
}
|
||||||
|
fn from_size (size: impl Size<N>) -> [N;4] {
|
||||||
|
let [w, h] = size.wh();
|
||||||
|
[0.into(), 0.into(), w, h]
|
||||||
|
}
|
||||||
|
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||||
|
if self.w() < w || self.h() < h {
|
||||||
|
Err(format!("min {w}x{h}").into())
|
||||||
|
} else {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn xy (&self) -> [N;2] {
|
||||||
|
[self.x(), self.y()]
|
||||||
|
}
|
||||||
|
fn wh (&self) -> [N;2] {
|
||||||
|
[self.w(), self.h()]
|
||||||
|
}
|
||||||
|
fn xywh (&self) -> [N;4] {
|
||||||
|
[self.x(), self.y(), self.w(), self.h()]
|
||||||
|
}
|
||||||
|
fn clip_h (&self, h: N) -> [N;4] {
|
||||||
|
[self.x(), self.y(), self.w(), self.h().min(h)]
|
||||||
|
}
|
||||||
|
fn clip_w (&self, w: N) -> [N;4] {
|
||||||
|
[self.x(), self.y(), self.w().min(w), self.h()]
|
||||||
|
}
|
||||||
|
fn clip (&self, wh: impl Size<N>) -> [N;4] {
|
||||||
|
[self.x(), self.y(), wh.w(), wh.h()]
|
||||||
|
}
|
||||||
|
fn set_w (&self, w: N) -> [N;4] {
|
||||||
|
[self.x(), self.y(), w, self.h()]
|
||||||
|
}
|
||||||
|
fn set_h (&self, h: N) -> [N;4] {
|
||||||
|
[self.x(), self.y(), self.w(), h]
|
||||||
|
}
|
||||||
|
fn x2 (&self) -> N {
|
||||||
|
self.x().plus(self.w())
|
||||||
|
}
|
||||||
|
fn y2 (&self) -> N {
|
||||||
|
self.y().plus(self.h())
|
||||||
|
}
|
||||||
|
fn lrtb (&self) -> [N;4] {
|
||||||
|
[self.x(), self.x2(), self.y(), self.y2()]
|
||||||
|
}
|
||||||
|
fn center (&self) -> [N;2] {
|
||||||
|
[self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())]
|
||||||
|
}
|
||||||
|
fn center_x (&self, n: N) -> [N;4] {
|
||||||
|
let [x, y, w, h] = self.xywh();
|
||||||
|
[(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()]
|
||||||
|
}
|
||||||
|
fn center_y (&self, n: N) -> [N;4] {
|
||||||
|
let [x, y, w, h] = self.xywh();
|
||||||
|
[x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n]
|
||||||
|
}
|
||||||
|
fn center_xy (&self, [n, m]: [N;2]) -> [N;4] {
|
||||||
|
let [x, y, w, h] = self.xywh();
|
||||||
|
[(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m]
|
||||||
|
}
|
||||||
|
fn centered (&self) -> [N;2] {
|
||||||
|
[self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())]
|
||||||
|
}
|
||||||
|
fn iter_x (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||||
|
self.x()..(self.x()+self.w())
|
||||||
|
}
|
||||||
|
fn iter_y (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
||||||
|
self.y()..(self.y()+self.h())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Coordinate> Area<N> for (N, N, N, N) {
|
||||||
|
fn x (&self) -> N { self.0 }
|
||||||
|
fn y (&self) -> N { self.1 }
|
||||||
|
fn w (&self) -> N { self.2 }
|
||||||
|
fn h (&self) -> N { self.3 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Coordinate> Area<N> for [N;4] {
|
||||||
|
fn x (&self) -> N { self[0] }
|
||||||
|
fn y (&self) -> N { self[1] }
|
||||||
|
fn w (&self) -> N { self[2] }
|
||||||
|
fn h (&self) -> N { self[3] }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)] #[test] fn test_area () { assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); }
|
||||||
|
#[cfg(test)] proptest! {
|
||||||
|
#[test] fn proptest_area (
|
||||||
|
x in u16::MIN..u16::MAX,
|
||||||
|
y in u16::MIN..u16::MAX,
|
||||||
|
w in u16::MIN..u16::MAX,
|
||||||
|
h in u16::MIN..u16::MAX,
|
||||||
|
a in u16::MIN..u16::MAX,
|
||||||
|
b in u16::MIN..u16::MAX,
|
||||||
|
) {
|
||||||
|
let _: [u16;4] = <[u16;4] as Area<u16>>::zero();
|
||||||
|
let _: [u16;4] = <[u16;4] as Area<u16>>::from_position([a, b]);
|
||||||
|
let _: [u16;4] = <[u16;4] as Area<u16>>::from_size([a, b]);
|
||||||
|
let area: [u16;4] = [x, y, w, h];
|
||||||
|
let _ = area.expect_min(a, b);
|
||||||
|
let _ = area.xy();
|
||||||
|
let _ = area.wh();
|
||||||
|
let _ = area.xywh();
|
||||||
|
let _ = area.clip_h(a);
|
||||||
|
let _ = area.clip_w(b);
|
||||||
|
let _ = area.clip([a, b]);
|
||||||
|
let _ = area.set_w(a);
|
||||||
|
let _ = area.set_h(b);
|
||||||
|
let _ = area.x2();
|
||||||
|
let _ = area.y2();
|
||||||
|
let _ = area.lrtb();
|
||||||
|
let _ = area.center();
|
||||||
|
let _ = area.center_x(a);
|
||||||
|
let _ = area.center_y(b);
|
||||||
|
let _ = area.center_xy([a, b]);
|
||||||
|
let _ = area.centered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::size::*; mod size {
|
||||||
|
use super::*;
|
||||||
|
/// The size of something.
|
||||||
|
pub trait Size<N: Coordinate>: From<[N;2]> + Debug/* + Copy*/ {
|
||||||
|
fn zero () -> [N;2] { [N::zero(), N::zero()] }
|
||||||
|
fn x (&self) -> N;
|
||||||
|
fn y (&self) -> N;
|
||||||
|
fn w (&self) -> N { self.x() }
|
||||||
|
fn h (&self) -> N { self.y() }
|
||||||
|
fn wh (&self) -> [N;2] { [self.x(), self.y()] }
|
||||||
|
fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
|
||||||
|
fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
|
||||||
|
fn to_area_pos (&self) -> [N;4] { let [x, y] = self.wh(); [x, y, 0.into(), 0.into()] }
|
||||||
|
fn to_area_size (&self) -> [N;4] { let [w, h] = self.wh(); [0.into(), 0.into(), w, h] }
|
||||||
|
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
||||||
|
if self.w() < w || self.h() < h { return Err(format!("min {w}x{h}").into()) }
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<N: Coordinate> Size<N> for (N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } }
|
||||||
|
impl<N: Coordinate> Size<N> for [N;2] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } }
|
||||||
|
#[cfg(test)] proptest! {
|
||||||
|
#[test] fn proptest_size (
|
||||||
|
x in u16::MIN..u16::MAX,
|
||||||
|
y in u16::MIN..u16::MAX,
|
||||||
|
a in u16::MIN..u16::MAX,
|
||||||
|
b in u16::MIN..u16::MAX,
|
||||||
|
) {
|
||||||
|
let size = [x, y];
|
||||||
|
let _ = size.w();
|
||||||
|
let _ = size.h();
|
||||||
|
let _ = size.wh();
|
||||||
|
let _ = size.clip_w(a);
|
||||||
|
let _ = size.clip_h(b);
|
||||||
|
let _ = size.expect_min(a, b);
|
||||||
|
let _ = size.to_area_pos();
|
||||||
|
let _ = size.to_area_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait HasSize<E: Out> {
|
||||||
|
fn size (&self) -> &Measure<E>;
|
||||||
|
fn width (&self) -> usize { self.size().w() }
|
||||||
|
fn height (&self) -> usize { self.size().h() }
|
||||||
|
}
|
||||||
|
impl<E: Out, T: Has<Measure<E>>> HasSize<E> for T { fn size (&self) -> &Measure<E> { self.get() } }
|
||||||
|
|
||||||
|
/// A widget that tracks its rendered width and height
|
||||||
|
#[derive(Default)] pub struct Measure<O: Out> { __: PhantomData<O>, pub x: Arc<AtomicUsize>, pub y: Arc<AtomicUsize>, }
|
||||||
|
impl<O: Out> Clone for Measure<O> { fn clone (&self) -> Self { Self { __: Default::default(), x: self.x.clone(), y: self.y.clone(), } } }
|
||||||
|
impl<O: Out> Layout<O> for Measure<O> {}
|
||||||
|
impl<O: Out> PartialEq for Measure<O> {
|
||||||
|
fn eq (&self, other: &Self) -> bool {
|
||||||
|
self.x.load(Relaxed) == other.x.load(Relaxed) &&
|
||||||
|
self.y.load(Relaxed) == other.y.load(Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
||||||
|
impl<O: Out> Draw<O> for Measure<O> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
self.x.store(to.area().w().into(), Relaxed);
|
||||||
|
self.y.store(to.area().h().into(), Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out> Debug for Measure<O> {
|
||||||
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
f.debug_struct("Measure").field("width", &self.x).field("height", &self.y).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out> Measure<O> {
|
||||||
|
pub fn set_w (&self, w: impl Into<usize>) -> &Self { self.x.store(w.into(), Relaxed); self }
|
||||||
|
pub fn set_h (&self, h: impl Into<usize>) -> &Self { self.y.store(h.into(), Relaxed); self }
|
||||||
|
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) -> &Self { self.set_w(w); self.set_h(h); self }
|
||||||
|
pub fn format (&self) -> Arc<str> { format!("{}x{}", self.w(), self.h()).into() }
|
||||||
|
pub fn of <T: Draw<O>> (&self, item: T) -> Bsp<Fill<&Self>, T> { Bsp::b(Fill::XY(self), item) }
|
||||||
|
pub fn new (x: O::Unit, y: O::Unit) -> Self {
|
||||||
|
Self { __: PhantomData::default(), x: Arc::new(x.into()), y: Arc::new(y.into()), }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<O: Out> From<[O::Unit; 2]> for Measure<O> {
|
||||||
|
fn from ([x, y]: [O::Unit; 2]) -> Self { Self::new(x, y) }
|
||||||
|
}
|
||||||
|
//impl<O: Out> Size<O::Unit> for Measure<O> {
|
||||||
|
//fn x (&self) -> O::Unit { self.x.load(Relaxed).into() }
|
||||||
|
//fn y (&self) -> O::Unit { self.y.load(Relaxed).into() }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::layout::*; mod layout {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Drawable area of display.
|
||||||
|
pub trait Layout<O: Out> {
|
||||||
|
fn x (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.x()
|
||||||
|
}
|
||||||
|
fn y (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.y()
|
||||||
|
}
|
||||||
|
fn min_w (&self, _to: O::Area) -> O::Unit {
|
||||||
|
0.into()
|
||||||
|
}
|
||||||
|
fn max_w (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.w()
|
||||||
|
}
|
||||||
|
fn w (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.w().max(self.min_w(to)).min(self.max_w(to))
|
||||||
|
}
|
||||||
|
fn min_h (&self, _to: O::Area) -> O::Unit {
|
||||||
|
0.into()
|
||||||
|
}
|
||||||
|
fn max_h (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.h()
|
||||||
|
}
|
||||||
|
fn h (&self, to: O::Area) -> O::Unit {
|
||||||
|
to.h().max(self.min_h(to)).min(self.max_h(to))
|
||||||
|
}
|
||||||
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
[self.x(to), self.y(to), self.w(to), self.h(to)].into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out> Layout<O> for () {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { a.x() }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { a.y() }
|
||||||
|
fn w (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn min_w (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn max_w (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn h (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn min_h (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn max_h (&self, _: O::Area) -> O::Unit { 0.into() }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for &L {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) }
|
||||||
|
fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) }
|
||||||
|
fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) }
|
||||||
|
fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) }
|
||||||
|
fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) }
|
||||||
|
fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) }
|
||||||
|
fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for &mut L {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
||||||
|
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
||||||
|
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
||||||
|
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
||||||
|
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
||||||
|
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
||||||
|
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for Arc<L> {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
||||||
|
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
||||||
|
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
||||||
|
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
||||||
|
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
||||||
|
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
||||||
|
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out> Layout<O> for Box<dyn Layout<O>> {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) }
|
||||||
|
fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) }
|
||||||
|
fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) }
|
||||||
|
fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) }
|
||||||
|
fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) }
|
||||||
|
fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) }
|
||||||
|
fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for RwLock<L> {
|
||||||
|
fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) }
|
||||||
|
fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) }
|
||||||
|
fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) }
|
||||||
|
fn min_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_w(a) }
|
||||||
|
fn max_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_w(a) }
|
||||||
|
fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) }
|
||||||
|
fn min_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_h(a) }
|
||||||
|
fn max_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_h(a) }
|
||||||
|
fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, L: Layout<O>> Layout<O> for Option<L> {
|
||||||
|
fn x (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.x(to)).unwrap_or(to.x())
|
||||||
|
}
|
||||||
|
fn y (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.y(to)).unwrap_or(to.y())
|
||||||
|
}
|
||||||
|
fn min_w (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn max_w (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn w (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.w(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn min_h (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn max_h (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn h (&self, to: O::Area) -> O::Unit {
|
||||||
|
self.as_ref().map(|c|c.h(to)).unwrap_or(0.into())
|
||||||
|
}
|
||||||
|
fn layout (&self, to: O::Area) -> O::Area {
|
||||||
|
self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into()))
|
||||||
|
.unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bounded<O: Out, D>(pub O::Area, pub D);
|
||||||
|
|
||||||
|
impl<O: Out, D: Content<O>> HasContent<O> for Bounded<O, D> {
|
||||||
|
fn content (&self) -> impl Content<O> {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O: Out, T: Draw<O>> Draw<O> for Bounded<O, T> {
|
||||||
|
fn draw (&self, to: &mut O) {
|
||||||
|
let area = to.area();
|
||||||
|
*to.area_mut() = self.0;
|
||||||
|
self.1.draw(to);
|
||||||
|
*to.area_mut() = area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub trait Area<N: Coordinate>: From<[N;4]> + Debug + Copy {
|
|
||||||
fn x (&self) -> N;
|
|
||||||
fn y (&self) -> N;
|
|
||||||
fn w (&self) -> N;
|
|
||||||
fn h (&self) -> N;
|
|
||||||
fn zero () -> [N;4] {
|
|
||||||
[N::zero(), N::zero(), N::zero(), N::zero()]
|
|
||||||
}
|
|
||||||
fn from_position (pos: impl Size<N>) -> [N;4] {
|
|
||||||
let [x, y] = pos.wh();
|
|
||||||
[x, y, 0.into(), 0.into()]
|
|
||||||
}
|
|
||||||
fn from_size (size: impl Size<N>) -> [N;4] {
|
|
||||||
let [w, h] = size.wh();
|
|
||||||
[0.into(), 0.into(), w, h]
|
|
||||||
}
|
|
||||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
|
||||||
if self.w() < w || self.h() < h {
|
|
||||||
Err(format!("min {w}x{h}").into())
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn xy (&self) -> [N;2] {
|
|
||||||
[self.x(), self.y()]
|
|
||||||
}
|
|
||||||
fn wh (&self) -> [N;2] {
|
|
||||||
[self.w(), self.h()]
|
|
||||||
}
|
|
||||||
fn xywh (&self) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), self.h()]
|
|
||||||
}
|
|
||||||
fn clip_h (&self, h: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), self.h().min(h)]
|
|
||||||
}
|
|
||||||
fn clip_w (&self, w: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w().min(w), self.h()]
|
|
||||||
}
|
|
||||||
fn clip (&self, wh: impl Size<N>) -> [N;4] {
|
|
||||||
[self.x(), self.y(), wh.w(), wh.h()]
|
|
||||||
}
|
|
||||||
fn set_w (&self, w: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), w, self.h()]
|
|
||||||
}
|
|
||||||
fn set_h (&self, h: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), h]
|
|
||||||
}
|
|
||||||
fn x2 (&self) -> N {
|
|
||||||
self.x().plus(self.w())
|
|
||||||
}
|
|
||||||
fn y2 (&self) -> N {
|
|
||||||
self.y().plus(self.h())
|
|
||||||
}
|
|
||||||
fn lrtb (&self) -> [N;4] {
|
|
||||||
[self.x(), self.x2(), self.y(), self.y2()]
|
|
||||||
}
|
|
||||||
fn center (&self) -> [N;2] {
|
|
||||||
[self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())]
|
|
||||||
}
|
|
||||||
fn center_x (&self, n: N) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()]
|
|
||||||
}
|
|
||||||
fn center_y (&self, n: N) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n]
|
|
||||||
}
|
|
||||||
fn center_xy (&self, [n, m]: [N;2]) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m]
|
|
||||||
}
|
|
||||||
fn centered (&self) -> [N;2] {
|
|
||||||
[self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())]
|
|
||||||
}
|
|
||||||
fn iter_x (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
|
||||||
self.x()..(self.x()+self.w())
|
|
||||||
}
|
|
||||||
fn iter_y (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
|
||||||
self.y()..(self.y()+self.h())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Area<N> for (N, N, N, N) {
|
|
||||||
fn x (&self) -> N { self.0 }
|
|
||||||
fn y (&self) -> N { self.1 }
|
|
||||||
fn w (&self) -> N { self.2 }
|
|
||||||
fn h (&self) -> N { self.3 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Area<N> for [N;4] {
|
|
||||||
fn x (&self) -> N { self[0] }
|
|
||||||
fn y (&self) -> N { self[1] }
|
|
||||||
fn w (&self) -> N { self[2] }
|
|
||||||
fn h (&self) -> N { self[3] }
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// A linear coordinate.
|
|
||||||
pub trait Coordinate: 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>
|
|
||||||
{
|
|
||||||
fn zero () -> Self { 0.into() }
|
|
||||||
fn plus (self, other: Self) -> Self;
|
|
||||||
fn minus (self, other: Self) -> Self {
|
|
||||||
if self >= other {
|
|
||||||
self - other
|
|
||||||
} else {
|
|
||||||
0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Coordinate for u16 {
|
|
||||||
fn plus (self, other: Self) -> Self {
|
|
||||||
self.saturating_add(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// A cardinal direction.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
pub enum Direction {
|
|
||||||
North, South, East, West, Above, Below
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
|
|
||||||
let [x, y, w, h] = area.xywh();
|
|
||||||
match self {
|
|
||||||
North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]),
|
|
||||||
South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]),
|
|
||||||
East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]),
|
|
||||||
West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]),
|
|
||||||
Above | Below => (area.xywh(), area.xywh())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
/// A widget that tracks its render width and height
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Measure<O: Out> {
|
|
||||||
_engine: PhantomData<O>,
|
|
||||||
pub x: Arc<AtomicUsize>,
|
|
||||||
pub y: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> PartialEq for Measure<O> {
|
|
||||||
fn eq (&self, other: &Self) -> bool {
|
|
||||||
self.x.load(Relaxed) == other.x.load(Relaxed) &&
|
|
||||||
self.y.load(Relaxed) == other.y.load(Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> Layout<O> for Measure<O> {}
|
|
||||||
|
|
||||||
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
|
||||||
impl<O: Out> Draw<O> for Measure<O> {
|
|
||||||
fn draw (&self, to: &mut O) {
|
|
||||||
self.x.store(to.area().w().into(), Relaxed);
|
|
||||||
self.y.store(to.area().h().into(), Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> Clone for Measure<O> {
|
|
||||||
fn clone (&self) -> Self {
|
|
||||||
Self {
|
|
||||||
_engine: Default::default(),
|
|
||||||
x: self.x.clone(),
|
|
||||||
y: self.y.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> std::fmt::Debug for Measure<O> {
|
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
|
||||||
f.debug_struct("Measure")
|
|
||||||
.field("width", &self.x)
|
|
||||||
.field("height", &self.y)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O: Out> Measure<O> {
|
|
||||||
pub fn new () -> Self {
|
|
||||||
Self {
|
|
||||||
_engine: PhantomData::default(),
|
|
||||||
x: Arc::new(0.into()),
|
|
||||||
y: Arc::new(0.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn set_w (&self, w: impl Into<usize>) -> &Self {
|
|
||||||
self.x.store(w.into(), Relaxed);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn set_h (&self, h: impl Into<usize>) -> &Self {
|
|
||||||
self.y.store(h.into(), Relaxed);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) -> &Self {
|
|
||||||
self.set_w(w);
|
|
||||||
self.set_h(h);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn w (&self) -> usize {
|
|
||||||
self.x.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn h (&self) -> usize {
|
|
||||||
self.y.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn wh (&self) -> [usize;2] {
|
|
||||||
[self.w(), self.h()]
|
|
||||||
}
|
|
||||||
pub fn format (&self) -> Arc<str> {
|
|
||||||
format!("{}x{}", self.w(), self.h()).into()
|
|
||||||
}
|
|
||||||
pub fn of <T: Draw<O>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
|
||||||
Bsp::b(Fill::XY(self), item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub trait Size<N: Coordinate>: From<[N;2]> + Debug + Copy {
|
|
||||||
fn x (&self) -> N;
|
|
||||||
fn y (&self) -> N;
|
|
||||||
fn w (&self) -> N { self.x() }
|
|
||||||
fn h (&self) -> N { self.y() }
|
|
||||||
fn wh (&self) -> [N;2] { [self.x(), self.y()] }
|
|
||||||
fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
|
|
||||||
fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
|
|
||||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
|
||||||
if self.w() < w || self.h() < h {
|
|
||||||
Err(format!("min {w}x{h}").into())
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn zero () -> [N;2] {
|
|
||||||
[N::zero(), N::zero()]
|
|
||||||
}
|
|
||||||
fn to_area_pos (&self) -> [N;4] {
|
|
||||||
let [x, y] = self.wh();
|
|
||||||
[x, y, 0.into(), 0.into()]
|
|
||||||
}
|
|
||||||
fn to_area_size (&self) -> [N;4] {
|
|
||||||
let [w, h] = self.wh();
|
|
||||||
[0.into(), 0.into(), w, h]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Size<N> for (N, N) {
|
|
||||||
fn x (&self) -> N { self.0 }
|
|
||||||
fn y (&self) -> N { self.1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Size<N> for [N;2] {
|
|
||||||
fn x (&self) -> N { self[0] }
|
|
||||||
fn y (&self) -> N { self[1] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasSize<E: Out> {
|
|
||||||
fn size (&self) -> &Measure<E>;
|
|
||||||
fn width (&self) -> usize {
|
|
||||||
self.size().w()
|
|
||||||
}
|
|
||||||
fn height (&self) -> usize {
|
|
||||||
self.size().h()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Out, T: Has<Measure<E>>> HasSize<E> for T {
|
|
||||||
fn size (&self) -> &Measure<E> {
|
|
||||||
self.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,34 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use ::dizzle::{Language, Expression, Symbol, Namespace};
|
use ::dizzle::{Language, Expression, Symbol, Namespace};
|
||||||
|
|
||||||
|
/// FIXME: This macro should be some variant of `eval`, too.
|
||||||
|
/// But taking into account the different signatures (resolving them into 1?)
|
||||||
|
#[macro_export] macro_rules! draw {
|
||||||
|
($State:ident: $Output:ident: $layers:expr) => {
|
||||||
|
impl Draw<$Output> for $State {
|
||||||
|
fn draw (&self, to: &mut $Output) {
|
||||||
|
for layer in $layers { layer(self, to) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME: This is generic: should be called `eval` and be part of [dizzle].
|
||||||
|
#[macro_export] macro_rules! view {
|
||||||
|
($State:ident: $Output:ident: $namespaces:expr) => {
|
||||||
|
impl View<$Output, ()> for $State {
|
||||||
|
fn view_expr <'a> (&'a self, to: &mut $Output, expr: &'a impl Expression) -> Usually<()> {
|
||||||
|
for namespace in $namespaces { if namespace(self, to, expr)? { return Ok(()) } }
|
||||||
|
Err(format!("{}::<{}, ()>::view_expr: unexpected: {expr:?}",
|
||||||
|
stringify! { $State },
|
||||||
|
stringify! { $Output }).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME: This is generic: should be called `Eval` and be part of [dizzle].
|
||||||
|
/// Matches [Language] expressions to renderings for a given [Output] target.
|
||||||
pub trait View<O, U> {
|
pub trait View<O, U> {
|
||||||
fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually<U> {
|
fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually<U> {
|
||||||
Err(format!("View::view_expr: no exprs defined: {expr:?}").into())
|
Err(format!("View::view_expr: no exprs defined: {expr:?}").into())
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,69 @@
|
||||||
use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*};
|
use ::{std::sync::{Arc, RwLock}, ratatui::style::Color,
|
||||||
use std::sync::{Arc, RwLock};
|
tengri::{*, input::*, output::*}, tengri_tui::*};
|
||||||
use crate::ratatui::style::Color;
|
tui_main!(State { cursor: 10, ..Default::default() });
|
||||||
//use crossterm::event::{*, KeyCode::*};
|
namespace!(State: bool {});
|
||||||
|
namespace!(State: u16 {});
|
||||||
fn main () -> Usually<()> {
|
namespace!(State: Color {});
|
||||||
let state = Example::new();
|
handle!(TuiIn: |self: State, input|Action::from(input).eval(self).map(|_|None));
|
||||||
Tui::new().unwrap().run(&state)?;
|
from!(Action: |input: &TuiIn| todo!());
|
||||||
Ok(())
|
view!(State: TuiOut: [ evaluate_output_expression, evaluate_output_expression_tui ]);
|
||||||
|
draw!(State: TuiOut: [ draw_example ]);
|
||||||
|
#[derive(Debug, Default)] struct State {
|
||||||
|
/** Rendered window size */ size: Measure<TuiOut>,
|
||||||
|
/** Command history (undo/redo) */ history: Vec<Action>,
|
||||||
|
/** User-controllable value */ cursor: usize,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug)] enum Action {
|
||||||
#[derive(Debug)] struct Example(usize, Measure<TuiOut>);
|
/** Increment cursor */ Next,
|
||||||
|
/** Decrement cursor */ Prev
|
||||||
handle!(TuiIn: |self: Example, input|Ok(None));
|
}
|
||||||
enum ExampleCommand { Next, Prev }
|
fn draw_example (state: &State, to: &mut TuiOut) {
|
||||||
impl ExampleCommand {
|
let index = state.cursor + 1;
|
||||||
fn eval (&self, state: &mut Example) -> Perhaps<Self> {
|
let wh = state.size.wh();
|
||||||
match self {
|
let src = VIEWS.get(state.cursor).unwrap_or(&"");
|
||||||
Self::Next => {
|
let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh);
|
||||||
state.0 = (state.0 + 1) % Example::VIEWS.len();
|
let title = Tui::bg(Color::Rgb(60, 10, 10), Push::Y(1, Align::n(heading)));
|
||||||
|
let code = Tui::bg(Color::Rgb(10, 60, 10), Push::Y(2, Align::n(format!("{}", src))));
|
||||||
|
//let content = ;//();//Tui::bg(Color::Rgb(10, 10, 60), View(state, CstIter::new(src)));
|
||||||
|
state.size.of(Bsp::s(title, Bsp::n(code, state.view(to, &src).unwrap()))).draw(to)
|
||||||
|
}
|
||||||
|
impl Action {
|
||||||
|
const BINDS: &'static str = stringify! { (@left prev) (@right next) };
|
||||||
|
fn eval (&self, state: &mut State) -> Perhaps<Self> {
|
||||||
|
use Action::*;
|
||||||
|
match self { Next => Self::next(state), Prev => Self::prev(state), }
|
||||||
|
}
|
||||||
|
fn next (state: &mut State) -> Perhaps<Self> {
|
||||||
|
state.cursor = (state.cursor + 1) % VIEWS.len();
|
||||||
Ok(Some(Self::Prev))
|
Ok(Some(Self::Prev))
|
||||||
},
|
}
|
||||||
Self::Prev => {
|
fn prev (state: &mut State) -> Perhaps<Self> {
|
||||||
state.0 = if state.0 > 0 { state.0 - 1 } else { Example::VIEWS.len() - 1 };
|
state.cursor = if state.cursor > 0 { state.cursor - 1 } else { VIEWS.len() - 1 };
|
||||||
Ok(Some(Self::Next))
|
Ok(Some(Self::Next))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Draw<TuiOut> for Example {
|
|
||||||
fn content (&self) -> impl Draw<TuiOut> {
|
|
||||||
let index = self.0 + 1;
|
|
||||||
let wh = self.1.wh();
|
|
||||||
let src = Self::VIEWS.get(self.0).unwrap_or(&"");
|
|
||||||
let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh);
|
|
||||||
let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading)));
|
|
||||||
let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src))));
|
|
||||||
let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src)));
|
|
||||||
self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl View<TuiOut, ()> for Example {
|
|
||||||
fn view_expr <'a> (&'a self, to: &mut TuiOut, expr: &'a impl DslExpr) -> Usually<()> {
|
|
||||||
if evaluate_output_expression(self, to, expr)?
|
|
||||||
|| evaluate_output_expression_tui(self, to, expr)? {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!("Example::view_expr: unexpected: {expr:?}").into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Example {
|
|
||||||
fn new () -> Arc<RwLock<Self>> {
|
|
||||||
Arc::new(RwLock::new(Example(10, Measure::new())))
|
|
||||||
}
|
|
||||||
const BINDS: &'static str = stringify! {
|
|
||||||
(@left prev)
|
|
||||||
(@right next)
|
|
||||||
};
|
|
||||||
const VIEWS: &'static [&'static str] = &[
|
const VIEWS: &'static [&'static str] = &[
|
||||||
|
|
||||||
stringify! { :hello-world },
|
stringify! { :hello-world },
|
||||||
|
|
||||||
stringify! { (fill/xy :hello-world) },
|
stringify! { (fill/xy :hello-world) },
|
||||||
|
|
||||||
stringify! { (bsp/s :hello :world) },
|
stringify! { (bsp/s :hello :world) },
|
||||||
|
|
||||||
stringify! { (fixed/xy 20 10 :hello-world) },
|
stringify! { (fixed/xy 20 10 :hello-world) },
|
||||||
|
|
||||||
stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) },
|
||||||
|
|
||||||
stringify! {
|
stringify! {
|
||||||
(bsp/s
|
(bsp/s
|
||||||
(bsp/e (align/nw (fixed/xy 5 3 :hello))
|
(bsp/e (align/nw (fixed/xy 5 3 :hello))
|
||||||
|
|
@ -83,6 +77,7 @@ impl Example {
|
||||||
(bsp/e (align/s (fixed/xy 5 3 :hello))
|
(bsp/e (align/s (fixed/xy 5 3 :hello))
|
||||||
(align/se (fixed/xy 5 3 :hello))))))
|
(align/se (fixed/xy 5 3 :hello))))))
|
||||||
},
|
},
|
||||||
|
|
||||||
stringify! {
|
stringify! {
|
||||||
(bsp/s
|
(bsp/s
|
||||||
(bsp/e (fixed/xy 8 5 (align/nw :hello))
|
(bsp/e (fixed/xy 8 5 (align/nw :hello))
|
||||||
|
|
@ -96,6 +91,7 @@ impl Example {
|
||||||
(bsp/e (fixed/xy 8 5 (align/s :hello))
|
(bsp/e (fixed/xy 8 5 (align/s :hello))
|
||||||
(fixed/xy 8 5 (align/se :hello))))))
|
(fixed/xy 8 5 (align/se :hello))))))
|
||||||
},
|
},
|
||||||
|
|
||||||
stringify! {
|
stringify! {
|
||||||
(bsp/s
|
(bsp/s
|
||||||
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello)))
|
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello)))
|
||||||
|
|
@ -109,15 +105,20 @@ impl Example {
|
||||||
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello)))
|
(bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello)))
|
||||||
(grow/xy 1 1 (fixed/xy 8 5 (align/se :hello)))))))
|
(grow/xy 1 1 (fixed/xy 8 5 (align/se :hello)))))))
|
||||||
},
|
},
|
||||||
|
|
||||||
stringify! { :map-e },
|
stringify! { :map-e },
|
||||||
|
|
||||||
stringify! { (align/c :map-e) },
|
stringify! { (align/c :map-e) },
|
||||||
|
|
||||||
stringify! { :map-s },
|
stringify! { :map-s },
|
||||||
|
|
||||||
stringify! { (align/c :map-s) },
|
stringify! { (align/c :map-s) },
|
||||||
|
|
||||||
stringify! {
|
stringify! {
|
||||||
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
|
(align/c (bg/behind :bg0 (margin/xy 1 1 (col
|
||||||
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
|
(bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1)))
|
||||||
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
|
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
|
||||||
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
|
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
#![feature(type_changing_struct_update, trait_alias)]
|
#![feature(type_changing_struct_update, trait_alias)]
|
||||||
pub use ::{tengri_input, tengri_output, ratatui, crossterm, palette, better_panic};
|
pub use ::{
|
||||||
|
dizzle,
|
||||||
|
tengri_input,
|
||||||
|
tengri_output,
|
||||||
|
ratatui,
|
||||||
|
crossterm,
|
||||||
|
palette,
|
||||||
|
better_panic,
|
||||||
|
};
|
||||||
pub(crate) use ::{
|
pub(crate) use ::{
|
||||||
dizzle::*,
|
dizzle::*,
|
||||||
tengri_input::*,
|
tengri_input::*,
|
||||||
|
|
@ -23,13 +31,23 @@ pub(crate) use ::{
|
||||||
};
|
};
|
||||||
mod tui_engine; pub use self::tui_engine::*;
|
mod tui_engine; pub use self::tui_engine::*;
|
||||||
mod tui_content; pub use self::tui_content::*;
|
mod tui_content; pub use self::tui_content::*;
|
||||||
|
#[macro_export] macro_rules! tui_main {
|
||||||
|
($expr:expr) => {
|
||||||
|
fn main () -> Usually<()> {
|
||||||
|
let state = Arc::new(RwLock::new($expr));
|
||||||
|
tengri_tui::Tui::new().unwrap().run(&state)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)] mod tui_test {
|
#[cfg(test)] mod tui_test {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
#[test] fn test_tui_engine () -> Usually<()> {
|
#[test] fn test_tui_engine () -> Usually<()> {
|
||||||
//use std::sync::{Arc, RwLock};
|
//use std::sync::{Arc, RwLock};
|
||||||
struct TestComponent(String);
|
struct TestComponent(String);
|
||||||
impl Content<TuiOut> for TestComponent {
|
impl HasContent<TuiOut> for TestComponent {
|
||||||
fn content (&self) -> impl Draw<TuiOut> {
|
fn content (&self) -> impl Content<TuiOut> {
|
||||||
Some(self.0.as_str())
|
Some(self.0.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,12 @@ pub trait HasColor { fn color (&self) -> ItemColor; }
|
||||||
pub okhsl: Okhsl<f32>,
|
pub okhsl: Okhsl<f32>,
|
||||||
pub rgb: Color,
|
pub rgb: Color,
|
||||||
}
|
}
|
||||||
|
from!(ItemColor: |okhsl: Okhsl<f32>|Self { okhsl, rgb: okhsl_to_rgb(okhsl) });
|
||||||
from!(|okhsl: Okhsl<f32>|ItemColor = Self { okhsl, rgb: okhsl_to_rgb(okhsl) });
|
from!(ItemColor: |rgb: Color|Self { rgb, okhsl: rgb_to_okhsl(rgb) });
|
||||||
|
|
||||||
pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
|
pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
|
||||||
let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(color);
|
let Srgb { red, green, blue, .. }: Srgb<f32> = Srgb::from_color_unclamped(color);
|
||||||
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)
|
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) });
|
|
||||||
|
|
||||||
pub fn rgb_to_okhsl (color: Color) -> Okhsl<f32> {
|
pub fn rgb_to_okhsl (color: Color) -> Okhsl<f32> {
|
||||||
if let Color::Rgb(r, g, b) = color {
|
if let Color::Rgb(r, g, b) = color {
|
||||||
Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0))
|
Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0))
|
||||||
|
|
@ -98,6 +94,8 @@ impl ItemColor {
|
||||||
pub darkest: ItemColor,
|
pub darkest: ItemColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
from!(ItemTheme: |base: Color| Self::from_tui_color(base));
|
||||||
|
from!(ItemTheme: |base: ItemColor|Self::from_item_color(base));
|
||||||
impl ItemTheme {
|
impl ItemTheme {
|
||||||
pub const G: [Self;256] = {
|
pub const G: [Self;256] = {
|
||||||
let mut builder = konst::array::ArrayBuilder::new();
|
let mut builder = konst::array::ArrayBuilder::new();
|
||||||
|
|
@ -170,6 +168,3 @@ impl ItemTheme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|base: Color| ItemTheme = Self::from_tui_color(base));
|
|
||||||
from!(|base: ItemColor|ItemTheme = Self::from_item_color(base));
|
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,4 @@ impl BigBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from!(|size:(usize, usize)| BigBuffer = Self::new(size.0, size.1));
|
from!(BigBuffer: |size:(usize, usize)| Self::new(size.0, size.1));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue