mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
459 lines
12 KiB
Rust
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)
|
|
}
|