mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-01-11 18:46:41 +01:00
This commit is contained in:
parent
194f2f9874
commit
277f96d5cc
63 changed files with 1389 additions and 909 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1084,6 +1084,7 @@ dependencies = [
|
||||||
name = "tengri_output"
|
name = "tengri_output"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
"proptest",
|
"proptest",
|
||||||
"proptest-derive",
|
"proptest-derive",
|
||||||
"tengri",
|
"tengri",
|
||||||
|
|
@ -1109,6 +1110,7 @@ version = "0.14.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic_float",
|
"atomic_float",
|
||||||
"better-panic",
|
"better-panic",
|
||||||
|
"bumpalo",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
"konst",
|
"konst",
|
||||||
"palette",
|
"palette",
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ tengri_proc = { path = "./proc" }
|
||||||
anyhow = { version = "1.0" }
|
anyhow = { version = "1.0" }
|
||||||
atomic_float = { version = "1" }
|
atomic_float = { version = "1" }
|
||||||
better-panic = { version = "0.3.0" }
|
better-panic = { version = "0.3.0" }
|
||||||
|
bumpalo = { version = "3.19.0" }
|
||||||
const_panic = { version = "0.2.12", features = [ "derive" ] }
|
const_panic = { version = "0.2.12", features = [ "derive" ] }
|
||||||
crossterm = { version = "0.29.0" }
|
crossterm = { version = "0.29.0" }
|
||||||
heck = { version = "0.5" }
|
heck = { version = "0.5" }
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,17 @@ description = "UI metaframework, output layer."
|
||||||
version = { workspace = true }
|
version = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/output.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dsl = [ "tengri_dsl" ]
|
bumpalo = [ "dep:bumpalo" ]
|
||||||
|
dsl = [ "dep:tengri_dsl" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tengri_core = { path = "../core" }
|
tengri_core = { path = "../core" }
|
||||||
tengri_dsl = { optional = true, path = "../dsl" }
|
tengri_dsl = { optional = true, path = "../dsl" }
|
||||||
|
bumpalo = { optional = true, workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tengri = { path = "../tengri", features = [ "dsl", "tui" ] }
|
tengri = { path = "../tengri", features = [ "dsl", "tui" ] }
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ it expresses the following notions:
|
||||||
|
|
||||||
* [**space:**](./src/space.rs) `Direction`, `Coordinate`, `Area`, `Size`, `Measure`
|
* [**space:**](./src/space.rs) `Direction`, `Coordinate`, `Area`, `Size`, `Measure`
|
||||||
|
|
||||||
* [**output:**](./src/output.rs) `Output`, `Render`, `Content`
|
* [**output:**](./src/output.rs) `Out`, `Draw`, `Content`
|
||||||
* the layout operators are generic over `Render` and/or `Content`
|
* the layout operators are generic over `Draw` and/or `Content`
|
||||||
* the traits `Render` and `Content` are generic over `Output`
|
* the traits `Draw` and `Content` are generic over `Out`
|
||||||
* implement `Output` to bring a layout to a new backend:
|
* implement `Out` to bring a layout to a new backend:
|
||||||
[see `TuiOut` in `tengri_tui`](../tui/src/tui_engine/tui_output.rs)
|
[see `TuiOut` in `tengri_tui`](../tui/src/tui_engine/tui_output.rs)
|
||||||
|
|
||||||
* [**layout:**](./src/layout.rs)
|
* [**layout:**](./src/layout.rs)
|
||||||
|
|
|
||||||
40
output/src/content.rs
Normal file
40
output/src/content.rs
Normal 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
85
output/src/draw.rs
Normal 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
86
output/src/layout.rs
Normal 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::*;
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! use ::tengri::{output::*, tui::*};
|
//! use ::tengri::{output::*, tui::*};
|
||||||
//! let area: [u16;4] = [10, 10, 20, 20];
|
//! 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!(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, "");
|
//! 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) }
|
#[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 {
|
fn layout (&self, on: E::Area) -> E::Area {
|
||||||
self.0.align(on, &self.1)
|
self.0.align(on, &self.1)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E) {
|
|
||||||
to.place(self.layout(to.area()), &self.1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Alignment {
|
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::*;
|
use Alignment::*;
|
||||||
let it = content.layout(on).xywh();
|
let it = content.layout(on).xywh();
|
||||||
let cx = on.x()+(on.w().minus(it.w())/2.into());
|
let cx = on.x()+(on.w().minus(it.w())/2.into());
|
||||||
95
output/src/layout/layout_bsp.rs
Normal file
95
output/src/layout/layout_bsp.rs
Normal 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 }});
|
||||||
|
|
@ -6,7 +6,7 @@ impl<A> When<A> {
|
||||||
/// Create a binary condition.
|
/// Create a binary condition.
|
||||||
pub const fn new (c: bool, a: A) -> Self { Self(c, a) }
|
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 {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, item) = self;
|
let Self(cond, item) = self;
|
||||||
let mut area = E::Area::zero();
|
let mut area = E::Area::zero();
|
||||||
|
|
@ -19,9 +19,11 @@ impl<E: Output, A: Render<E>> Render<E> for When<A> {
|
||||||
}
|
}
|
||||||
area.into()
|
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;
|
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.
|
/// Create a ternary view condition.
|
||||||
pub const fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) }
|
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 {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let Self(cond, a, b) = self;
|
let Self(cond, a, b) = self;
|
||||||
if *cond { a.layout(to) } else { b.layout(to) }
|
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;
|
let Self(cond, a, b) = self;
|
||||||
if *cond { a.render(to) } else { b.render(to) }
|
if *cond { a.draw(to) } else { b.draw(to) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
39
output/src/layout/layout_expand.rs
Normal file
39
output/src/layout/layout_expand.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
52
output/src/layout/layout_fill.rs
Normal file
52
output/src/layout/layout_fill.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
53
output/src/layout/layout_fixed.rs
Normal file
53
output/src/layout/layout_fixed.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
0
output/src/layout/layout_iter.rs
Normal file
0
output/src/layout/layout_iter.rs
Normal file
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Renders items from an iterator.
|
/// Draws items from an iterator.
|
||||||
pub struct Map<E, A, B, I, F, G>
|
pub struct Map<E, A, B, I, F, G>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = A> + Send + Sync,
|
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<
|
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
|
E, A, Push<E::Unit, Align<Fixed<E::Unit, Fill<B>>>>, I, F, fn(A, usize)->B
|
||||||
> where
|
> where
|
||||||
E: Output,
|
E: Out,
|
||||||
B: Render<E>,
|
B: Draw<E>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
F: Fn() -> I + 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!(west, x, e);
|
||||||
impl_map_direction!(north, y, s);
|
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
|
impl<'a, E, A, B, I, F, G> Layout<E> for Map<E, A, B, I, F, G> where
|
||||||
E: Output,
|
E: Out,
|
||||||
B: Render<E>,
|
B: Draw<E> + Layout<E>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
F: Fn() -> I + Send + Sync + 'a,
|
F: Fn() -> I + Send + Sync + 'a,
|
||||||
G: Fn(A, usize)->B + Send + Sync
|
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()
|
//[min_x.into(), min_y.into(), w.into(), h.into()].into()
|
||||||
area.center_xy([w.into(), h.into()].into()).into()
|
area.center_xy([w.into(), h.into()].into()).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 Self { get_iter, get_item, .. } = self;
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let area = Render::layout(self, to.area());
|
let area = self.layout(to.area());
|
||||||
for item in get_iter() {
|
for item in get_iter() {
|
||||||
let item = get_item(item, index);
|
let item = get_item(item, index);
|
||||||
//to.place(area.into(), &item);
|
//to.place_at(area.into(), &item);
|
||||||
to.place(item.layout(area), &item);
|
to.place_at(item.layout(area), &item);
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn map_south<O: Output>(
|
#[inline] pub fn map_south<O: Out>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Render<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Render<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::y(item_offset, Fixed::y(item_height, Fill::x(item)))
|
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_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Render<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Render<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::y(item_offset, Align::nw(Fixed::y(item_height, Fill::x(item))))
|
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_offset: O::Unit,
|
||||||
item_width: O::Unit,
|
item_width: O::Unit,
|
||||||
item: impl Render<O>
|
item: impl Draw<O> + Layout<O>
|
||||||
) -> impl Render<O> {
|
) -> impl Draw<O> + Layout<O> {
|
||||||
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
|
Push::x(item_offset, Align::w(Fixed::x(item_width, Fill::y(item))))
|
||||||
}
|
}
|
||||||
|
|
||||||
50
output/src/layout/layout_margin.rs
Normal file
50
output/src/layout/layout_margin.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
47
output/src/layout/layout_max.rs
Normal file
47
output/src/layout/layout_max.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
47
output/src/layout/layout_min.rs
Normal file
47
output/src/layout/layout_min.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
50
output/src/layout/layout_padding.rs
Normal file
50
output/src/layout/layout_padding.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
48
output/src/layout/layout_pull.rs
Normal file
48
output/src/layout/layout_pull.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
output/src/layout/layout_push.rs
Normal file
43
output/src/layout/layout_push.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
43
output/src/layout/layout_shrink.rs
Normal file
43
output/src/layout/layout_shrink.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
170
output/src/layout/layout_stack.rs
Normal file
170
output/src/layout/layout_stack.rs
Normal 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(())
|
||||||
|
//}));*/
|
||||||
|
|
@ -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 }});
|
|
||||||
|
|
@ -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(())
|
|
||||||
}));*/
|
|
||||||
|
|
@ -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))] });
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
@ -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.
|
mod content; pub use self::content::*;
|
||||||
pub trait Output: Send + Sync + Sized {
|
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
|
/// Unit of length
|
||||||
type Unit: Coordinate;
|
type Unit: Coordinate;
|
||||||
/// Rectangle without offset
|
/// Rectangle without offset
|
||||||
|
|
@ -12,11 +32,26 @@ pub trait Output: Send + Sync + Sized {
|
||||||
fn area (&self) -> Self::Area;
|
fn area (&self) -> Self::Area;
|
||||||
/// Mutable pointer to area
|
/// Mutable pointer to area
|
||||||
fn area_mut (&mut self) -> &mut Self::Area;
|
fn area_mut (&mut self) -> &mut Self::Area;
|
||||||
/// Render widget in area
|
/// Draw widget in area
|
||||||
fn place <'t, T: Render<Self> + ?Sized> (&mut self, area: Self::Area, content: &'t T);
|
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 x (&self) -> Self::Unit { self.area().x() }
|
||||||
#[inline] fn y (&self) -> Self::Unit { self.area().y() }
|
#[inline] fn y (&self) -> Self::Unit { self.area().y() }
|
||||||
#[inline] fn w (&self) -> Self::Unit { self.area().w() }
|
#[inline] fn w (&self) -> Self::Unit { self.area().w() }
|
||||||
#[inline] fn h (&self) -> Self::Unit { self.area().h() }
|
#[inline] fn h (&self) -> Self::Unit { self.area().h() }
|
||||||
#[inline] fn wh (&self) -> Self::Size { self.area().wh().into() }
|
#[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);
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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
5
output/src/space.rs
Normal 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::*;
|
||||||
|
|
@ -2,13 +2,13 @@ use crate::*;
|
||||||
|
|
||||||
/// A widget that tracks its render width and height
|
/// A widget that tracks its render width and height
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Measure<E: Output> {
|
pub struct Measure<E: Out> {
|
||||||
_engine: PhantomData<E>,
|
_engine: PhantomData<E>,
|
||||||
pub x: Arc<AtomicUsize>,
|
pub x: Arc<AtomicUsize>,
|
||||||
pub y: 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 {
|
fn eq (&self, other: &Self) -> bool {
|
||||||
self.x.load(Relaxed) == other.x.load(Relaxed) &&
|
self.x.load(Relaxed) == other.x.load(Relaxed) &&
|
||||||
self.y.load(Relaxed) == other.y.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
|
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
||||||
impl<E: Output> Render<E> for Measure<E> {
|
impl<E: Out> Draw<E> for Measure<E> {
|
||||||
fn render (&self, to: &mut E) {
|
fn draw (&self, to: &mut E) {
|
||||||
self.x.store(to.area().w().into(), Relaxed);
|
self.x.store(to.area().w().into(), Relaxed);
|
||||||
self.y.store(to.area().h().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 {
|
fn clone (&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
_engine: Default::default(),
|
_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> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("Measure")
|
f.debug_struct("Measure")
|
||||||
.field("width", &self.x)
|
.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 {
|
pub fn new () -> Self {
|
||||||
Self {
|
Self {
|
||||||
_engine: PhantomData::default(),
|
_engine: PhantomData::default(),
|
||||||
|
|
@ -75,7 +75,7 @@ impl<E: Output> Measure<E> {
|
||||||
pub fn format (&self) -> Arc<str> {
|
pub fn format (&self) -> Arc<str> {
|
||||||
format!("{}x{}", self.w(), self.h()).into()
|
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)
|
Bsp::b(Fill::xy(self), item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ impl<N: Coordinate> Size<N> for [N;2] {
|
||||||
fn y (&self) -> N { self[1] }
|
fn y (&self) -> N { self[1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasSize<E: Output> {
|
pub trait HasSize<E: Out> {
|
||||||
fn size (&self) -> &Measure<E>;
|
fn size (&self) -> &Measure<E>;
|
||||||
fn width (&self) -> usize {
|
fn width (&self) -> usize {
|
||||||
self.size().w()
|
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> {
|
fn size (&self) -> &Measure<E> {
|
||||||
self.get()
|
self.get()
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ macro_rules! test_op_transform {
|
||||||
_ => None
|
_ => None
|
||||||
} {
|
} {
|
||||||
assert_eq!(Content::layout(&op, [x, y, w, h]),
|
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);
|
let bsp = Bsp(d, a, b);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Content::layout(&bsp, [x, y, w, h]),
|
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<()> {
|
#[test] fn test_stub_output () -> Usually<()> {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
struct TestOutput([u16;4]);
|
struct TestOut([u16;4]);
|
||||||
impl Output for TestOutput {
|
impl Out for TestOut {
|
||||||
type Unit = u16;
|
type Unit = u16;
|
||||||
type Size = [u16;2];
|
type Size = [u16;2];
|
||||||
type Area = [u16;4];
|
type Area = [u16;4];
|
||||||
|
|
@ -142,12 +142,12 @@ proptest! {
|
||||||
fn area_mut (&mut self) -> &mut [u16;4] {
|
fn area_mut (&mut self) -> &mut [u16;4] {
|
||||||
&mut self.0
|
&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 {
|
impl Draw<TestOut> for String {
|
||||||
fn render (&self, to: &mut TestOutput) {
|
fn draw (&self, to: &mut TestOut) {
|
||||||
to.area_mut().set_w(self.len() as u16);
|
to.area_mut().set_w(self.len() as u16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,8 +161,8 @@ proptest! {
|
||||||
|
|
||||||
#[test] fn test_iter_map () {
|
#[test] fn test_iter_map () {
|
||||||
struct Foo;
|
struct Foo;
|
||||||
impl<T: Output> Content<T> for Foo {}
|
impl<T: Out> Content<T> for Foo {}
|
||||||
fn _make_map <T: Output, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Render<T> {
|
fn _make_map <T: Out, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Draw<T> {
|
||||||
Map::new(||data.iter(), |_foo, _index|{})
|
Map::new(||data.iter(), |_foo, _index|{})
|
||||||
}
|
}
|
||||||
let _data = vec![Foo, Foo, Foo];
|
let _data = vec![Foo, Foo, Foo];
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,38 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Lazily-evaluated [Render]able.
|
pub struct Group<T>(T);
|
||||||
pub struct Thunk<E: Output, T: Render<E>, F: Fn()->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>,
|
PhantomData<E>,
|
||||||
F
|
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 {
|
pub const fn new (thunk: F) -> Self {
|
||||||
Self(PhantomData, thunk)
|
Self(PhantomData, thunk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
impl<E: Out, T: Draw<E> + Layout<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
||||||
fn content (&self) -> Option<impl Render<E>> {
|
fn content (&self) -> impl Draw<E> + Layout<E> + '_ {
|
||||||
Some((self.1)())
|
(self.1)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
|
pub struct ThunkDraw<E: Out, F: Fn(&mut E)>(PhantomData<E>, F);
|
||||||
impl<E: Output, F: Fn(&mut E)> ThunkRender<E, F> {
|
impl<E: Out, F: Fn(&mut E)> ThunkDraw<E, F> {
|
||||||
pub fn new (render: F) -> Self { Self(PhantomData, render) }
|
pub fn new (render: F) -> Self { Self(PhantomData, render) }
|
||||||
}
|
}
|
||||||
impl<E: Output, F: Fn(&mut E)> Render<E> for ThunkRender<E, F> {
|
impl<E: Out, F: Fn(&mut E)> Draw<E> for ThunkDraw<E, F> {
|
||||||
fn render (&self, to: &mut E) { (self.1)(to) }
|
fn draw (&self, to: &mut E) { (self.1)(to) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||||
4
output/src/widget.rs
Normal file
4
output/src/widget.rs
Normal 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::*;
|
||||||
10
output/src/widget/widget_border.rs
Normal file
10
output/src/widget/widget_border.rs
Normal 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);
|
||||||
25
output/src/widget/widget_field.rs
Normal file
25
output/src/widget/widget_field.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
15
output/src/widget/widget_style.rs
Normal file
15
output/src/widget/widget_style.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
31
output/src/widget/widget_tryptich.rs
Normal file
31
output/src/widget/widget_tryptich.rs
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,8 +71,8 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] #[test] fn test_proc_view () {
|
#[cfg(test)] #[test] fn test_proc_view () {
|
||||||
let x: crate::proc_view::ViewMeta = pq! { SomeOutput };
|
let x: crate::proc_view::ViewMeta = pq! { SomeOut };
|
||||||
let output: Ident = pq! { SomeOutput };
|
let output: Ident = pq! { SomeOut };
|
||||||
assert_eq!(x.output, output);
|
assert_eq!(x.output, output);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
|
|
@ -103,10 +103,10 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
|
||||||
//]);
|
//]);
|
||||||
// FIXME
|
// FIXME
|
||||||
//let parsed: ViewDefinition = pq! {
|
//let parsed: ViewDefinition = pq! {
|
||||||
//#[tengri_proc::view(SomeOutput)]
|
//#[tengri_proc::view(SomeOut)]
|
||||||
//impl SomeView {
|
//impl SomeView {
|
||||||
//#[tengri::view(":view-1")]
|
//#[tengri::view(":view-1")]
|
||||||
//fn view_1 (&self) -> impl Render<SomeOutput> + use<'_> {
|
//fn view_1 (&self) -> impl Draw<SomeOut> + use<'_> {
|
||||||
//"view-1"
|
//"view-1"
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
@ -114,19 +114,19 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
|
||||||
//let written = quote! { #parsed };
|
//let written = quote! { #parsed };
|
||||||
//assert_eq!(format!("{written}"), format!("{}", quote! {
|
//assert_eq!(format!("{written}"), format!("{}", quote! {
|
||||||
//impl SomeView {
|
//impl SomeView {
|
||||||
//fn view_1 (&self) -> impl Render<SomeOutput> + use<'_> {
|
//fn view_1 (&self) -> impl Draw<SomeOut> + use<'_> {
|
||||||
//"view-1"
|
//"view-1"
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
///// Generated by [tengri_proc].
|
///// Generated by [tengri_proc].
|
||||||
//impl ::tengri::output::Content<SomeOutput> for SomeView {
|
//impl ::tengri::output::Content<SomeOut> for SomeView {
|
||||||
//fn content (&self) -> impl Render<SomeOutput> {
|
//fn content (&self) -> impl Draw<SomeOut> {
|
||||||
//self.size.of(::tengri::output::View(self, self.config.view))
|
//self.size.of(::tengri::output::View(self, self.config.view))
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
///// Generated by [tengri_proc].
|
///// Generated by [tengri_proc].
|
||||||
//impl<'a> ::tengri::dsl::ViewContext<'a, SomeOutput> for SomeView {
|
//impl<'a> ::tengri::dsl::ViewContext<'a, SomeOut> for SomeView {
|
||||||
//fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, SomeOutput>> {
|
//fn get_content_sym (&'a self, value: &Value<'a>) -> Option<DrawBox<'a, SomeOut>> {
|
||||||
//match value {
|
//match value {
|
||||||
//::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(),
|
//::tengri::dsl::Value::Sym(":view-1") => self.view_1().boxed(),
|
||||||
//_ => panic!("expected Sym(content), got: {value:?}")
|
//_ => panic!("expected Sym(content), got: {value:?}")
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,12 @@ impl ViewDef {
|
||||||
quote! {
|
quote! {
|
||||||
/// Generated by [tengri_proc].
|
/// Generated by [tengri_proc].
|
||||||
///
|
///
|
||||||
/// Makes [#self_ty] able to construct the [Render]able
|
/// Makes [#self_ty] able to construct the [Draw]able
|
||||||
/// which might correspond to a given [TokenStream],
|
/// which might correspond to a given [TokenStream],
|
||||||
/// while taking [#self_ty]'s state into consideration.
|
/// while taking [#self_ty]'s state into consideration.
|
||||||
impl<'state> ::tengri::dsl::DslInto<Box<dyn ::tengri::output::Render<#output> + 'state>> for #self_ty {
|
impl<'state> ::tengri::dsl::DslInto<Box<dyn ::tengri::output::Draw<#output> + 'state>> for #self_ty {
|
||||||
fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl)
|
fn dsl_into (&self, dsl: &impl ::tengri::dsl::Dsl)
|
||||||
-> Perhaps<Box<dyn ::tengri::output::Render<#output> + 'state>>
|
-> Perhaps<Box<dyn ::tengri::output::Draw<#output> + 'state>>
|
||||||
{
|
{
|
||||||
#(#builtins)*
|
#(#builtins)*
|
||||||
if let Some(sym) = dsl.sym()? {
|
if let Some(sym) = dsl.sym()? {
|
||||||
|
|
@ -128,9 +128,9 @@ fn _builtins_with_holes () -> impl Iterator<Item=(Builtin, TokenStream2)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _builtins_with_boxes () -> impl Iterator<Item=(Builtin, TokenStream2)> {
|
fn _builtins_with_boxes () -> impl Iterator<Item=(Builtin, TokenStream2)> {
|
||||||
builtins_with(quote! { _ }, quote! { Box<dyn Render<_>> })
|
builtins_with(quote! { _ }, quote! { Box<dyn Draw<_>> })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn builtins_with_boxes_output (o: TokenStream2) -> impl Iterator<Item=(Builtin, TokenStream2)> {
|
fn builtins_with_boxes_output (o: TokenStream2) -> impl Iterator<Item=(Builtin, TokenStream2)> {
|
||||||
builtins_with(quote! { _ }, quote! { Box<dyn Render<#o>> })
|
builtins_with(quote! { _ }, quote! { Box<dyn Draw<#o>> })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ version = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
dsl = [ "tengri_dsl", "tengri_output/dsl" ]
|
dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ]
|
||||||
|
bumpalo = [ "dep:bumpalo" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tengri_core = { workspace = true }
|
tengri_core = { workspace = true }
|
||||||
|
|
@ -13,14 +14,15 @@ tengri_input = { workspace = true }
|
||||||
tengri_output = { workspace = true }
|
tengri_output = { workspace = true }
|
||||||
tengri_dsl = { workspace = true, optional = true }
|
tengri_dsl = { workspace = true, optional = true }
|
||||||
|
|
||||||
palette = { workspace = true }
|
|
||||||
rand = { workspace = true }
|
|
||||||
crossterm = { workspace = true }
|
|
||||||
ratatui = { workspace = true }
|
|
||||||
better-panic = { workspace = true }
|
|
||||||
konst = { workspace = true }
|
|
||||||
atomic_float = { workspace = true }
|
atomic_float = { workspace = true }
|
||||||
|
better-panic = { workspace = true }
|
||||||
|
bumpalo = { workspace = true, optional = true }
|
||||||
|
crossterm = { workspace = true }
|
||||||
|
konst = { workspace = true }
|
||||||
|
palette = { workspace = true }
|
||||||
quanta = { workspace = true }
|
quanta = { workspace = true }
|
||||||
|
rand = { workspace = true }
|
||||||
|
ratatui = { workspace = true }
|
||||||
unicode-width = { workspace = true }
|
unicode-width = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
|
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
|
||||||
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
|
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
|
||||||
|
|
||||||
fn content (&self) -> dyn Render<Engine = Tui> {
|
fn content (&self) -> dyn Draw<Engine = Tui> {
|
||||||
let border_style = Style::default().fg(Color::Rgb(0,0,0));
|
let border_style = Style::default().fg(Color::Rgb(0,0,0));
|
||||||
Align::Center(Layers::new(move|add|{
|
Align::Center(Layers::new(move|add|{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,25 +76,25 @@ content!(TuiOut: |self: Example|{
|
||||||
|
|
||||||
//#[tengri_proc::view(TuiOut)]
|
//#[tengri_proc::view(TuiOut)]
|
||||||
//impl Example {
|
//impl Example {
|
||||||
//pub fn title (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn title (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed()
|
//Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn code (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn code (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed()
|
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn hello (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn hello (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
|
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn world (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn world (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
|
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn hello_world (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn hello_world (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//"Hello world!".boxed()
|
//"Hello world!".boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn map_e (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn map_e (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn map_s (&self) -> impl Render<TuiOut> + use<'_> {
|
//pub fn map_s (&self) -> impl Draw<TuiOut> + use<'_> {
|
||||||
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub(crate) use std::io::{stdout, Stdout};
|
||||||
//use std::sync::{Arc, RwLock};
|
//use std::sync::{Arc, RwLock};
|
||||||
struct TestComponent(String);
|
struct TestComponent(String);
|
||||||
impl Content<TuiOut> for TestComponent {
|
impl Content<TuiOut> for TestComponent {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> impl Draw<TuiOut> {
|
||||||
Some(self.0.as_str())
|
Some(self.0.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,42 @@
|
||||||
use crate::*;
|
#[allow(unused)] use crate::*;
|
||||||
|
|
||||||
macro_rules! impl_content_layout_render {
|
impl Tui {
|
||||||
($Output:ty: |$self:ident: $Struct:ty, $to:ident|
|
pub const fn fg <T> (color: Color, w: T) -> TuiForeground<T> {
|
||||||
layout = $layout:expr;
|
TuiForeground(Foreground(color, w))
|
||||||
render = $render:expr) =>
|
|
||||||
{
|
|
||||||
impl Render<$Output> for $Struct {
|
|
||||||
fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout }
|
|
||||||
fn render (&$self, $to: &mut $Output) { $render }
|
|
||||||
}
|
}
|
||||||
|
pub const fn bg <T> (color: Color, w: T) -> TuiBackground<T> {
|
||||||
|
TuiBackground(Background(color, w))
|
||||||
|
}
|
||||||
|
pub const fn fg_bg <T> (fg: Color, bg: Color, w: T) -> TuiBackground<TuiForeground<T>> {
|
||||||
|
TuiBackground(Background(bg, TuiForeground(Foreground(fg, w))))
|
||||||
|
}
|
||||||
|
pub const fn modify <T> (enable: bool, modifier: Modifier, w: T) -> Modify<T> {
|
||||||
|
Modify(enable, modifier, w)
|
||||||
|
}
|
||||||
|
pub const fn bold <T> (enable: bool, w: T) -> Modify<T> {
|
||||||
|
Self::modify(enable, Modifier::BOLD, w)
|
||||||
|
}
|
||||||
|
pub const fn border <S, T> (enable: bool, style: S, w: T) -> Bordered<S, T> {
|
||||||
|
Bordered(enable, style, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{
|
||||||
|
impl Layout<TuiOut> for $Self {
|
||||||
|
fn layout (&$self, $to: [u16;4]) -> [u16;4] {
|
||||||
|
$expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
macro_rules! tui_draw ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{
|
||||||
|
impl Draw<TuiOut> for $Self {
|
||||||
|
fn draw (&$self, $to: &mut TuiOut) {
|
||||||
|
$expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
mod tui_border; pub use self::tui_border::*;
|
mod tui_border; pub use self::tui_border::*;
|
||||||
mod tui_button; //pub use self::tui_button::*;
|
mod tui_button; //pub use self::tui_button::*;
|
||||||
mod tui_color; pub use self::tui_color::*;
|
mod tui_color; pub use self::tui_color::*;
|
||||||
|
|
@ -23,34 +48,26 @@ mod tui_repeat; pub use self::tui_repeat::*;
|
||||||
mod tui_scroll; pub use self::tui_scroll::*;
|
mod tui_scroll; pub use self::tui_scroll::*;
|
||||||
mod tui_string; pub use self::tui_string::*;
|
mod tui_string; pub use self::tui_string::*;
|
||||||
mod tui_style; pub use self::tui_style::*;
|
mod tui_style; pub use self::tui_style::*;
|
||||||
mod tui_tryptich; pub use self::tui_tryptich::*;
|
mod tui_tryptich; //pub use self::tui_tryptich::*;
|
||||||
|
|
||||||
impl<T: Render<TuiOut>> Render<TuiOut> for std::sync::Arc<T> {
|
//impl<T: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
//fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
Render::<TuiOut>::layout(&**self, to)
|
//Bsp::a(self.as_ref().ok(), self.as_ref().err().map(
|
||||||
}
|
//|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string())
|
||||||
fn render (&self, to: &mut TuiOut) {
|
//))
|
||||||
Render::<TuiOut>::render(&**self, to)
|
//}
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Render<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
//impl<T: Draw<TuiOut>> Draw<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
|
||||||
Some(Bsp::a(self.as_ref().ok(), self.as_ref().err()
|
|
||||||
.map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl<T: Render<TuiOut>> Render<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
|
||||||
//fn layout (&self, to: [u16;4]) -> [u16;4] {
|
//fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
//match self {
|
//match self {
|
||||||
//Ok(content) => content.layout(to),
|
//Ok(content) => content.layout(to),
|
||||||
//Err(e) => [0, 0, to.w(), to.h()]
|
//Err(e) => [0, 0, to.w(), to.h()]
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
//fn render (&self, to: &mut TuiOut) {
|
//fn draw (&self, to: &mut TuiOut) {
|
||||||
//match self {
|
//match self {
|
||||||
//Ok(content) => content.render(to),
|
//Ok(content) => content.draw(to),
|
||||||
//Err(e) => to.blit(&e.to_string(), 0, 0, Some(Style::default()
|
//Err(e) => to.blit(&e.to_string(), 0, 0, Some(Style::default()
|
||||||
//.bg(Color::Rgb(32,32,32))
|
//.bg(Color::Rgb(32,32,32))
|
||||||
//.fg(Color::Rgb(255,255,255))))
|
//.fg(Color::Rgb(255,255,255))))
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ impl<T: FocusGrid + HasEnter> FocusOrder for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FocusWrap<T> {
|
pub trait FocusWrap<T> {
|
||||||
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Render<TuiOut> + '_;
|
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Draw<TuiOut> + '_;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {
|
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
impl<S: BorderStyle, W: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Bordered<S, W> {
|
||||||
impl<S: BorderStyle, W: Render<TuiOut>> Content<TuiOut> for Bordered<S, W> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
Fill::xy(lay!(
|
||||||
Some(Fill::xy(
|
When::new(self.0, Border(self.0, self.1)),
|
||||||
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
|
Padding::xy(1, 1, &self.2)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Border<S: BorderStyle>(pub bool, pub S);
|
impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
|
||||||
impl<S: BorderStyle> Render<TuiOut> for Border<S> {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
fn layout (&self, area: [u16;4]) -> [u16;4] {
|
|
||||||
self.1.layout(area)
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
if self.0 {
|
if self.0 {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
if area.w() > 0 && area.y() > 0 {
|
if area.w() > 0 && area.y() > 0 {
|
||||||
|
|
@ -35,15 +31,15 @@ impl<S: BorderStyle> Render<TuiOut> for Border<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BorderStyle: Render<TuiOut> + Copy {
|
pub trait BorderStyle: Draw<TuiOut> + Layout<TuiOut> + Copy {
|
||||||
fn enabled (&self) -> bool;
|
fn enabled (&self) -> bool;
|
||||||
fn enclose <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
fn enclose (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
||||||
}
|
}
|
||||||
fn enclose2 <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
fn enclose2 (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
|
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
|
||||||
}
|
}
|
||||||
fn enclose_bg <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
fn enclose_bg (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
|
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
|
||||||
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
|
||||||
}
|
}
|
||||||
|
|
@ -147,9 +143,10 @@ macro_rules! border {
|
||||||
fn enabled (&self) -> bool { self.0 }
|
fn enabled (&self) -> bool { self.0 }
|
||||||
}
|
}
|
||||||
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
|
||||||
impl Render<TuiOut> for $T {
|
impl Layout<TuiOut> for $T {}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
impl Draw<TuiOut> for $T {
|
||||||
if self.enabled() { let _ = self.draw(to); }
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
|
if self.enabled() { let _ = BorderStyle::draw(self, to); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+}
|
)+}
|
||||||
|
|
@ -260,11 +257,3 @@ border! {
|
||||||
fn style (&self) -> Option<Style> { Some(self.1) }
|
fn style (&self) -> Option<Style> { Some(self.1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//impl<S: BorderStyle, R: Content<TuiOut>> Content<TuiOut> for Bordered<S, R> {
|
|
||||||
//fn content (&self) -> impl Render<TuiOut> {
|
|
||||||
//let content: &dyn Content<TuiOut> = &self.1;
|
|
||||||
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,28 @@ use crate::*;
|
||||||
use ratatui::style::Stylize;
|
use ratatui::style::Stylize;
|
||||||
|
|
||||||
// Thunks can be natural error boundaries!
|
// Thunks can be natural error boundaries!
|
||||||
pub struct ErrorBoundary<O: Output, T: Render<O>>(
|
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
|
||||||
std::marker::PhantomData<O>, Perhaps<T>
|
std::marker::PhantomData<O>, Perhaps<T>
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<O: Output, T: Render<O>> ErrorBoundary<O, T> {
|
impl<O: Out, T: Draw<O>> ErrorBoundary<O, T> {
|
||||||
pub fn new (content: Perhaps<T>) -> Self {
|
pub fn new (content: Perhaps<T>) -> Self {
|
||||||
Self(Default::default(), content)
|
Self(Default::default(), content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Render<TuiOut>> Render<TuiOut> for ErrorBoundary<TuiOut, T> {
|
impl<T: Draw<TuiOut>> Draw<TuiOut> for ErrorBoundary<TuiOut, T> {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
match self.1.as_ref() {
|
match self.1.as_ref() {
|
||||||
Ok(Some(content)) => content.render(to),
|
Ok(Some(content)) => content.draw(to),
|
||||||
Ok(None) => to.blit(&"empty?", 0, 0, Some(Style::default().yellow())),
|
Ok(None) => to.blit(&"empty?", 0, 0, Some(Style::default().yellow())),
|
||||||
Err(e) => Tui::fg_bg(
|
Err(e) => {
|
||||||
Color::Rgb(255,224,244), Color::Rgb(96,24,24), Bsp::s(
|
let err_fg = Color::Rgb(255,224,244);
|
||||||
Bsp::e(Tui::bold(true, "oops. "), "rendering failed."),
|
let err_bg = Color::Rgb(96,24,24);
|
||||||
Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))))
|
let title = Bsp::e(Tui::bold(true, "oops. "), "rendering failed.");
|
||||||
).render(to)
|
let error = Bsp::e("\"why?\" ", Tui::bold(true, format!("{e}")));
|
||||||
|
to.place(&Tui::fg_bg(err_fg, err_bg, Bsp::s(title, error)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,34 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct FieldH<T, U>(pub ItemTheme, pub T, pub U);
|
impl<
|
||||||
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldH<T, U> {
|
Label: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
Value: Draw<TuiOut> + Layout<TuiOut>
|
||||||
|
> Content<TuiOut> for FieldH<ItemTheme, Label, Value> {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
Some(row!(
|
row!(
|
||||||
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
Tui::fg_bg(dark.rgb, darkest.rgb, "▐"),
|
||||||
Tui::fg_bg(lightest.rgb, dark.rgb, title),
|
Tui::fg_bg(lightest.rgb, dark.rgb, title),
|
||||||
Tui::fg_bg(dark.rgb, darkest.rgb, "▌"),
|
Tui::fg_bg(dark.rgb, darkest.rgb, "▌"),
|
||||||
Tui::fg_bg(lightest.rgb, darkest.rgb, Tui::bold(true, value)),
|
Tui::fg_bg(lightest.rgb, darkest.rgb, Tui::bold(true, value)),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FieldV<T, U>(pub ItemTheme, pub T, pub U);
|
impl<
|
||||||
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldV<T, U> {
|
Label: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
Value: Draw<TuiOut> + Layout<TuiOut>
|
||||||
|
> Content<TuiOut> for FieldV<ItemTheme, Label, Value> {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
Some(Bsp::n(
|
Bsp::n(
|
||||||
Align::w(Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, Tui::bold(true, value)))),
|
Align::w(Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, Tui::bold(true, value)))),
|
||||||
Fill::x(Align::w(row!(
|
Fill::x(Align::w(row!(
|
||||||
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")),
|
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▐")),
|
||||||
Tui::bg(dark.rgb, Tui::fg(lightest.rgb, title)),
|
Tui::bg(dark.rgb, Tui::fg(lightest.rgb, title)),
|
||||||
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")),
|
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "▌")),
|
||||||
)))
|
)))
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,9 +44,9 @@ pub struct Field<T, U> {
|
||||||
pub value_bg: Option<ItemColor>,
|
pub value_bg: Option<ItemColor>,
|
||||||
pub value_align: Option<Direction>,
|
pub value_align: Option<Direction>,
|
||||||
}
|
}
|
||||||
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for Field<T, U> {
|
impl<T: Draw<TuiOut>, U: Draw<TuiOut>> Content<TuiOut> for Field<T, U> {
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
Some("TODO")
|
"TODO"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl Render<TuiOut> for u64 {
|
impl Draw<TuiOut> for u64 {
|
||||||
fn render (&self, _to: &mut TuiOut) {
|
fn draw (&self, _to: &mut TuiOut) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render<TuiOut> for f64 {
|
impl Draw<TuiOut> for f64 {
|
||||||
fn render (&self, _to: &mut TuiOut) {
|
fn draw (&self, _to: &mut TuiOut) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,26 @@ pub struct Phat<T> {
|
||||||
pub content: T,
|
pub content: T,
|
||||||
pub colors: [Color;4],
|
pub colors: [Color;4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Phat<T> {
|
impl<T> Phat<T> {
|
||||||
pub const LO: &'static str = "▄";
|
pub const LO: &'static str = "▄";
|
||||||
pub const HI: &'static str = "▀";
|
pub const HI: &'static str = "▀";
|
||||||
/// A phat line
|
/// A phat line
|
||||||
pub fn lo (fg: Color, bg: Color) -> impl Render<TuiOut> {
|
pub fn lo (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
|
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
|
||||||
}
|
}
|
||||||
/// A phat line
|
/// A phat line
|
||||||
pub fn hi (fg: Color, bg: Color) -> impl Render<TuiOut> {
|
pub fn hi (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
|
||||||
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
|
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Render<TuiOut>> Content<TuiOut> for Phat<T> {
|
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Content<TuiOut> for Phat<T> {
|
||||||
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
let [fg, bg, hi, lo] = self.colors;
|
let [fg, bg, hi, lo] = self.colors;
|
||||||
let top = Fixed::y(1, Self::lo(bg, hi));
|
let top = Fixed::y(1, Self::lo(bg, hi));
|
||||||
let low = Fixed::y(1, Self::hi(bg, lo));
|
let low = Fixed::y(1, Self::hi(bg, lo));
|
||||||
let content = Tui::fg_bg(fg, bg, &self.content);
|
let content = Tui::fg_bg(fg, bg, &self.content);
|
||||||
Some(Min::xy(
|
Min::xy(self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::xy(content))))
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
Bsp::s(top, Bsp::n(low, Fill::xy(content)))
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ use crate::*;
|
||||||
use ratatui::prelude::Position;
|
use ratatui::prelude::Position;
|
||||||
|
|
||||||
pub struct Repeat<'a>(pub &'a str);
|
pub struct Repeat<'a>(pub &'a str);
|
||||||
impl Render<TuiOut> for Repeat<'_> {
|
impl Layout<TuiOut> for Repeat<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
to
|
to
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl Draw<TuiOut> for Repeat<'_> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, w, h] = to.area().xywh();
|
let [x, y, w, h] = to.area().xywh();
|
||||||
let a = self.0.len();
|
let a = self.0.len();
|
||||||
for (_v, y) in (y..y+h).enumerate() {
|
for (_v, y) in (y..y+h).enumerate() {
|
||||||
|
|
@ -21,11 +23,13 @@ impl Render<TuiOut> for Repeat<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatV<'a>(pub &'a str);
|
pub struct RepeatV<'a>(pub &'a str);
|
||||||
impl Render<TuiOut> for RepeatV<'_> {
|
impl Layout<TuiOut> for RepeatV<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
to
|
to
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl Draw<TuiOut> for RepeatV<'_> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, _w, h] = to.area().xywh();
|
let [x, y, _w, h] = to.area().xywh();
|
||||||
for y in y..y+h {
|
for y in y..y+h {
|
||||||
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {
|
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {
|
||||||
|
|
@ -36,11 +40,13 @@ impl Render<TuiOut> for RepeatV<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatH<'a>(pub &'a str);
|
pub struct RepeatH<'a>(pub &'a str);
|
||||||
impl Render<TuiOut> for RepeatH<'_> {
|
impl Layout<TuiOut> for RepeatH<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
to
|
to
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl Draw<TuiOut> for RepeatH<'_> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y, w, _h] = to.area().xywh();
|
let [x, y, w, _h] = to.area().xywh();
|
||||||
for x in x..x+w {
|
for x in x..x+w {
|
||||||
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {
|
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ impl ScrollbarH {
|
||||||
const ICON_INC: &[char] = &[' ', '🞂', ' '];
|
const ICON_INC: &[char] = &[' ', '🞂', ' '];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render<TuiOut> for ScrollbarV {
|
impl Draw<TuiOut> for ScrollbarV {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x, y1, _w, h] = to.area().xywh();
|
let [x, y1, _w, h] = to.area().xywh();
|
||||||
let y2 = y1 + h;
|
let y2 = y1 + h;
|
||||||
for (i, y) in (y1..=y2).enumerate() {
|
for (i, y) in (y1..=y2).enumerate() {
|
||||||
|
|
@ -51,8 +51,8 @@ impl Render<TuiOut> for ScrollbarV {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render<TuiOut> for ScrollbarH {
|
impl Draw<TuiOut> for ScrollbarH {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let [x1, y, w, _h] = to.area().xywh();
|
let [x1, y, w, _h] = to.area().xywh();
|
||||||
let x2 = x1 + w;
|
let x2 = x1 + w;
|
||||||
for (i, x) in (x1..=x2).enumerate() {
|
for (i, x) in (x1..=x2).enumerate() {
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,14 @@ use crate::*;
|
||||||
use crate::ratatui::prelude::Position;
|
use crate::ratatui::prelude::Position;
|
||||||
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: &str, to|
|
tui_layout!(|self: &str, to|to.center_xy([width_chars_max(to.w(), self), 1]));
|
||||||
layout = to.center_xy([width_chars_max(to.w(), self), 1]);
|
tui_draw!(|self: &str, to|{
|
||||||
render = {let [x, y, w, ..] = Render::layout(self, to.area());
|
let [x, y, w, ..] = self.layout(to.area());
|
||||||
to.text(self, x, y, w)});
|
to.text(&self, x, y, w)
|
||||||
|
});
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: String, to|
|
tui_layout!(|self: String, to|self.as_str().layout(to));
|
||||||
layout = Render::<TuiOut>::layout(&self.as_str(), to);
|
tui_draw!(|self: String, to|self.as_str().draw(to));
|
||||||
render = Render::<TuiOut>::render(&self.as_str(), to));
|
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: Arc<str>, to|
|
|
||||||
layout = Render::<TuiOut>::layout(&self.as_ref(), to);
|
|
||||||
render = Render::<TuiOut>::render(&self.as_ref(), to));
|
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: std::sync::RwLock<String>, to|
|
|
||||||
layout = Render::<TuiOut>::layout(&self.read().unwrap(), to);
|
|
||||||
render = Render::<TuiOut>::render(&self.read().unwrap(), to));
|
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: std::sync::RwLockReadGuard<'_, String>, to|
|
|
||||||
layout = Render::<TuiOut>::layout(&**self, to);
|
|
||||||
render = Render::<TuiOut>::render(&**self, to));
|
|
||||||
|
|
||||||
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
|
||||||
let mut width: u16 = 0;
|
let mut width: u16 = 0;
|
||||||
|
|
@ -61,12 +49,14 @@ impl<'a, T: AsRef<str>> TrimString<T> {
|
||||||
TrimStringRef(self.0, &self.1)
|
TrimStringRef(self.0, &self.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: AsRef<str>> Render<TuiOut> for TrimString<T> {
|
impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimString<T> {
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
||||||
Render::layout(&self.as_ref(), to)
|
Layout::layout(&self.as_ref(), to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
Render::render(&self.as_ref(), to)
|
impl<'a, T: AsRef<str>> Draw<TuiOut> for TrimString<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
|
Draw::draw(&self.as_ref(), to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,11 +65,13 @@ impl<'a, T: AsRef<str>> Render<TuiOut> for TrimString<T> {
|
||||||
/// Width is computed using [unicode_width].
|
/// Width is computed using [unicode_width].
|
||||||
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
||||||
|
|
||||||
impl<T: AsRef<str>> Render<TuiOut> for TrimStringRef<'_, T> {
|
impl<T: AsRef<str>> Layout<TuiOut> for TrimStringRef<'_, T> {
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
||||||
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
|
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
|
||||||
}
|
}
|
||||||
fn render (&self, target: &mut TuiOut) {
|
}
|
||||||
|
impl<T: AsRef<str>> Draw<TuiOut> for TrimStringRef<'_, T> {
|
||||||
|
fn draw (&self, target: &mut TuiOut) {
|
||||||
let area = target.area();
|
let area = target.area();
|
||||||
let mut width: u16 = 1;
|
let mut width: u16 = 1;
|
||||||
let mut chars = self.1.as_ref().chars();
|
let mut chars = self.1.as_ref().chars();
|
||||||
|
|
|
||||||
|
|
@ -1,70 +1,56 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait TuiStyle {
|
pub struct TuiForeground<T>(pub(crate) Foreground<Color, T>);
|
||||||
fn fg <R: Render<TuiOut>> (color: Color, w: R) -> Foreground<R> {
|
pub struct TuiBackground<T>(pub(crate) Background<Color, T>);
|
||||||
Foreground(color, w)
|
pub struct Modify<T>(pub bool, pub Modifier, pub T);
|
||||||
}
|
pub struct Styled<T>(pub Option<Style>, pub T);
|
||||||
fn bg <R: Render<TuiOut>> (color: Color, w: R) -> Background<R> {
|
|
||||||
Background(color, w)
|
|
||||||
}
|
|
||||||
fn fg_bg <R: Render<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
|
|
||||||
Background(bg, Foreground(fg, w))
|
|
||||||
}
|
|
||||||
fn modify <R: Render<TuiOut>> (enable: bool, modifier: Modifier, w: R) -> Modify<R> {
|
|
||||||
Modify(enable, modifier, w)
|
|
||||||
}
|
|
||||||
fn bold <R: Render<TuiOut>> (enable: bool, w: R) -> Modify<R> {
|
|
||||||
Self::modify(enable, Modifier::BOLD, w)
|
|
||||||
}
|
|
||||||
fn border <R: Render<TuiOut>, S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered<S, R> {
|
|
||||||
Bordered(enable, style, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TuiStyle for Tui {}
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiForeground<T> {
|
||||||
|
|
||||||
pub struct Foreground<R>(pub Color, pub R);
|
|
||||||
impl<R: Render<TuiOut>> Render<TuiOut> for Foreground<R> {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
self.1.layout(to)
|
self.0.layout(to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiForeground<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let area = self.layout(to.area());
|
let area = self.layout(to.area());
|
||||||
to.fill_fg(area, self.0);
|
to.fill_bg(area, self.0.0);
|
||||||
to.place(area, &self.1);
|
to.place_at(area, &self.0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Background<R>(pub Color, pub R);
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiBackground<T> {
|
||||||
impl<R: Render<TuiOut>> Render<TuiOut> for Background<R> {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
self.1.layout(to)
|
self.0.layout(to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiBackground<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
let area = self.layout(to.area());
|
let area = self.layout(to.area());
|
||||||
to.fill_bg(area, self.0);
|
to.fill_bg(area, self.0.0);
|
||||||
to.place(area, &self.1);
|
to.place_at(area, &self.0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Modify<R: Render<TuiOut>>(pub bool, pub Modifier, pub R);
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for Modify<T> {
|
||||||
impl<R: Render<TuiOut>> Render<TuiOut> for Modify<R> {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
self.2.layout(to)
|
self.2.layout(to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
|
impl<T: Draw<TuiOut>> Draw<TuiOut> for Modify<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
to.fill_mod(to.area(), self.0, self.1);
|
to.fill_mod(to.area(), self.0, self.1);
|
||||||
self.2.render(to)
|
self.2.draw(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Styled<R: Render<TuiOut>>(pub Option<Style>, pub R);
|
impl<T: Layout<TuiOut>> Layout<TuiOut> for Styled<T> {
|
||||||
impl<R: Render<TuiOut>> Render<TuiOut> for Styled<R> {
|
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
self.1.layout(to)
|
self.1.layout(to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
}
|
||||||
to.place(self.1.layout(to.area()), &self.1);
|
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for Styled<T> {
|
||||||
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
|
to.place(&self.1);
|
||||||
// TODO write style over area
|
// TODO write style over area
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,13 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// A three-column layout.
|
impl<
|
||||||
pub struct Tryptich<A, B, C> {
|
A: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
pub top: bool,
|
B: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
pub h: u16,
|
C: Draw<TuiOut> + Layout<TuiOut>,
|
||||||
pub left: (u16, A),
|
> Content<TuiOut> for Tryptich<A, B, C> {
|
||||||
pub middle: (u16, B),
|
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A, B, C> Content<TuiOut> for Tryptich<A, B, C>
|
|
||||||
where A: Render<TuiOut>, B: Render<TuiOut>, C: Render<TuiOut> {
|
|
||||||
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
|
||||||
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
|
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
|
||||||
Some(Fixed::y(h, if top {
|
Fixed::y(h, if top {
|
||||||
Bsp::a(
|
Bsp::a(
|
||||||
Fill::x(Align::n(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
|
Fill::x(Align::n(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
|
||||||
Bsp::a(
|
Bsp::a(
|
||||||
|
|
@ -50,7 +23,7 @@ where A: Render<TuiOut>, B: Render<TuiOut>, C: Render<TuiOut> {
|
||||||
Fill::xy(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
|
Fill::xy(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ mod tui_output; pub use self::tui_output::*;
|
||||||
mod tui_perf; pub use self::tui_perf::*;
|
mod tui_perf; pub use self::tui_perf::*;
|
||||||
|
|
||||||
// The `Tui` struct (the *engine*) implements the
|
// The `Tui` struct (the *engine*) implements the
|
||||||
// `tengri_input::Input` and `tengri_output::Output` traits.
|
// `tengri_input::Input` and `tengri_output::Out` traits.
|
||||||
|
|
||||||
// At launch, the `Tui` engine spawns two threads, the render thread and the input thread.
|
// At launch, the `Tui` engine spawns two threads, the render thread and the input thread.
|
||||||
// the application may further spawn other threads. All threads communicate using shared ownership:
|
// the application may further spawn other threads. All threads communicate using shared ownership:
|
||||||
|
|
@ -73,12 +73,12 @@ impl Tui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
|
pub trait TuiRun<R: Draw<TuiOut> + Handle<TuiIn> + 'static> {
|
||||||
/// Run an app in the main loop.
|
/// Run an app in the main loop.
|
||||||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
impl<T: Draw<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||||
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
||||||
self.write().unwrap().setup()?;
|
self.write().unwrap().setup()?;
|
||||||
|
|
@ -90,7 +90,7 @@ impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Ar
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
self.write().unwrap().teardown()?;
|
self.write().unwrap().teardown()?;
|
||||||
panic!("\n\rRender thread failed: {error:?}.\n\r")
|
panic!("\n\rDraw thread failed: {error:?}.\n\r")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub struct TuiOut {
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub area: [u16;4]
|
pub area: [u16;4]
|
||||||
}
|
}
|
||||||
impl Output for TuiOut {
|
impl Out for TuiOut {
|
||||||
type Unit = u16;
|
type Unit = u16;
|
||||||
type Size = [Self::Unit;2];
|
type Size = [Self::Unit;2];
|
||||||
type Area = [Self::Unit;4];
|
type Area = [Self::Unit;4];
|
||||||
|
|
@ -17,16 +17,16 @@ impl Output for TuiOut {
|
||||||
#[inline] fn area_mut (&mut self) -> &mut [u16;4] {
|
#[inline] fn area_mut (&mut self) -> &mut [u16;4] {
|
||||||
&mut self.area
|
&mut self.area
|
||||||
}
|
}
|
||||||
#[inline] fn place <'t, T: Render<Self> + ?Sized> (&mut self, area: [u16;4], content: &'t T) {
|
#[inline] fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], content: &'t T) {
|
||||||
let last = self.area();
|
let last = self.area();
|
||||||
*self.area_mut() = area;
|
*self.area_mut() = area;
|
||||||
content.render(self);
|
content.draw(self);
|
||||||
*self.area_mut() = last;
|
*self.area_mut() = last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl TuiOut {
|
impl TuiOut {
|
||||||
/// Spawn the output thread.
|
/// Spawn the output thread.
|
||||||
pub fn run_output <T: Render<TuiOut> + Send + Sync + 'static> (
|
pub fn run_output <T: Draw<TuiOut> + Send + Sync + 'static> (
|
||||||
engine: &Arc<RwLock<Tui>>,
|
engine: &Arc<RwLock<Tui>>,
|
||||||
state: &Arc<RwLock<T>>,
|
state: &Arc<RwLock<T>>,
|
||||||
timer: Duration
|
timer: Duration
|
||||||
|
|
@ -52,7 +52,7 @@ impl TuiOut {
|
||||||
buffer.reset();
|
buffer.reset();
|
||||||
}
|
}
|
||||||
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
|
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
|
||||||
state.render(&mut output);
|
state.draw(&mut output);
|
||||||
buffer = engine.write().unwrap().flip(output.buffer, size);
|
buffer = engine.write().unwrap().flip(output.buffer, size);
|
||||||
}
|
}
|
||||||
//let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();
|
//let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue