mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-09-18 13:26:42 +02:00
274 lines
9.3 KiB
Rust
274 lines
9.3 KiB
Rust
use crate::*;
|
|
|
|
impl<S: Screen, T: Draw<S>> Layout<S> for T {}
|
|
|
|
pub trait Layout<S: Screen>: Draw<S> + Sized {
|
|
fn origin (self, azimuth: impl Into<Option<Azimuth>>) -> impl Draw<S> {
|
|
Origin(azimuth.into(), self)
|
|
}
|
|
fn origin_c (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::C), self)
|
|
}
|
|
fn origin_x (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::X), self)
|
|
}
|
|
fn origin_y (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::Y), self)
|
|
}
|
|
|
|
fn origin_n (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::N), self)
|
|
}
|
|
fn origin_s (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::S), self)
|
|
}
|
|
fn origin_e (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::E), self)
|
|
}
|
|
fn origin_w (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::W), self)
|
|
}
|
|
|
|
fn origin_ne (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::NE), self)
|
|
}
|
|
fn origin_se (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::SE), self)
|
|
}
|
|
fn origin_nw (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::NW), self)
|
|
}
|
|
fn origin_sw (self) -> impl Draw<S> {
|
|
Origin(Some(Azimuth::SW), self)
|
|
}
|
|
}
|
|
|
|
/// Uses [AtomicUsize] to measure size during\
|
|
/// rendering (which is normally read-only).
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Sizer(
|
|
/// Width
|
|
pub Arc<AtomicUsize>,
|
|
/// Height
|
|
pub Arc<AtomicUsize>,
|
|
);
|
|
|
|
impl Xy<u16> for Sizer {
|
|
fn x (&self) -> u16 {
|
|
self.0.load(Relaxed) as u16
|
|
}
|
|
fn y (&self) -> u16 {
|
|
self.1.load(Relaxed) as u16
|
|
}
|
|
}
|
|
impl Wide<u16> for Sizer {
|
|
fn w (&self) -> u16 {
|
|
self.0.load(Relaxed) as u16
|
|
}
|
|
}
|
|
impl Tall<u16> for Sizer {
|
|
fn h (&self) -> u16 {
|
|
self.1.load(Relaxed) as u16
|
|
}
|
|
}
|
|
impl PartialEq for Sizer {
|
|
fn eq (&self, _: &Self) -> bool { todo!() }
|
|
}
|
|
|
|
impl Sizer {
|
|
pub const fn of <T: Screen> (&self, of: impl Draw<T>) -> impl Draw<T> {
|
|
draw(move|to: &mut T|{
|
|
let area = of.draw(to)?;
|
|
self.0.store(area.map(|a|a.w()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
|
self.1.store(area.map(|a|a.h()).unwrap_or(T::Unit::zero()).into(), Relaxed);
|
|
Ok(area)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Some layout operations exist in multiple variants that take a single argument.
|
|
/// Their handling in [eval_view] is uniform and goes like this:
|
|
#[macro_export] macro_rules! eval_enum ((
|
|
$name:literal, $output:ident, $state:ident, $value:expr, $Enum:ident {
|
|
$($v:literal => $V:ident),* $(,)?
|
|
}
|
|
) => {{
|
|
match $value {
|
|
$(Some($v) => $Enum::$V,)*
|
|
frag => return Err(format!("invalid variant: {}/{frag:?}", $name).into())
|
|
}
|
|
}});
|
|
|
|
/// Some layout operations exist in XY, X, and Y variants that take 3 or 2 arguments.
|
|
/// Their handling in [eval_view] is uniform and goes like this:
|
|
#[macro_export] macro_rules! eval_xy (
|
|
// Valueless variant:
|
|
// (fill/x ...)
|
|
// (fill/y ...)
|
|
// (fill/xy ...)
|
|
(
|
|
$name:expr => $expr:expr, $head:expr, $output:ident, $state:ident,
|
|
$variant:expr, $xy:ident, $x:ident, $y:ident, $arg:expr,
|
|
) => {{
|
|
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
|
let variant = $variant;
|
|
let thunk = draw(move|screen|$state.interpret(screen, &$arg));
|
|
match variant {
|
|
// X variant
|
|
Some("x") => thunk.$x().draw($output),
|
|
// Y variant
|
|
Some("y") => thunk.$y().draw($output),
|
|
// XY variant (can be omitted)
|
|
Some("xy") | None => thunk.$xy().draw($output),
|
|
// Other namespace members are invalid
|
|
frag => invalid_variant($name, frag, $expr, $head)
|
|
}
|
|
}};
|
|
|
|
// Variadic variant:
|
|
// (push/x n ...)
|
|
// (push/y n ...)
|
|
// (push/xy n m ...)
|
|
(
|
|
$name:expr => $expr:expr, $head:expr, $output:ident, $state:ident,
|
|
$variant:expr, $xy:ident, $x:ident, $y:ident, $arg0:expr, $arg1:expr, $arg2:expr,
|
|
) => {{
|
|
// frags.next(): 2nd slash-delimited fragment: /x, /y, /xy
|
|
let variant = $variant;
|
|
let thunk = draw(move|screen|$state.interpret(screen, &match variant {
|
|
Some("x") | Some("y") => $arg1,
|
|
Some("xy") | None => $arg2,
|
|
_ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name)
|
|
}));
|
|
match variant {
|
|
// X variant
|
|
Some("x") => thunk.$x($state.namespace($arg0?)?)
|
|
.draw($output),
|
|
// Y variant
|
|
Some("y") => thunk.$y($state.namespace($arg0?)?)
|
|
.draw($output),
|
|
// XY variant (can be omitted)
|
|
Some("xy") | None => thunk.$xy($state.namespace($arg0?)?, $state.namespace($arg1?)?)
|
|
.draw($output),
|
|
// Other namespace members are invalid
|
|
frag => invalid_variant($name, frag, $expr, $head)
|
|
}
|
|
}};
|
|
);
|
|
|
|
#[macro_export] macro_rules! fn_kw_layout {
|
|
($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => {
|
|
#[cfg(all(feature = "draw", feature = "eval"))]
|
|
pub fn $name <O: Screen, S> (
|
|
$state: &S, $output: &mut O, $expr: &str
|
|
) -> Perhaps<XYWH<O::Unit>> where
|
|
S: Interpret<O, Option<XYWH<O::Unit>>>
|
|
+ for<'b> Namespace<'b, bool>
|
|
+ for<'b> Namespace<'b, O::Unit>
|
|
+ for<'b> Namespace<'b, Option<O::Unit>>
|
|
$body
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! fn_kw_layout_tui {
|
|
($name:ident |$state:ident, $output: ident, $expr:ident| $body:block) => {
|
|
#[cfg(all(feature = "draw", feature = "eval", feature = "term"))]
|
|
pub fn $name <S> (
|
|
$state: &S, $output: &mut Tui, $expr: &str
|
|
) -> Perhaps<XYWH<u16>> where
|
|
S: Interpret<Tui, Option<XYWH<u16>>>
|
|
+ for<'b> Namespace<'b, bool>
|
|
+ for<'b> Namespace<'b, u16>
|
|
+ for<'b> Namespace<'b, Option<u16>>
|
|
+ for<'b> Namespace<'b, Color>
|
|
$body
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! def_layout_modifier {
|
|
(
|
|
$Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident),
|
|
$Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal
|
|
|$self:ident, $to:ident| $body:block
|
|
) => {
|
|
impl<S: Screen, T: Draw<S>> $Trait<S> for T {}
|
|
|
|
pub trait $Trait<S: Screen>: Draw<S> + Sized {
|
|
fn $fn_x <N: Into<Option<S::Unit>> + Copy> (self, x: N)
|
|
-> $Struct<S, Self, N> { $Struct::$X(self, x) }
|
|
fn $fn_y <N: Into<Option<S::Unit>> + Copy> (self, y: N)
|
|
-> $Struct<S, Self, N> { $Struct::$Y(self, y) }
|
|
fn $fn_xy <N: Into<Option<S::Unit>> + Copy> (self, x: N, y: N)
|
|
-> $Struct<S, Self, N> { $Struct::$XY(self, x, y) }
|
|
}
|
|
|
|
pub enum $Struct<S: Screen, I: Draw<S>, X: Into<Option<S::Unit>>> {
|
|
__(PhantomData<S>), $X(I, X), $Y(I, X), $XY(I, X, X),
|
|
}
|
|
|
|
impl <S: Screen, I: Draw<S>, X: Into<Option<S::Unit>> + Copy> Draw<S> for $Struct<S, I, X> {
|
|
fn draw (&$self, $to: &mut S) -> Perhaps<XYWH<S::Unit>> { $body }
|
|
}
|
|
|
|
fn_kw_layout!($kw_name |state, output, expr| {
|
|
let head = expr.head()?;
|
|
let mut frags = head.src()?.unwrap_or_default().split("/");
|
|
Ok(matches!(frags.next(), Some($name)).then(||{
|
|
let args = expr.tail();
|
|
let arg0 = args.head();
|
|
let tail0 = args.tail();
|
|
let arg1 = tail0.head();
|
|
let tail1 = tail0.tail();
|
|
let arg2 = tail1.head();
|
|
eval_xy!(
|
|
$name => expr, head, output, state, frags.next(),
|
|
$fn_xy, $fn_x, $fn_y,
|
|
arg0, arg1, arg2,
|
|
)
|
|
}).transpose()?.flatten())
|
|
});
|
|
}
|
|
}
|
|
|
|
#[macro_export] macro_rules! def_layout_modifier_flat {
|
|
(
|
|
$Trait:ident ($fn_x:ident $fn_y:ident $fn_xy:ident),
|
|
$Struct:ident { $X:ident $Y:ident $XY:ident } $kw_name:ident $name:literal
|
|
|$self:ident, $to:ident| $body:block
|
|
) => {
|
|
impl<S: Screen, T: Draw<S>> $Trait<S> for T {}
|
|
|
|
pub trait $Trait<S: Screen>: Draw<S> + Sized {
|
|
fn $fn_x (self) -> $Struct<S, Self> { $Struct::$X(self) }
|
|
fn $fn_y (self) -> $Struct<S, Self> { $Struct::$Y(self) }
|
|
fn $fn_xy (self) -> $Struct<S, Self> { $Struct::$XY(self) }
|
|
}
|
|
|
|
pub enum $Struct<S: Screen, I: Draw<S>> {
|
|
__(PhantomData<S>), $X(I), $Y(I), $XY(I),
|
|
}
|
|
|
|
impl <S: Screen, I: Draw<S>> Draw<S> for $Struct<S, I> {
|
|
fn draw (&$self, $to: &mut S) -> Perhaps<XYWH<S::Unit>> { $body }
|
|
}
|
|
|
|
fn_kw_layout!($kw_name |state, output, expr| {
|
|
let head = expr.head()?;
|
|
let mut frags = head.src()?.unwrap_or_default().split("/");
|
|
Ok(matches!(frags.next(), Some($name)).then(||{
|
|
let args = expr.tail();
|
|
let arg0 = args.head();
|
|
eval_xy!(
|
|
$name => expr, head, output, state, frags.next(),
|
|
$fn_xy, $fn_x, $fn_y, arg0,
|
|
)
|
|
}).transpose()?.flatten())
|
|
});
|
|
}
|
|
}
|
|
|
|
mod area; pub use self::area::*;
|
|
mod axis; pub use self::axis::*;
|
|
mod azimuth; pub use self::azimuth::*;
|
|
mod cond; pub use self::cond::*;
|
|
mod iter; pub use self::iter::*;
|