mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 19:56:44 +01:00
Compare commits
2 commits
74b3af2212
...
194f2f9874
| Author | SHA1 | Date | |
|---|---|---|---|
| 194f2f9874 | |||
| 1c21a85f27 |
28 changed files with 355 additions and 635 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! 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 Content<TuiOut>, expected: [u16;4]) {
|
//! fn test (area: [u16;4], item: &impl Render<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!(Render::layout(item, area), expected);
|
||||||
//! };
|
//! };
|
||||||
|
|
@ -48,18 +48,24 @@ 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: Content<E>> Content<E> for Align<A> {
|
impl<E: Output, A: Render<E>> Render<E> for Align<A> {
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
|
||||||
&self.1
|
|
||||||
}
|
|
||||||
fn layout (&self, on: E::Area) -> E::Area {
|
fn layout (&self, on: E::Area) -> E::Area {
|
||||||
|
self.0.align(on, &self.1)
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut E) {
|
||||||
|
to.place(self.layout(to.area()), &self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Alignment {
|
||||||
|
fn align <E: Output> (&self, on: E::Area, content: impl Render<E>) -> E::Area {
|
||||||
use Alignment::*;
|
use Alignment::*;
|
||||||
let it = Render::layout(&self.content(), 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());
|
||||||
let cy = on.y()+(on.h().minus(it.h())/2.into());
|
let cy = on.y()+(on.h().minus(it.h())/2.into());
|
||||||
let fx = (on.x()+on.w()).minus(it.w());
|
let fx = (on.x()+on.w()).minus(it.w());
|
||||||
let fy = (on.y()+on.h()).minus(it.h());
|
let fy = (on.y()+on.h()).minus(it.h());
|
||||||
let [x, y] = match self.0 {
|
let [x, y] = match self {
|
||||||
Center => [cx, cy],
|
Center => [cx, cy],
|
||||||
X => [cx, it.y()],
|
X => [cx, it.y()],
|
||||||
Y => [it.x(), cy],
|
Y => [it.x(), cy],
|
||||||
|
|
@ -71,10 +77,7 @@ impl<E: Output, A: Content<E>> Content<E> for Align<A> {
|
||||||
SW => [on.x(), fy],
|
SW => [on.x(), fy],
|
||||||
S => [cx, fy],
|
S => [cx, fy],
|
||||||
SE => [fx, fy],
|
SE => [fx, fy],
|
||||||
}.into();
|
};
|
||||||
[x, y, it.w(), it.h()].into()
|
[x, y, it.w(), it.h()].into()
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut E) {
|
|
||||||
to.place(Content::layout(self, to.area()), &self.content())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ impl<A, B> Bsp<A, B> {
|
||||||
#[inline] pub const fn a (a: A, b: B) -> Self { Self(Above, 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) }
|
#[inline] pub const fn b (a: A, b: B) -> Self { Self(Below, a, b) }
|
||||||
}
|
}
|
||||||
impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<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 layout (&self, outer: E::Area) -> E::Area { let [_, _, c] = self.areas(outer); c }
|
||||||
fn render (&self, to: &mut E) {
|
fn render (&self, to: &mut E) {
|
||||||
let [area_a, area_b, _] = self.areas(to.area());
|
let [area_a, area_b, _] = self.areas(to.area());
|
||||||
|
|
@ -26,11 +26,11 @@ impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Output, A: Content<E>, B: Content<E>> BspAreas<E, A, B> for Bsp<A, B> {
|
impl<E: Output, A: Render<E>, B: Render<E>> BspAreas<E, A, B> for Bsp<A, B> {
|
||||||
fn direction (&self) -> Direction { self.0 }
|
fn direction (&self) -> Direction { self.0 }
|
||||||
fn contents (&self) -> (&A, &B) { (&self.1, &self.2) }
|
fn contents (&self) -> (&A, &B) { (&self.1, &self.2) }
|
||||||
}
|
}
|
||||||
pub trait BspAreas<E: Output, A: Content<E>, B: Content<E>> {
|
pub trait BspAreas<E: Output, A: Render<E>, B: Render<E>> {
|
||||||
fn direction (&self) -> Direction;
|
fn direction (&self) -> Direction;
|
||||||
fn contents (&self) -> (&A, &B);
|
fn contents (&self) -> (&A, &B);
|
||||||
fn areas (&self, outer: E::Area) -> [E::Area;3] {
|
fn areas (&self, outer: E::Area) -> [E::Area;3] {
|
||||||
|
|
|
||||||
|
|
@ -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>> Content<E> for When<A> {
|
impl<E: Output, A: Render<E>> Render<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();
|
||||||
|
|
@ -31,7 +31,7 @@ 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>> Content<E> for Either<A, B> {
|
impl<E: Output, A: Render<E>, B: Render<E>> Render<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) }
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ 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> Content<E> for Map<E, A, B, I, F, G> where
|
impl<'a, E, A, B, I, F, G> Render<E> for Map<E, A, B, I, F, G> where
|
||||||
E: Output,
|
E: Output,
|
||||||
B: Render<E>,
|
B: Render<E>,
|
||||||
I: Iterator<Item = A> + Send + Sync + 'a,
|
I: Iterator<Item = A> + Send + Sync + 'a,
|
||||||
|
|
@ -94,7 +94,7 @@ impl<'a, E, A, B, I, F, G> Content<E> for Map<E, A, B, I, F, G> where
|
||||||
fn render (&self, to: &mut E) {
|
fn render (&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 = Content::layout(self, to.area());
|
let area = Render::layout(self, 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(area.into(), &item);
|
||||||
|
|
@ -107,24 +107,24 @@ impl<'a, E, A, B, I, F, G> Content<E> for Map<E, A, B, I, F, G> where
|
||||||
#[inline] pub fn map_south<O: Output>(
|
#[inline] pub fn map_south<O: Output>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Render<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Render<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: Output>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_height: O::Unit,
|
item_height: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Render<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Render<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: Output>(
|
||||||
item_offset: O::Unit,
|
item_offset: O::Unit,
|
||||||
item_width: O::Unit,
|
item_width: O::Unit,
|
||||||
item: impl Content<O>
|
item: impl Render<O>
|
||||||
) -> impl Content<O> {
|
) -> impl Render<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))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,44 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use Direction::*;
|
use Direction::*;
|
||||||
|
|
||||||
pub struct Stack<'x, E, F> {
|
pub struct Stack<'x, E, F1> {
|
||||||
__: PhantomData<&'x E>,
|
__: PhantomData<&'x (E, F1)>,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
callback: F
|
callback: F1
|
||||||
}
|
}
|
||||||
impl<'x, E, F: Fn(&mut dyn FnMut(&dyn Render<E>)) + 'x> Stack<'x, E, F> {
|
|
||||||
pub fn new (direction: Direction, callback: F) -> Self {
|
impl<'x, E, F1> Stack<'x, E, F1> {
|
||||||
|
pub fn new (direction: Direction, callback: F1) -> Self {
|
||||||
Self { direction, callback, __: Default::default(), }
|
Self { direction, callback, __: Default::default(), }
|
||||||
}
|
}
|
||||||
pub fn above (callback: F) -> Self {
|
pub fn above (callback: F1) -> Self {
|
||||||
Self::new(Above, callback)
|
Self::new(Above, callback)
|
||||||
}
|
}
|
||||||
pub fn below (callback: F) -> Self {
|
pub fn below (callback: F1) -> Self {
|
||||||
Self::new(Below, callback)
|
Self::new(Below, callback)
|
||||||
}
|
}
|
||||||
pub fn north (callback: F) -> Self {
|
pub fn north (callback: F1) -> Self {
|
||||||
Self::new(North, callback)
|
Self::new(North, callback)
|
||||||
}
|
}
|
||||||
pub fn south (callback: F) -> Self {
|
pub fn south (callback: F1) -> Self {
|
||||||
Self::new(South, callback)
|
Self::new(South, callback)
|
||||||
}
|
}
|
||||||
pub fn east (callback: F) -> Self {
|
pub fn east (callback: F1) -> Self {
|
||||||
Self::new(East, callback)
|
Self::new(East, callback)
|
||||||
}
|
}
|
||||||
pub fn west (callback: F) -> Self {
|
pub fn west (callback: F1) -> Self {
|
||||||
Self::new(West, callback)
|
Self::new(West, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'x, E: Output, F: Fn(&mut dyn FnMut(&dyn Render<E>)) + 'x> Content<E> for Stack<'x, E, F> {
|
|
||||||
|
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 {
|
fn layout (&self, to: E::Area) -> E::Area {
|
||||||
let state = StackLayoutState::<E>::new(self.direction, to);
|
let state = StackLayoutState::<E>::new(self.direction, to);
|
||||||
let mut adder = |component: &dyn Render<E>|{
|
(self.callback)(&mut |component: &dyn Render<E>|{
|
||||||
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
|
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
|
||||||
let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
|
let [_, _, w, h] = component.layout([x, y, w_remaining, h_remaining].into()).xywh();
|
||||||
state.borrow_mut().grow(w, h);
|
state.borrow_mut().grow(w, h);
|
||||||
};
|
});
|
||||||
(self.callback)(&mut adder);
|
|
||||||
let StackLayoutState { w_used, h_used, .. } = *state.borrow();
|
let StackLayoutState { w_used, h_used, .. } = *state.borrow();
|
||||||
match self.direction {
|
match self.direction {
|
||||||
North | West => { todo!() },
|
North | West => { todo!() },
|
||||||
|
|
@ -48,13 +49,12 @@ impl<'x, E: Output, F: Fn(&mut dyn FnMut(&dyn Render<E>)) + 'x> Content<E> for S
|
||||||
fn render (&self, to: &mut E) {
|
fn render (&self, to: &mut E) {
|
||||||
let state = StackLayoutState::<E>::new(self.direction, to.area());
|
let state = StackLayoutState::<E>::new(self.direction, to.area());
|
||||||
let to = Rc::new(RefCell::new(to));
|
let to = Rc::new(RefCell::new(to));
|
||||||
let mut adder = |component: &dyn Render<E>|{
|
(self.callback)(&mut |component: &dyn Render<E>|{
|
||||||
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
|
let StackLayoutState { x, y, w_remaining, h_remaining, .. } = *state.borrow();
|
||||||
let layout = component.layout([x, y, w_remaining, h_remaining].into());
|
let layout = component.layout([x, y, w_remaining, h_remaining].into());
|
||||||
state.borrow_mut().grow(layout.w(), layout.h());
|
state.borrow_mut().grow(layout.w(), layout.h());
|
||||||
to.borrow_mut().place(layout, component);
|
to.borrow_mut().place(layout, component);
|
||||||
};
|
});
|
||||||
(self.callback)(&mut adder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ impl<E: Output> StackLayoutState<E> {
|
||||||
//Self { direction, callback, __: Default::default(), }
|
//Self { direction, callback, __: Default::default(), }
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
//impl<'a, E, F1> Content<E> for Stack<'a, E, F1> where
|
//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,
|
//E: Output, F1: Fn(&mut dyn FnMut(&'a dyn Render<E>)) + Send + Sync,
|
||||||
//{
|
//{
|
||||||
//fn layout (&self, to: E::Area) -> E::Area {
|
//fn layout (&self, to: E::Area) -> E::Area {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! 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 Content<TuiOut>, expected: [u16;4]) {
|
//! fn test (area: [u16;4], item: &impl Render<TuiOut>, expected: [u16;4]) {
|
||||||
//! assert_eq!(Content::layout(item, area), expected);
|
//! assert_eq!(item.layout(area), expected);
|
||||||
//! assert_eq!(Render::layout(item, area), expected);
|
|
||||||
//! };
|
//! };
|
||||||
//! test(area, &(), [20, 20, 0, 0]);
|
//! test(area, &(), [20, 20, 0, 0]);
|
||||||
//!
|
//!
|
||||||
|
|
@ -28,18 +27,20 @@ macro_rules! transform_xy {
|
||||||
#[inline] pub const fn y (item: A) -> Self { Self::Y(item) }
|
#[inline] pub const fn y (item: A) -> Self { Self::Y(item) }
|
||||||
#[inline] pub const fn xy (item: A) -> Self { Self::XY(item) }
|
#[inline] pub const fn xy (item: A) -> Self { Self::XY(item) }
|
||||||
}
|
}
|
||||||
impl<E: Output, T: Content<E>> Content<E> for $Enum<T> {
|
impl<E: Output, T: Render<E>> Content<E> for $Enum<T> {
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
fn content (&self) -> Option<impl Render<E> + '_> {
|
||||||
match self {
|
use $Enum::*;
|
||||||
Self::X(item) => item,
|
Some(match self { X(item) | Y(item) | XY(item) => item, })
|
||||||
Self::Y(item) => item,
|
|
||||||
Self::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 {
|
fn layout (&$self, $to: <E as Output>::Area) -> <E as Output>::Area {
|
||||||
use $Enum::*;
|
use $Enum::*;
|
||||||
$area
|
$area
|
||||||
}
|
}
|
||||||
|
fn render (&self, output: &mut E) {
|
||||||
|
output.place(self.layout(output.area()), &self.content())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,13 +55,18 @@ macro_rules! transform_xy_unit {
|
||||||
#[inline] pub const fn y (y: U, item: A) -> Self { Self::Y(y, 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 xy (x: U, y: U, item: A) -> Self { Self::XY(x, y, item) }
|
||||||
}
|
}
|
||||||
impl<E: Output, T: Content<E>> Content<E> for $Enum<E::Unit, T> {
|
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 {
|
fn layout (&$self, $to: E::Area) -> E::Area {
|
||||||
$layout.into()
|
$layout.into()
|
||||||
}
|
}
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
fn render (&self, output: &mut E) {
|
||||||
use $Enum::*;
|
output.place(self.layout(output.area()), &self.content())
|
||||||
Some(match self { X(_, c) => c, Y(_, c) => c, XY(_, _, c) => c, })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<U: Coordinate, T> $Enum<U, T> {
|
impl<U: Coordinate, T> $Enum<U, T> {
|
||||||
|
|
@ -88,12 +94,11 @@ transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{
|
||||||
|
|
||||||
transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{
|
transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{
|
||||||
let [x, y, w, h] = area.xywh();
|
let [x, y, w, h] = area.xywh();
|
||||||
let fixed_area = match self {
|
let [x, y, w, h] = self.content().layout(match self {
|
||||||
Self::X(fw, _) => [x, y, *fw, h],
|
Self::X(fw, _) => [x, y, *fw, h],
|
||||||
Self::Y(fh, _) => [x, y, w, *fh],
|
Self::Y(fh, _) => [x, y, w, *fh],
|
||||||
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
|
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
|
||||||
};
|
}.into()).xywh();
|
||||||
let [x, y, w, h] = Render::layout(&self.content(), fixed_area.into()).xywh();
|
|
||||||
let fixed_area = match self {
|
let fixed_area = match self {
|
||||||
Self::X(fw, _) => [x, y, *fw, h],
|
Self::X(fw, _) => [x, y, *fw, h],
|
||||||
Self::Y(fh, _) => [x, y, w, *fh],
|
Self::Y(fh, _) => [x, y, w, *fh],
|
||||||
|
|
@ -103,51 +108,41 @@ transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{
|
||||||
});
|
});
|
||||||
|
|
||||||
transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{
|
transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{
|
||||||
let area = Render::layout(&self.content(), area);
|
let [x, y, w, h] = self.content().layout(area).xywh();
|
||||||
match self {
|
match self {
|
||||||
Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()],
|
Self::X(mw, _) => [x, y, w.max(*mw), h],
|
||||||
Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)],
|
Self::Y(mh, _) => [x, y, w, h.max(*mh)],
|
||||||
Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.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|{
|
transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{
|
||||||
let [x, y, w, h] = area.xywh();
|
let [x, y, w, h] = area.xywh();
|
||||||
Render::layout(&self.content(), match self {
|
self.content().layout(match self {
|
||||||
Self::X(fw, _) => [x, y, *fw, h],
|
Self::X(fw, _) => [x, y, *fw, h],
|
||||||
Self::Y(fh, _) => [x, y, w, *fh],
|
Self::Y(fh, _) => [x, y, w, *fh],
|
||||||
Self::XY(fw, fh, _) => [x, y, *fw, *fh],
|
Self::XY(fw, fh, _) => [x, y, *fw, *fh], }.into()) });
|
||||||
}.into())
|
|
||||||
});
|
|
||||||
|
|
||||||
transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|Render::layout(
|
transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|self.content().layout(
|
||||||
&self.content(),
|
|
||||||
[area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into()));
|
[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|Render::layout(
|
transform_xy_unit!("expand/x" "expand/y" "expand/xy"|self: Expand, area|self.content().layout(
|
||||||
&self.content(),
|
|
||||||
[area.x(), area.y(), area.w().plus(self.dx()), area.h().plus(self.dy())].into()));
|
[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|{
|
transform_xy_unit!("push/x" "push/y" "push/xy"|self: Push, area|{
|
||||||
let area = Render::layout(&self.content(), area);
|
let area = self.content().layout(area);
|
||||||
[area.x().plus(self.dx()), area.y().plus(self.dy()), area.w(), area.h()]
|
[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|{
|
transform_xy_unit!("pull/x" "pull/y" "pull/xy"|self: Pull, area|{
|
||||||
let area = Render::layout(&self.content(), area);
|
let area = self.content().layout(area);
|
||||||
[area.x().minus(self.dx()), area.y().minus(self.dy()), area.w(), area.h()]
|
[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|{
|
transform_xy_unit!("margin/x" "margin/y" "margin/xy"|self: Margin, area|{
|
||||||
let area = Render::layout(&self.content(), area);
|
let area = self.content().layout(area);
|
||||||
let dx = self.dx();
|
let dx = self.dx();
|
||||||
let dy = self.dy();
|
let dy = self.dy();
|
||||||
[area.x().minus(dx), area.y().minus(dy), area.w().plus(dy.plus(dy)), area.h().plus(dy.plus(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|{
|
transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{
|
||||||
let area = Render::layout(&self.content(), area);
|
let area = self.content().layout(area);
|
||||||
let dx = self.dx();
|
let dx = self.dx();
|
||||||
let dy = self.dy();
|
let dy = self.dy();
|
||||||
[area.x().plus(dx), area.y().plus(dy), area.w().minus(dy.plus(dy)), area.h().minus(dy.plus(dy))]
|
[area.x().plus(dx), area.y().plus(dy), area.w().minus(dy.plus(dy)), area.h().minus(dy.plus(dy))] });
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
/// Render target.
|
/// Render target.
|
||||||
pub trait Output: Send + Sync + Sized {
|
pub trait Output: Send + Sync + Sized {
|
||||||
|
|
|
||||||
|
|
@ -1,88 +1,60 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Composable renderable with static dispatch.
|
/// Composable renderable with static dispatch.
|
||||||
pub trait Content<E: Output> {
|
pub trait Content<E: Output>: Sized {
|
||||||
/// Return a [Render]able of a specific type.
|
/// Return opaque [Render]able.
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
fn content (&self) -> Option<impl Render<E> + '_> { Option::<()>::None }
|
||||||
()
|
|
||||||
}
|
|
||||||
/// Perform layout. By default, delegates to [Self::content].
|
|
||||||
fn layout (&self, area: E::Area) -> E::Area {
|
|
||||||
self.content().layout(area)
|
|
||||||
}
|
|
||||||
/// Draw to output. By default, delegates to [Self::content].
|
|
||||||
fn render (&self, output: &mut E) {
|
|
||||||
self.content().render(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Every pointer to [Content] is a [Content].
|
|
||||||
impl<E: Output, C: Content<E>> Content<E> for &C {
|
|
||||||
fn content (&self) -> impl Render<E> + '_ { (*self).content() }
|
|
||||||
fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) }
|
|
||||||
fn render (&self, output: &mut E) { (*self).render(output) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The platonic ideal unit of [Content]: total emptiness at dead center (e=1vg^sqrt(-1))
|
/// 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 layout (&self, area: E::Area) -> E::Area { area.center().to_area_pos().into() }
|
|
||||||
fn render (&self, _: &mut E) {}
|
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> {
|
impl<E: Output, T: Content<E>> Content<E> for Option<T> {
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
fn content (&self) -> Option<impl Render<E> + '_> {
|
||||||
self.as_ref()
|
if let Some(content) = self {
|
||||||
|
content.content()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
fn layout (&self, area: E::Area) -> E::Area {
|
|
||||||
self.as_ref()
|
|
||||||
.map(|content|content.layout(area))
|
|
||||||
.unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into())
|
|
||||||
}
|
}
|
||||||
fn render (&self, output: &mut E) {
|
|
||||||
self.as_ref()
|
|
||||||
.map(|content|content.render(output));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// You can render from a box.
|
|
||||||
impl<E: Output> Content<E> for RenderBox<E> {
|
|
||||||
fn content (&self) -> impl Render<E> + '_ { self.deref() }
|
|
||||||
//fn boxed <'b> (self) -> RenderBox<'b, E> where Self: Sized + 'b { self }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// You can render from an opaque pointer.
|
/// You can render from an opaque pointer.
|
||||||
impl<E: Output> Content<E> for &dyn Render<E> where Self: Sized {
|
impl<E: Output> Content<E> for &dyn Render<E> where Self: Sized {
|
||||||
fn content (&self) -> impl Render<E> + '_ {
|
fn content (&self) -> Option<impl Render<E> + '_> {
|
||||||
#[allow(suspicious_double_ref_op)]
|
#[allow(suspicious_double_ref_op)]
|
||||||
self.deref()
|
Some(self.deref())
|
||||||
}
|
|
||||||
fn layout (&self, area: E::Area) -> E::Area {
|
|
||||||
#[allow(suspicious_double_ref_op)]
|
|
||||||
Render::layout(self.deref(), area)
|
|
||||||
}
|
|
||||||
fn render (&self, output: &mut E) {
|
|
||||||
#[allow(suspicious_double_ref_op)]
|
|
||||||
Render::render(self.deref(), output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement [Content] with custom rendering for a struct.
|
/// Implement composable content for a struct.
|
||||||
#[macro_export] macro_rules! render {
|
#[macro_export] macro_rules! content {
|
||||||
(|$self:ident:$Struct:ident $(<
|
// Implement for all [Output]s.
|
||||||
$($L:lifetime),* $($T:ident $(:$Trait:path)?),*
|
(|$self:ident:$Struct:ty| $content:expr) => {
|
||||||
>)?, $to:ident | $render:expr) => {
|
impl<E: Output> Content<E> for $Struct {
|
||||||
impl <$($($L),*)? E: Output, $($T$(:$Trait)?),*> Content<E>
|
fn content (&$self) -> impl Render<E> + '_ { Some($content) }
|
||||||
for $Struct $(<$($L),* $($T),*>>)? {
|
|
||||||
fn render (&$self, $to: &mut E) { $render }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Implement for specific [Output].
|
||||||
($Output:ty:|
|
($Output:ty:|
|
||||||
$self:ident:
|
$self:ident:
|
||||||
$Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident
|
$Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?
|
||||||
|$render:expr) => {
|
|$content:expr) => {
|
||||||
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
|
||||||
for $Struct $(<$($($L)? $($T)?),+>)? {
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn render (&$self, $to: &mut $Output) { $render }
|
fn content (&$self) -> impl Render<$Output> + '_ { $content }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,46 +2,86 @@ use crate::*;
|
||||||
|
|
||||||
/// Renderable with dynamic dispatch.
|
/// Renderable with dynamic dispatch.
|
||||||
pub trait Render<E: Output> {
|
pub trait Render<E: Output> {
|
||||||
/// Compute layout.
|
|
||||||
fn layout (&self, area: E::Area) -> E::Area;
|
|
||||||
/// Write data to display.
|
/// Write data to display.
|
||||||
fn render (&self, output: &mut E);
|
fn render (&self, output: &mut E);
|
||||||
/// Perform type erasure, turning `self` into an opaque [RenderBox].
|
/// Compute layout.
|
||||||
|
fn layout (&self, area: E::Area) -> E::Area { area }
|
||||||
|
|
||||||
fn boxed <'a> (self) -> Box<dyn Render<E> + 'a> where Self: Sized + 'a {
|
fn boxed <'a> (self) -> Box<dyn Render<E> + 'a> where Self: Sized + 'a {
|
||||||
Box::new(self) as Box<dyn Render<E> + '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>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Every [Content] is also a [Render].
|
impl<E: Output> Render<E> for () {
|
||||||
/// However, the converse does not hold true.
|
fn render (&self, _: &mut E) {}
|
||||||
/// Instead, the [Content::content] method returns an
|
|
||||||
/// opaque [Render] pointer.
|
|
||||||
impl<E: Output, C: Content<E>> Render<E> for C {
|
|
||||||
fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) }
|
|
||||||
fn render (&self, output: &mut E) { Content::render(self, output) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opaque pointer to a renderable living on the heap.
|
impl<E: Output> Render<E> for fn(&mut E) {
|
||||||
///
|
fn render (&self, output: &mut E) {
|
||||||
/// Return this from [Content::content] to use dynamic dispatch.
|
self(output)
|
||||||
pub type RenderBox<E> = Box<dyn Render<E>>;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implement [Content] with composable content for a struct.
|
impl<'x, E: Output> Render<E> for &(dyn Render<E> + 'x) {
|
||||||
#[macro_export] macro_rules! content {
|
fn render (&self, output: &mut E) {
|
||||||
// Implement for all [Output]s.
|
(*self).render(output)
|
||||||
(|$self:ident:$Struct:ty| $content:expr) => {
|
}
|
||||||
impl<E: Output> Content<E> for $Struct {
|
}
|
||||||
fn content (&$self) -> impl Render<E> + '_ { Some($content) }
|
|
||||||
|
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 }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Implement for specific [Output].
|
|
||||||
($Output:ty:|
|
($Output:ty:|
|
||||||
$self:ident:
|
$self:ident:
|
||||||
$Struct:ident$(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?
|
$Struct:ident $(<$($($L:lifetime)? $($T:ident)? $(:$Trait:path)?),+>)?, $to:ident
|
||||||
|$content:expr) => {
|
|$render:expr) => {
|
||||||
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Content<$Output>
|
impl $(<$($($L)? $($T)? $(:$Trait)?),+>)? Render<$Output>
|
||||||
for $Struct $(<$($($L)? $($T)?),+>)? {
|
for $Struct $(<$($($L)? $($T)?),+>)? {
|
||||||
fn content (&$self) -> impl Render<$Output> + '_ { $content }
|
fn render (&$self, $to: &mut $Output) { $render }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,65 +11,19 @@ impl<E: Output, T: Render<E>, F: Fn()->T> Thunk<E, T, F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
impl<E: Output, T: Render<E>, F: Fn()->T> Content<E> for Thunk<E, T, F> {
|
||||||
fn content (&self) -> impl Render<E> { (self.1)() }
|
fn content (&self) -> Option<impl Render<E>> {
|
||||||
}
|
Some((self.1)())
|
||||||
|
|
||||||
/// Lazily-evaluated [Render]able with
|
|
||||||
/// mandatory stack allocation and dynamic dispatch.
|
|
||||||
pub struct ThunkBox<E: Output>(
|
|
||||||
PhantomData<E>,
|
|
||||||
Box<dyn Fn()->Box<dyn Render<E>>>,
|
|
||||||
);
|
|
||||||
impl<E: Output> ThunkBox<E> {
|
|
||||||
pub const fn new (thunk: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
|
||||||
Self(PhantomData, thunk)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<E: Output> Content<E> for ThunkBox<E> {
|
|
||||||
fn content (&self) -> impl Render<E> { (&self.1)() }
|
|
||||||
}
|
|
||||||
impl<E: Output> From<Box<dyn Fn()->Box<dyn Render<E>>>> for ThunkBox<E> {
|
|
||||||
fn from (f: Box<dyn Fn()->Box<dyn Render<E>>>) -> Self {
|
|
||||||
Self(PhantomData, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl<'a, E: Output, F: Fn()->Box<dyn Render<E> + 'a> + 'a> From<F> for ThunkBox<'a, E> {
|
|
||||||
//fn from (f: F) -> Self {
|
|
||||||
//Self(Default::default(), Box::new(f))
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
|
pub struct ThunkRender<E: Output, F: Fn(&mut E)>(PhantomData<E>, F);
|
||||||
impl<E: Output, F: Fn(&mut E)> ThunkRender<E, F> {
|
impl<E: Output, F: Fn(&mut E)> ThunkRender<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)> Content<E> for ThunkRender<E, F> {
|
impl<E: Output, F: Fn(&mut E)> Render<E> for ThunkRender<E, F> {
|
||||||
fn render (&self, to: &mut E) { (self.1)(to) }
|
fn render (&self, to: &mut E) { (self.1)(to) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThunkLayout<
|
|
||||||
E: Output,
|
|
||||||
F1: Fn(E::Area)->E::Area,
|
|
||||||
F2: Fn(&mut E)
|
|
||||||
>(
|
|
||||||
PhantomData<E>,
|
|
||||||
F1,
|
|
||||||
F2
|
|
||||||
);
|
|
||||||
impl<E: Output, F1: Fn(E::Area)->E::Area, F2: Fn(&mut E)> ThunkLayout<E, F1, F2> {
|
|
||||||
pub fn new (layout: F1, render: F2) -> Self { Self(PhantomData, layout, render) }
|
|
||||||
}
|
|
||||||
impl<E, F1, F2> Content<E> for ThunkLayout<E, F1, F2>
|
|
||||||
where
|
|
||||||
E: Output,
|
|
||||||
F1: Fn(E::Area)->E::Area,
|
|
||||||
F2: Fn(&mut E)
|
|
||||||
{
|
|
||||||
fn layout (&self, to: E::Area) -> E::Area { (self.1)(to) }
|
|
||||||
fn render (&self, to: &mut E) { (self.2)(to) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
#[derive(Debug, Default)] pub struct Memo<T, U> {
|
||||||
pub value: T,
|
pub value: T,
|
||||||
pub view: Arc<RwLock<U>>
|
pub view: Arc<RwLock<U>>
|
||||||
|
|
|
||||||
|
|
@ -1,281 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use Direction::*;
|
|
||||||
|
|
||||||
/// A cardinal direction.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
#[cfg_attr(test, derive(Arbitrary))]
|
|
||||||
pub enum Direction {
|
|
||||||
North, South, East, West, Above, Below
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direction {
|
|
||||||
pub fn split_fixed <N: Coordinate> (self, area: impl Area<N>, a: N) -> ([N;4],[N;4]) {
|
|
||||||
let [x, y, w, h] = area.xywh();
|
|
||||||
match self {
|
|
||||||
North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]),
|
|
||||||
South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]),
|
|
||||||
East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]),
|
|
||||||
West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]),
|
|
||||||
Above | Below => (area.xywh(), area.xywh())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A linear coordinate.
|
|
||||||
pub trait Coordinate: Send + Sync + Copy
|
|
||||||
+ Add<Self, Output=Self>
|
|
||||||
+ Sub<Self, Output=Self>
|
|
||||||
+ Mul<Self, Output=Self>
|
|
||||||
+ Div<Self, Output=Self>
|
|
||||||
+ Ord + PartialEq + Eq
|
|
||||||
+ Debug + Display + Default
|
|
||||||
+ From<u16> + Into<u16>
|
|
||||||
+ Into<usize>
|
|
||||||
+ Into<f64>
|
|
||||||
{
|
|
||||||
fn zero () -> Self { 0.into() }
|
|
||||||
fn plus (self, other: Self) -> Self;
|
|
||||||
fn minus (self, other: Self) -> Self {
|
|
||||||
if self >= other {
|
|
||||||
self - other
|
|
||||||
} else {
|
|
||||||
0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Coordinate for u16 {
|
|
||||||
fn plus (self, other: Self) -> Self {
|
|
||||||
self.saturating_add(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Area<N: Coordinate>: From<[N;4]> + Debug + Copy {
|
|
||||||
fn x (&self) -> N;
|
|
||||||
fn y (&self) -> N;
|
|
||||||
fn w (&self) -> N;
|
|
||||||
fn h (&self) -> N;
|
|
||||||
fn zero () -> [N;4] {
|
|
||||||
[N::zero(), N::zero(), N::zero(), N::zero()]
|
|
||||||
}
|
|
||||||
fn from_position (pos: impl Size<N>) -> [N;4] {
|
|
||||||
let [x, y] = pos.wh();
|
|
||||||
[x, y, 0.into(), 0.into()]
|
|
||||||
}
|
|
||||||
fn from_size (size: impl Size<N>) -> [N;4] {
|
|
||||||
let [w, h] = size.wh();
|
|
||||||
[0.into(), 0.into(), w, h]
|
|
||||||
}
|
|
||||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
|
||||||
if self.w() < w || self.h() < h {
|
|
||||||
Err(format!("min {w}x{h}").into())
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn xy (&self) -> [N;2] {
|
|
||||||
[self.x(), self.y()]
|
|
||||||
}
|
|
||||||
fn wh (&self) -> [N;2] {
|
|
||||||
[self.w(), self.h()]
|
|
||||||
}
|
|
||||||
fn xywh (&self) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), self.h()]
|
|
||||||
}
|
|
||||||
fn clip_h (&self, h: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), self.h().min(h)]
|
|
||||||
}
|
|
||||||
fn clip_w (&self, w: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w().min(w), self.h()]
|
|
||||||
}
|
|
||||||
fn clip (&self, wh: impl Size<N>) -> [N;4] {
|
|
||||||
[self.x(), self.y(), wh.w(), wh.h()]
|
|
||||||
}
|
|
||||||
fn set_w (&self, w: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), w, self.h()]
|
|
||||||
}
|
|
||||||
fn set_h (&self, h: N) -> [N;4] {
|
|
||||||
[self.x(), self.y(), self.w(), h]
|
|
||||||
}
|
|
||||||
fn x2 (&self) -> N {
|
|
||||||
self.x().plus(self.w())
|
|
||||||
}
|
|
||||||
fn y2 (&self) -> N {
|
|
||||||
self.y().plus(self.h())
|
|
||||||
}
|
|
||||||
fn lrtb (&self) -> [N;4] {
|
|
||||||
[self.x(), self.x2(), self.y(), self.y2()]
|
|
||||||
}
|
|
||||||
fn center (&self) -> [N;2] {
|
|
||||||
[self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())]
|
|
||||||
}
|
|
||||||
fn center_x (&self, n: N) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()]
|
|
||||||
}
|
|
||||||
fn center_y (&self, n: N) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n]
|
|
||||||
}
|
|
||||||
fn center_xy (&self, [n, m]: [N;2]) -> [N;4] {
|
|
||||||
let [x, y, w, h] = self.xywh();
|
|
||||||
[(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m]
|
|
||||||
}
|
|
||||||
fn centered (&self) -> [N;2] {
|
|
||||||
[self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())]
|
|
||||||
}
|
|
||||||
fn iter_x (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
|
||||||
self.x()..(self.x()+self.w())
|
|
||||||
}
|
|
||||||
fn iter_y (&self) -> impl Iterator<Item = N> where N: std::iter::Step {
|
|
||||||
self.y()..(self.y()+self.h())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Area<N> for (N, N, N, N) {
|
|
||||||
fn x (&self) -> N { self.0 }
|
|
||||||
fn y (&self) -> N { self.1 }
|
|
||||||
fn w (&self) -> N { self.2 }
|
|
||||||
fn h (&self) -> N { self.3 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Area<N> for [N;4] {
|
|
||||||
fn x (&self) -> N { self[0] }
|
|
||||||
fn y (&self) -> N { self[1] }
|
|
||||||
fn w (&self) -> N { self[2] }
|
|
||||||
fn h (&self) -> N { self[3] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Size<N: Coordinate>: From<[N;2]> + Debug + Copy {
|
|
||||||
fn x (&self) -> N;
|
|
||||||
fn y (&self) -> N;
|
|
||||||
fn w (&self) -> N { self.x() }
|
|
||||||
fn h (&self) -> N { self.y() }
|
|
||||||
fn wh (&self) -> [N;2] { [self.x(), self.y()] }
|
|
||||||
fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] }
|
|
||||||
fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] }
|
|
||||||
fn expect_min (&self, w: N, h: N) -> Usually<&Self> {
|
|
||||||
if self.w() < w || self.h() < h {
|
|
||||||
Err(format!("min {w}x{h}").into())
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn zero () -> [N;2] {
|
|
||||||
[N::zero(), N::zero()]
|
|
||||||
}
|
|
||||||
fn to_area_pos (&self) -> [N;4] {
|
|
||||||
let [x, y] = self.wh();
|
|
||||||
[x, y, 0.into(), 0.into()]
|
|
||||||
}
|
|
||||||
fn to_area_size (&self) -> [N;4] {
|
|
||||||
let [w, h] = self.wh();
|
|
||||||
[0.into(), 0.into(), w, h]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Size<N> for (N, N) {
|
|
||||||
fn x (&self) -> N { self.0 }
|
|
||||||
fn y (&self) -> N { self.1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: Coordinate> Size<N> for [N;2] {
|
|
||||||
fn x (&self) -> N { self[0] }
|
|
||||||
fn y (&self) -> N { self[1] }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasSize<E: Output> {
|
|
||||||
fn size (&self) -> &Measure<E>;
|
|
||||||
fn width (&self) -> usize {
|
|
||||||
self.size().w()
|
|
||||||
}
|
|
||||||
fn height (&self) -> usize {
|
|
||||||
self.size().h()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Output, T: Has<Measure<E>>> HasSize<E> for T {
|
|
||||||
fn size (&self) -> &Measure<E> {
|
|
||||||
self.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A widget that tracks its render width and height
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Measure<E: Output> {
|
|
||||||
_engine: PhantomData<E>,
|
|
||||||
pub x: Arc<AtomicUsize>,
|
|
||||||
pub y: Arc<AtomicUsize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Output> PartialEq for Measure<E> {
|
|
||||||
fn eq (&self, other: &Self) -> bool {
|
|
||||||
self.x.load(Relaxed) == other.x.load(Relaxed) &&
|
|
||||||
self.y.load(Relaxed) == other.y.load(Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
|
|
||||||
impl<E: Output> Content<E> for Measure<E> {
|
|
||||||
fn render (&self, to: &mut E) {
|
|
||||||
self.x.store(to.area().w().into(), Relaxed);
|
|
||||||
self.y.store(to.area().h().into(), Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Output> Clone for Measure<E> {
|
|
||||||
fn clone (&self) -> Self {
|
|
||||||
Self {
|
|
||||||
_engine: Default::default(),
|
|
||||||
x: self.x.clone(),
|
|
||||||
y: self.y.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Output> std::fmt::Debug for Measure<E> {
|
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
|
||||||
f.debug_struct("Measure")
|
|
||||||
.field("width", &self.x)
|
|
||||||
.field("height", &self.y)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Output> Measure<E> {
|
|
||||||
pub fn new () -> Self {
|
|
||||||
Self {
|
|
||||||
_engine: PhantomData::default(),
|
|
||||||
x: Arc::new(0.into()),
|
|
||||||
y: Arc::new(0.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn set_w (&self, w: impl Into<usize>) -> &Self {
|
|
||||||
self.x.store(w.into(), Relaxed);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn set_h (&self, h: impl Into<usize>) -> &Self {
|
|
||||||
self.y.store(h.into(), Relaxed);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) -> &Self {
|
|
||||||
self.set_w(w);
|
|
||||||
self.set_h(h);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn w (&self) -> usize {
|
|
||||||
self.x.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn h (&self) -> usize {
|
|
||||||
self.y.load(Relaxed)
|
|
||||||
}
|
|
||||||
pub fn wh (&self) -> [usize;2] {
|
|
||||||
[self.w(), self.h()]
|
|
||||||
}
|
|
||||||
pub fn format (&self) -> Arc<str> {
|
|
||||||
format!("{}x{}", self.w(), self.h()).into()
|
|
||||||
}
|
|
||||||
pub fn of <T: Render<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
|
|
||||||
Bsp::b(Fill::xy(self), item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,7 @@ 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> Content<E> for Measure<E> {
|
impl<E: Output> Render<E> for Measure<E> {
|
||||||
fn render (&self, to: &mut E) {
|
fn render (&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);
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ proptest! {
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Content<TestOutput> for String {
|
impl Render<TestOutput> for String {
|
||||||
fn render (&self, to: &mut TestOutput) {
|
fn render (&self, to: &mut TestOutput) {
|
||||||
to.area_mut().set_w(self.len() as u16);
|
to.area_mut().set_w(self.len() as u16);
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ proptest! {
|
||||||
#[test] fn test_iter_map () {
|
#[test] fn test_iter_map () {
|
||||||
struct Foo;
|
struct Foo;
|
||||||
impl<T: Output> Content<T> for Foo {}
|
impl<T: Output> Content<T> for Foo {}
|
||||||
fn _make_map <T: Output, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Content<T> {
|
fn _make_map <T: Output, U: Content<T> + Send + Sync> (data: &Vec<U>) -> impl Render<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];
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
|
||||||
//#[tengri_proc::view(SomeOutput)]
|
//#[tengri_proc::view(SomeOutput)]
|
||||||
//impl SomeView {
|
//impl SomeView {
|
||||||
//#[tengri::view(":view-1")]
|
//#[tengri::view(":view-1")]
|
||||||
//fn view_1 (&self) -> impl Content<SomeOutput> + use<'_> {
|
//fn view_1 (&self) -> impl Render<SomeOutput> + use<'_> {
|
||||||
//"view-1"
|
//"view-1"
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
@ -114,7 +114,7 @@ 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 Content<SomeOutput> + use<'_> {
|
//fn view_1 (&self) -> impl Render<SomeOutput> + use<'_> {
|
||||||
//"view-1"
|
//"view-1"
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
|
||||||
|
|
@ -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 Content<TuiOut> + use<'_> {
|
//pub fn title (&self) -> impl Render<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 Content<TuiOut> + use<'_> {
|
//pub fn code (&self) -> impl Render<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 Content<TuiOut> + use<'_> {
|
//pub fn hello (&self) -> impl Render<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 Content<TuiOut> + use<'_> {
|
//pub fn world (&self) -> impl Render<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 Content<TuiOut> + use<'_> {
|
//pub fn hello_world (&self) -> impl Render<TuiOut> + use<'_> {
|
||||||
//"Hello world!".boxed()
|
//"Hello world!".boxed()
|
||||||
//}
|
//}
|
||||||
//pub fn map_e (&self) -> impl Content<TuiOut> + use<'_> {
|
//pub fn map_e (&self) -> impl Render<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 Content<TuiOut> + use<'_> {
|
//pub fn map_s (&self) -> impl Render<TuiOut> + use<'_> {
|
||||||
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ macro_rules! impl_content_layout_render {
|
||||||
layout = $layout:expr;
|
layout = $layout:expr;
|
||||||
render = $render:expr) =>
|
render = $render:expr) =>
|
||||||
{
|
{
|
||||||
impl Content<$Output> for $Struct {
|
impl Render<$Output> for $Struct {
|
||||||
fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout }
|
fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout }
|
||||||
fn render (&$self, $to: &mut $Output) { $render }
|
fn render (&$self, $to: &mut $Output) { $render }
|
||||||
}
|
}
|
||||||
|
|
@ -25,19 +25,19 @@ 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: Content<TuiOut>> Content<TuiOut> for std::sync::Arc<T> {
|
impl<T: Render<TuiOut>> Render<TuiOut> for std::sync::Arc<T> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
Content::<TuiOut>::layout(&**self, to)
|
Render::<TuiOut>::layout(&**self, to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
Content::<TuiOut>::render(&**self, to)
|
Render::<TuiOut>::render(&**self, to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Content<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
impl<T: Render<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
||||||
Bsp::a(self.as_ref().ok(), self.as_ref().err()
|
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())))
|
.map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 Content<TuiOut> + '_;
|
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Render<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,12 +1,20 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct Bordered<S: BorderStyle, W: Content<TuiOut>>(pub bool, pub S, pub W);
|
pub struct Bordered<S, W>(pub bool, pub S, pub W);
|
||||||
content!(TuiOut: |self: Bordered<S: BorderStyle, W: Content<TuiOut>>|Fill::xy(
|
impl<S: BorderStyle, W: Render<TuiOut>> Content<TuiOut> for Bordered<S, W> {
|
||||||
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
||||||
|
Some(Fill::xy(
|
||||||
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
|
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
|
||||||
));
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Border<S: BorderStyle>(pub bool, pub S);
|
pub struct Border<S: BorderStyle>(pub bool, pub S);
|
||||||
render!(TuiOut: |self: Border<S: BorderStyle>, to| {
|
impl<S: BorderStyle> Render<TuiOut> for Border<S> {
|
||||||
|
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 {
|
||||||
|
|
@ -24,17 +32,18 @@ render!(TuiOut: |self: Border<S: BorderStyle>, to| {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait BorderStyle: Send + Sync + Copy {
|
pub trait BorderStyle: Render<TuiOut> + Copy {
|
||||||
fn enabled (&self) -> bool;
|
fn enabled (&self) -> bool;
|
||||||
fn enclose <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
|
fn enclose <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
|
||||||
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
|
||||||
}
|
}
|
||||||
fn enclose2 <W: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
|
fn enclose2 <W: Render<TuiOut>> (self, w: W) -> impl Render<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: Content<TuiOut>> (self, w: W) -> impl Content<TuiOut> {
|
fn enclose_bg <W: Render<TuiOut>> (self, w: W) -> impl Render<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))
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +147,7 @@ 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 Content<TuiOut> for $T {
|
impl Render<TuiOut> for $T {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
if self.enabled() { let _ = self.draw(to); }
|
if self.enabled() { let _ = self.draw(to); }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,26 @@ 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: Content<O>>(std::marker::PhantomData<O>, Perhaps<T>);
|
pub struct ErrorBoundary<O: Output, T: Render<O>>(
|
||||||
|
std::marker::PhantomData<O>, Perhaps<T>
|
||||||
|
);
|
||||||
|
|
||||||
impl<O: Output, T: Content<O>> ErrorBoundary<O, T> {
|
impl<O: Output, T: Render<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: Content<TuiOut>> Content<TuiOut> for ErrorBoundary<TuiOut, T> {
|
impl<T: Render<TuiOut>> Render<TuiOut> for ErrorBoundary<TuiOut, T> {
|
||||||
fn content (&self) -> impl Render<TuiOut> + '_ {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
ThunkRender::new(|to|match self.1.as_ref() {
|
match self.1.as_ref() {
|
||||||
Ok(Some(content)) => content.render(to),
|
Ok(Some(content)) => content.render(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) => Content::render(&Tui::fg_bg(
|
Err(e) => Tui::fg_bg(
|
||||||
Color::Rgb(255,224,244), Color::Rgb(96,24,24), Bsp::s(
|
Color::Rgb(255,224,244), Color::Rgb(96,24,24), Bsp::s(
|
||||||
Bsp::e(Tui::bold(true, "oops. "), "rendering failed."),
|
Bsp::e(Tui::bold(true, "oops. "), "rendering failed."),
|
||||||
Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))))), to)
|
Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))))
|
||||||
})
|
).render(to)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,30 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub struct FieldH<T, U>(pub ItemTheme, pub T, pub U);
|
pub struct FieldH<T, U>(pub ItemTheme, pub T, pub U);
|
||||||
impl<T: Content<TuiOut>, U: Content<TuiOut>> Content<TuiOut> for FieldH<T, U> {
|
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldH<T, U> {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
||||||
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
row!(
|
Some(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);
|
pub struct FieldV<T, U>(pub ItemTheme, pub T, pub U);
|
||||||
impl<T: Content<TuiOut>, U: Content<TuiOut>> Content<TuiOut> for FieldV<T, U> {
|
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldV<T, U> {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
||||||
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
|
||||||
Bsp::n(
|
Some(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 +40,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: Content<TuiOut>, U: Content<TuiOut>> Content<TuiOut> for Field<T, U> {
|
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for Field<T, U> {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
|
||||||
"TODO"
|
Some("TODO")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
render!(TuiOut: |self: u64, _to|todo!());
|
impl Render<TuiOut> for u64 {
|
||||||
|
fn render (&self, _to: &mut TuiOut) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render!(TuiOut: |self: f64, _to|todo!());
|
impl Render<TuiOut> for f64 {
|
||||||
|
fn render (&self, _to: &mut TuiOut) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,24 +12,24 @@ 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 Content<TuiOut> {
|
pub fn lo (fg: Color, bg: Color) -> impl Render<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 Content<TuiOut> {
|
pub fn hi (fg: Color, bg: Color) -> impl Render<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: Content<TuiOut>> Content<TuiOut> for Phat<T> {
|
impl<T: Render<TuiOut>> Content<TuiOut> for Phat<T> {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> Option<impl Render<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);
|
||||||
Min::xy(
|
Some(Min::xy(
|
||||||
self.width,
|
self.width,
|
||||||
self.height,
|
self.height,
|
||||||
Bsp::s(top, Bsp::n(low, Fill::xy(content)))
|
Bsp::s(top, Bsp::n(low, Fill::xy(content)))
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ 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 Content<TuiOut> for Repeat<'_> {
|
impl Render<TuiOut> for Repeat<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] { to }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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();
|
||||||
|
|
@ -19,8 +21,10 @@ impl Content<TuiOut> for Repeat<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatV<'a>(pub &'a str);
|
pub struct RepeatV<'a>(pub &'a str);
|
||||||
impl Content<TuiOut> for RepeatV<'_> {
|
impl Render<TuiOut> for RepeatV<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] { to }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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 {
|
||||||
|
|
@ -32,8 +36,10 @@ impl Content<TuiOut> for RepeatV<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RepeatH<'a>(pub &'a str);
|
pub struct RepeatH<'a>(pub &'a str);
|
||||||
impl Content<TuiOut> for RepeatH<'_> {
|
impl Render<TuiOut> for RepeatH<'_> {
|
||||||
fn layout (&self, to: [u16;4]) -> [u16;4] { to }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
to
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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 {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ impl ScrollbarH {
|
||||||
const ICON_INC: &[char] = &[' ', '🞂', ' '];
|
const ICON_INC: &[char] = &[' ', '🞂', ' '];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<TuiOut> for ScrollbarV {
|
impl Render<TuiOut> for ScrollbarV {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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;
|
||||||
|
|
@ -51,7 +51,7 @@ impl Content<TuiOut> for ScrollbarV {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Content<TuiOut> for ScrollbarH {
|
impl Render<TuiOut> for ScrollbarH {
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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;
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,24 @@ use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: &str, to|
|
impl_content_layout_render!(TuiOut: |self: &str, to|
|
||||||
layout = to.center_xy([width_chars_max(to.w(), self), 1]);
|
layout = to.center_xy([width_chars_max(to.w(), self), 1]);
|
||||||
render = {let [x, y, w, ..] = Content::layout(self, to.area());
|
render = {let [x, y, w, ..] = Render::layout(self, to.area());
|
||||||
to.text(self, x, y, w)});
|
to.text(self, x, y, w)});
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: String, to|
|
impl_content_layout_render!(TuiOut: |self: String, to|
|
||||||
layout = Content::<TuiOut>::layout(&self.as_str(), to);
|
layout = Render::<TuiOut>::layout(&self.as_str(), to);
|
||||||
render = Content::<TuiOut>::render(&self.as_str(), to));
|
render = Render::<TuiOut>::render(&self.as_str(), to));
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: Arc<str>, to|
|
impl_content_layout_render!(TuiOut: |self: Arc<str>, to|
|
||||||
layout = Content::<TuiOut>::layout(&self.as_ref(), to);
|
layout = Render::<TuiOut>::layout(&self.as_ref(), to);
|
||||||
render = Content::<TuiOut>::render(&self.as_ref(), to));
|
render = Render::<TuiOut>::render(&self.as_ref(), to));
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: std::sync::RwLock<String>, to|
|
impl_content_layout_render!(TuiOut: |self: std::sync::RwLock<String>, to|
|
||||||
layout = Content::<TuiOut>::layout(&self.read().unwrap(), to);
|
layout = Render::<TuiOut>::layout(&self.read().unwrap(), to);
|
||||||
render = Content::<TuiOut>::render(&self.read().unwrap(), to));
|
render = Render::<TuiOut>::render(&self.read().unwrap(), to));
|
||||||
|
|
||||||
impl_content_layout_render!(TuiOut: |self: std::sync::RwLockReadGuard<'_, String>, to|
|
impl_content_layout_render!(TuiOut: |self: std::sync::RwLockReadGuard<'_, String>, to|
|
||||||
layout = Content::<TuiOut>::layout(&**self, to);
|
layout = Render::<TuiOut>::layout(&**self, to);
|
||||||
render = Content::<TuiOut>::render(&**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 +61,12 @@ impl<'a, T: AsRef<str>> TrimString<T> {
|
||||||
TrimStringRef(self.0, &self.1)
|
TrimStringRef(self.0, &self.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, T: AsRef<str>> Content<TuiOut> for TrimString<T> {
|
impl<'a, T: AsRef<str>> Render<TuiOut> for TrimString<T> {
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
||||||
Content::layout(&self.as_ref(), to)
|
Render::layout(&self.as_ref(), to)
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
Content::render(&self.as_ref(), to)
|
Render::render(&self.as_ref(), to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ impl<'a, T: AsRef<str>> Content<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>> Content<TuiOut> for TrimStringRef<'_, T> {
|
impl<T: AsRef<str>> Render<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()]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,70 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait TuiStyle {
|
pub trait TuiStyle {
|
||||||
fn fg <R: Content<TuiOut>> (color: Color, w: R) -> Foreground<R> {
|
fn fg <R: Render<TuiOut>> (color: Color, w: R) -> Foreground<R> {
|
||||||
Foreground(color, w)
|
Foreground(color, w)
|
||||||
}
|
}
|
||||||
fn bg <R: Content<TuiOut>> (color: Color, w: R) -> Background<R> {
|
fn bg <R: Render<TuiOut>> (color: Color, w: R) -> Background<R> {
|
||||||
Background(color, w)
|
Background(color, w)
|
||||||
}
|
}
|
||||||
fn fg_bg <R: Content<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
|
fn fg_bg <R: Render<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
|
||||||
Background(bg, Foreground(fg, w))
|
Background(bg, Foreground(fg, w))
|
||||||
}
|
}
|
||||||
fn modify <R: Content<TuiOut>> (enable: bool, modifier: Modifier, w: R) -> Modify<R> {
|
fn modify <R: Render<TuiOut>> (enable: bool, modifier: Modifier, w: R) -> Modify<R> {
|
||||||
Modify(enable, modifier, w)
|
Modify(enable, modifier, w)
|
||||||
}
|
}
|
||||||
fn bold <R: Content<TuiOut>> (enable: bool, w: R) -> Modify<R> {
|
fn bold <R: Render<TuiOut>> (enable: bool, w: R) -> Modify<R> {
|
||||||
Self::modify(enable, Modifier::BOLD, w)
|
Self::modify(enable, Modifier::BOLD, w)
|
||||||
}
|
}
|
||||||
fn border <R: Content<TuiOut>, S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered<S, R> {
|
fn border <R: Render<TuiOut>, S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered<S, R> {
|
||||||
Bordered(enable, style, w)
|
Bordered(enable, style, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TuiStyle for Tui {}
|
impl TuiStyle for Tui {}
|
||||||
|
|
||||||
pub struct Foreground<R: Content<TuiOut>>(pub Color, pub R);
|
pub struct Foreground<R>(pub Color, pub R);
|
||||||
impl<R: Content<TuiOut>> Content<TuiOut> for Foreground<R> {
|
impl<R: Render<TuiOut>> Render<TuiOut> for Foreground<R> {
|
||||||
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.1.layout(to)
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.fill_fg(to.area(), self.0);
|
let area = self.layout(to.area());
|
||||||
self.1.render(to)
|
to.fill_fg(area, self.0);
|
||||||
|
to.place(area, &self.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Background<R: Content<TuiOut>>(pub Color, pub R);
|
pub struct Background<R>(pub Color, pub R);
|
||||||
impl<R: Content<TuiOut>> Content<TuiOut> for Background<R> {
|
impl<R: Render<TuiOut>> Render<TuiOut> for Background<R> {
|
||||||
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.1.layout(to)
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.fill_bg(to.area(), self.0);
|
let area = self.layout(to.area());
|
||||||
self.1.render(to)
|
to.fill_bg(area, self.0);
|
||||||
|
to.place(area, &self.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Modify<R: Content<TuiOut>>(pub bool, pub Modifier, pub R);
|
pub struct Modify<R: Render<TuiOut>>(pub bool, pub Modifier, pub R);
|
||||||
impl<R: Content<TuiOut>> Content<TuiOut> for Modify<R> {
|
impl<R: Render<TuiOut>> Render<TuiOut> for Modify<R> {
|
||||||
fn content (&self) -> impl Render<TuiOut> { &self.2 }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.2.layout(to)
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&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.render(to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Styled<R: Content<TuiOut>>(pub Option<Style>, pub R);
|
pub struct Styled<R: Render<TuiOut>>(pub Option<Style>, pub R);
|
||||||
impl<R: Content<TuiOut>> Content<TuiOut> for Styled<R> {
|
impl<R: Render<TuiOut>> Render<TuiOut> for Styled<R> {
|
||||||
fn content (&self) -> impl Render<TuiOut> { &self.1 }
|
fn layout (&self, to: [u16;4]) -> [u16;4] {
|
||||||
|
self.1.layout(to)
|
||||||
|
}
|
||||||
fn render (&self, to: &mut TuiOut) {
|
fn render (&self, to: &mut TuiOut) {
|
||||||
to.place(self.content().layout(to.area()), &self.content());
|
to.place(self.1.layout(to.area()), &self.1);
|
||||||
// TODO write style over area
|
// TODO write style over area
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ impl<A, B, C> Tryptich<A, B, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B, C> Content<TuiOut> for Tryptich<A, B, C>
|
impl<A, B, C> Content<TuiOut> for Tryptich<A, B, C>
|
||||||
where A: Content<TuiOut>, B: Content<TuiOut>, C: Content<TuiOut> {
|
where A: Render<TuiOut>, B: Render<TuiOut>, C: Render<TuiOut> {
|
||||||
fn content (&self) -> impl 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;
|
||||||
Fixed::y(h, if top {
|
Some(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 +50,7 @@ where A: Content<TuiOut>, B: Content<TuiOut>, C: Content<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)))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ impl TuiEvent {
|
||||||
pub fn from_crossterm (event: Event) -> Self {
|
pub fn from_crossterm (event: Event) -> Self {
|
||||||
Self(event)
|
Self(event)
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "dsl")]
|
||||||
pub fn from_dsl (dsl: impl Dsl) -> Perhaps<Self> {
|
pub fn from_dsl (dsl: impl Dsl) -> Perhaps<Self> {
|
||||||
Ok(TuiKey::from_dsl(dsl)?.to_crossterm().map(Self))
|
Ok(TuiKey::from_dsl(dsl)?.to_crossterm().map(Self))
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +18,7 @@ impl TuiEvent {
|
||||||
pub struct TuiKey(Option<KeyCode>, KeyModifiers);
|
pub struct TuiKey(Option<KeyCode>, KeyModifiers);
|
||||||
impl TuiKey {
|
impl TuiKey {
|
||||||
const SPLIT: char = '/';
|
const SPLIT: char = '/';
|
||||||
|
#[cfg(feature = "dsl")]
|
||||||
pub fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
pub fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
||||||
if let Some(word) = dsl.word()? {
|
if let Some(word) = dsl.word()? {
|
||||||
let word = word.trim();
|
let word = word.trim();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue