fokken wip

This commit is contained in:
facile pop culture reference 2026-06-16 14:23:03 +03:00
parent be14173126
commit 2dc501c184
11 changed files with 129 additions and 136 deletions

2
dizzle

@ -1 +1 @@
Subproject commit e984fbb9fe8e588399744d50841d006454c16657
Subproject commit 2dbf9d8797a3642f69813be4ef3613836e061b94

View file

@ -1,5 +1,5 @@
mod draw;
pub use self::draw::*;
use crate::*;
pub use crate::space::*;
mod view;
pub use self::view::*;
@ -10,29 +10,57 @@ pub use self::thunk::*;
mod screen;
pub use self::screen::*;
use crate::*;
pub use crate::space::*;
/// Only render when condition is true.
/// Implement the [Draw] trait for a particular drawable and [Screen].
///
/// ```
/// # use tengri::draw::*;
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// when(true, "Yes")
/// # }
/// use tengri::{*, draw::*, term::*};
/// struct MyDrawable;
/// impl_draw!(|self: MyDrawable, to: Tui|{
/// todo!("your draw logic")
/// });
/// ```
pub const fn when <T: Screen> (condition: bool, draw: impl Draw<T>) -> impl Draw<T> {
thunk(move|to: &mut T|if condition { draw.draw(to) } else { Ok(Default::default()) })
}
#[macro_export] macro_rules! impl_draw ((|
$self:ident:$Self:ty, $to:ident:$To:ty
|$draw:block)=>{ impl Draw<$To> for $Self {
fn draw ($self, $to: &mut $To) -> Usually<XYWH<u16>> $draw
} });
/// Render one thing if a condition is true and another false.
/// Drawable that supports dynamic dispatch.
///
/// Drawables are composable, e.g. the [when] and [either] conditionals
/// or the layout constraints.
///
/// Drawables are consumable, i.e. the [Draw::draw] method receives an
/// owned `self` and does not return it, consuming the drawable.
///
/// To draw a thing multiple times, instead of explicitly constructing it
/// every time, implement the [View] trait instead, which will construct
/// a [Draw]able.
///
/// ```
/// # use tengri::draw::*;
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// either(true, "Yes", "No")
/// # }
/// use tengri::{*, draw::*, term::*};
/// struct MyWidget(bool);
/// impl Draw<Tui> for MyWidget {
/// fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
/// todo!("your draw logic")
/// }
/// }
/// ```
pub const fn either <T: Screen> (condition: bool, a: impl Draw<T>, b: impl Draw<T>) -> impl Draw<T> {
thunk(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) })
pub trait Draw<T: Screen> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
}
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
todo!()
}
}
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
todo!()
}
}
//impl<T: Screen, D: Draw<T>> Draw<T> for Arc<D> {
//fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
//todo!()
//}
//}

View file

@ -1,60 +0,0 @@
use super::*;
/// Implement the [Draw] trait for a particular drawable and [Screen].
///
/// ```
/// use tengri::{*, draw::*, term::*};
/// struct MyDrawable;
/// impl_draw!(|self: MyDrawable, to: Tui|{
/// todo!()
/// });
/// ```
#[macro_export] macro_rules! impl_draw ((|
$self:ident:$Self:ty, $to:ident:$To:ty
|$draw:block)=>{ impl Draw<$To> for $Self {
fn draw ($self, $to: &mut $To) -> Usually<XYWH<u16>> $draw
} });
/// Drawable that supports dynamic dispatch.
///
/// Drawables are composable, e.g. the [when] and [either] conditionals
/// or the layout constraints.
///
/// Drawables are consumable, i.e. the [Draw::draw] method receives an
/// owned `self` and does not return it, consuming the drawable.
///
/// To draw a thing multiple times, instead of explicitly constructing it
/// every time, implement the [View] trait instead, which will construct
/// a [Draw]able.
///
/// ```
/// use tengri::{*, draw::*};
/// struct MyScreen(bool);
/// impl Screen for MyScreen { type Unit = u16; }
/// impl X<u16> for MyScreen { fn x (&self) -> u16 { 0 } }
/// impl Y<u16> for MyScreen { fn y (&self) -> u16 { 0 } }
/// struct MyWidget(bool);
/// impl Draw<MyScreen> for MyWidget {
/// fn draw (self, screen: &mut MyScreen) -> Usually<XYWH<u16>> {
/// screen.0 |= self.0;
/// }
/// }
/// let mut screen = MyScreen(false);
/// MyWidget(false).draw(&mut screen);
/// MyWidget(true).draw(&mut screen);
/// MyWidget(false).draw(&mut screen);
/// ```
pub trait Draw<T: Screen> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>>;
}
impl<T: Screen, D: Draw<T>> Draw<T> for &D {
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
todo!()
}
}
impl<T: Screen, D: Draw<T>> Draw<T> for Option<D> {
fn draw (self, __: &mut T) -> Usually<XYWH<T::Unit>> {
todo!()
}
}

View file

@ -9,7 +9,7 @@ use super::*;
///
/// impl<T: Screen<Unit = u16>> Screen for TestOut {
/// type Unit = u16;
/// fn place_at <D: Draw<Self> + ?Sized> (&mut self, area: T, _: D) {
/// fn show <D: Draw<Self> + ?Sized> (&mut self, area: T, _: D) {
/// println!("placed: {area:?}");
/// ()
/// }
@ -21,14 +21,6 @@ use super::*;
/// ```
pub trait Screen: Space<Self::Unit> + Send + Sync + Sized {
type Unit: Coord;
/// Render drawable in area.
fn place <'t, T: Draw<Self> + ?Sized> (&mut self, content: &'t T) {
self.place_at(self.xywh(), content)
}
/// Render drawable in subarea specified by `area`
fn place_at <'t, T: Draw<Self> + ?Sized> (
&mut self, _area: XYWH<Self::Unit>, _content: &'t T
) {
unimplemented!("place_at")
}
fn show <'t, T: Draw<Self>> (&mut self, area: XYWH<Self::Unit>, content: T);
}

View file

@ -1,16 +1,51 @@
use super::*;
pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> (draw: F) -> Thunk<T, F> {
Thunk(draw, std::marker::PhantomData)
}
/// Because we can't implement [Draw] for `F: FnOnce...` without conflicts.
pub struct Thunk<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>>(
pub F,
std::marker::PhantomData<T>
);
impl<T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> Draw<T> for Thunk<T, F> {
fn draw (self, to: &mut T) -> Usually<XYWH<T::Unit>> {
(self.0)(to)
}
}
/// Basic [Draw]able closure.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # fn test () -> impl tengri::draw::Draw<tengri::term::Tui> {
/// thunk(|to: &mut Tui|Ok(to.1))
/// # }
/// ```
pub const fn thunk <T: Screen, F: FnOnce(&mut T)->Usually<XYWH<T::Unit>>> (
draw: F
) -> Thunk<T, F> {
Thunk(draw, std::marker::PhantomData)
}
/// Only render when condition is true.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # 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> {
thunk(move|to: &mut T|if condition { draw.draw(to) } else { Ok(Default::default()) })
}
/// Render one thing if a condition is true and another false.
///
/// ```
/// # use tengri::{draw::*, term::*};
/// # 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> {
thunk(move|to: &mut T|if condition { a.draw(to) } else { b.draw(to) })
}

View file

@ -59,23 +59,16 @@ macro_rules! eval_enum ((
/// Interpret layout operation.
///
/// ```
/// # use tengri::{lang::*, draw::*, eval::*};
/// struct Target { xywh: XYWH<u16> /*platform-specific*/}
/// impl tengri::Out for Target {
/// type Unit = u16;
/// fn area (&self) -> XYWH<u16> { self.xywh }
/// fn area_mut (&mut self) -> &mut XYWH<u16> { &mut self.xywh }
/// fn show <'t, T: Draw<Self> + ?Sized> (&mut self, area: XYWH<u16>, content: &'t T) {}
/// }
/// # use tengri::{lang::*, draw::*, eval::*, term::*};
///
/// struct State {/*app-specific*/}
/// impl<'b> Namespace<'b, bool> for State {}
/// impl<'b> Namespace<'b, u16> for State {}
/// impl Interpret<Target, ()> for State {}
/// impl Interpret<Tui, XYWH<u16>> for State {}
///
/// # fn main () -> tengri::Usually<()> {
/// let state = State {};
/// let mut target = Target { xywh: Default::default() };
/// let mut target = Tui::new(80, 25);
/// eval_view(&state, &mut target, &"")?;
/// eval_view(&state, &mut target, &"(when true (text hello))")?;
/// eval_view(&state, &mut target, &"(either true (text hello) (text world))")?;
@ -197,13 +190,15 @@ pub fn eval_view_tui <'a, S> (
match frags.next() {
Some("text") => {
if let Some(src) = args?.src()? { output.place(&src) }
if let Some(src) = args?.src()? {
output.show(output.xywh(), &src)
}
},
Some("fg") => {
let arg0 = arg0?.expect("fg: expected arg 0 (color)");
let arg0 = arg0?.expect("fg: expected arg 0 (color)");
let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color"));
output.place(&fg(color, thunk(move|output: &mut Tui|{
output.show(output.xywh(), &fg(color, thunk(move|output: &mut Tui|{
state.interpret(output, &arg1)?;
// FIXME?: don't max out the used area?
Ok(output.area().into())
@ -211,9 +206,9 @@ pub fn eval_view_tui <'a, S> (
},
Some("bg") => {
let arg0 = arg0?.expect("bg: expected arg 0 (color)");
let arg0 = arg0?.expect("bg: expected arg 0 (color)");
let color = Namespace::namespace(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color"));
output.place(&bg(color, thunk(move|output: &mut Tui|{
output.show(output.xywh(), &bg(color, thunk(move|output: &mut Tui|{
state.interpret(output, &arg1)?;
// FIXME?: don't max out the used area?
Ok(output.area().into())

View file

@ -93,7 +93,7 @@ pub use ::dizzle::{Usually, Perhaps, impl_default};
// FIXME: support attrs (docstrings)
$($Variant $({ $($arg: $Arg),* })?),*
}
impl ::tengri::dizzle::Act<$State> for $Command {
impl ::tengri::lang::Act<$State> for $Command {
fn act (&self, $state: &mut $State) -> Perhaps<Self> {
match self {
$(Self::$Variant $({ $($arg),* })? => $body,)*

View file

@ -125,13 +125,17 @@ pub trait Space<N: Coord>: X<N> + Y<N> {
// 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.
///
/// ```
@ -142,6 +146,7 @@ pub const fn wh_pad <T: Screen> (w: T::Unit, h: T::Unit, draw: impl Draw<T>)
{
thunk(move|to: &mut T|draw.draw(todo!()))
}
/// Only draw content if area is above a certain size.
///
/// ```
@ -161,7 +166,7 @@ pub const fn wh_min <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw: i
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!()))
thunk(move|_to: &mut T|draw.draw(todo!()))
}
/// Set the maximum width and/or height of the content.
@ -172,7 +177,7 @@ pub const fn wh_max <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw: i
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!()))
thunk(move|_to: &mut T|draw.draw(todo!()))
}
/// Limit size of drawing area
@ -182,7 +187,7 @@ pub const fn wh_exact <T: Screen> (w: Option<T::Unit>, h: Option<T::Unit>, draw:
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!()))
thunk(move|_to: &mut T|draw.draw(todo!()))
}
pub const fn wh_full <T: Screen> (a: impl Draw<T>) -> impl Draw<T> { a }

View file

@ -33,7 +33,7 @@ pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator<Item = D>, U: Draw<S>> (
}
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,
_iter: impl Fn()->I, _draw: impl Fn(D, usize)->U,
) -> impl Draw<S> {
thunk(move|_to: &mut S|{ todo!() })
}

View file

@ -54,12 +54,12 @@ impl Split {
let b = origin_b.align(b);
match self {
Self::Below => {
to.place_at(area_b, &b);
to.place_at(area_b, &a);
to.show(area_b, &b);
to.show(area_b, &a);
},
_ => {
to.place_at(area_a, &a);
to.place_at(area_a, &b);
to.show(area_a, &a);
to.show(area_a, &b);
}
}
Ok(to.xywh()) // FIXME: compute and return actually used area

View file

@ -119,25 +119,12 @@ pub fn tui_output <
prev.resize(&mut backend, width, height);
state.view().draw(&mut next).expect("draw failed"); // TODO draw error
prev.redraw(&mut backend, &mut next);
//tui_redraw(&mut backend, &mut prev, &mut next);
}
let timer = format!("{:>3.3}ms", perf.used.load(Relaxed));
prev.set_string(0, 0, &timer, Style::default());
})?)
}
pub fn tui_redraw <'b, W: Write> (
back: &mut CrosstermBackend<W>,
mut prev: &'b mut Buffer,
mut next: &'b mut Buffer
) {
let updates = prev.diff(&next);
back.draw(updates.into_iter()).expect("failed to render");
Backend::flush(back).expect("failed to flush output new");
std::mem::swap(&mut prev, &mut next);
next.reset();
}
/// Terminal output.
pub struct Tui(
/// Ratatui buffer; area is screen size
@ -147,10 +134,10 @@ pub struct Tui(
);
impl Tui {
fn new (width: u16, height: u16) -> Self {
pub fn new (width: u16, height: u16) -> Self {
Self(Buffer::empty(Rect { x: 0, y: 0, width, height }), XYWH(0, 0, width, height))
}
fn resize <W: Write> (&mut self, back: &mut CrosstermBackend<W>, width: u16, height: u16) {
pub fn resize <W: Write> (&mut self, back: &mut CrosstermBackend<W>, width: u16, height: u16) {
let size = Rect { x: 0, y: 0, width, height };
if self.0.area != size {
back.clear_region(ClearType::All).unwrap();
@ -158,7 +145,7 @@ impl Tui {
self.0.reset();
}
}
fn redraw <'b, W: Write> (&'b mut self, back: &mut CrosstermBackend<W>, mut next: &'b mut Self) {
pub fn redraw <'b, W: Write> (&'b mut self, back: &mut CrosstermBackend<W>, mut next: &'b mut Self) {
let updates = self.0.diff(&next.0);
back.draw(updates.into_iter()).expect("failed to render");
Backend::flush(back).expect("failed to flush output new");
@ -167,7 +154,18 @@ impl Tui {
}
}
impl Screen for Tui { type Unit = u16; }
impl Screen for Tui {
type Unit = u16;
/// Render drawable in subarea specified by `area`
fn show <'t, T: Draw<Self>> (
&mut self, area: XYWH<u16>, content: T
) {
let previous_area = self.1;
self.1 = area;
let _result_area = content.draw(self);
self.1 = previous_area;
}
}
impl Deref for Tui { type Target = Buffer; fn deref (&self) -> &Buffer { &self.0 } }