output: more big refactors
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-09-07 12:34:30 +03:00
parent 194f2f9874
commit 277f96d5cc
63 changed files with 1389 additions and 909 deletions

View file

@ -4,12 +4,17 @@ description = "UI metaframework, output layer."
version = { workspace = true }
edition = { workspace = true }
[lib]
path = "src/output.rs"
[features]
dsl = [ "tengri_dsl" ]
bumpalo = [ "dep:bumpalo" ]
dsl = [ "dep:tengri_dsl" ]
[dependencies]
tengri_core = { path = "../core" }
tengri_dsl = { optional = true, path = "../dsl" }
bumpalo = { optional = true, workspace = true }
[dev-dependencies]
tengri = { path = "../tengri", features = [ "dsl", "tui" ] }

View file

@ -4,10 +4,10 @@ it expresses the following notions:
* [**space:**](./src/space.rs) `Direction`, `Coordinate`, `Area`, `Size`, `Measure`
* [**output:**](./src/output.rs) `Output`, `Render`, `Content`
* the layout operators are generic over `Render` and/or `Content`
* the traits `Render` and `Content` are generic over `Output`
* implement `Output` to bring a layout to a new backend:
* [**output:**](./src/output.rs) `Out`, `Draw`, `Content`
* the layout operators are generic over `Draw` and/or `Content`
* the traits `Draw` and `Content` are generic over `Out`
* implement `Out` to bring a layout to a new backend:
[see `TuiOut` in `tengri_tui`](../tui/src/tui_engine/tui_output.rs)
* [**layout:**](./src/layout.rs)

40
output/src/content.rs Normal file
View file

@ -0,0 +1,40 @@
use crate::*;
/// Composable renderable with static dispatch.
pub trait Content<E: Out>: Sized {
/// Return opaque [Draw]able.
fn content (&self) -> impl Draw<E> + Layout<E> + '_ { () }
}
/// The platonic ideal unit of [Content]:
/// total emptiness at dead center (e=1vg^sqrt(-1))
impl<E: Out> Content<E> for () {}
impl<E: Out, T: Draw<E> + Layout<E>> Content<E> for fn()->T {
fn content (&self) -> impl Draw<E> + Layout<E> + '_ {
self()
}
}
/// Implement composable content for a struct.
#[macro_export] macro_rules! content {
// Implement for all [Out]s.
(|$self:ident:$Struct:ty| $content:expr) => {
impl<E: Out> Content<E> for $Struct {
fn content (&$self) -> impl Draw<E> + Layout<E> + '_ { Some($content) }
}
};
// Implement for specific [Out].
($Out:ty:|
$self:ident:
$Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?
|$content:expr) => {
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Out>
for $Struct $(<$($($L)? $($T)?),+>)? {
fn content (&$self) -> impl Draw<$Out> + Layout<$Out> + '_ { $content }
}
};
}

85
output/src/draw.rs Normal file
View file

@ -0,0 +1,85 @@
use crate::*;
/// Drawable with dynamic dispatch.
pub trait Draw<O: Out> {
/// Write data to display.
fn draw (&self, to: &mut O);
fn boxed <'a> (self) -> Box<dyn Draw<O> + 'a> where Self: Sized + 'a {
Box::new(self) as Box<dyn Draw<O> + 'a>
}
fn rc <'a> (self) -> Rc<dyn Draw<O> + 'a> where Self: Sized + 'a {
Rc::new(self) as Rc<dyn Draw<O> + 'a>
}
}
impl<O: Out> Draw<O> for () {
fn draw (&self, _: &mut O) {}
}
impl<O: Out, T: Draw<O>> Draw<O> for &T {
fn draw (&self, to: &mut O) {
(*self).draw(to)
}
}
impl<O: Out, T: Draw<O>> Draw<O> for &mut T {
fn draw (&self, to: &mut O) {
(**self).draw(to)
}
}
impl<O: Out, T: Draw<O>> Draw<O> for [T] {
fn draw (&self, to: &mut O) {
for draw in self.iter() {
draw.draw(to)
}
}
}
impl<O: Out> Draw<O> for fn(&mut O) {
fn draw (&self, to: &mut O) {
self(to)
}
}
impl<'x, O: Out> Draw<O> for &(dyn Draw<O> + 'x) {
fn draw (&self, to: &mut O) {
(*self).draw(to)
}
}
impl<'x, O: Out> Draw<O> for &mut (dyn Draw<O> + 'x) {
fn draw (&self, to: &mut O) {
(**self).draw(to)
}
}
/// Implement custom drawing for a struct.
#[macro_export] macro_rules! draw {
// Implement for all [Out] backends.
(|$self:ident:$Struct:ident $(<
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
>)?, $to:ident | $draw:expr) => {
impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Draw<O>
for $Struct $(<$($L),* $($T),*>)? {
fn draw (&$self, $to: &mut O) { $draw }
}
};
// Implement for a specific [Out] backend.
($O:ty:|
$self:ident:
$Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident
|$draw:expr) => {
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Draw<$O>
for $Struct $(<$($($L)? $($T)?),+>)? {
fn draw (&$self, $to: &mut $O) { $draw }
}
};
}
draw!(|self: Arc<T: Draw<O>>, to|(**self).draw(to));
draw!(|self: Box<T: Draw<O>>, to|(**self).draw(to));
draw!(|self: Option<T: Draw<O>>, to|if let Some(draw) = self { draw.draw(to) });

86
output/src/layout.rs Normal file
View file

@ -0,0 +1,86 @@
use crate::*;
pub trait Layout<O: Out> {
fn x (&self, area: O::Area) -> O::Unit {
area.x()
}
fn y (&self, area: O::Area) -> O::Unit {
area.y()
}
fn min_w (&self, _area: O::Area) -> O::Unit {
0.into()
}
fn max_w (&self, area: O::Area) -> O::Unit {
area.w()
}
fn min_h (&self, _area: O::Area) -> O::Unit {
0.into()
}
fn max_h (&self, area: O::Area) -> O::Unit {
area.h()
}
fn layout (&self, area: O::Area) -> O::Area {
O::Area::from([
self.x(area),
self.y(area),
area.w().max(self.min_w(area)).min(self.max_w(area)),
area.h().max(self.min_h(area)).min(self.max_h(area)),
])
}
}
macro_rules! layout {
// Implement for all [Out] backends.
(|$self:ident:$Struct:ident $(<
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
>)?, $to:ident|$($method:ident = |$area:ident|$body:expr;)*) => {
impl <$($($L),*)? O: Out, $($($T$(:$Trait)?),*)?> Layout<O>
for $Struct $(<$($L),* $($T),*>)? {
$(fn $method (&$self, $area: O::Area) -> O::Area {
$body
})*
}
};
// Implement for a specific [Out] backend.
($O:ty:|
$self:ident:
$Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?,
$to:ident
|$($method:ident = |$area:ident|$body:expr;)*) => {
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Layout<$O>
for $Struct $(<$($($L)? $($T)?),+>)? {
$(fn $method (&$self, $area: <$O as Out>::Area) -> <$O as Out>::Area {
$body
})*
}
};
}
impl<O: Out> Layout<O> for () {}
impl<O: Out, L: Layout<O>> Layout<O> for &L { /*FIXME*/ }
impl<O: Out, L: Layout<O>> Layout<O> for Arc<L> {
fn layout (&self, to: O::Area) -> O::Area {
(**self).layout(to)
}
}
mod layout_align; pub use self::layout_align::*;
mod layout_bsp; pub use self::layout_bsp::*;
mod layout_cond; pub use self::layout_cond::*;
mod layout_expand; pub use self::layout_expand::*;
mod layout_fill; pub use self::layout_fill::*;
mod layout_fixed; pub use self::layout_fixed::*;
mod layout_map; pub use self::layout_map::*;
mod layout_margin; pub use self::layout_margin::*;
mod layout_max; pub use self::layout_max::*;
mod layout_min; pub use self::layout_min::*;
mod layout_padding; pub use self::layout_padding::*;
mod layout_pull; pub use self::layout_pull::*;
mod layout_push; pub use self::layout_push::*;
mod layout_shrink; pub use self::layout_shrink::*;
mod layout_stack; pub use self::layout_stack::*;

View file

@ -1,9 +1,9 @@
//! ```
//! use ::tengri::{output::*, tui::*};
//! let area: [u16;4] = [10, 10, 20, 20];
//! fn test (area: [u16;4], item: &impl Render<TuiOut>, expected: [u16;4]) {
//! fn test (area: [u16;4], item: &impl Draw<TuiOut>, expected: [u16;4]) {
//! assert_eq!(Content::layout(item, area), expected);
//! assert_eq!(Render::layout(item, area), expected);
//! assert_eq!(Draw::layout(item, area), expected);
//! };
//!
//! let four = ||Fixed::xy(4, 4, "");
@ -48,17 +48,19 @@ impl<A> Align<A> {
#[inline] pub const fn se (a: A) -> Self { Self(Alignment::SE, a) }
}
impl<E: Output, A: Render<E>> Render<E> for Align<A> {
impl<E: Out, A: Draw<E>> Draw<E> for Align<A> {
fn draw (&self, to: &mut E) {
self.1.draw(to)
}
}
impl<E: Out, A: Layout<E>> Layout<E> for Align<A> {
fn layout (&self, on: E::Area) -> E::Area {
self.0.align(on, &self.1)
}
fn render (&self, to: &mut E) {
to.place(self.layout(to.area()), &self.1)
}
}
impl Alignment {
fn align <E: Output> (&self, on: E::Area, content: impl Render<E>) -> E::Area {
fn align <E: Out> (&self, on: E::Area, content: &impl Layout<E>) -> E::Area {
use Alignment::*;
let it = content.layout(on).xywh();
let cx = on.x()+(on.w().minus(it.w())/2.into());

View file

@ -0,0 +1,95 @@
use crate::*;
use Direction::*;
/// A split or layer.
pub struct Bsp<A, B>(
pub(crate) Direction,
pub(crate) A,
pub(crate) B,
);
impl<A, B> Bsp<A, B> {
#[inline] pub const fn n (a: A, b: B) -> Self { Self(North, a, b) }
#[inline] pub const fn s (a: A, b: B) -> Self { Self(South, a, b) }
#[inline] pub const fn e (a: A, b: B) -> Self { Self(East, a, b) }
#[inline] pub const fn w (a: A, b: B) -> Self { Self(West, a, b) }
#[inline] pub const fn a (a: A, b: B) -> Self { Self(Above, a, b) }
#[inline] pub const fn b (a: A, b: B) -> Self { Self(Below, a, b) }
}
impl<E: Out, A: Layout<E>, B: Layout<E>> Layout<E> for Bsp<A, B> {
fn layout (&self, area: E::Area) -> E::Area {
bsp_areas(area, self.0, &self.1, &self.2)[2]
}
}
impl<E: Out, A: Draw<E> + Layout<E>, B: Draw<E> + Layout<E>> Draw<E> for Bsp<A, B> {
fn draw (&self, to: &mut E) {
let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2);
if self.0 == Below {
to.place_at(a, &self.1);
to.place_at(b, &self.2);
} else {
to.place_at(b, &self.2);
to.place_at(a, &self.1);
}
}
}
fn bsp_areas <E: Out> (
area: E::Area, direction: Direction, a: &impl Layout<E>, b: &impl Layout<E>,
) -> [E::Area;3] {
let [x, y, w, h] = area.xywh();
let [aw, ah] = a.layout(area).wh();
let [bw, bh] = b.layout(match direction {
Above | Below => area,
South => [x, y + ah, w, h.minus(ah)].into(),
North => [x, y, w, h.minus(ah)].into(),
East => [x + aw, y, w.minus(aw), h].into(),
West => [x, y, w.minus(aw), h].into(),
}).wh();
match direction {
Above | Below => {
let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]);
let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
South => {
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah];
let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
North => {
let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]);
let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah];
let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
East => {
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
West => {
let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]);
let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
}
}
/// Stack things on top of each other,
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
/// Stack southward.
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
/// Stack northward.
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
/// Stack eastward.
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});

View file

@ -6,7 +6,7 @@ impl<A> When<A> {
/// Create a binary condition.
pub const fn new (c: bool, a: A) -> Self { Self(c, a) }
}
impl<E: Output, A: Render<E>> Render<E> for When<A> {
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();
@ -19,9 +19,11 @@ impl<E: Output, A: Render<E>> Render<E> for When<A> {
}
area.into()
}
fn render (&self, to: &mut E) {
}
impl<E: Out, A: Draw<E>> Draw<E> for When<A> {
fn draw (&self, to: &mut E) {
let Self(cond, item) = self;
if *cond { item.render(to) }
if *cond { item.draw(to) }
}
}
@ -31,14 +33,16 @@ impl<A, B> Either<A, B> {
/// Create a ternary view condition.
pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) }
}
impl<E: Output, A: Render<E>, B: Render<E>> Render<E> for Either<A, B> {
impl<E: Out, A: Layout<E>, B: Layout<E>> Layout<E> for Either<A, B> {
fn layout (&self, to: E::Area) -> E::Area {
let Self(cond, a, b) = self;
if *cond { a.layout(to) } else { b.layout(to) }
}
fn render (&self, to: &mut E) {
}
impl<E: Out, A: Draw<E>, B: Draw<E>> Draw<E> for Either<A, B> {
fn draw (&self, to: &mut E) {
let Self(cond, a, b) = self;
if *cond { a.render(to) } else { b.render(to) }
if *cond { a.draw(to) } else { b.draw(to) }
}
}

View file

@ -0,0 +1,39 @@
use crate::*;
pub enum Expand<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Expand<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) }
#[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) }
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) }
}
impl<O: Out, T: Draw<O> + Layout<O>> Content<O> for Expand<O::Unit, T> {
fn content (&self) -> impl Draw<O> + Layout<O> + '_ {
use Expand::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<O: Out, T: Draw<O> + Layout<O>> Draw<O> for Expand<O::Unit, T> {
fn draw (&self, to: &mut O) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Expand<U, T> {
#[inline] pub fn dx (&self) -> U {
use Expand::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Expand::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<O: Out, T: Layout<O>> Layout<O> for Expand<O::Unit, T> {
fn layout (&self, to: O::Area) -> O::Area {
[to.x(), to.y(), to.w().plus(self.dx()), to.h().plus(self.dy())].into()
}
}

View file

@ -0,0 +1,52 @@
use crate::*;
pub enum Fill<A> {
X(A),
Y(A),
XY(A)
}
impl<T> Fill<T> {
#[inline] pub const fn x (item: T) -> Self {
Self::X(item)
}
#[inline] pub const fn y (item: T) -> Self {
Self::Y(item)
}
#[inline] pub const fn xy (item: T) -> Self {
Self::XY(item)
}
#[inline] pub const fn has_x (&self) -> bool {
matches!(self, Self::X(_) | Self::XY(_))
}
#[inline] pub const fn has_y (&self) -> bool {
matches!(self, Self::Y(_) | Self::XY(_))
}
#[inline] pub const fn content (&self) -> &T {
use Fill::*;
match self { X(item) | Y(item) | XY(item) => item }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Layout<E> for Fill<T> {
fn min_w (&self, area: E::Area) -> E::Unit {
if self.has_x() {
area.w()
} else {
self.content().min_w(area)
}
}
fn min_h (&self, area: E::Area) -> E::Unit {
if self.has_y() {
area.h()
} else {
self.content().min_h(area)
}
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Fill<T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}

View file

@ -0,0 +1,53 @@
use crate::*;
pub enum Fixed<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Fixed<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Fixed::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<U: Coordinate, T> Fixed<U, T> {
#[inline] pub fn dx (&self) -> U {
use Fixed::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Fixed::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Fixed<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Fixed<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let [x, y, w, h] = area.xywh();
let [x, y, w, h] = self.content().layout(match self {
Self::X(fw, _) => [x, y, *fw, h],
Self::Y(fh, _) => [x, y, w, *fh],
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
}.into()).xywh();
let fixed_area = match self {
Self::X(fw, _) => [x, y, *fw, h],
Self::Y(fh, _) => [x, y, w, *fh],
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
};
fixed_area.into()
}
}

View file

View file

@ -1,6 +1,6 @@
use crate::*;
/// Renders items from an iterator.
/// Draws items from an iterator.
pub struct Map<E, A, B, I, F, G>
where
I: Iterator<Item = A> + Send + Sync,
@ -30,8 +30,8 @@ macro_rules! impl_map_direction (($name:ident, $axis:ident, $align:ident)=>{
impl<'a, E, A, B, I, F> Map<
E, A, Push<E::Unit, Align<Fixed<E::Unit, Fill<B>>>>, I, F, fn(A, usize)->B
> where
E: Output,
B: Render<E>,
E: Out,
B: Draw<E>,
I: Iterator<Item = A> + Send + Sync + 'a,
F: Fn() -> I + Send + Sync + 'a
{
@ -66,9 +66,9 @@ impl_map_direction!(south, y, n);
impl_map_direction!(west, x, e);
impl_map_direction!(north, y, s);
impl<'a, E, A, B, I, F, G> Render<E> for Map<E, A, B, I, F, G> where
E: Output,
B: Render<E>,
impl<'a, E, A, B, I, F, G> Layout<E> for Map<E, A, B, I, F, G> where
E: Out,
B: Draw<E> + Layout<E>,
I: Iterator<Item = A> + Send + Sync + 'a,
F: Fn() -> I + Send + Sync + 'a,
G: Fn(A, usize)->B + Send + Sync
@ -91,40 +91,48 @@ impl<'a, E, A, B, I, F, G> Render<E> for Map<E, A, B, I, F, G> where
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
area.center_xy([w.into(), h.into()].into()).into()
}
fn render (&self, to: &mut E) {
}
impl<'a, E, A, B, I, F, G> Draw<E> for Map<E, A, B, I, F, G> where
E: Out,
B: Draw<E> + Layout<E>,
I: Iterator<Item = A> + Send + Sync + 'a,
F: Fn() -> I + Send + Sync + 'a,
G: Fn(A, usize)->B + Send + Sync
{
fn draw (&self, to: &mut E) {
let Self { get_iter, get_item, .. } = self;
let mut index = 0;
let area = Render::layout(self, to.area());
let area = self.layout(to.area());
for item in get_iter() {
let item = get_item(item, index);
//to.place(area.into(), &item);
to.place(item.layout(area), &item);
//to.place_at(area.into(), &item);
to.place_at(item.layout(area), &item);
index += 1;
}
}
}
#[inline] pub fn map_south<O: Output>(
#[inline] pub fn map_south<O: Out>(
item_offset: O::Unit,
item_height: O::Unit,
item: impl Render<O>
) -> impl Render<O> {
item: impl Draw<O> + Layout<O>
) -> impl Draw<O> + Layout<O> {
Push::y(item_offset, Fixed::y(item_height, Fill::x(item)))
}
#[inline] pub fn map_south_west<O: Output>(
#[inline] pub fn map_south_west<O: Out>(
item_offset: O::Unit,
item_height: O::Unit,
item: impl Render<O>
) -> impl Render<O> {
item: impl Draw<O> + Layout<O>
) -> impl Draw<O> + Layout<O> {
Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item))))
}
#[inline] pub fn map_east<O: Output>(
#[inline] pub fn map_east<O: Out>(
item_offset: O::Unit,
item_width: O::Unit,
item: impl Render<O>
) -> impl Render<O> {
item: impl Draw<O> + Layout<O>
) -> impl Draw<O> + Layout<O> {
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
}

View file

@ -0,0 +1,50 @@
use crate::*;
pub enum Margin<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Margin<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Margin::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Margin<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Margin<U, T> {
#[inline] pub fn dx (&self) -> U {
use Margin::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Margin::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Margin<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let area = self.content().layout(area);
let dx = self.dx();
let dy = self.dy();
[
area.x().minus(dx),
area.y().minus(dy),
area.w().plus(dy.plus(dy)),
area.h().plus(dy.plus(dy)),
].into()
}
}

View file

@ -0,0 +1,47 @@
use crate::*;
pub enum Max<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Max<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Max::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Max<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Max<U, T> {
#[inline] pub fn dx (&self) -> U {
use Max::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Max::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Max<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let [x, y, w, h] = self.content().layout(area).xywh();
match self {
Self::X(mw, _) => [x, y, w.min(*mw), h],
Self::Y(mh, _) => [x, y, w, h.min(*mh)],
Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)],
}.into()
}
}

View file

@ -0,0 +1,47 @@
use crate::*;
pub enum Min<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Min<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Min::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Min<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Min<U, T> {
#[inline] pub fn dx (&self) -> U {
use Min::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Min::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Min<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let [x, y, w, h] = self.content().layout(area).xywh();
match self {
Self::X(mw, _) => [x, y, w.max(*mw), h],
Self::Y(mh, _) => [x, y, w, h.max(*mh)],
Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)],
}.into()
}
}

View file

@ -0,0 +1,50 @@
use crate::*;
pub enum Padding<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Padding<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Padding::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Padding<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Padding<U, T> {
#[inline] pub fn dx (&self) -> U {
use Padding::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Padding::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Padding<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let area = self.content().layout(area);
let dx = self.dx();
let dy = self.dy();
[
area.x().plus(dx),
area.y().plus(dy),
area.w().minus(dy.plus(dy)),
area.h().minus(dy.plus(dy))
].into()
}
}

View file

@ -0,0 +1,48 @@
use crate::*;
pub enum Pull<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Pull<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Pull::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Pull<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Pull<U, T> {
#[inline] pub fn dx (&self) -> U {
use Pull::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Pull::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Pull<E::Unit, T> {
fn layout (&self, to: E::Area) -> E::Area {
let area = self.content().layout(to);
[
area.x().minus(self.dx()),
area.y().minus(self.dy()),
area.w(),
area.h()
].into()
}
}

View file

@ -0,0 +1,43 @@
use crate::*;
pub enum Push<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Push<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Push::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Push<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Push<U, T> {
#[inline] pub fn dx (&self) -> U {
use Push::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Push::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Push<E::Unit, T> {
fn layout (&self, area: E::Area) -> E::Area {
let area = self.content().layout(area);
[area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()].into()
}
}

View file

@ -0,0 +1,43 @@
use crate::*;
pub enum Shrink<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> Shrink<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self {
Self::X(x, item)
}
#[inline] pub const fn y (y: U, item: A) -> Self {
Self::Y(y, item)
}
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self {
Self::XY(x, y, item)
}
#[inline] pub const fn content (&self) -> &A {
use Shrink::*;
match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, }
}
}
impl<E: Out, T: Draw<E> + Layout<E>> Draw<E> for Shrink<E::Unit, T> {
fn draw (&self, to: &mut E) {
to.place_at(self.layout(to.area()), &self.content())
}
}
impl<U: Coordinate, T> Shrink<U, T> {
#[inline] pub fn dx (&self) -> U {
use Shrink::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use Shrink::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
impl<E: Out, T: Layout<E>> Layout<E> for Shrink<E::Unit, T> {
fn layout (&self, to: E::Area) -> E::Area {
let area = self.content().layout(to);
[area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into()
}
}

View file

@ -0,0 +1,170 @@
//use crate::*;
//use Direction::*;
//pub struct Stack<'x, E, F1> {
//__: PhantomData<&'x (E, F1)>,
//direction: Direction,
//callback: F1
//}
//impl<'x, E, F1> Stack<'x, E, F1> {
//pub fn new (direction: Direction, callback: F1) -> Self {
//Self { direction, callback, __: Default::default(), }
//}
//pub fn above (callback: F1) -> Self {
//Self::new(Above, callback)
//}
//pub fn below (callback: F1) -> Self {
//Self::new(Below, callback)
//}
//pub fn north (callback: F1) -> Self {
//Self::new(North, callback)
//}
//pub fn south (callback: F1) -> Self {
//Self::new(South, callback)
//}
//pub fn east (callback: F1) -> Self {
//Self::new(East, callback)
//}
//pub fn west (callback: F1) -> Self {
//Self::new(West, callback)
//}
//}
//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Layout<E>))> Layout<E> for Stack<'x, E, F1> {
//fn layout (&self, to: E::Area) -> E::Area {
//let state = StackLayoutState::<E>::new(self.direction, to);
//(self.callback)(&mut |component: &dyn Layout<E>|{
//let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
//let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
//state.borrow_mut().grow(w, h);
//});
//let StackLayoutState { w_used, h_used, .. } = *state.borrow();
//match self.direction {
//North | West => { todo!() },
//South | East => { [to.x(), to.y(), w_used, h_used].into() },
//_ => unreachable!(),
//}
//}
//}
//impl<'x, E: Out, F1: Fn(&mut dyn FnMut(&dyn Draw<E>))> Draw<E> for Stack<'x, E, F1> {
//fn draw (&self, to: &mut E) {
//let state = StackLayoutState::<E>::new(self.direction, to.area());
//let to = Rc::new(RefCell::new(to));
//(self.callback)(&mut |component: &dyn Draw<E>|{
//let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
//let layout = component.layout([x, y, w_remaining, h_remaining].into());
//state.borrow_mut().grow(layout.w(), layout.h());
//to.borrow_mut().place_at(layout, component);
//});
//}
//}
//#[derive(Copy, Clone)]
//struct StackLayoutState<E: Out> {
//direction: Direction,
//x: E::Unit,
//y: E::Unit,
//w_used: E::Unit,
//h_used: E::Unit,
//w_remaining: E::Unit,
//h_remaining: E::Unit,
//}
//impl<E: Out> StackLayoutState<E> {
//fn new (direction: Direction, area: E::Area) -> std::rc::Rc<std::cell::RefCell<Self>> {
//let [x, y, w_remaining, h_remaining] = area.xywh();
//std::rc::Rc::new(std::cell::RefCell::new(Self {
//direction,
//x, y, w_remaining, h_remaining,
//w_used: E::Unit::zero(), h_used: E::Unit::zero()
//}))
//}
//fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self {
//match self.direction {
//South => { self.y = self.y.plus(h);
//self.h_used = self.h_used.plus(h);
//self.h_remaining = self.h_remaining.minus(h);
//self.w_used = self.w_used.max(w); },
//East => { self.x = self.x.plus(w);
//self.w_used = self.w_used.plus(w);
//self.w_remaining = self.w_remaining.minus(w);
//self.h_used = self.h_used.max(h); },
//North | West => { todo!() },
//Above | Below => {},
//};
//self
//}
//fn area_remaining (&self) -> E::Area {
//[self.x, self.y, self.w_remaining, self.h_remaining].into()
//}
//}
////pub struct Stack<'a, E, F1> {
////__: PhantomData<&'a (E, F1)>,
////direction: Direction,
////callback: F1
////}
////impl<'a, E, F1> Stack<'a, E, F1> where
////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw<E>)) + Send + Sync,
////{
////pub fn north (callback: F1) -> Self { Self::new(North, callback) }
////pub fn south (callback: F1) -> Self { Self::new(South, callback) }
////pub fn east (callback: F1) -> Self { Self::new(East, callback) }
////pub fn west (callback: F1) -> Self { Self::new(West, callback) }
////pub fn above (callback: F1) -> Self { Self::new(Above, callback) }
////pub fn below (callback: F1) -> Self { Self::new(Below, callback) }
////pub fn new (direction: Direction, callback: F1) -> Self {
////Self { direction, callback, __: Default::default(), }
////}
////}
////impl<'a, E, F1> Draw<E> for Stack<'a, E, F1> where
////E: Out, F1: Fn(&mut dyn FnMut(&'a dyn Draw<E>)) + Send + Sync,
////{
////fn layout (&self, to: E::Area) -> E::Area {
////let state = StackLayoutState::<E>::new(self.direction, to);
////let mut adder = {
////let state = state.clone();
////move|component: &dyn Draw<E>|{
////let [w, h] = component.layout(state.borrow().area_remaining()).wh();
////state.borrow_mut().grow(w, h);
////}
////};
////(self.callback)(&mut adder);
////let StackLayoutState { w_used, h_used, .. } = *state.borrow();
////match self.direction {
////North | West => { todo!() },
////South | East => { [to.x(), to.y(), w_used, h_used].into() },
////Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() },
////}
////}
////fn draw (&self, to: &mut E) {
////let state = StackLayoutState::<E>::new(self.direction, to.area());
////let mut adder = {
////let state = state.clone();
////move|component: &dyn Draw<E>|{
////let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh();
////state.borrow_mut().grow(w, h);
////to.place_at([x, y, w, h].into(), component);
////}
////};
////(self.callback)(&mut adder);
////}
////}
//[>Stack::down(|add|{
//let mut i = 0;
//for (_, name) in self.dirs.iter() {
//if i >= self.scroll {
//add(&Tui::bold(i == self.index, name.as_str()))?;
//}
//i += 1;
//}
//for (_, name) in self.files.iter() {
//if i >= self.scroll {
//add(&Tui::bold(i == self.index, name.as_str()))?;
//}
//i += 1;
//}
//add(&format!("{}/{i}", self.index))?;
//Ok(())
//}));*/

View file

@ -1,97 +0,0 @@
use crate::*;
use Direction::*;
/// A split or layer.
pub struct Bsp<A, B>(
pub(crate) Direction,
pub(crate) A,
pub(crate) B,
);
impl<A, B> Bsp<A, B> {
#[inline] pub const fn n (a: A, b: B) -> Self { Self(North, a, b) }
#[inline] pub const fn s (a: A, b: B) -> Self { Self(South, a, b) }
#[inline] pub const fn e (a: A, b: B) -> Self { Self(East, a, b) }
#[inline] pub const fn w (a: A, b: B) -> Self { Self(West, a, b) }
#[inline] pub const fn a (a: A, b: B) -> Self { Self(Above, a, b) }
#[inline] pub const fn b (a: A, b: B) -> Self { Self(Below, a, b) }
}
impl<E: Output, A: Render<E>, B: Render<E>> Render<E> for Bsp<A, B> {
fn layout (&self, outer: E::Area) -> E::Area { let [_, _, c] = self.areas(outer); c }
fn render (&self, to: &mut E) {
let [area_a, area_b, _] = self.areas(to.area());
let (a, b) = self.contents();
match self.0 {
Below => { to.place(area_a, a); to.place(area_b, b); },
_ => { to.place(area_b, b); to.place(area_a, a); }
}
}
}
impl<E: Output, A: Render<E>, B: Render<E>> BspAreas<E, A, B> for Bsp<A, B> {
fn direction (&self) -> Direction { self.0 }
fn contents (&self) -> (&A, &B) { (&self.1, &self.2) }
}
pub trait BspAreas<E: Output, A: Render<E>, B: Render<E>> {
fn direction (&self) -> Direction;
fn contents (&self) -> (&A, &B);
fn areas (&self, outer: E::Area) -> [E::Area;3] {
let direction = self.direction();
let [x, y, w, h] = outer.xywh();
let (a, b) = self.contents();
let [aw, ah] = a.layout(outer).wh();
let [bw, bh] = b.layout(match direction {
Above | Below => outer,
South => [x, y + ah, w, h.minus(ah)].into(),
North => [x, y, w, h.minus(ah)].into(),
East => [x + aw, y, w.minus(aw), h].into(),
West => [x, y, w.minus(aw), h].into(),
}).wh();
match direction {
Above | Below => {
let [x, y, w, h] = outer.center_xy([aw.max(bw), ah.max(bh)]);
let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
South => {
let [x, y, w, h] = outer.center_xy([aw.max(bw), ah + bh]);
let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah];
let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
North => {
let [x, y, w, h] = outer.center_xy([aw.max(bw), ah + bh]);
let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah];
let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
East => {
let [x, y, w, h] = outer.center_xy([aw + bw, ah.max(bh)]);
let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
West => {
let [x, y, w, h] = outer.center_xy([aw + bw, ah.max(bh)]);
let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah];
let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh];
[a.into(), b.into(), [x, y, w, h].into()]
},
}
}
}
/// Stack things on top of each other,
#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }});
/// Stack southward.
#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }});
/// Stack northward.
#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }});
/// Stack eastward.
#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) =>
{{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }});

View file

@ -1,168 +0,0 @@
use crate::*;
use Direction::*;
pub struct Stack<'x, E, F1> {
__: PhantomData<&'x (E, F1)>,
direction: Direction,
callback: F1
}
impl<'x, E, F1> Stack<'x, E, F1> {
pub fn new (direction: Direction, callback: F1) -> Self {
Self { direction, callback, __: Default::default(), }
}
pub fn above (callback: F1) -> Self {
Self::new(Above, callback)
}
pub fn below (callback: F1) -> Self {
Self::new(Below, callback)
}
pub fn north (callback: F1) -> Self {
Self::new(North, callback)
}
pub fn south (callback: F1) -> Self {
Self::new(South, callback)
}
pub fn east (callback: F1) -> Self {
Self::new(East, callback)
}
pub fn west (callback: F1) -> Self {
Self::new(West, callback)
}
}
impl<'x, E: Output, F1: Fn(&mut dyn FnMut(&dyn Render<E>))> Render<E> for Stack<'x, E, F1> {
fn layout (&self, to: E::Area) -> E::Area {
let state = StackLayoutState::<E>::new(self.direction, to);
(self.callback)(&mut |component: &dyn Render<E>|{
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
state.borrow_mut().grow(w, h);
});
let StackLayoutState { w_used, h_used, .. } = *state.borrow();
match self.direction {
North | West => { todo!() },
South | East => { [to.x(), to.y(), w_used, h_used].into() },
_ => unreachable!(),
}
}
fn render (&self, to: &mut E) {
let state = StackLayoutState::<E>::new(self.direction, to.area());
let to = Rc::new(RefCell::new(to));
(self.callback)(&mut |component: &dyn Render<E>|{
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
let layout = component.layout([x, y, w_remaining, h_remaining].into());
state.borrow_mut().grow(layout.w(), layout.h());
to.borrow_mut().place(layout, component);
});
}
}
#[derive(Copy, Clone)]
struct StackLayoutState<E: Output> {
direction: Direction,
x: E::Unit,
y: E::Unit,
w_used: E::Unit,
h_used: E::Unit,
w_remaining: E::Unit,
h_remaining: E::Unit,
}
impl<E: Output> StackLayoutState<E> {
fn new (direction: Direction, area: E::Area) -> std::rc::Rc<std::cell::RefCell<Self>> {
let [x, y, w_remaining, h_remaining] = area.xywh();
std::rc::Rc::new(std::cell::RefCell::new(Self {
direction,
x, y, w_remaining, h_remaining,
w_used: E::Unit::zero(), h_used: E::Unit::zero()
}))
}
fn grow (&mut self, w: E::Unit, h: E::Unit) -> &mut Self {
match self.direction {
South => { self.y = self.y.plus(h);
self.h_used = self.h_used.plus(h);
self.h_remaining = self.h_remaining.minus(h);
self.w_used = self.w_used.max(w); },
East => { self.x = self.x.plus(w);
self.w_used = self.w_used.plus(w);
self.w_remaining = self.w_remaining.minus(w);
self.h_used = self.h_used.max(h); },
North | West => { todo!() },
Above | Below => {},
};
self
}
fn area_remaining (&self) -> E::Area {
[self.x, self.y, self.w_remaining, self.h_remaining].into()
}
}
//pub struct Stack<'a, E, F1> {
//__: PhantomData<&'a (E, F1)>,
//direction: Direction,
//callback: F1
//}
//impl<'a, E, F1> Stack<'a, E, F1> where
//E: Output, F1: Fn(&mut dyn FnMut(&'a dyn Render<E>)) + Send + Sync,
//{
//pub fn north (callback: F1) -> Self { Self::new(North, callback) }
//pub fn south (callback: F1) -> Self { Self::new(South, callback) }
//pub fn east (callback: F1) -> Self { Self::new(East, callback) }
//pub fn west (callback: F1) -> Self { Self::new(West, callback) }
//pub fn above (callback: F1) -> Self { Self::new(Above, callback) }
//pub fn below (callback: F1) -> Self { Self::new(Below, callback) }
//pub fn new (direction: Direction, callback: F1) -> Self {
//Self { direction, callback, __: Default::default(), }
//}
//}
//impl<'a, E, F1> Render<E> for Stack<'a, E, F1> where
//E: Output, F1: Fn(&mut dyn FnMut(&'a dyn Render<E>)) + Send + Sync,
//{
//fn layout (&self, to: E::Area) -> E::Area {
//let state = StackLayoutState::<E>::new(self.direction, to);
//let mut adder = {
//let state = state.clone();
//move|component: &dyn Render<E>|{
//let [w, h] = component.layout(state.borrow().area_remaining()).wh();
//state.borrow_mut().grow(w, h);
//}
//};
//(self.callback)(&mut adder);
//let StackLayoutState { w_used, h_used, .. } = *state.borrow();
//match self.direction {
//North | West => { todo!() },
//South | East => { [to.x(), to.y(), w_used, h_used].into() },
//Above | Below => { [to.x(), to.y(), to.w(), to.h()].into() },
//}
//}
//fn render (&self, to: &mut E) {
//let state = StackLayoutState::<E>::new(self.direction, to.area());
//let mut adder = {
//let state = state.clone();
//move|component: &dyn Render<E>|{
//let [x, y, w, h] = component.layout(state.borrow().area_remaining()).xywh();
//state.borrow_mut().grow(w, h);
//to.place([x, y, w, h].into(), component);
//}
//};
//(self.callback)(&mut adder);
//}
//}
/*Stack::down(|add|{
let mut i = 0;
for (_, name) in self.dirs.iter() {
if i >= self.scroll {
add(&Tui::bold(i == self.index, name.as_str()))?;
}
i += 1;
}
for (_, name) in self.files.iter() {
if i >= self.scroll {
add(&Tui::bold(i == self.index, name.as_str()))?;
}
i += 1;
}
add(&format!("{}/{i}", self.index))?;
Ok(())
}));*/

View file

@ -1,148 +0,0 @@
//! Transform:
//! ```
//! use ::tengri::{output::*, tui::*};
//! let area: [u16;4] = [10, 10, 20, 20];
//! fn test (area: [u16;4], item: &impl Render<TuiOut>, expected: [u16;4]) {
//! assert_eq!(item.layout(area), expected);
//! };
//! test(area, &(), [20, 20, 0, 0]);
//!
//! test(area, &Fill::xy(()), area);
//! test(area, &Fill::x(()), [10, 20, 20, 0]);
//! test(area, &Fill::y(()), [20, 10, 0, 20]);
//!
//! //FIXME:test(area, &Fixed::x(4, ()), [18, 20, 4, 0]);
//! //FIXME:test(area, &Fixed::y(4, ()), [20, 18, 0, 4]);
//! //FIXME:test(area, &Fixed::xy(4, 4, unit), [18, 18, 4, 4]);
//! ```
use crate::*;
/// Defines an enum that transforms its content
/// along either the X axis, the Y axis, or both.
macro_rules! transform_xy {
($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$area:expr) => {
pub enum $Enum<A> { X(A), Y(A), XY(A) }
impl<A> $Enum<A> {
#[inline] pub const fn x (item: A) -> Self { Self::X(item) }
#[inline] pub const fn y (item: A) -> Self { Self::Y(item) }
#[inline] pub const fn xy (item: A) -> Self { Self::XY(item) }
}
impl<E: Output, T: Render<E>> Content<E> for $Enum<T> {
fn content (&self) -> Option<impl Render<E> + '_> {
use $Enum::*;
Some(match self { X(item) | Y(item) | XY(item) => item, })
}
}
impl<E: Output, T: Render<E>> Render<E> for $Enum<T> {
fn layout (&$self, $to: <E as Output>::Area) -> <E as Output>::Area {
use $Enum::*;
$area
}
fn render (&self, output: &mut E) {
output.place(self.layout(output.area()), &self.content())
}
}
}
}
/// Defines an enum that parametrically transforms its content
/// along either the X axis, the Y axis, or both.
macro_rules! transform_xy_unit {
($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$layout:expr) => {
pub enum $Enum<U, A> { X(U, A), Y(U, A), XY(U, U, A), }
impl<U, A> $Enum<U, A> {
#[inline] pub const fn x (x: U, item: A) -> Self { Self::X(x, item) }
#[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, item) }
#[inline] pub const fn xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) }
}
impl<E: Output, T: Render<E>> Content<E> for $Enum<E::Unit, T> {
fn content (&self) -> Option<impl Render<E> + '_> {
use $Enum::*;
Some(match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, })
}
}
impl<E: Output, T: Render<E>> Render<E> for $Enum<E::Unit, T> {
fn layout (&$self, $to: E::Area) -> E::Area {
$layout.into()
}
fn render (&self, output: &mut E) {
output.place(self.layout(output.area()), &self.content())
}
}
impl<U: Coordinate, T> $Enum<U, T> {
#[inline] pub fn dx (&self) -> U {
use $Enum::*;
match self { X(x, _) => *x, Y(_, _) => 0.into(), XY(x, _, _) => *x, }
}
#[inline] pub fn dy (&self) -> U {
use $Enum::*;
match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, }
}
}
}
}
transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{
let [x0, y0, wmax, hmax] = to.xywh();
let [x, y, w, h] = self.content().layout(to).xywh();
match self {
X(_) => [x0, y, wmax, h],
Y(_) => [x, y0, w, hmax],
XY(_) => [x0, y0, wmax, hmax],
}.into()
});
transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{
let [x, y, w, h] = area.xywh();
let [x, y, w, h] = self.content().layout(match self {
Self::X(fw, _) => [x, y, *fw, h],
Self::Y(fh, _) => [x, y, w, *fh],
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
}.into()).xywh();
let fixed_area = match self {
Self::X(fw, _) => [x, y, *fw, h],
Self::Y(fh, _) => [x, y, w, *fh],
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
};
fixed_area
});
transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{
let [x, y, w, h] = self.content().layout(area).xywh();
match self {
Self::X(mw, _) => [x, y, w.max(*mw), h],
Self::Y(mh, _) => [x, y, w, h.max(*mh)],
Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], } });
transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{
let [x, y, w, h] = area.xywh();
self.content().layout(match self {
Self::X(fw, _) => [x, y, *fw, h],
Self::Y(fh, _) => [x, y, w, *fh],
Self::XY(fw, fh, _) => [x, y, *fw, *fh], }.into()) });
transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|self.content().layout(
[area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into()));
transform_xy_unit!("expand/x" "expand/y" "expand/xy"|self: Expand, area|self.content().layout(
[area.x(), area.y(), area.w().plus(self.dx()), area.h().plus(self.dy())].into()));
transform_xy_unit!("push/x" "push/y" "push/xy"|self: Push, area|{
let area = self.content().layout(area);
[area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()] });
transform_xy_unit!("pull/x" "pull/y" "pull/xy"|self: Pull, area|{
let area = self.content().layout(area);
[area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()] });
transform_xy_unit!("margin/x" "margin/y" "margin/xy"|self: Margin, area|{
let area = self.content().layout(area);
let dx = self.dx();
let dy = self.dy();
[area.x().minus(dx), area.y().minus(dy), area.w().plus(dy.plus(dy)), area.h().plus(dy.plus(dy))] });
transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{
let area = self.content().layout(area);
let dx = self.dx();
let dy = self.dy();
[area.x().plus(dx), area.y().plus(dy), area.w().minus(dy.plus(dy)), area.h().minus(dy.plus(dy))] });

View file

@ -1,35 +0,0 @@
#![feature(step_trait)]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]
//#![feature(non_lifetime_binders)]
pub(crate) use std::cell::RefCell;
pub(crate) use std::fmt::{Debug, Display};
pub(crate) use std::marker::PhantomData;
pub(crate) use std::ops::{Add, Sub, Mul, Div, Deref};
pub(crate) use std::rc::Rc;
pub(crate) use std::sync::RwLock;
pub(crate) use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}};
pub(crate) use tengri_core::*;
#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*;
mod output; pub use self::output::*;
mod output_render; pub use self::output_render::*;
mod output_content; pub use self::output_content::*;
mod output_thunk; pub use self::output_thunk::*;
mod space_area; pub use self::space_area::*;
mod space_coordinate; pub use self::space_coordinate::*;
mod space_direction; pub use self::space_direction::*;
mod space_measure; pub use self::space_measure::*;
mod space_size; pub use self::space_size::*;
mod layout_align; pub use self::layout_align::*;
mod layout_bsp; pub use self::layout_bsp::*;
mod layout_cond; pub use self::layout_cond::*;
mod layout_map; pub use self::layout_map::*;
mod layout_stack; pub use self::layout_stack::*;
mod layout_xy; pub use self::layout_xy::*;
pub(crate) use self::Direction::*;
#[cfg(test)] mod test;
#[cfg(test)] pub(crate) use proptest_derive::Arbitrary;

View file

@ -1,7 +1,27 @@
use crate::*;
#![feature(step_trait)]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]
#![feature(const_precise_live_drops)]
#![feature(type_changing_struct_update)]
//#![feature(non_lifetime_binders)]
/// Render target.
pub trait Output: Send + Sync + Sized {
mod content; pub use self::content::*;
mod draw; pub use self::draw::*;
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;
pub(crate) use std::ops::{Add, Sub, Mul, Div, Deref};
pub(crate) use std::rc::Rc;
pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed}};
pub(crate) use tengri_core::*;
#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*;
/// Draw target.
pub trait Out: Send + Sync + Sized {
/// Unit of length
type Unit: Coordinate;
/// Rectangle without offset
@ -12,11 +32,26 @@ pub trait Output: Send + Sync + Sized {
fn area (&self) -> Self::Area;
/// Mutable pointer to area
fn area_mut (&mut self) -> &mut Self::Area;
/// Render widget in area
fn place <'t, T: Render<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
/// 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) {
self.place_at(content.layout(self.area()), content)
}
#[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() }
}
#[cfg(test)] mod 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);
//}
//}
//}

View file

@ -1,60 +0,0 @@
use crate::*;
/// Composable renderable with static dispatch.
pub trait Content<E: Output>: Sized {
/// Return opaque [Render]able.
fn content (&self) -> Option<impl Render<E> + '_> { Option::<()>::None }
}
/// The platonic ideal unit of [Content]: total emptiness at dead center (e=1vg^sqrt(-1))
impl<E: Output> Content<E> for () {}
impl<E: Output> Content<E> for fn(&mut E) {
fn content (&self) -> Option<impl Render<E> + '_> {
Some(self)
}
}
impl<E: Output, T: Render<E>> Content<E> for fn()->T {
fn content (&self) -> Option<impl Render<E> + '_> {
Some(self())
}
}
impl<E: Output, T: Content<E>> Content<E> for Option<T> {
fn content (&self) -> Option<impl Render<E> + '_> {
if let Some(content) = self {
content.content()
} else {
None
}
}
}
/// You can render from an opaque pointer.
impl<E: Output> Content<E> for &dyn Render<E> where Self: Sized {
fn content (&self) -> Option<impl Render<E> + '_> {
#[allow(suspicious_double_ref_op)]
Some(self.deref())
}
}
/// Implement composable content for a struct.
#[macro_export] macro_rules! content {
// Implement for all [Output]s.
(|$self:ident:$Struct:ty| $content:expr) => {
impl<E: Output> Content<E> for $Struct {
fn content (&$self) -> impl Render<E> + '_ { Some($content) }
}
};
// Implement for specific [Output].
($Output:ty:|
$self:ident:
$Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?
|$content:expr) => {
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
for $Struct $(<$($($L)? $($T)?),+>)? {
fn content (&$self) -> impl Render<$Output> + '_ { $content }
}
};
}

View file

@ -1,87 +0,0 @@
use crate::*;
/// Renderable with dynamic dispatch.
pub trait Render<E: Output> {
/// Write data to display.
fn render (&self, output: &mut E);
/// Compute layout.
fn layout (&self, area: E::Area) -> E::Area { area }
fn boxed <'a> (self) -> Box<dyn Render<E> + 'a> where Self: Sized + 'a {
Box::new(self) as Box<dyn Render<E> + 'a>
}
fn rc <'a> (self) -> Rc<dyn Render<E> + 'a> where Self: Sized + 'a {
Rc::new(self) as Rc<dyn Render<E> + 'a>
}
}
impl<E: Output> Render<E> for () {
fn render (&self, _: &mut E) {}
}
impl<E: Output> Render<E> for fn(&mut E) {
fn render (&self, output: &mut E) {
self(output)
}
}
impl<'x, E: Output> Render<E> for &(dyn Render<E> + 'x) {
fn render (&self, output: &mut E) {
(*self).render(output)
}
}
impl<E: Output, R: Render<E>> Render<E> for Box<R> {
fn render (&self, output: &mut E) {
(**self).render(output)
}
}
impl<E: Output, R: Render<E>> Render<E> for Option<R> {
fn render (&self, output: &mut E) {
if let Some(render) = self {
render.render(output)
}
}
}
impl<E: Output, R: Render<E>> Render<E> for &R {
fn render (&self, output: &mut E) {
(*self).render(output)
}
}
impl<E: Output, R: Render<E>> Render<E> for &mut R {
fn render (&self, output: &mut E) {
(**self).render(output)
}
}
impl<E: Output, R: Render<E>> Render<E> for [R] {
fn render (&self, output: &mut E) {
for render in self.iter() {
render.render(output)
}
}
}
/// Implement custom rendering for a struct.
#[macro_export] macro_rules! render {
(|$self:ident:$Struct:ident $(<
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
>)?, $to:ident | $render:expr) => {
impl <$($($L),*)? E: Output, $($T$(:$Trait)?),*> Render<E>
for $Struct $(<$($L),* $($T),*>>)? {
fn render (&$self, $to: &mut E) { $render }
}
};
($Output:ty:|
$self:ident:
$Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident
|$render:expr) => {
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Render<$Output>
for $Struct $(<$($($L)? $($T)?),+>)? {
fn render (&$self, $to: &mut $Output) { $render }
}
};
}

5
output/src/space.rs Normal file
View file

@ -0,0 +1,5 @@
mod space_area; pub use self::space_area::*;
mod space_coordinate; pub use self::space_coordinate::*;
mod space_direction; pub use self::space_direction::*;
mod space_measure; pub use self::space_measure::*;
mod space_size; pub use self::space_size::*;

View file

@ -2,13 +2,13 @@ use crate::*;
/// A widget that tracks its render width and height
#[derive(Default)]
pub struct Measure<E: Output> {
pub struct Measure<E: Out> {
_engine: PhantomData<E>,
pub x: Arc<AtomicUsize>,
pub y: Arc<AtomicUsize>,
}
impl<E: Output> PartialEq for Measure<E> {
impl<E: Out> PartialEq for Measure<E> {
fn eq (&self, other: &Self) -> bool {
self.x.load(Relaxed) == other.x.load(Relaxed) &&
self.y.load(Relaxed) == other.y.load(Relaxed)
@ -16,14 +16,14 @@ impl<E: Output> PartialEq for Measure<E> {
}
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
impl<E: Output> Render<E> for Measure<E> {
fn render (&self, to: &mut E) {
impl<E: Out> Draw<E> for Measure<E> {
fn draw (&self, to: &mut E) {
self.x.store(to.area().w().into(), Relaxed);
self.y.store(to.area().h().into(), Relaxed);
}
}
impl<E: Output> Clone for Measure<E> {
impl<E: Out> Clone for Measure<E> {
fn clone (&self) -> Self {
Self {
_engine: Default::default(),
@ -33,7 +33,7 @@ impl<E: Output> Clone for Measure<E> {
}
}
impl<E: Output> std::fmt::Debug for Measure<E> {
impl<E: Out> std::fmt::Debug for Measure<E> {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("Measure")
.field("width", &self.x)
@ -42,7 +42,7 @@ impl<E: Output> std::fmt::Debug for Measure<E> {
}
}
impl<E: Output> Measure<E> {
impl<E: Out> Measure<E> {
pub fn new () -> Self {
Self {
_engine: PhantomData::default(),
@ -75,7 +75,7 @@ impl<E: Output> Measure<E> {
pub fn format (&self) -> Arc<str> {
format!("{}x{}", self.w(), self.h()).into()
}
pub fn of <T: Render<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
pub fn of <T: Draw<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
Bsp::b(Fill::xy(self), item)
}
}

View file

@ -38,7 +38,7 @@ impl<N: Coordinate> Size<N> for [N;2] {
fn y (&self) -> N { self[1] }
}
pub trait HasSize<E: Output> {
pub trait HasSize<E: Out> {
fn size (&self) -> &Measure<E>;
fn width (&self) -> usize {
self.size().w()
@ -48,7 +48,7 @@ pub trait HasSize<E: Output> {
}
}
impl<E: Output, T: Has<Measure<E>>> HasSize<E> for T {
impl<E: Out, T: Has<Measure<E>>> HasSize<E> for T {
fn size (&self) -> &Measure<E> {
self.get()
}

View file

@ -90,7 +90,7 @@ macro_rules! test_op_transform {
_ => None
} {
assert_eq!(Content::layout(&op, [x, y, w, h]),
Render::layout(&op, [x, y, w, h]));
Draw::layout(&op, [x, y, w, h]));
}
}
}
@ -124,15 +124,15 @@ proptest! {
let bsp = Bsp(d, a, b);
assert_eq!(
Content::layout(&bsp, [x, y, w, h]),
Render::layout(&bsp, [x, y, w, h]),
Draw::layout(&bsp, [x, y, w, h]),
);
}
}
#[test] fn test_stub_output () -> Usually<()> {
use crate::*;
struct TestOutput([u16;4]);
impl Output for TestOutput {
struct TestOut([u16;4]);
impl Out for TestOut {
type Unit = u16;
type Size = [u16;2];
type Area = [u16;4];
@ -142,12 +142,12 @@ proptest! {
fn area_mut (&mut self) -> &mut [u16;4] {
&mut self.0
}
fn place <T: Render<Self> + ?Sized> (&mut self, _: [u16;4], _: &T) {
fn place <T: Draw<Self> + ?Sized> (&mut self, _: [u16;4], _: &T) {
()
}
}
impl Render<TestOutput> for String {
fn render (&self, to: &mut TestOutput) {
impl Draw<TestOut> for String {
fn draw (&self, to: &mut TestOut) {
to.area_mut().set_w(self.len() as u16);
}
}
@ -161,8 +161,8 @@ proptest! {
#[test] fn test_iter_map () {
struct Foo;
impl<T: Output> Content<T> for Foo {}
fn _make_map <T: Output, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Render<T> {
impl<T: Out> Content<T> for Foo {}
fn _make_map <T: Out, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Draw<T> {
Map::new(||data.iter(), |_foo, _index|{})
}
let _data = vec![Foo, Foo, Foo];

View file

@ -1,27 +1,38 @@
use crate::*;
/// Lazily-evaluated [Render]able.
pub struct Thunk<E: Output, T: Render<E>, F: Fn()->T>(
pub struct Group<T>(T);
impl<T> Group<T> {
pub const fn new () -> Group<()> {
Group(())
}
pub const fn add <U> (self, value: U) -> Group<(T, U)> {
Group((self.0, value))
}
}
/// Lazily-evaluated [Draw]able.
pub struct Thunk<E: Out, T: Draw<E>, F: Fn()->T>(
PhantomData<E>,
F
);
impl<E: Output, T: Render<E>, F: Fn()->T> Thunk<E, T, F> {
impl<E: Out, T: Draw<E>, F: Fn()->T> Thunk<E, T, F> {
pub const fn new (thunk: F) -> Self {
Self(PhantomData, thunk)
}
}
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
fn content (&self) -> Option<impl Render<E>> {
Some((self.1)())
impl<E: Out, T: Draw<E> + Layout<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
fn content (&self) -> impl Draw<E> + Layout<E> + '_ {
(self.1)()
}
}
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
impl<E: Output, F: Fn(&mut E)> ThunkRender<E, F> {
pub struct ThunkDraw<E: Out, F: Fn(&mut E)>(PhantomData<E>, F);
impl<E: Out, F: Fn(&mut E)> ThunkDraw<E, F> {
pub fn new (render: F) -> Self { Self(PhantomData, render) }
}
impl<E: Output, F: Fn(&mut E)> Render<E> for ThunkRender<E, F> {
fn render (&self, to: &mut E) { (self.1)(to) }
impl<E: Out, F: Fn(&mut E)> Draw<E> for ThunkDraw<E, F> {
fn draw (&self, to: &mut E) { (self.1)(to) }
}
#[derive(Debug, Default)] pub struct Memo<T, U> {

4
output/src/widget.rs Normal file
View file

@ -0,0 +1,4 @@
mod widget_border; pub use self::widget_border::*;
mod widget_field; pub use self::widget_field::*;
mod widget_style; pub use self::widget_style::*;
mod widget_tryptich; pub use self::widget_tryptich::*;

View file

@ -0,0 +1,10 @@
use crate::*;
pub struct Border<S>(pub bool, pub S);
impl<O: Out, S: Layout<O>> Layout<O> for Border<S> {
fn layout (&self, area: O::Area) -> O::Area {
self.1.layout(area)
}
}
pub struct Bordered<S, W>(pub bool, pub S, pub W);

View file

@ -0,0 +1,25 @@
use crate::*;
pub struct FieldH<Theme, Label, Value>(pub Theme, pub Label, pub Value);
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Layout<O> for FieldH<T, L, V> where Self: Content<O> {
fn layout (&self, to: O::Area) -> O::Area {
self.content().layout(to)
}
}
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Draw<O> for FieldH<T, L, V> where Self: Content<O> {
fn draw (&self, to: &mut O) {
self.content().draw(to)
}
}
pub struct FieldV<Theme, Label, Value>(pub Theme, pub Label, pub Value);
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Layout<O> for FieldV<T, L, V> where Self: Content<O> {
fn layout (&self, to: O::Area) -> O::Area {
self.content().layout(to)
}
}
impl<O: Out, T, L: Draw<O>, V: Draw<O>> Draw<O> for FieldV<T, L, V> where Self: Content<O> {
fn draw (&self, to: &mut O) {
self.content().draw(to)
}
}

View file

@ -0,0 +1,15 @@
#[allow(unused)] use crate::*;
pub struct Foreground<Color, Item>(pub Color, pub Item);
impl<O: Out, Color, Item: Layout<O>> Layout<O> for Foreground<Color, Item> {
fn layout (&self, to: O::Area) -> O::Area {
self.1.layout(to)
}
}
pub struct Background<Color, Item>(pub Color, pub Item);
impl<O: Out, Color, Item: Layout<O>> Layout<O> for Background<Color, Item> {
fn layout (&self, to: O::Area) -> O::Area {
self.1.layout(to)
}
}

View file

@ -0,0 +1,31 @@
#[allow(unused)] use crate::*;
/// A three-column layout.
pub struct Tryptich<A, B, C> {
pub top: bool,
pub h: u16,
pub left: (u16, A),
pub middle: (u16, B),
pub right: (u16, C),
}
impl Tryptich<(), (), ()> {
pub fn center (h: u16) -> Self {
Self { h, top: false, left: (0, ()), middle: (0, ()), right: (0, ()) }
}
pub fn top (h: u16) -> Self {
Self { h, top: true, left: (0, ()), middle: (0, ()), right: (0, ()) }
}
}
impl<A, B, C> Tryptich<A, B, C> {
pub fn left <D> (self, w: u16, content: D) -> Tryptich<D, B, C> {
Tryptich { left: (w, content), ..self }
}
pub fn middle <D> (self, w: u16, content: D) -> Tryptich<A, D, C> {
Tryptich { middle: (w, content), ..self }
}
pub fn right <D> (self, w: u16, content: D) -> Tryptich<A, B, D> {
Tryptich { right: (w, content), ..self }
}
}