mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-07-17 15:56:57 +02:00
fixes and refactors
- use macros for the evals - move some space stuff into submodules - partial update to doctests
This commit is contained in:
parent
b44dc02f33
commit
e074712459
9 changed files with 464 additions and 380 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use ::{
|
use ::{
|
||||||
std::{io::stdout, sync::{Arc, RwLock}},
|
std::{io::stdout, sync::{Arc, RwLock}},
|
||||||
tengri::{*, term::*, lang::*, keys::*, draw::*, space::*, dizzle::*},
|
tengri::{*, term::*, lang::*, keys::*, draw::*, space::*, lang::*},
|
||||||
ratatui::style::Color,
|
ratatui::style::Color,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
30
src/draw.rs
30
src/draw.rs
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{*, lang::*, color::*, space::*};
|
use crate::{*, space::*};
|
||||||
|
|
||||||
/// Drawable that supports dynamic dispatch.
|
/// Drawable that supports dynamic dispatch.
|
||||||
///
|
///
|
||||||
|
|
@ -13,12 +13,12 @@ use crate::{*, lang::*, color::*, space::*};
|
||||||
/// a [Draw]able.
|
/// a [Draw]able.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::draw::*;
|
/// use tengri::{*, draw::*, space::*};
|
||||||
/// struct TestScreen(bool);
|
/// struct TestScreen(bool);
|
||||||
/// impl Screen for TestScreen { type Unit = u16; }
|
/// impl Screen for TestScreen { type Unit = u16; }
|
||||||
/// struct TestWidget(bool);
|
/// struct TestWidget(bool);
|
||||||
/// impl Draw<TestScreen> for TestWidget {
|
/// impl Draw<TestScreen> for TestWidget {
|
||||||
/// fn draw (&self, screen: &mut T) -> Usually<XYWH<u16>> {
|
/// fn draw (&self, screen: &mut TestScreen) -> Usually<XYWH<u16>> {
|
||||||
/// screen.0 |= self.0;
|
/// screen.0 |= self.0;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -31,12 +31,12 @@ pub trait Draw<T: Screen> {
|
||||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
|
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
|
||||||
}
|
}
|
||||||
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
|
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
|
||||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
|
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
|
||||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,8 +66,9 @@ impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T,
|
||||||
/// Only render when condition is true.
|
/// Only render when condition is true.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn test () -> impl tengri::Draw<tengri::Tui> {
|
/// # use tengri::draw::*;
|
||||||
/// tengri::when(true, "Yes")
|
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
|
||||||
|
/// when(true, "Yes")
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw<T> {
|
pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
|
@ -77,8 +78,9 @@ pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw
|
||||||
/// Render one thing if a condition is true and another false.
|
/// Render one thing if a condition is true and another false.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn test () -> impl tengri::Draw<tengri::Tui> {
|
/// # use tengri::draw::*;
|
||||||
/// tengri::either(true, "Yes", "No")
|
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
|
||||||
|
/// either(true, "Yes", "No")
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
|
@ -88,13 +90,13 @@ pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<
|
||||||
/// Output target.
|
/// Output target.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::*;
|
/// use tengri::draw::*;
|
||||||
///
|
///
|
||||||
/// struct TestOut(impl TwoD<u16>);
|
/// struct TestOut<T: Screen<Unit = u16>>(T);
|
||||||
///
|
///
|
||||||
/// impl Screen for TestOut {
|
/// impl<T: Screen<Unit = u16>> Screen<T> for TestOut {
|
||||||
/// type Unit = u16;
|
/// type Unit = u16;
|
||||||
/// fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: impl TwoD<u16>, _: &T) {
|
/// fn place_at <D: Draw<Self> + ?Sized> (&mut self, area: T, _: D) {
|
||||||
/// println!("placed: {area:?}");
|
/// println!("placed: {area:?}");
|
||||||
/// ()
|
/// ()
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -114,7 +116,7 @@ pub trait Screen: Space<Self::Unit> + Send + Sync + Sized {
|
||||||
}
|
}
|
||||||
/// Render drawable in subarea specified by `area`
|
/// Render drawable in subarea specified by `area`
|
||||||
fn place_at <'t, T: Draw<Self> + ?Sized> (
|
fn place_at <'t, T: Draw<Self> + ?Sized> (
|
||||||
&mut self, area: XYWH<Self::Unit>, content: &'t T
|
&mut self, _area: XYWH<Self::Unit>, _content: &'t T
|
||||||
) {
|
) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
211
src/eval.rs
211
src/eval.rs
|
|
@ -1,10 +1,65 @@
|
||||||
use crate::{*, term::*, draw::*, ratatui::prelude::*};
|
use crate::{*, term::*, draw::*, space::*, lang::*, ratatui::prelude::*};
|
||||||
use dizzle::*;
|
|
||||||
|
/// 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_rules! eval_xy ((
|
||||||
|
$name:expr, $expr:expr, $head:expr, $output:ident, $state:ident,
|
||||||
|
$variant:expr, $arg0: ident, $arg1: ident, $arg2: ident,
|
||||||
|
$xy:ident, $x:ident, $y:ident
|
||||||
|
) => {{
|
||||||
|
let variant = $variant;
|
||||||
|
let cb = thunk(move|output: &mut O|$state.interpret(output, &match variant {
|
||||||
|
Some("x") | Some("y") => $arg1,
|
||||||
|
Some("xy") | None => $arg2,
|
||||||
|
_ => panic!("{}: unsupported axis {variant:?}; try /x, /y, /xy", $name)
|
||||||
|
}));
|
||||||
|
match variant {
|
||||||
|
// XY variant (can be omitted)
|
||||||
|
Some("xy") | None => xy_push(
|
||||||
|
$state.namespace($arg0?)?.unwrap(),
|
||||||
|
$state.namespace($arg1?)?.unwrap(),
|
||||||
|
cb
|
||||||
|
).draw($output),
|
||||||
|
// X variant
|
||||||
|
Some("x") => x_push(
|
||||||
|
$state.namespace($arg0?)?.unwrap(),
|
||||||
|
cb
|
||||||
|
).draw($output),
|
||||||
|
// Y variant
|
||||||
|
Some("y") => y_push(
|
||||||
|
$state.namespace($arg0?)?.unwrap(),
|
||||||
|
cb
|
||||||
|
).draw($output),
|
||||||
|
// Other namespace members are invalid
|
||||||
|
frag => {
|
||||||
|
let name = $name;
|
||||||
|
let expr = $expr;
|
||||||
|
let head = $head;
|
||||||
|
unimplemented!(
|
||||||
|
"{name}/{frag:?} ({expr:?}) ({head:?}) ({:?})",
|
||||||
|
head.src()?.unwrap_or_default().split("/").next()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
|
||||||
|
/// Some layout operations exist in multiple variants that take a single argument.
|
||||||
|
/// Their handling in [eval_view] is uniform and goes like this:
|
||||||
|
macro_rules! eval_enum ((
|
||||||
|
$name:literal, $output:ident, $state:ident, $value:expr, $arg0: ident, $Enum:ident {
|
||||||
|
$($v:literal => $V:ident),* $(,)?
|
||||||
|
}
|
||||||
|
) => {{
|
||||||
|
match $value {
|
||||||
|
$(Some($v) => $Enum::$V,)*
|
||||||
|
frag => unimplemented!("{}/{frag:?}", $name)
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
|
||||||
/// Interpret layout operation.
|
/// Interpret layout operation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use tengri::*;
|
/// # use tengri::{*, space::*};
|
||||||
/// struct Target { xywh: XYWH<u16> /*platform-specific*/}
|
/// struct Target { xywh: XYWH<u16> /*platform-specific*/}
|
||||||
/// impl tengri::Out for Target {
|
/// impl tengri::Out for Target {
|
||||||
/// type Unit = u16;
|
/// type Unit = u16;
|
||||||
|
|
@ -27,137 +82,87 @@ use dizzle::*;
|
||||||
/// // TODO test all
|
/// // TODO test all
|
||||||
/// # Ok(()) }
|
/// # Ok(()) }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "dsl")] pub fn eval_view <'a, O: Screen + 'a, S> (
|
pub fn eval_view <'a, O: Screen + 'a, S> (
|
||||||
state: &S, output: &mut O, expr: &'a impl Expression
|
state: &S, output: &mut O, expr: &'a impl Expression
|
||||||
) -> Usually<bool> where
|
) -> Usually<bool> where
|
||||||
S: Interpret<O, ()>
|
S: Interpret<O, XYWH<O::Unit>>
|
||||||
+ for<'b>Namespace<'b, bool>
|
+ for<'b> Namespace<'b, bool>
|
||||||
+ for<'b>Namespace<'b, O::Unit>
|
+ for<'b> Namespace<'b, O::Unit>
|
||||||
|
+ for<'b> Namespace<'b, Option<O::Unit>>
|
||||||
{
|
{
|
||||||
// First element of expression is used for dispatch.
|
// First element of expression is name of the operation.
|
||||||
// Dispatch is proto-namespaced using separator character
|
// These are quasi-namespaced using the separator character, `/`.
|
||||||
let head = expr.head()?;
|
let head = expr.head()?;
|
||||||
let mut frags = head.src()?.unwrap_or_default().split("/");
|
let mut frags = head.src()?.unwrap_or_default().split("/");
|
||||||
|
|
||||||
// The rest of the tokens in the expr are arguments.
|
// The rest of the tokens in the expr are arguments.
|
||||||
// Their meanings depend on the dispatched operation
|
// Their meanings depend on the dispatched operation
|
||||||
|
// Here we just reference them, so that they are in scope.
|
||||||
|
// Dereferencing them happens in the dispatch branch.
|
||||||
let args = expr.tail();
|
let args = expr.tail();
|
||||||
let arg0 = args.head();
|
let arg0 = args.head();
|
||||||
let tail0 = args.tail();
|
let tail0 = args.tail();
|
||||||
let arg1 = tail0.head();
|
let arg1 = tail0.head();
|
||||||
let tail1 = tail0.tail();
|
let tail1 = tail0.tail();
|
||||||
let arg2 = tail1.head();
|
let arg2 = tail1.head();
|
||||||
// And we also have to do the above binding dance
|
|
||||||
// so that the Perhaps<token>s remain in scope.
|
// First `frags.next()` calls returns the namespace.
|
||||||
match frags.next() {
|
match frags.next() {
|
||||||
|
|
||||||
Some("when") => output.place(&when(
|
Some("when") => when(
|
||||||
state.namespace(arg0?)?.unwrap(),
|
state.namespace(arg0?)?.unwrap(),
|
||||||
move|output: &mut O|state.interpret(output, &arg1)
|
thunk(move|output: &mut O|state.interpret(output, &arg1))
|
||||||
)),
|
).draw(output),
|
||||||
|
|
||||||
Some("either") => output.place(&either(
|
Some("either") => either(
|
||||||
state.namespace(arg0?)?.unwrap(),
|
state.namespace(arg0?)?.unwrap(),
|
||||||
move|output: &mut O|state.interpret(output, &arg1),
|
thunk(move|output: &mut O|state.interpret(output, &arg1)),
|
||||||
move|output: &mut O|state.interpret(output, &arg2),
|
thunk(move|output: &mut O|state.interpret(output, &arg2)),
|
||||||
)),
|
).draw(output),
|
||||||
|
|
||||||
Some("bsp") => output.place(&{
|
// Second `frags.next()` calls returns the namespace member.
|
||||||
let a = move|output: &mut O|state.interpret(output, &arg0);
|
Some("bsp") => eval_enum!(
|
||||||
let b = move|output: &mut O|state.interpret(output, &arg1);
|
"bsp", output, state, frags.next(), arg0, Split {
|
||||||
bsp(match frags.next() {
|
"n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below
|
||||||
Some("n") => Alignment::N,
|
|
||||||
Some("s") => Alignment::S,
|
|
||||||
Some("e") => Alignment::E,
|
|
||||||
Some("w") => Alignment::W,
|
|
||||||
Some("a") => Alignment::A,
|
|
||||||
Some("b") => Alignment::B,
|
|
||||||
frag => unimplemented!("bsp/{frag:?}")
|
|
||||||
}, a, b)
|
|
||||||
}),
|
|
||||||
|
|
||||||
Some("align") => output.place(&{
|
|
||||||
let a = move|output: &mut O|state.interpret(output, &arg0).unwrap();
|
|
||||||
align(match frags.next() {
|
|
||||||
Some("c") => Alignment::Center,
|
|
||||||
Some("n") => Alignment::N,
|
|
||||||
Some("s") => Alignment::S,
|
|
||||||
Some("e") => Alignment::E,
|
|
||||||
Some("w") => Alignment::W,
|
|
||||||
Some("x") => Alignment::X,
|
|
||||||
Some("y") => Alignment::Y,
|
|
||||||
frag => unimplemented!("align/{frag:?}")
|
|
||||||
}, a)
|
|
||||||
}),
|
|
||||||
|
|
||||||
Some("fill") => output.place(&{
|
|
||||||
let a = move|output: &mut O|state.interpret(output, &arg0).unwrap();
|
|
||||||
match frags.next() {
|
|
||||||
Some("xy") | None => fill_wh(a),
|
|
||||||
Some("x") => fill_w(a),
|
|
||||||
Some("y") => fill_h(a),
|
|
||||||
frag => unimplemented!("fill/{frag:?}")
|
|
||||||
}
|
}
|
||||||
}),
|
).half(
|
||||||
|
thunk(move|output: &mut O|state.interpret(output, &arg0)),
|
||||||
|
thunk(move|output: &mut O|state.interpret(output, &arg1)),
|
||||||
|
).draw(output),
|
||||||
|
|
||||||
Some("exact") => output.place(&{
|
Some("align") => eval_enum!(
|
||||||
let axis = frags.next();
|
"align", output, state, frags.next(), arg0, Origin {
|
||||||
let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("exact: unsupported axis {axis:?}") };
|
"c" => C, "n" => N, "s" => S, "e" => E, "w" => W, "x" => X, "y" => Y
|
||||||
let cb = move|output: &mut O|state.interpret(output, &arg).unwrap();
|
|
||||||
match axis {
|
|
||||||
Some("xy") | None => exact_wh(state.namespace(arg0?)?.unwrap(), state.namespace(arg1?)?.unwrap(), cb),
|
|
||||||
Some("x") => exact_w(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
Some("y") => exact_h(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
frag => unimplemented!("exact/{frag:?} ({expr:?}) ({head:?}) ({:?})",
|
|
||||||
head.src()?.unwrap_or_default().split("/").next())
|
|
||||||
}
|
}
|
||||||
}),
|
).align(
|
||||||
|
thunk(move|output: &mut O|state.interpret(output, &arg0))
|
||||||
|
).draw(output),
|
||||||
|
|
||||||
Some("min") => output.place(&{
|
Some("exact") => eval_xy!(
|
||||||
let axis = frags.next();
|
"exact", expr, head, output, state, frags.next(), arg0, arg1, arg2, wh_exact, w_exact, h_exact
|
||||||
let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("min: unsupported axis {axis:?}") };
|
),
|
||||||
let cb = move|output: &mut O|state.interpret(output, &arg).unwrap();
|
Some("min") => eval_xy!(
|
||||||
match axis {
|
"min", expr, head, output, state, frags.next(), arg0, arg1, arg2, wh_min, w_min, h_min
|
||||||
Some("xy") | None => min_wh(state.namespace(arg0?)?.unwrap(), state.namespace(arg1?)?.unwrap(), cb),
|
),
|
||||||
Some("x") => min_w(state.namespace(arg0?)?.unwrap(), cb),
|
Some("max") => eval_xy!(
|
||||||
Some("y") => min_h(state.namespace(arg0?)?.unwrap(), cb),
|
"max", expr, head, output, state, frags.next(), arg0, arg1, arg2, wh_max, w_max, h_max
|
||||||
frag => unimplemented!("min/{frag:?}")
|
),
|
||||||
}
|
Some("push") => eval_xy!(
|
||||||
}),
|
"push", expr, head, output, state, frags.next(), arg0, arg1, arg2, xy_push, x_push, y_push
|
||||||
|
),
|
||||||
Some("max") => output.place(&{
|
|
||||||
let axis = frags.next();
|
|
||||||
let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("max: unsupported axis {axis:?}") };
|
|
||||||
let cb = move|output: &mut O|state.interpret(output, &arg).unwrap();
|
|
||||||
match axis {
|
|
||||||
Some("xy") | None => max_wh(state.namespace(arg0?)?.unwrap(), state.namespace(arg1?)?.unwrap(), cb),
|
|
||||||
Some("x") => max_w(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
Some("y") => max_h(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
frag => unimplemented!("max/{frag:?}")
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
Some("push") => output.place(&{
|
|
||||||
let axis = frags.next();
|
|
||||||
let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") | None => arg2, _ => panic!("push: unsupported axis {axis:?}") };
|
|
||||||
let cb = move|output: &mut O|state.interpret(output, &arg);
|
|
||||||
match axis {
|
|
||||||
Some("xy") | None => push_xy(state.namespace(arg0?)?.unwrap(), state.namespace(arg1?)?.unwrap(), cb),
|
|
||||||
Some("x") => push_x(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
Some("y") => push_y(state.namespace(arg0?)?.unwrap(), cb),
|
|
||||||
frag => unimplemented!("push/{frag:?}")
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
_ => return Ok(false)
|
_ => return Ok(false)
|
||||||
|
|
||||||
};
|
}?;
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Interpret TUI-specific layout operation.
|
/// Interpret TUI-specific layout operation.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::{Namespace, Interpret, Tui, ratatui::prelude::Color};
|
/// use tengri::{lang::{Namespace, Interpret}, term::Tui, ratatui::prelude::Color};
|
||||||
///
|
///
|
||||||
/// struct State;
|
/// struct State;
|
||||||
/// impl<'b> Namespace<'b, bool> for State {}
|
/// impl<'b> Namespace<'b, bool> for State {}
|
||||||
|
|
@ -189,8 +194,6 @@ pub fn eval_view_tui <'a, S> (
|
||||||
let arg0 = args.head();
|
let arg0 = args.head();
|
||||||
let tail0 = args.tail();
|
let tail0 = args.tail();
|
||||||
let arg1 = tail0.head();
|
let arg1 = tail0.head();
|
||||||
let tail1 = tail0.tail();
|
|
||||||
let arg2 = tail1.head();
|
|
||||||
match frags.next() {
|
match frags.next() {
|
||||||
|
|
||||||
Some("text") => {
|
Some("text") => {
|
||||||
|
|
|
||||||
41
src/lib.rs
41
src/lib.rs
|
|
@ -1,18 +1,22 @@
|
||||||
#![feature(anonymous_lifetime_in_impl_trait)]
|
#![feature(anonymous_lifetime_in_impl_trait)]
|
||||||
#![feature(associated_type_defaults)]
|
//#![feature(associated_type_defaults)]
|
||||||
#![feature(const_default)]
|
//#![feature(const_default)]
|
||||||
#![feature(const_option_ops)]
|
//#![feature(const_option_ops)]
|
||||||
#![feature(const_precise_live_drops)]
|
#![feature(const_precise_live_drops)]
|
||||||
#![feature(const_trait_impl)]
|
#![feature(const_trait_impl)]
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
//#![feature(impl_trait_in_assoc_type)]
|
||||||
#![feature(step_trait)]
|
#![feature(step_trait)]
|
||||||
#![feature(trait_alias)]
|
//#![feature(trait_alias)]
|
||||||
#![feature(type_alias_impl_trait)]
|
//#![feature(type_alias_impl_trait)]
|
||||||
#![feature(type_changing_struct_update)]
|
//#![feature(type_changing_struct_update)]
|
||||||
pub extern crate atomic_float;
|
pub extern crate atomic_float;
|
||||||
pub extern crate palette;
|
pub extern crate palette;
|
||||||
pub extern crate better_panic;
|
pub extern crate better_panic;
|
||||||
pub extern crate unicode_width;
|
pub extern crate unicode_width;
|
||||||
|
#[cfg(feature = "sing")] pub extern crate jack;
|
||||||
|
#[cfg(feature = "term")] pub extern crate ratatui;
|
||||||
|
#[cfg(feature = "term")] pub extern crate crossterm;
|
||||||
|
#[cfg(feature = "lang")] pub extern crate dizzle as lang;
|
||||||
#[cfg(test)] #[macro_use] pub extern crate proptest;
|
#[cfg(test)] #[macro_use] pub extern crate proptest;
|
||||||
|
|
||||||
pub(crate) use ::{
|
pub(crate) use ::{
|
||||||
|
|
@ -23,27 +27,30 @@ pub(crate) use ::{
|
||||||
std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::*},
|
std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::*},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "lang")] pub extern crate dizzle as lang;
|
#[cfg(feature = "lang")]
|
||||||
#[cfg(feature = "lang")] pub use ::dizzle::{self, Usually, Perhaps, impl_default};
|
pub use ::dizzle::{Usually, Perhaps, impl_default};
|
||||||
|
/// DSL builtins.
|
||||||
#[cfg(feature = "lang")] pub mod eval;
|
#[cfg(feature = "lang")] pub mod eval;
|
||||||
|
/// Temporal dimension.
|
||||||
#[cfg(feature = "time")] pub mod time;
|
#[cfg(feature = "time")] pub mod time;
|
||||||
|
/// Circuit breaker for main loop.
|
||||||
#[cfg(feature = "play")] pub mod exit;
|
#[cfg(feature = "play")] pub mod exit;
|
||||||
|
/// Thread management.
|
||||||
#[cfg(feature = "play")] pub mod task;
|
#[cfg(feature = "play")] pub mod task;
|
||||||
|
/// Integration with JACK audio backend.
|
||||||
#[cfg(feature = "sing")] pub extern crate jack;
|
|
||||||
#[cfg(feature = "sing")] pub mod sing;
|
#[cfg(feature = "sing")] pub mod sing;
|
||||||
|
/// Generic drawing utilities.
|
||||||
#[cfg(feature = "draw")] pub mod draw;
|
#[cfg(feature = "draw")] pub mod draw;
|
||||||
|
/// Spatial dimension.
|
||||||
#[cfg(feature = "draw")] pub mod space;
|
#[cfg(feature = "draw")] pub mod space;
|
||||||
|
/// Color handling.
|
||||||
#[cfg(feature = "draw")] pub mod color;
|
#[cfg(feature = "draw")] pub mod color;
|
||||||
|
/// Text handling.
|
||||||
#[cfg(feature = "text")] pub mod text;
|
#[cfg(feature = "text")] pub mod text;
|
||||||
|
/// Terminal output.
|
||||||
#[cfg(feature = "term")] pub mod term;
|
#[cfg(feature = "term")] pub mod term;
|
||||||
|
/// Terminal keyboard input.
|
||||||
#[cfg(feature = "term")] pub mod keys;
|
#[cfg(feature = "term")] pub mod keys;
|
||||||
#[cfg(feature = "term")] pub extern crate ratatui;
|
|
||||||
#[cfg(feature = "term")] pub extern crate crossterm;
|
|
||||||
|
|
||||||
/// Define a trait an implement it for various mutation-enabled wrapper types. */
|
/// Define a trait an implement it for various mutation-enabled wrapper types. */
|
||||||
#[macro_export] macro_rules! flex_trait_mut (
|
#[macro_export] macro_rules! flex_trait_mut (
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ pub trait Audio {
|
||||||
/// Wraps [JackState], and through it [jack::Client] when connected.
|
/// Wraps [JackState], and through it [jack::Client] when connected.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// let jack = tengri::Jack::default();
|
/// let jack = tengri::sing::Jack::default();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug, Default)] pub struct Jack<'j> (
|
#[derive(Clone, Debug, Default)] pub struct Jack<'j> (
|
||||||
pub(crate) Arc<RwLock<JackState<'j>>>
|
pub(crate) Arc<RwLock<JackState<'j>>>
|
||||||
|
|
@ -36,7 +36,7 @@ pub trait Audio {
|
||||||
/// [jack::Client], which you can use to talk to the JACK API.
|
/// [jack::Client], which you can use to talk to the JACK API.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// let state = tengri::JackState::default();
|
/// let state = tengri::sing::JackState::default();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Default)] pub enum JackState<'j> {
|
#[derive(Debug, Default)] pub enum JackState<'j> {
|
||||||
/// Unused
|
/// Unused
|
||||||
|
|
@ -223,7 +223,7 @@ impl JackPerfModel for PerfModel {
|
||||||
/// Things that can provide a [jack::Client] reference.
|
/// Things that can provide a [jack::Client] reference.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use tengri::{Jack, HasJack};
|
/// use tengri::sing::{Jack, HasJack};
|
||||||
///
|
///
|
||||||
/// let jack: &Jack = Jacked::default().jack();
|
/// let jack: &Jack = Jacked::default().jack();
|
||||||
///
|
///
|
||||||
|
|
|
||||||
247
src/space.rs
247
src/space.rs
|
|
@ -1,144 +1,14 @@
|
||||||
use crate::{*, draw::*};
|
use crate::{*, draw::*};
|
||||||
#[cfg(test)] use proptest_derive::Arbitrary;
|
#[cfg(test)] use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
/// Point with size.
|
mod xywh;
|
||||||
///
|
pub use self::xywh::*;
|
||||||
/// ```
|
|
||||||
/// let xywh = tengri::XYWH(0u16, 0, 0, 0);
|
|
||||||
/// assert_eq!(tengri::XYWH(10u16, 10, 20, 20).center(), tengri::XY(20, 20));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// * [ ] TODO: origin field (determines at which corner/side is X0 Y0)
|
|
||||||
///
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
pub struct XYWH<N: Coord>(pub N, pub N, pub N, pub N);
|
|
||||||
impl From<&ratatui::prelude::Rect> for XYWH<u16> {
|
|
||||||
fn from (rect: &ratatui::prelude::Rect) -> Self {
|
|
||||||
Self(rect.x, rect.y, rect.width, rect.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<N: Coord> X<N> for XYWH<N> { fn x (&self) -> N { self.0 } fn w (&self) -> N { self.2 } }
|
|
||||||
impl<N: Coord> Y<N> for XYWH<N> { fn y (&self) -> N { self.0 } fn h (&self) -> N { self.2 } }
|
|
||||||
impl<N: Coord> XYWH<N> {
|
|
||||||
pub fn zero () -> Self {
|
|
||||||
Self(0.into(), 0.into(), 0.into(), 0.into())
|
|
||||||
}
|
|
||||||
pub fn center (&self) -> (N, N) {
|
|
||||||
let Self(x, y, w, h) = *self;
|
|
||||||
(x.plus(w/2.into()), y.plus(h/2.into()))
|
|
||||||
}
|
|
||||||
pub fn centered (&self) -> (N, N) {
|
|
||||||
let Self(x, y, w, h) = *self;
|
|
||||||
(x.minus(w/2.into()), y.minus(h/2.into()))
|
|
||||||
}
|
|
||||||
pub fn centered_x (&self, n: N) -> Self {
|
|
||||||
let Self(x, y, w, h) = *self;
|
|
||||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
|
||||||
let y_center = y.plus(h / 2.into());
|
|
||||||
XYWH(x_center, y_center, n, 1.into())
|
|
||||||
}
|
|
||||||
pub fn centered_y (&self, n: N) -> Self {
|
|
||||||
let Self(x, y, w, h) = *self;
|
|
||||||
let x_center = x.plus(w / 2.into());
|
|
||||||
let y_corner = (y.plus(h / 2.into())).minus(n / 2.into());
|
|
||||||
XYWH(x_center, y_corner, 1.into(), n)
|
|
||||||
}
|
|
||||||
pub fn centered_xy (&self, [n, m]: [N;2]) -> Self {
|
|
||||||
let Self(x, y, w, h) = *self;
|
|
||||||
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
|
||||||
let y_corner = (y.plus(h / 2.into())).minus(m / 2.into());
|
|
||||||
XYWH(x_center, y_corner, n, m)
|
|
||||||
}
|
|
||||||
pub fn split_half (&self, direction: &Split) -> (Self, Self) {
|
|
||||||
use Split::*;
|
|
||||||
let XYWH(x, y, w, h) = self.xywh();
|
|
||||||
match direction {
|
|
||||||
South => (XYWH(x, y, w, h - h / 2.into()), XYWH(x, y + h / 2.into(), w, h / 2.into())),
|
|
||||||
East => (XYWH(x, y, w - w / 2.into(), h), XYWH(x + w / 2.into(), y, w / 2.into(), h)),
|
|
||||||
North => (XYWH(x, y + h / 2.into(), w, h - h / 2.into()), XYWH(x, y, w, h / 2.into())),
|
|
||||||
West => (XYWH(x + w / 2.into(), y, w - w / 2.into(), h), XYWH(x, y, w / 2.into(), h)),
|
|
||||||
Above | Below => (XYWH(x, y, w, h), XYWH(x, y, w, h))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Something that has `[0, 0]` at a particular point.
|
mod origin;
|
||||||
pub trait HasOrigin { fn origin (&self) -> Origin;
|
pub use self::origin::*;
|
||||||
}
|
|
||||||
impl<T: AsRef<Origin>> HasOrigin for T { fn origin (&self) -> Origin { *self.as_ref() } }
|
|
||||||
pub struct Anchor<T>(Origin, T);
|
|
||||||
impl<T> AsRef<Origin> for Anchor<T> {
|
|
||||||
fn as_ref (&self) -> &Origin {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: Screen, U: Draw<T>> Draw<T> for Anchor<U> {
|
|
||||||
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn origin_c <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
mod split;
|
||||||
Anchor(Origin::C, a)
|
pub use self::split::*;
|
||||||
}
|
|
||||||
pub const fn origin_x <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::X, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_y <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::Y, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_nw <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::NW, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_n <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::N, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_ne <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::NE, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_w <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::W, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_e <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::E, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_sw <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::SW, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_s <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::S, a)
|
|
||||||
}
|
|
||||||
pub const fn origin_se <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Anchor(Origin::SE, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Where is [0, 0] located?
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use tengri::draw::Origin;
|
|
||||||
/// let _ = Origin::NW.align(())
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Debug, Copy, Clone, Default)] pub enum Origin {
|
|
||||||
#[default] C, X, Y, NW, N, NE, E, SE, S, SW, W
|
|
||||||
}
|
|
||||||
impl Origin {
|
|
||||||
pub fn align <T: Screen> (&self, a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
align(*self, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ```
|
|
||||||
/// use tengri::draw::{align, Origin::*};
|
|
||||||
/// let _ = align(NW, "test");
|
|
||||||
/// let _ = align(SE, "test");
|
|
||||||
/// ```
|
|
||||||
pub fn align <T: Screen> (origin: Origin, a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
thunk(move|to: &mut T| { todo!() })
|
|
||||||
}
|
|
||||||
pub fn align_n <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
align(Origin::N, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A numeric type that can be used as coordinate.
|
/// A numeric type that can be used as coordinate.
|
||||||
///
|
///
|
||||||
|
|
@ -174,88 +44,6 @@ pub trait Coord: Send + Sync + Copy
|
||||||
fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) }
|
fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cardinal direction.
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug, Default)] pub enum Split {
|
|
||||||
North, South, East, West, Above, #[default] Below
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn east <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::East.half(a, b)
|
|
||||||
}
|
|
||||||
pub const fn north <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::North.half(a, b)
|
|
||||||
}
|
|
||||||
pub const fn west <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::West.half(a, b)
|
|
||||||
}
|
|
||||||
pub const fn south <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::South.half(a, b)
|
|
||||||
}
|
|
||||||
pub const fn above <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::Above.half(a, b)
|
|
||||||
}
|
|
||||||
pub const fn below <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
Split::Below.half(a, b)
|
|
||||||
}
|
|
||||||
impl Split {
|
|
||||||
/// ```
|
|
||||||
/// use tengri::draw::Split::*;
|
|
||||||
/// let _ = Above.bsp((), ());
|
|
||||||
/// let _ = Below.bsp((), ());
|
|
||||||
/// let _ = North.bsp((), ());
|
|
||||||
/// let _ = South.bsp((), ());
|
|
||||||
/// let _ = East.bsp((), ());
|
|
||||||
/// let _ = West.bsp((), ());
|
|
||||||
/// ```
|
|
||||||
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
|
||||||
thunk(move|to: &mut T|{
|
|
||||||
let (area_a, area_b) = to.xywh().split_half(self);
|
|
||||||
let (origin_a, origin_b) = self.origins();
|
|
||||||
let a = origin_a.align(a);
|
|
||||||
let b = origin_b.align(b);
|
|
||||||
match self {
|
|
||||||
Self::Below => {
|
|
||||||
to.place_at(area_b, &b);
|
|
||||||
to.place_at(area_b, &a);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
to.place_at(area_a, &a);
|
|
||||||
to.place_at(area_a, &b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(to.xywh()) // FIXME: compute and return actually used area
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// Newly split areas begin at the center of the split
|
|
||||||
/// to maintain centeredness in the user's field of view.
|
|
||||||
///
|
|
||||||
/// Use [align] to override that and always start
|
|
||||||
/// at the top, bottom, etc.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// /*
|
|
||||||
///
|
|
||||||
/// Split east: Split south:
|
|
||||||
/// | | | | A |
|
|
||||||
/// | <-A|B-> | |---------|
|
|
||||||
/// | | | | B |
|
|
||||||
///
|
|
||||||
/// */
|
|
||||||
/// ```
|
|
||||||
const fn origins (&self) -> (Origin, Origin) {
|
|
||||||
use Origin::*;
|
|
||||||
match self {
|
|
||||||
Self::South => (S, N),
|
|
||||||
Self::East => (E, W),
|
|
||||||
Self::North => (N, S),
|
|
||||||
Self::West => (W, E),
|
|
||||||
Self::Above => (C, C),
|
|
||||||
Self::Below => (C, C),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Horizontal axis.
|
/// Horizontal axis.
|
||||||
pub trait X<N: Coord> {
|
pub trait X<N: Coord> {
|
||||||
fn x (&self) -> N;
|
fn x (&self) -> N;
|
||||||
|
|
@ -426,29 +214,6 @@ 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 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 }
|
pub const fn h_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }
|
||||||
|
|
||||||
#[macro_export] macro_rules! north {
|
|
||||||
($head:expr $(,)?) => { $head };
|
|
||||||
($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
#[macro_export] macro_rules! south {
|
|
||||||
($head:expr $(,)?) => { $head };
|
|
||||||
($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
#[macro_export] macro_rules! east {
|
|
||||||
($head:expr $(,)?) => { $head };
|
|
||||||
($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
#[macro_export] macro_rules! west {
|
|
||||||
($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
#[macro_export] macro_rules! above {
|
|
||||||
($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
#[macro_export] macro_rules! below {
|
|
||||||
($head:expr $(,)?) => { $head };
|
|
||||||
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over a collection of renderables:
|
/// Iterate over a collection of renderables:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
100
src/space/origin.rs
Normal file
100
src/space/origin.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub const fn origin_c <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::C, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_x <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::X, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_y <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::Y, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_nw <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::NW, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_n <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::N, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_ne <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::NE, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_w <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::W, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_e <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::E, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_sw <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::SW, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_s <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::S, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn origin_se <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Anchor(Origin::SE, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Something that has `[0, 0]` at a particular point.
|
||||||
|
pub trait HasOrigin {
|
||||||
|
fn origin (&self) -> Origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<Origin>> HasOrigin for T {
|
||||||
|
fn origin (&self) -> Origin {
|
||||||
|
*self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Anchor<T>(Origin, T);
|
||||||
|
|
||||||
|
impl<T> AsRef<Origin> for Anchor<T> {
|
||||||
|
fn as_ref (&self) -> &Origin {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Screen, U: Draw<T>> Draw<T> for Anchor<U> {
|
||||||
|
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where is [0, 0] located?
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use tengri::draw::Origin;
|
||||||
|
/// let _ = Origin::NW.align(())
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Debug, Copy, Clone, Default)] pub enum Origin {
|
||||||
|
#[default] C, X, Y, NW, N, NE, E, SE, S, SW, W
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Origin {
|
||||||
|
pub fn align <T: Screen> (&self, a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
align(*self, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// use tengri::draw::{align, Origin::*};
|
||||||
|
/// let _ = align(NW, "test");
|
||||||
|
/// let _ = align(SE, "test");
|
||||||
|
/// ```
|
||||||
|
pub fn align <T: Screen> (origin: Origin, a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
thunk(move|to: &mut T| { todo!() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_n <T: Screen> (a: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
align(Origin::N, a)
|
||||||
|
}
|
||||||
127
src/space/split.rs
Normal file
127
src/space/split.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub const fn east <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::East.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn north <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::North.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn west <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::West.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn south <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::South.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn above <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::Above.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn below <T: Screen> (a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
Split::Below.half(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split along an axis. Direction determines order.
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug, Default)] pub enum Split {
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
East,
|
||||||
|
West,
|
||||||
|
Above,
|
||||||
|
#[default] Below
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Split {
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// use tengri::draw::Split::*;
|
||||||
|
/// let _ = Above.bsp((), ());
|
||||||
|
/// let _ = Below.bsp((), ());
|
||||||
|
/// let _ = North.bsp((), ());
|
||||||
|
/// let _ = South.bsp((), ());
|
||||||
|
/// let _ = East.bsp((), ());
|
||||||
|
/// let _ = West.bsp((), ());
|
||||||
|
/// ```
|
||||||
|
pub const fn half <T: Screen> (&self, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
|
||||||
|
thunk(move|to: &mut T|{
|
||||||
|
let (area_a, area_b) = to.xywh().split_half(self);
|
||||||
|
let (origin_a, origin_b) = self.origins();
|
||||||
|
let a = origin_a.align(a);
|
||||||
|
let b = origin_b.align(b);
|
||||||
|
match self {
|
||||||
|
Self::Below => {
|
||||||
|
to.place_at(area_b, &b);
|
||||||
|
to.place_at(area_b, &a);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
to.place_at(area_a, &a);
|
||||||
|
to.place_at(area_a, &b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(to.xywh()) // FIXME: compute and return actually used area
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Newly split areas begin at the center of the split
|
||||||
|
/// to maintain centeredness in the user's field of view.
|
||||||
|
///
|
||||||
|
/// Use [align] to override that and always start
|
||||||
|
/// at the top, bottom, etc.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// /*
|
||||||
|
///
|
||||||
|
/// Split east: Split south:
|
||||||
|
/// | | | | A |
|
||||||
|
/// | <-A|B-> | |---------|
|
||||||
|
/// | | | | B |
|
||||||
|
///
|
||||||
|
/// */
|
||||||
|
/// ```
|
||||||
|
const fn origins (&self) -> (Origin, Origin) {
|
||||||
|
use Origin::*;
|
||||||
|
match self {
|
||||||
|
Self::South => (S, N),
|
||||||
|
Self::East => (E, W),
|
||||||
|
Self::North => (N, S),
|
||||||
|
Self::West => (W, E),
|
||||||
|
Self::Above => (C, C),
|
||||||
|
Self::Below => (C, C),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! north {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr, $($tail:expr),* $(,)?) => { north($head, north!($($tail,)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! south {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr, $($tail:expr),* $(,)?) => { south($head, south!($($tail,)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! east {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr, $($tail:expr),* $(,)?) => { east($head, east!($($tail,)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! west {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr $(, $tail:expr)* $(,)?) => { west($head, west!($($tail,)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! above {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr $(, $tail:expr)* $(,)?) => { above($head, above!($($tail,)*)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export] macro_rules! below {
|
||||||
|
($head:expr $(,)?) => { $head };
|
||||||
|
($head:expr, $($tail:expr),* $(,)?) => { below($head, below!($($tail,)*)) };
|
||||||
|
}
|
||||||
80
src/space/xywh.rs
Normal file
80
src/space/xywh.rs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Point with size.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let xywh = tengri::XYWH(0u16, 0, 0, 0);
|
||||||
|
/// assert_eq!(tengri::XYWH(10u16, 10, 20, 20).center(), tengri::XY(20, 20));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// * [ ] TODO: origin field (determines at which corner/side is X0 Y0)
|
||||||
|
///
|
||||||
|
#[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct XYWH<N: Coord>(pub N, pub N, pub N, pub N);
|
||||||
|
|
||||||
|
impl From<&ratatui::prelude::Rect> for XYWH<u16> {
|
||||||
|
fn from (rect: &ratatui::prelude::Rect) -> Self {
|
||||||
|
Self(rect.x, rect.y, rect.width, rect.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Coord> X<N> for XYWH<N> {
|
||||||
|
fn x (&self) -> N { self.0 }
|
||||||
|
fn w (&self) -> N { self.2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Coord> Y<N> for XYWH<N> {
|
||||||
|
fn y (&self) -> N { self.0 }
|
||||||
|
fn h (&self) -> N { self.2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Coord> XYWH<N> {
|
||||||
|
|
||||||
|
pub fn zero () -> Self {
|
||||||
|
Self(0.into(), 0.into(), 0.into(), 0.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn center (&self) -> (N, N) {
|
||||||
|
let Self(x, y, w, h) = *self;
|
||||||
|
(x.plus(w/2.into()), y.plus(h/2.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centered (&self) -> (N, N) {
|
||||||
|
let Self(x, y, w, h) = *self;
|
||||||
|
(x.minus(w/2.into()), y.minus(h/2.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centered_x (&self, n: N) -> Self {
|
||||||
|
let Self(x, y, w, h) = *self;
|
||||||
|
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||||
|
let y_center = y.plus(h / 2.into());
|
||||||
|
XYWH(x_center, y_center, n, 1.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centered_y (&self, n: N) -> Self {
|
||||||
|
let Self(x, y, w, h) = *self;
|
||||||
|
let x_center = x.plus(w / 2.into());
|
||||||
|
let y_corner = (y.plus(h / 2.into())).minus(n / 2.into());
|
||||||
|
XYWH(x_center, y_corner, 1.into(), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn centered_xy (&self, [n, m]: [N;2]) -> Self {
|
||||||
|
let Self(x, y, w, h) = *self;
|
||||||
|
let x_center = (x.plus(w / 2.into())).minus(n / 2.into());
|
||||||
|
let y_corner = (y.plus(h / 2.into())).minus(m / 2.into());
|
||||||
|
XYWH(x_center, y_corner, n, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_half (&self, direction: &Split) -> (Self, Self) {
|
||||||
|
use Split::*;
|
||||||
|
let XYWH(x, y, w, h) = self.xywh();
|
||||||
|
match direction {
|
||||||
|
South => (XYWH(x, y, w, h - h / 2.into()), XYWH(x, y + h / 2.into(), w, h / 2.into())),
|
||||||
|
East => (XYWH(x, y, w - w / 2.into(), h), XYWH(x + w / 2.into(), y, w / 2.into(), h)),
|
||||||
|
North => (XYWH(x, y + h / 2.into(), w, h - h / 2.into()), XYWH(x, y, w, h / 2.into())),
|
||||||
|
West => (XYWH(x + w / 2.into(), y, w - w / 2.into(), h), XYWH(x, y, w / 2.into(), h)),
|
||||||
|
Above | Below => (XYWH(x, y, w, h), XYWH(x, y, w, h))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue