mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-07-17 07:56:56 +02:00
- use macros for the evals - move some space stuff into submodules - partial update to doctests
280 lines
8.1 KiB
Rust
280 lines
8.1 KiB
Rust
use crate::{*, draw::*};
|
|
#[cfg(test)] use proptest_derive::Arbitrary;
|
|
|
|
mod xywh;
|
|
pub use self::xywh::*;
|
|
|
|
mod origin;
|
|
pub use self::origin::*;
|
|
|
|
mod split;
|
|
pub use self::split::*;
|
|
|
|
/// A numeric type that can be used as coordinate.
|
|
///
|
|
/// FIXME: Replace with `num` crate?
|
|
/// FIXME: Use AsRef/AsMut?
|
|
///
|
|
/// ```
|
|
/// use tengri::draw::Coord;
|
|
/// let a: u16 = Coord::zero();
|
|
/// let b: u16 = a.plus(1);
|
|
/// let c: u16 = a.minus(2);
|
|
/// let d = a.atomic();
|
|
/// ```
|
|
pub trait Coord: Send + Sync + Copy
|
|
+ Add<Self, Output=Self>
|
|
+ Sub<Self, Output=Self>
|
|
+ Mul<Self, Output=Self>
|
|
+ Div<Self, Output=Self>
|
|
+ Ord + PartialEq + Eq
|
|
+ Debug + Display + Default
|
|
+ From<u16> + Into<u16>
|
|
+ Into<usize>
|
|
+ Into<f64>
|
|
+ std::iter::Step
|
|
{
|
|
/// Zero in own type.
|
|
fn zero () -> Self { 0.into() }
|
|
/// Addition.
|
|
fn plus (self, other: Self) -> Self;
|
|
/// Saturating subtraction.
|
|
fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } }
|
|
/// Convert to [AtomicUsize].
|
|
fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) }
|
|
}
|
|
|
|
/// Horizontal axis.
|
|
pub trait X<N: Coord> {
|
|
fn x (&self) -> N;
|
|
fn w (&self) -> N { N::zero() }
|
|
fn w_min (&self) -> N { self.w() }
|
|
fn w_max (&self) -> N { self.w() }
|
|
fn iter_x (&self) -> impl Iterator<Item = N> where Self: HasOrigin {
|
|
self.x_west()..self.x_east()
|
|
}
|
|
fn x_west (&self) -> N where Self: HasOrigin {
|
|
use Origin::*;
|
|
let w = self.w();
|
|
let a = self.origin();
|
|
let d = match a { NW|W|SW => 0.into(), N|X|C|Y|S => w/2.into(), NE|E|SE => w };
|
|
self.x().minus(d)
|
|
}
|
|
fn x_east (&self) -> N where Self: HasOrigin {
|
|
use Origin::*;
|
|
let w = self.w();
|
|
let a = self.origin();
|
|
let d = match a { NW|W|SW => w, N|X|C|Y|S => w/2.into(), NE|E|SE => 0.into() };
|
|
self.x().plus(d)
|
|
}
|
|
fn x_center (&self) -> N where Self: HasOrigin {
|
|
todo!()
|
|
}
|
|
}
|
|
pub const fn x_push <T: Screen> (x: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
pub const fn x_pull <T: Screen> (x: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
pub const fn w_max <T: Screen> (w: Option<T::Unit>, a: impl Draw<T>) -> impl Draw<T> {
|
|
wh_max(w, None, a)
|
|
}
|
|
pub const fn w_min <T: Screen> (w: Option<T::Unit>, a: impl Draw<T>) -> impl Draw<T> {
|
|
wh_min(w, None, a)
|
|
}
|
|
pub const fn w_exact <T: Screen> (w: T::Unit, c: impl Draw<T>) -> impl Draw<T> {
|
|
wh_exact(Some(w), None, c)
|
|
}
|
|
/// Shrink drawing area symmetrically.
|
|
///
|
|
/// ```
|
|
/// let padded = tengri::W(3).pad("Hello");
|
|
/// ```
|
|
pub const fn w_pad <T: Screen> (x: T::Unit, draw: impl Draw<T>) -> impl Draw<T> {
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
pub trait Y<N: Coord> {
|
|
fn y (&self) -> N;
|
|
fn h (&self) -> N { N::zero() }
|
|
fn h_min (&self) -> N { self.h() }
|
|
fn h_max (&self) -> N { self.h() }
|
|
fn iter_y (&self) -> impl Iterator<Item = N> where Self: HasOrigin {
|
|
self.y_north()..self.y_south()
|
|
}
|
|
fn y_north (&self) -> N where Self: HasOrigin {
|
|
let a = self.origin();
|
|
let h = self.h();
|
|
use Origin::*;
|
|
let d = match a { NW|N|NE => 0.into(), W|X|C|Y|E => h/2.into(), SW|S|SE => h };
|
|
self.y().minus(d)
|
|
}
|
|
fn y_south (&self) -> N where Self: HasOrigin {
|
|
let a = self.origin();
|
|
let h = self.h();
|
|
use Origin::*;
|
|
let d = match a { NW|N|NE => h, W|X|C|Y|E => h/2.into(), SW|S|SE => 0.into() };
|
|
self.y().plus(d)
|
|
}
|
|
fn y_center (&self) -> N where Self: HasOrigin {
|
|
todo!()
|
|
}
|
|
}
|
|
pub const fn y_push <T: Screen> (x: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
pub const fn y_pull <T: Screen> (x: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
pub const fn h_max <T: Screen> (h: Option<T::Unit>, a: impl Draw<T>) -> impl Draw<T> {
|
|
wh_max(None, h, a)
|
|
}
|
|
pub const fn h_min <T: Screen> (h: Option<T::Unit>, a: impl Draw<T>) -> impl Draw<T> {
|
|
wh_min(None, h, a)
|
|
}
|
|
pub const fn h_exact <T: Screen> (h: T::Unit, c: impl Draw<T>) -> impl Draw<T> {
|
|
wh_exact(None, Some(h), c)
|
|
}
|
|
/// Shrink drawing area symmetrically.
|
|
///
|
|
/// ```
|
|
/// let padded = tengri::W::pad(3, "Hello");
|
|
/// ```
|
|
pub const fn h_pad <T: Screen> (x: T::Unit, draw: impl Draw<T>) -> impl Draw<T> {
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
pub trait Space<N: Coord>: X<N> + Y<N> {
|
|
fn xywh (&self) -> XYWH<N> { XYWH(self.x(), self.y(), self.w(), self.h()) }
|
|
// FIXME: factor origin
|
|
fn lrtb (&self) -> [N;4] { [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] }
|
|
}
|
|
impl<N: Coord, T: X<N> + Y<N>> Space<N> for T {}
|
|
pub const fn xy_push <T: Screen> (x: T::Unit, y: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
pub const fn xy_pull <T: Screen> (x: T::Unit, y: T::Unit, a: impl Draw<T>) -> impl Draw<T> {
|
|
a
|
|
}
|
|
/// Shrink drawing area symmetrically.
|
|
///
|
|
/// ```
|
|
/// let padded = tengri::WH(3, 5).pad("Hello");
|
|
/// ```
|
|
pub const fn wh_pad <T: Screen> (w: T::Unit, h: T::Unit, draw: impl Draw<T>)
|
|
-> impl Draw<T>
|
|
{
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
/// Only draw content if area is above a certain size.
|
|
///
|
|
/// ```
|
|
/// let min = tengri::wh_min(3, 5, "Hello"); // 5x5
|
|
/// ```
|
|
pub const fn wh_min <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw: impl Draw<T>)
|
|
-> impl Draw<T>
|
|
{
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
/// Set the maximum width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let max = tengri::wh_max(Some(3), Some(5), "Hello");
|
|
/// ```
|
|
pub const fn wh_max <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw: impl Draw<T>)
|
|
-> impl Draw<T>
|
|
{
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
/// Set the maximum width and/or height of the content.
|
|
///
|
|
/// ```
|
|
/// let exact = tengri::wh_exact(Some(3), Some(5), "Hello");
|
|
/// ```
|
|
pub const fn wh_exact <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw: impl Draw<T>)
|
|
-> impl Draw<T>
|
|
{
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
/// Limit size of drawing area
|
|
/// ```
|
|
/// let clipped = tengri::wh_clip(Some(3), Some(5), "Hello");
|
|
/// ```
|
|
pub const fn wh_clip <T: Screen> (
|
|
w: Option<T::Unit>, h: Option<T::Unit>, draw: impl Draw<T>
|
|
) -> impl Draw<T> {
|
|
thunk(move|to: &mut T|draw.draw(todo!()))
|
|
}
|
|
|
|
pub const fn wh_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
|
|
pub const fn w_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
|
|
pub const fn h_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
|
|
|
|
/// Iterate over a collection of renderables:
|
|
///
|
|
/// ```
|
|
/// use tengri::draw::{Origin::*, Split::*};
|
|
/// let _ = Below.iter([
|
|
/// NW.align(W(15).max(W(10).min("Leftbar"))),
|
|
/// NE.align(W(12).max(W(10).min("Rightbar"))),
|
|
/// Center.align(W(40).max(H(20.max("Center"))))
|
|
/// ].iter(), |x|x);
|
|
/// ```
|
|
pub fn iter <
|
|
S: Screen,
|
|
D: Draw<S>,
|
|
V: Fn()->I,
|
|
I: Iterator<Item = D>,
|
|
F: Fn(&D)->dyn Draw<S>,
|
|
> (_items: V, _cb: F) -> impl Draw<S> {
|
|
thunk(move|_to: &mut S|{ todo!() })
|
|
}
|
|
|
|
pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
|
_iter: impl Fn()->I, _draw: impl Fn(D)->U,
|
|
) -> impl Draw<S> {
|
|
thunk(move|_to: &mut S|{ todo!() })
|
|
}
|
|
|
|
pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
|
_iter: impl Fn()->I, _draw: impl Fn(D)->U,
|
|
) -> impl Draw<S> {
|
|
thunk(move|_to: &mut S|{ todo!() })
|
|
}
|
|
|
|
pub fn iter_south <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
|
_iter: impl Fn()->I, _draw: impl Fn(D)->U,
|
|
) -> impl Draw<S> {
|
|
thunk(move|_to: &mut S|{ todo!() })
|
|
}
|
|
|
|
pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
|
|
_iter: impl Fn()->I, _draw: impl Fn(D)->U,
|
|
) -> impl Draw<S> {
|
|
thunk(move|_to: &mut S|{ todo!() })
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Size(AtomicUsize, AtomicUsize);
|
|
impl X<u16> for Size {
|
|
fn x (&self) -> u16 { 0 }
|
|
fn w (&self) -> u16 { self.0.load(Relaxed) as u16 }
|
|
}
|
|
impl Y<u16> for Size {
|
|
fn y (&self) -> u16 { 0 }
|
|
fn h (&self) -> u16 { self.1.load(Relaxed) as u16 }
|
|
}
|
|
impl Size {
|
|
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
|
|
thunk(move|to: &mut T|{
|
|
let area = of.draw(to)?;
|
|
self.0.store(area.w().into(), Relaxed);
|
|
self.1.store(area.h().into(), Relaxed);
|
|
Ok(area)
|
|
})
|
|
}
|
|
}
|