mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 03:36:42 +01:00
dsl, output, tui: add tests, examples, root dispatchers
This commit is contained in:
parent
8dfe20a58c
commit
ca862b9802
16 changed files with 637 additions and 377 deletions
|
|
@ -1,15 +1,15 @@
|
|||
use crate::*;
|
||||
|
||||
/// Show an item only when a condition is true.
|
||||
pub struct When<A>(pub bool, pub A);
|
||||
impl<A> When<A> {
|
||||
pub struct When<O, T>(bool, T, PhantomData<O>);
|
||||
impl<O, T> When<O, T> {
|
||||
/// Create a binary condition.
|
||||
pub const fn new (c: bool, a: A) -> Self { Self(c, a) }
|
||||
pub const fn new (c: bool, a: T) -> Self { Self(c, a, PhantomData) }
|
||||
}
|
||||
impl<E: Out, A: Layout<E>> Layout<E> for When<A> {
|
||||
fn layout (&self, to: E::Area) -> E::Area {
|
||||
let Self(cond, item) = self;
|
||||
let mut area = E::Area::zero();
|
||||
impl<O: Out, T: Layout<O>> Layout<O> for When<O, T> {
|
||||
fn layout (&self, to: O::Area) -> O::Area {
|
||||
let Self(cond, item, ..) = self;
|
||||
let mut area = O::Area::zero();
|
||||
if *cond {
|
||||
let item_area = item.layout(to);
|
||||
area[0] = item_area.x();
|
||||
|
|
@ -20,9 +20,9 @@ impl<E: Out, A: Layout<E>> Layout<E> for When<A> {
|
|||
area.into()
|
||||
}
|
||||
}
|
||||
impl<E: Out, A: Draw<E>> Draw<E> for When<A> {
|
||||
fn draw (&self, to: &mut E) {
|
||||
let Self(cond, item) = self;
|
||||
impl<O: Out, T: Draw<O>> Draw<O> for When<O, T> {
|
||||
fn draw (&self, to: &mut O) {
|
||||
let Self(cond, item, ..) = self;
|
||||
if *cond { item.draw(to) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,16 +80,16 @@ impl<'a, O, A, B, I, F, G> Layout<O> for Map<O, A, B, I, F, G> where
|
|||
let [mut max_x, mut max_y] = area.center();
|
||||
for item in get_iter() {
|
||||
let [x,y,w,h] = get_item(item, index).layout(area).xywh();
|
||||
min_x = min_x.min(x.into());
|
||||
min_y = min_y.min(y.into());
|
||||
max_x = max_x.max((x + w).into());
|
||||
max_y = max_y.max((y + h).into());
|
||||
min_x = min_x.min(x);
|
||||
min_y = min_y.min(y);
|
||||
max_x = max_x.max(x + w);
|
||||
max_y = max_y.max(y + h);
|
||||
index += 1;
|
||||
}
|
||||
let w = max_x - min_x;
|
||||
let h = max_y - min_y;
|
||||
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
||||
area.center_xy([w.into(), h.into()].into()).into()
|
||||
area.center_xy([w.into(), h.into()]).into()
|
||||
}
|
||||
}
|
||||
impl<'a, O, A, B, I, F, G> Draw<O> for Map<O, A, B, I, F, G> where
|
||||
|
|
|
|||
|
|
@ -3,15 +3,9 @@
|
|||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![feature(const_precise_live_drops)]
|
||||
#![feature(type_changing_struct_update)]
|
||||
#![feature(anonymous_lifetime_in_impl_trait)]
|
||||
//#![feature(non_lifetime_binders)]
|
||||
|
||||
mod content; pub use self::content::*;
|
||||
mod draw; pub use self::draw::*;
|
||||
mod group; pub use self::group::*;
|
||||
mod layout; pub use self::layout::*;
|
||||
mod space; pub use self::space::*;
|
||||
mod thunk; pub use self::thunk::*;
|
||||
mod widget; pub use self::widget::*;
|
||||
pub(crate) use self::Direction::*;
|
||||
pub(crate) use std::fmt::{Debug, Display};
|
||||
pub(crate) use std::marker::PhantomData;
|
||||
|
|
@ -19,38 +13,49 @@ pub(crate) use std::ops::{Add, Sub, Mul, Div};
|
|||
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}};
|
||||
pub(crate) use tengri_core::*;
|
||||
|
||||
/// Draw target.
|
||||
/// Drawing target.
|
||||
pub trait Out: Send + Sync + Sized {
|
||||
/// Unit of length
|
||||
type Unit: Coordinate;
|
||||
|
||||
/// Rectangle without offset
|
||||
type Size: Size<Self::Unit>;
|
||||
|
||||
/// Rectangle with offset
|
||||
type Area: Area<Self::Unit>;
|
||||
/// Current output area
|
||||
fn area (&self) -> Self::Area;
|
||||
/// Mutable pointer to area
|
||||
fn area_mut (&mut self) -> &mut Self::Area;
|
||||
/// Draw widget in area
|
||||
fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
|
||||
|
||||
fn place <'t, T: Draw<Self> + Layout<Self> + ?Sized> (&mut self, content: &'t T) {
|
||||
/// Render drawable in area specified by `T::layout(self.area())`
|
||||
#[inline] fn place <'t, T: Draw<Self> + Layout<Self> + ?Sized> (
|
||||
&mut self, content: &'t T
|
||||
) {
|
||||
self.place_at(content.layout(self.area()), content)
|
||||
}
|
||||
|
||||
/// Render drawable in area specified by `area`
|
||||
fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
|
||||
|
||||
/// Current output area
|
||||
fn area (&self) -> Self::Area;
|
||||
#[inline] fn x (&self) -> Self::Unit { self.area().x() }
|
||||
#[inline] fn y (&self) -> Self::Unit { self.area().y() }
|
||||
#[inline] fn w (&self) -> Self::Unit { self.area().w() }
|
||||
#[inline] fn h (&self) -> Self::Unit { self.area().h() }
|
||||
#[inline] fn wh (&self) -> Self::Size { self.area().wh().into() }
|
||||
|
||||
/// Mutable pointer to area.
|
||||
fn area_mut (&mut self) -> &mut Self::Area;
|
||||
}
|
||||
|
||||
#[cfg(test)] mod test;
|
||||
#[cfg(test)] mod output_test;
|
||||
#[cfg(test)] pub(crate) use proptest_derive::Arbitrary;
|
||||
|
||||
//impl<E: Out, C: Content<E> + Layout<E>> Draw<E> for C { // if only
|
||||
//fn draw (&self, to: &mut E) {
|
||||
//if let Some(content) = self.content() {
|
||||
//to.place_at(self.layout(to.area()), &content);
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
mod content; pub use self::content::*;
|
||||
mod draw; pub use self::draw::*;
|
||||
mod group; pub use self::group::*;
|
||||
mod layout; pub use self::layout::*;
|
||||
mod space; pub use self::space::*;
|
||||
mod thunk; pub use self::thunk::*;
|
||||
mod widget; pub use self::widget::*;
|
||||
|
||||
#[cfg(feature = "dsl")] mod view;
|
||||
#[cfg(feature = "dsl")] pub use self::view::*;
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ macro_rules! test_op_transform {
|
|||
(None, Some(y)) => Some($Op::y(y, content)),
|
||||
_ => None
|
||||
} {
|
||||
assert_eq!(Content::layout(&op, [x, y, w, h]),
|
||||
Draw::layout(&op, [x, y, w, h]));
|
||||
//assert_eq!(Content::layout(&op, [x, y, w, h]),
|
||||
//Draw::layout(&op, [x, y, w, h]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,10 +122,10 @@ proptest! {
|
|||
h in u16::MIN..u16::MAX,
|
||||
) {
|
||||
let bsp = Bsp(d, a, b);
|
||||
assert_eq!(
|
||||
Content::layout(&bsp, [x, y, w, h]),
|
||||
Draw::layout(&bsp, [x, y, w, h]),
|
||||
);
|
||||
//assert_eq!(
|
||||
//Content::layout(&bsp, [x, y, w, h]),
|
||||
//Draw::layout(&bsp, [x, y, w, h]),
|
||||
//);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +142,8 @@ proptest! {
|
|||
fn area_mut (&mut self) -> &mut [u16;4] {
|
||||
&mut self.0
|
||||
}
|
||||
fn place <T: Draw<Self> + ?Sized> (&mut self, _: [u16;4], _: &T) {
|
||||
fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], _: &T) {
|
||||
println!("place_at: {area:?}");
|
||||
()
|
||||
}
|
||||
}
|
||||
137
output/src/view.rs
Normal file
137
output/src/view.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
use crate::*;
|
||||
use ::tengri_dsl::{Dsl, DslExpr, DslWord, DslNs};
|
||||
|
||||
pub trait View<O, U> {
|
||||
fn view_expr <'a> (&'a self, output: &mut O, expr: &'a impl DslExpr) -> Usually<U> {
|
||||
Err(format!("View::view_expr: no exprs defined: {expr:?}").into())
|
||||
}
|
||||
fn view_word <'a> (&'a self, output: &mut O, word: &'a impl DslWord) -> Usually<U> {
|
||||
Err(format!("View::view_word: no words defined: {word:?}").into())
|
||||
}
|
||||
fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Dsl) -> Usually<U> {
|
||||
if let Ok(Some(expr)) = dsl.expr() {
|
||||
self.view_expr(output, &expr)
|
||||
} else if let Ok(Some(word)) = dsl.word() {
|
||||
self.view_word(output, &word)
|
||||
} else {
|
||||
panic!("{dsl:?}: invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_output_expression <'a, O: Out + 'a, S> (
|
||||
state: &S, output: &mut O, expr: &'a impl DslExpr
|
||||
) -> Usually<bool> where
|
||||
S: View<O, ()>
|
||||
+ for<'b>DslNs<'b, bool>
|
||||
+ for<'b>DslNs<'b, O::Unit>
|
||||
{
|
||||
// First element of expression is used for dispatch.
|
||||
// Dispatch is proto-namespaced using separator character
|
||||
let head = expr.head()?;
|
||||
let mut frags = head.src()?.unwrap_or_default().split("/");
|
||||
// The rest of the tokens in the expr are arguments.
|
||||
// Their meanings depend on the dispatched operation
|
||||
let args = expr.tail();
|
||||
let arg0 = args.head();
|
||||
let tail0 = args.tail();
|
||||
let arg1 = tail0.head();
|
||||
let tail1 = tail0.tail();
|
||||
let arg2 = tail1.head();
|
||||
// And we also have to do the above binding dance
|
||||
// so that the Perhaps<token>s remain in scope.
|
||||
match frags.next() {
|
||||
|
||||
Some("when") => output.place(&When::new(
|
||||
state.from(arg0?)?.unwrap(),
|
||||
Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap())
|
||||
)),
|
||||
|
||||
Some("either") => output.place(&Either::new(
|
||||
state.from(arg0?)?.unwrap(),
|
||||
Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap()),
|
||||
Thunk::new(move|output: &mut O|state.view(output, &arg2).unwrap())
|
||||
)),
|
||||
|
||||
Some("bsp") => output.place(&{
|
||||
let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap());
|
||||
let b = Thunk::new(move|output: &mut O|state.view(output, &arg1).unwrap());
|
||||
match frags.next() {
|
||||
Some("n") => Bsp::n(a, b),
|
||||
Some("s") => Bsp::s(a, b),
|
||||
Some("e") => Bsp::e(a, b),
|
||||
Some("w") => Bsp::w(a, b),
|
||||
Some("a") => Bsp::a(a, b),
|
||||
Some("b") => Bsp::b(a, b),
|
||||
frag => unimplemented!("bsp/{frag:?}")
|
||||
}
|
||||
}),
|
||||
|
||||
Some("align") => output.place(&{
|
||||
let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap());
|
||||
match frags.next() {
|
||||
Some("n") => Align::n(a),
|
||||
Some("s") => Align::s(a),
|
||||
Some("e") => Align::e(a),
|
||||
Some("w") => Align::w(a),
|
||||
Some("x") => Align::x(a),
|
||||
Some("y") => Align::y(a),
|
||||
Some("c") => Align::c(a),
|
||||
frag => unimplemented!("align/{frag:?}")
|
||||
}
|
||||
}),
|
||||
|
||||
Some("fill") => output.place(&{
|
||||
let a = Thunk::new(move|output: &mut O|state.view(output, &arg0).unwrap());
|
||||
match frags.next() {
|
||||
Some("x") => Fill::X(a),
|
||||
Some("y") => Fill::Y(a),
|
||||
Some("xy") => Fill::XY(a),
|
||||
frag => unimplemented!("fill/{frag:?}")
|
||||
}
|
||||
}),
|
||||
|
||||
Some("fixed") => output.place(&{
|
||||
let axis = frags.next();
|
||||
let arg = match axis { Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!() };
|
||||
let cb = Thunk::new(move|output: &mut O|state.view(output, &arg).unwrap());
|
||||
match axis {
|
||||
Some("x") => Fixed::X(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("y") => Fixed::Y(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("xy") => Fixed::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb),
|
||||
frag => unimplemented!("fixed/{frag:?} ({expr:?}) ({head:?}) ({:?})",
|
||||
head.src()?.unwrap_or_default().split("/").next())
|
||||
}
|
||||
}),
|
||||
|
||||
Some("min") => output.place(&{
|
||||
let c = match frags.next() {
|
||||
Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!()
|
||||
};
|
||||
let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap());
|
||||
match frags.next() {
|
||||
Some("x") => Min::X(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("y") => Min::Y(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("xy") => Min::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb),
|
||||
frag => unimplemented!("min/{frag:?}")
|
||||
}
|
||||
}),
|
||||
|
||||
Some("max") => output.place(&{
|
||||
let c = match frags.next() {
|
||||
Some("x") | Some("y") => arg1, Some("xy") => arg2, _ => panic!()
|
||||
};
|
||||
let cb = Thunk::new(move|output: &mut O|state.view(output, &c).unwrap());
|
||||
match frags.next() {
|
||||
Some("x") => Max::X(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("y") => Max::Y(state.from(arg0?)?.unwrap(), cb),
|
||||
Some("xy") => Max::XY(state.from(arg0?)?.unwrap(), state.from(arg1?)?.unwrap(), cb),
|
||||
frag => unimplemented!("max/{frag:?}")
|
||||
}
|
||||
}),
|
||||
|
||||
_ => return Ok(false)
|
||||
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue