diff --git a/src/draw.rs b/src/draw.rs index 23c55b5..6675132 100644 --- a/src/draw.rs +++ b/src/draw.rs @@ -35,11 +35,18 @@ pub trait Screen: Xy + Wh + Send + Sync + Sized { /// todo!("your draw logic") /// }); /// ``` -#[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::Unit>> $draw -} }); +#[macro_export] macro_rules! impl_draw ( + ($(<$($T:ident: $Trait:path,)+>)?| + $self:ident:$Self:path, $to:ident:$To:ty + |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self { + fn draw ($self, $to: &mut $To) -> Perhaps::Unit>> $draw + } }; + ($(<$($T:ident: $Trait:path,)+>)?| + $self:ident:$Self:ty, $to:ident:$To:ty + |$draw:block)=>{ impl$(<$($T:$Trait),+>)? Draw<$To> for $Self { + fn draw ($self, $to: &mut $To) -> Perhaps::Unit>> $draw + } } +); /// Drawable that supports dynamic dispatch. /// @@ -57,38 +64,36 @@ pub trait Screen: Xy + Wh + Send + Sync + Sized { /// use tengri::{*, draw::*, term::*}; /// struct MyWidget(bool); /// impl Draw for MyWidget { -/// fn draw (self, to: &mut Tui) -> Usually> { +/// fn draw (self, to: &mut Tui) -> Perhaps> { /// todo!("your draw logic") /// } /// } /// ``` pub trait Draw { - fn draw (self, to: &mut S) -> Usually>; + fn draw (self, to: &mut S) -> Perhaps>; fn layout (&self, area: XYWH) -> Perhaps> { Ok(Some(area)) } } impl Draw for () { - fn draw (self, __: &mut S) -> Usually> { - Ok(Default::default()) + fn draw (self, _: &mut S) -> Perhaps> { + Ok(None) } } -impl> Draw for Option { - fn draw (self, to: &mut S) -> Usually> { - self.map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) - } -} +impl_draw!(,>|self: Option, to: S|{ + self.map(|it|it.draw(to)).transpose().map(Option::unwrap_or_default) +}); //impl> Draw for RwLock { - //fn draw (self, __: &mut S) -> Usually> { + //fn draw (self, __: &mut S) -> Perhaps> { //todo!() //} //} //impl> Draw for Arc { - //fn draw (self, __: &mut T) -> Usually> { + //fn draw (self, __: &mut T) -> Perhaps> { //todo!() //} //} @@ -101,19 +106,19 @@ pub trait View { } impl> Draw for &V { - fn draw (self, to: &mut T) -> Usually> { + fn draw (self, to: &mut T) -> Perhaps> { self.view().draw(to) } } /// Because we can't implement [Draw] for `F: FnOnce...` without conflicts. -pub struct ThunkUsually>>( +pub struct ThunkPerhaps>>( pub F, std::marker::PhantomData ); -implUsually>> Draw for Thunk { - fn draw (self, to: &mut T) -> Usually> { +implPerhaps>> Draw for Thunk { + fn draw (self, to: &mut T) -> Perhaps> { (self.0)(to) } } @@ -126,7 +131,7 @@ implUsually>> Draw for ThunkUsually>> ( +pub const fn thunk Perhaps>> ( draw: F ) -> Thunk { Thunk(draw, std::marker::PhantomData) diff --git a/src/eval.rs b/src/eval.rs index 2da55fd..63c579c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -114,22 +114,23 @@ pub fn eval_view <'a, O: Screen + 'a, S> ( ).draw(output), // Second `frags.next()` calls returns the namespace member. - Some("bsp") => eval_enum!( - "bsp", output, state, frags.next(), arg0, Split { + Some("bsp") => { + let direction = eval_enum!("bsp", output, state, frags.next(), arg0, Split { "n" => North, "s" => South, "e" => East, "w" => West, "a" => Above, "b" => Below - } - ).half( - thunk(move|output: &mut O|state.interpret(output, &arg0)), - thunk(move|output: &mut O|state.interpret(output, &arg1)), - ).draw(output), + }); + direction.half( + thunk(move|output: &mut O|state.interpret(output, &arg0)), + thunk(move|output: &mut O|state.interpret(output, &arg1)), + ).draw(output) + }, - Some("align") => eval_enum!( - "align", output, state, frags.next(), arg0, Origin { + Some("align") => { + let alignment = eval_enum!("align", output, state, frags.next(), arg0, Origin { "c" => C, "n" => N, "s" => S, "e" => E, "w" => W, "x" => X, "y" => Y - } - ).align( - thunk(move|output: &mut O|state.interpret(output, &arg0)) - ).draw(output), + }); + let content = thunk(move|output: &mut O|state.interpret(output, &arg0)); + align(alignment, content).draw(output) + }, Some("exact") => eval_xy!( "exact", expr, head, output, state, frags.next(), @@ -191,11 +192,11 @@ pub fn eval_view_tui <'a, S> ( let arg0 = args.head(); let tail0 = args.tail(); let arg1 = tail0.head(); - Ok(Some(match frags.next() { + match frags.next() { Some("text") => { if let Some(src) = args?.src()? { - output.show(src)? + output.show(src) } else { return Ok(None) } @@ -208,7 +209,7 @@ pub fn eval_view_tui <'a, S> ( state.interpret(output, &arg1)?; // FIXME?: don't max out the used area? Ok(output.area().into()) - })))? + }))) }, Some("bg") => { @@ -218,10 +219,10 @@ pub fn eval_view_tui <'a, S> ( state.interpret(output, &arg1)?; // FIXME?: don't max out the used area? Ok(output.area().into()) - })))? + }))) }, _ => return Ok(None) - })) + } } diff --git a/src/layout.rs b/src/layout.rs index 5fa4660..3f0173f 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -2,7 +2,6 @@ use crate::*; mod align; pub use self::align::*; mod area; pub use self::area::*; -mod origin; pub use self::origin::*; mod split; pub use self::split::*; pub enum Layout>, I: Draw> { @@ -21,67 +20,65 @@ pub enum Layout>, I: Draw> { ClipWH(X, X, I), } -impl< +impl_draw!(< T: Screen, X: Into>, - I: Draw -> Draw for Layout { - fn draw (self, to: &mut T) -> Usually> { - use Layout::*; - match self { - __(_) => unreachable!(), - MinW(x, it) => { - let x = x.into(); - it.draw(to) - }, - MinH(y, it) => { - let y = y.into(); - it.draw(to) - }, - MinWH(x, y, it) => { - let x = x.into(); - let y = y.into(); - it.draw(to) - }, - MaxW(x, it) => { - let x = x.into(); - it.draw(to) - }, - MaxH(y, it) => { - let y = y.into(); - it.draw(to) - }, - MaxWH(x, y, it) => { - let x = x.into(); - let y = y.into(); - it.draw(to) - }, - ExactW(x, it) => { - let x = x.into(); - it.draw(to) - }, - ExactH(y, it) => { - let y = y.into(); - it.draw(to) - }, - ExactWH(x, y, it) => { - let x = x.into(); - let y = y.into(); - it.draw(to) - }, - ClipW(x, it) => { - let x = x.into(); - it.draw(to) - }, - ClipH(y, it) => { - let y = y.into(); - it.draw(to) - }, - ClipWH(x, y, it) => { - let x = x.into(); - let y = y.into(); - it.draw(to) - }, - } + I: Draw, +>|self: Layout, to: T|{ + use Layout::*; + match self { + __(_) => unreachable!(), + MinW(x, it) => { + let x = x.into(); + it.draw(to) + }, + MinH(y, it) => { + let y = y.into(); + it.draw(to) + }, + MinWH(x, y, it) => { + let x = x.into(); + let y = y.into(); + it.draw(to) + }, + MaxW(x, it) => { + let x = x.into(); + it.draw(to) + }, + MaxH(y, it) => { + let y = y.into(); + it.draw(to) + }, + MaxWH(x, y, it) => { + let x = x.into(); + let y = y.into(); + it.draw(to) + }, + ExactW(x, it) => { + let x = x.into(); + it.draw(to) + }, + ExactH(y, it) => { + let y = y.into(); + it.draw(to) + }, + ExactWH(x, y, it) => { + let x = x.into(); + let y = y.into(); + it.draw(to) + }, + ClipW(x, it) => { + let x = x.into(); + it.draw(to) + }, + ClipH(y, it) => { + let y = y.into(); + it.draw(to) + }, + ClipWH(x, y, it) => { + let x = x.into(); + let y = y.into(); + it.draw(to) + }, } -} +}); diff --git a/src/layout/align.rs b/src/layout/align.rs index eac9e76..74af5c6 100644 --- a/src/layout/align.rs +++ b/src/layout/align.rs @@ -1,7 +1,5 @@ use crate::*; -pub struct Align(Option, T); - /// ``` /// use tengri::*; /// let _ = align(None, "unaligned"); @@ -11,3 +9,7 @@ pub struct Align(Option, T); pub fn align , U: Into>> (origin: U, it: T) -> Align { Align(origin.into(), it) } + +pub struct Align(Option, T); + +impl_draw!(,>|self: Align, to: S|{ todo!() }); diff --git a/src/layout/area.rs b/src/layout/area.rs index eb71215..58e8b6c 100644 --- a/src/layout/area.rs +++ b/src/layout/area.rs @@ -1,7 +1,5 @@ use crate::*; -pub struct Area>(Option>, T); - /// ``` /// use tengri::*; /// let _ = area(None, "unareaed"); @@ -13,3 +11,7 @@ pub fn area , U: Into>>> ( ) -> Area { Area(origin.into(), it) } + +pub struct Area>(Option>, T); + +impl_draw!(,>|self: Area, to: S|{ todo!() }); diff --git a/src/layout/origin.rs b/src/layout/origin.rs deleted file mode 100644 index 8b8b85e..0000000 --- a/src/layout/origin.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::*; - -/// 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 -} - -/// Something that has `[0, 0]` at a particular point. -pub trait HasOrigin { - fn origin (&self) -> Origin; -} - -impl> HasOrigin for T { - fn origin (&self) -> Origin { - *self.as_ref() - } -} diff --git a/src/space.rs b/src/space.rs index 8be15b4..985dd15 100644 --- a/src/space.rs +++ b/src/space.rs @@ -5,3 +5,4 @@ mod coord; pub use self::coord::*; mod iter; pub use self::iter::*; mod sizer; pub use self::sizer::*; mod xywh; pub use self::xywh::*; +mod lrtb; pub use self::lrtb::*; diff --git a/src/space/lrtb.rs b/src/space/lrtb.rs new file mode 100644 index 0000000..a905e89 --- /dev/null +++ b/src/space/lrtb.rs @@ -0,0 +1,47 @@ +use crate::*; +use Origin::*; + +impl> Lrtb for T {} + +pub trait Lrtb: Xywh { + fn lrtb (&self) -> [N;4] { + // FIXME: factor origin + [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] + } + fn iter_x (&self) -> impl Iterator where Self: HasOrigin { + self.x_west()..self.x_east() + } + fn x_west (&self) -> N where Self: HasOrigin { + 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 { + 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!() + } + fn iter_y (&self) -> impl Iterator 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(); + 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(); + 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!() + } +} diff --git a/src/space/xywh.rs b/src/space/xywh.rs index 518b314..fa2cc8c 100644 --- a/src/space/xywh.rs +++ b/src/space/xywh.rs @@ -2,6 +2,28 @@ use super::*; use Origin::*; use Split::*; +/// 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 +} + +/// Something that has `[0, 0]` at a particular point. +pub trait HasOrigin { + fn origin (&self) -> Origin; +} + +impl> HasOrigin for T { + fn origin (&self) -> Origin { + *self.as_ref() + } +} + pub trait Xy { fn x (&self) -> N; fn y (&self) -> N; @@ -17,59 +39,16 @@ pub trait Xywh: Xy + Wh { } } -pub trait Lrtb { - // FIXME: factor origin - fn lrtb (&self) -> [N;4] { - [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] - } -} - -pub trait Wide { +pub trait Wide: Xy { 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 where Self: HasOrigin { - self.x_west()..self.x_east() - } - fn x_west (&self) -> N where Self: HasOrigin { - 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 { - 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 trait Tall { 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 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(); - 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(); - 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!() - } } /// Point with size. diff --git a/src/term.rs b/src/term.rs index bf4df07..7571155 100644 --- a/src/term.rs +++ b/src/term.rs @@ -325,13 +325,8 @@ impl Coord for u16 { } } -impl Draw for u64 { - fn draw (self, _to: &mut Tui) -> Usually> { todo!() } -} - -impl Draw for f64 { - fn draw (self, _to: &mut Tui) -> Usually> { todo!() } -} +impl_draw!(|self: u64, _to: Tui|{ todo!() }); +impl_draw!(|self: f64, _to: Tui|{ todo!() }); mod phat { use super::*; diff --git a/src/term/border.rs b/src/term/border.rs index 9a30995..837445a 100644 --- a/src/term/border.rs +++ b/src/term/border.rs @@ -23,7 +23,7 @@ pub const fn border (on: bool, style: impl BorderStyle, draw: impl Draw) -> to.blit(&style.border_e(), x + w - 1, y, style.style()); } } - Ok(XYWH(x, y, w, h)) + Ok(Some(XYWH(x, y, w, h))) })); above(outline, content) } @@ -48,7 +48,7 @@ macro_rules! border { #[derive(Copy, Clone)] pub struct $T(pub bool, pub Style); //impl Layout for $T {} impl Draw for $T { - fn draw (self, to: &mut Tui) -> Usually> { + fn draw (self, to: &mut Tui) -> Perhaps> { when(self.enabled(), thunk(|to: &mut Tui|BorderStyle::draw(self, to))).draw(to) } } diff --git a/src/text.rs b/src/text.rs index c9c899b..e79ae55 100644 --- a/src/text.rs +++ b/src/text.rs @@ -3,63 +3,41 @@ pub(crate) use ::unicode_width::*; #[cfg(feature = "term")] mod impl_term { use super::*; - use crate::term::Tui; + use crate::*; use ratatui::prelude::Position; - impl Draw for &str { - fn draw (self, to: &mut Tui) -> Usually> { - let XYWH(x, y, w, ..) = to.1.centered_xy([width_chars_max(to.w(), self), 1]); - to.text(&self, x, y, w) - } - } + impl_draw!(|self: String, to: Tui|{self.as_str().draw(to)}); + impl_draw!(|self: std::sync::Arc, to: Tui|{self.as_ref().draw(to)}); + impl_draw!(|self: &std::sync::Arc, to: Tui|{self.as_ref().draw(to)}); + impl_draw!(|self: &str, to: Tui|{ + let XYWH(x, y, w, ..) = to.1.centered_xy([width_chars_max(to.w(), self), 1]); + to.text(&self, x, y, w) + }); - impl Draw for String { - fn draw (self, to: &mut Tui) -> Usually> { - self.as_str().draw(to) - } - } - - impl Draw for std::sync::Arc { - fn draw (self, to: &mut Tui) -> Usually> { - self.as_ref().draw(to) - } - } - - impl Draw for &std::sync::Arc { - fn draw (self, to: &mut Tui) -> Usually> { - self.as_ref().draw(to) - } - } - - impl> Draw for TrimString { - fn draw (self, to: &mut Tui) -> Usually> { self.as_ref().draw(to) } - } - - impl> Draw for TrimStringRef<'_, T> { - fn draw (self, to: &mut Tui) -> Usually> { - let XYWH(x, y, w, ..) = to.1; - let mut width: u16 = 1; - let mut chars = self.1.as_ref().chars(); - while let Some(c) = chars.next() { - if width > self.0 || width > w { - break - } - let pos = Position { x: x + width - 1, y }; - if let Some(cell) = to.0.cell_mut(pos) { - cell.set_char(c); - } - width += c.width().unwrap_or(0) as u16; + impl_draw!(,>|self: TrimString, to: Tui|{self.as_ref().draw(to)}); + impl_draw!(,>|self: TrimStringRef<'_, T>, to: Tui|{ + let XYWH(x, y, w, ..) = to.1; + let mut width: u16 = 1; + let mut chars = self.1.as_ref().chars(); + while let Some(c) = chars.next() { + if width > self.0 || width > w { + break } - let XYWH(x, y, w, ..) = XYWH(to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()); - to.text(&self.as_ref(), x, y, w) + let pos = Position { x: x + width - 1, y }; + if let Some(cell) = to.0.cell_mut(pos) { + cell.set_char(c); + } + width += c.width().unwrap_or(0) as u16; } - } + let XYWH(x, y, w, ..) = XYWH(to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()); + to.text(&self.as_ref(), x, y, w) + }); impl Tui { /// Write a line of text /// /// TODO: do a paragraph (handle newlines) - pub fn text (&mut self, text: &impl AsRef, x0: u16, y: u16, max_width: u16) -> Usually> { + pub fn text (&mut self, text: &impl AsRef, x0: u16, y: u16, max_width: u16) -> Perhaps> { let text = text.as_ref(); let mut string_width: u16 = 0; for character in text.chars() { @@ -75,7 +53,7 @@ pub(crate) use ::unicode_width::*; break } } - Ok(XYWH(x0, y, string_width, 1)) + Ok(Some(XYWH(x0, y, string_width, 1))) } } }