tek/src/space.rs

459 lines
12 KiB
Rust

use crate::*;
use std::ops::{Add, Sub, Mul, Div};
use std::fmt::{Display, Debug};
mod cond; pub use self::cond::*;
mod coord; pub use self::coord::*;
mod layers; pub use self::layers::*;
mod measure; pub use self::measure::*;
mod position; pub use self::position::*;
mod scroll; pub use self::scroll::*;
mod size; pub use self::size::*;
mod split; pub use self::split::*;
/// Has static methods for conditional rendering,
/// in unary and binary forms.
pub struct Cond;
impl Cond {
/// Render `item` when `cond` is true.
pub fn when <E: Engine, A: Render<E>> (cond: bool, item: A) -> When<E, A> {
When(cond, item, Default::default())
}
/// Render `item` if `cond` is true, otherwise render `other`.
pub fn either <E: Engine, A: Render<E>, B: Render<E>> (cond: bool, item: A, other: B) -> Either<E, A, B> {
Either(cond, item, other, Default::default())
}
}
/// Renders `self.1` when `self.0` is true.
pub struct When<E: Engine, A: Render<E>>(bool, A, PhantomData<E>);
/// Renders `self.1` when `self.0` is true, otherwise renders `self.2`
pub struct Either<E: Engine, A: Render<E>, B: Render<E>>(bool, A, B, PhantomData<E>);
/// Renders multiple things on top of each other,
/// in the order they are provided by the callback.
/// Total size is largest width x largest height.
pub struct Layers<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
>(pub F, PhantomData<E>);
/// Shorthand for defining an instance of [Layers].
#[macro_export] macro_rules! lay {
([$($expr:expr),* $(,)?]) => {
Layers::new(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Layers::new(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Layers::new($expr)
};
}
/// 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 minus (self, other: Self) -> Self {
if self >= other {
self - other
} else {
0.into()
}
}
fn zero () -> Self {
0.into()
}
}
pub trait Size<N: Coordinate> {
fn x (&self) -> N;
fn y (&self) -> N;
#[inline] fn w (&self) -> N { self.x() }
#[inline] fn h (&self) -> N { self.y() }
#[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] }
#[inline] fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
#[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
#[inline] 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)
}
}
}
pub trait Area<N: Coordinate>: Copy {
fn x (&self) -> N;
fn y (&self) -> N;
fn w (&self) -> N;
fn h (&self) -> N;
fn x2 (&self) -> N {
self.x() + self.w()
}
fn y2 (&self) -> N {
self.y() + self.h()
}
#[inline] fn wh (&self) -> [N;2] {
[self.w(), self.h()]
}
#[inline] fn xywh (&self) -> [N;4] {
[self.x(), self.y(), self.w(), self.h()]
}
#[inline] fn lrtb (&self) -> [N;4] {
[self.x(), self.x2(), self.y(), self.y2()]
}
#[inline] fn push_x (&self, x: N) -> [N;4] {
[self.x() + x, self.y(), self.w(), self.h()]
}
#[inline] fn push_y (&self, y: N) -> [N;4] {
[self.x(), self.y() + y, self.w(), self.h()]
}
#[inline] fn shrink_x (&self, x: N) -> [N;4] {
[self.x(), self.y(), self.w().minus(x), self.h()]
}
#[inline] fn shrink_y (&self, y: N) -> [N;4] {
[self.x(), self.y(), self.w(), self.h().minus(y)]
}
#[inline] fn set_w (&self, w: N) -> [N;4] {
[self.x(), self.y(), w, self.h()]
}
#[inline] fn set_h (&self, h: N) -> [N;4] {
[self.x(), self.y(), self.w(), h]
}
#[inline] fn clip_h (&self, h: N) -> [N;4] {
[self.x(), self.y(), self.w(), self.h().min(h)]
}
#[inline] fn clip_w (&self, w: N) -> [N;4] {
[self.x(), self.y(), self.w().min(w), self.h()]
}
#[inline] fn clip (&self, wh: impl Size<N>) -> [N;4] {
[self.x(), self.y(), wh.w(), wh.h()]
}
#[inline] 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)
}
}
#[inline] fn split_fixed (&self, direction: Direction, a: N) -> ([N;4],[N;4]) {
match direction {
Direction::North => (
[self.x(), (self.y()+self.h()).minus(a), self.w(), a],
[self.x(), self.y(), self.w(), self.h().minus(a)],
),
Direction::South => (
[self.x(), self.y(), self.w(), a],
[self.x(), self.y() + a, self.w(), self.h().minus(a)],
),
Direction::East => (
[self.x(), self.y(), a, self.h()],
[self.x() + a, self.y(), self.w().minus(a), self.h()],
),
Direction::West => (
[self.x() + self.w() - a, self.y(), a, self.h()],
[self.x(), self.y(), self.w() - a, self.h()],
),
}
}
}
macro_rules! by_axis {
(!$Enum:ident) => {
impl<E: Engine, T: Render<E>> $Enum<E, T> {
pub fn x (item: T) -> Self {
Self::X(item)
}
pub fn y (item: T) -> Self {
Self::Y(item)
}
pub fn xy (item: T) -> Self {
Self::XY(item)
}
}
};
(+$Enum:ident) => {
impl<E: Engine, T: Render<E>> $Enum<E, T> {
pub fn x (x: E::Unit, item: T) -> Self {
Self::X(x, item)
}
pub fn y (y: E::Unit, item: T) -> Self {
Self::Y(y, item)
}
pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self {
Self::XY(x, y, item)
}
}
};
}
/// Shrink drawing area
pub enum Shrink<E: Engine, T: Render<E>> {
/// Decrease width
X(E::Unit, T),
/// Decrease height
Y(E::Unit, T),
/// Decrease width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Shrink);
/// Expand drawing area
pub enum Grow<E: Engine, T: Render<E>> {
/// Increase width
X(E::Unit, T),
/// Increase height
Y(E::Unit, T),
/// Increase width and height
XY(E::Unit, E::Unit, T)
}
by_axis!(+Grow);
pub enum Fill<E: Engine, W: Render<E>> {
_Unused(PhantomData<E>),
/// Maximize width
X(W),
/// Maximize height
Y(W),
/// Maximize width and height
XY(W),
}
by_axis!(!Fill);
/// Enforce fixed size of drawing area
pub enum Fixed<E: Engine, T: Render<E>> {
/// Set width
X(E::Unit, T),
/// Set height
Y(E::Unit, T),
/// Set width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Fixed);
/// Enforce minimum size of drawing area
pub enum Min<E: Engine, T: Render<E>> {
/// Enforce minimum width
X(E::Unit, T),
/// Enforce minimum height
Y(E::Unit, T),
/// Enforce minimum width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Min);
/// Enforce maximum size of drawing area
pub enum Max<E: Engine, T: Render<E>> {
/// Enforce maximum width
X(E::Unit, T),
/// Enforce maximum height
Y(E::Unit, T),
/// Enforce maximum width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Max);
/// Increment origin point of drawing area
pub enum Push<E: Engine, T: Render<E>> {
/// Move origin to the right
X(E::Unit, T),
/// Move origin downwards
Y(E::Unit, T),
/// Move origin to the right and downwards
XY(E::Unit, E::Unit, T),
}
by_axis!(+Push);
/// Decrement origin point of drawing area
pub enum Pull<E: Engine, T: Render<E>> {
/// Move origin to the right
X(E::Unit, T),
/// Move origin downwards
Y(E::Unit, T),
/// Move origin to the right and downwards
XY(E::Unit, E::Unit, T),
}
by_axis!(+Pull);
/// Shrink from each side
pub enum Inset<E: Engine, T> {
/// Decrease width
X(E::Unit, T),
/// Decrease height
Y(E::Unit, T),
/// Decrease width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Inset);
/// Grow on each side
pub enum Outset<E: Engine, T: Render<E>> {
/// Increase width
X(E::Unit, T),
/// Increase height
Y(E::Unit, T),
/// Increase width and height
XY(E::Unit, E::Unit, T),
}
by_axis!(+Outset);
/// A cardinal direction.
#[derive(Copy, Clone, PartialEq)]
pub enum Direction { North, South, West, East, }
/// A binary split with fixed proportion
pub struct Split<E: Engine, A: Render<E>, B: Render<E>>(
pub bool, pub Direction, pub E::Unit, A, B, PhantomData<E>
);
pub enum Bsp<E: Engine, X: Render<E>, Y: Render<E>> {
/// X is north of Y
N(Option<X>, Option<Y>),
/// X is south of Y
S(Option<X>, Option<Y>),
/// X is east of Y
E(Option<X>, Option<Y>),
/// X is west of Y
W(Option<X>, Option<Y>),
/// X is above Y
A(Option<X>, Option<Y>),
/// X is below Y
B(Option<X>, Option<Y>),
/// Should be avoided.
Null(PhantomData<E>),
}
pub struct Stack<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, PhantomData<E>);
#[macro_export] macro_rules! col {
([$($expr:expr),* $(,)?]) => {
Stack::down(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Stack::down(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Stack::down($expr)
};
($pat:pat in $collection:expr => $item:expr) => {
Stack::down(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
};
}
#[macro_export] macro_rules! col_up {
([$($expr:expr),* $(,)?]) => {
Stack::up(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Stack::up(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Stack::up(expr)
};
($pat:pat in $collection:expr => $item:expr) => {
Stack::up(move |add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
};
}
#[macro_export] macro_rules! row {
([$($expr:expr),* $(,)?]) => {
Stack::right(move|add|{ $(add(&$expr)?;)* Ok(()) })
};
(![$($expr:expr),* $(,)?]) => {
Stack::right(|add|{ $(add(&$expr)?;)* Ok(()) })
};
($expr:expr) => {
Stack::right($expr)
};
($pat:pat in $collection:expr => $item:expr) => {
Stack::right(move|add|{ for $pat in $collection { add(&$item)?; } Ok(()) })
};
}
pub trait HasSize<E: Engine> {
fn size (&self) -> &Measure<E>;
}
#[macro_export] macro_rules! has_size {
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasSize<$E> for $Struct $(<$($L),*$($T),*>)? {
fn size (&$self) -> &Measure<$E> { $cb }
}
}
}
pub trait LayoutDebug<E: Engine> {
fn debug <W: Render<E>> (other: W) -> DebugOverlay<E, W> {
DebugOverlay(Default::default(), other)
}
}
pub struct DebugOverlay<E: Engine, W: Render<E>>(PhantomData<E>, pub W);
/// A widget that tracks its render width and height
#[derive(Default)]
pub struct Measure<E: Engine> {
_engine: PhantomData<E>,
pub x: Arc<AtomicUsize>,
pub y: Arc<AtomicUsize>,
}
pub struct ShowMeasure<'a>(&'a Measure<Tui>);
/// A scrollable area.
pub struct Scroll<
E: Engine,
F: Send + Sync + Fn(&mut dyn FnMut(&dyn Render<E>)->Usually<()>)->Usually<()>
>(pub F, pub Direction, pub u64, PhantomData<E>);
/// Override X and Y coordinates, aligning to corner, side, or center of area
pub enum Align<E: Engine, T: Render<E>> {
_Unused(PhantomData<E>),
/// Draw at center of container
Center(T),
/// Draw at center of X axis
X(T),
/// Draw at center of Y axis
Y(T),
/// Draw at upper left corner of contaier
NW(T),
/// Draw at center of upper edge of container
N(T),
/// Draw at right left corner of contaier
NE(T),
/// Draw at center of left edge of container
W(T),
/// Draw at center of right edge of container
E(T),
/// Draw at lower left corner of container
SW(T),
/// Draw at center of lower edge of container
S(T),
/// Draw at lower right edge of container
SE(T)
}