mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: generic layout!
This commit is contained in:
parent
0bbf74e915
commit
93ba611e33
11 changed files with 267 additions and 82 deletions
|
|
@ -1,38 +0,0 @@
|
|||
use crate::*;
|
||||
use std::ops::Add;
|
||||
|
||||
pub struct Offset<N>(pub N, pub N);
|
||||
|
||||
impl<N: Copy> From<N> for Offset<N> {
|
||||
fn from (n: N) -> Self {
|
||||
Self(n, n)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Inset<N>(pub N, pub N, pub N, pub N);
|
||||
|
||||
impl<N: Copy> From<N> for Inset<N> {
|
||||
fn from (n: N) -> Self {
|
||||
Self(n, n, n, n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Copy> From<(N, N)> for Inset<N> {
|
||||
fn from ((n1, n2): (N, N)) -> Self {
|
||||
Self(n1, n2, n1, n2)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Outset<N>(pub N, pub N, pub N, pub N);
|
||||
|
||||
impl<N: Copy> From<N> for Outset<N> {
|
||||
fn from (n: N) -> Self {
|
||||
Self(n, n, n, n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Copy> From<(N, N)> for Outset<N> {
|
||||
fn from ((n1, n2): (N, N)) -> Self {
|
||||
Self(n1, n2, n1, n2)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,36 @@
|
|||
use crate::*;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::cmp::{Ord, Eq, PartialEq};
|
||||
|
||||
/// Entry point for main loop
|
||||
pub trait App<T: Engine> {
|
||||
fn run (self, context: T) -> Usually<T>;
|
||||
}
|
||||
|
||||
pub trait Number: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq {}
|
||||
|
||||
impl<T> Number for T where
|
||||
T: Send + Sync + Copy
|
||||
+ Add<Self, Output=Self>
|
||||
+ Sub<Self, Output=Self>
|
||||
+ Ord + PartialEq + Eq
|
||||
{}
|
||||
|
||||
/// Platform backend.
|
||||
pub trait Engine: Sized {
|
||||
fn setup (&mut self) -> Usually<()> { Ok(()) }
|
||||
fn exited (&self) -> bool;
|
||||
fn teardown (&mut self) -> Usually<()> { Ok(()) }
|
||||
|
||||
type HandleInput;
|
||||
fn handle (&self, _: &mut impl Handle<Self>) -> Usually<()> where Self: Sized;
|
||||
type Handled;
|
||||
/// Unit of distance.
|
||||
type Unit: Number;
|
||||
|
||||
type HandleInput;
|
||||
type Handled;
|
||||
type RenderInput;
|
||||
fn render (&mut self, _: &impl Render<Self>) -> Usually<()> where Self: Sized;
|
||||
type Rendered;
|
||||
}
|
||||
|
||||
|
|
@ -25,9 +39,14 @@ pub trait HandleContext {}
|
|||
pub trait RenderContext {}
|
||||
|
||||
submod! {
|
||||
collect
|
||||
component
|
||||
exit
|
||||
focus
|
||||
handle
|
||||
keymap
|
||||
layered
|
||||
layout
|
||||
render
|
||||
split
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,3 @@ pub trait Component<E: Engine>: Render<E> + Handle<E> {}
|
|||
|
||||
/// Everything that implements [Render] and [Handle] is a [Component].
|
||||
impl<E: Engine, C: Render<E> + Handle<E>> Component<E> for C {}
|
||||
|
||||
submod! {
|
||||
collect
|
||||
layered
|
||||
offset
|
||||
split
|
||||
}
|
||||
100
crates/tek_core/src/engine/layout.rs
Normal file
100
crates/tek_core/src/engine/layout.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::*;
|
||||
|
||||
/// Compute drawing area before rendering
|
||||
pub trait Layout<E: Engine> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>>;
|
||||
}
|
||||
/// Enforce minimum size of drawing area
|
||||
pub enum Min<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||
/// Enforce maximum size of drawing area
|
||||
pub enum Max<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||
/// Expand drawing area
|
||||
pub enum Outset<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||
/// Shrink drawing area
|
||||
pub enum Inset<U: Number, L> { W(U, L), H(U, L), WH(U, U, L), }
|
||||
/// Move origin point of drawing area
|
||||
pub enum Offset<U: Number, L> { X(U, L), Y(U, L), XY(U, U, L), }
|
||||
|
||||
impl<E: Engine, L: Layout<E>> Layout<E> for Min<E:: Unit, L> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>> {
|
||||
match self {
|
||||
Self::W(w, item) => if area.w() < *w { Ok(None) } else {
|
||||
// TODO: axis clamp (subtract from x if width goes out of area
|
||||
item.layout([area.x(), area.y(), area.w().max(*w), area.h()])
|
||||
},
|
||||
Self::H(h, item) => if area.w() < *h { Ok(None) } else {
|
||||
// TODO: axis clamp (subtract from x if width goes out of area
|
||||
item.layout([area.x(), area.y(), area.w(), area.h().max(*h)])
|
||||
},
|
||||
Self::WH(w, h, item) => if area.w() < *w || area.h() < *h { Ok(None) } else {
|
||||
item.layout([area.x(), area.y(), area.w().max(*w), area.h().max(*h)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, L: Layout<E>> Layout<E> for Max<E:: Unit, L> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>> {
|
||||
match self {
|
||||
Self::W(w, item) => {
|
||||
// TODO: axis clamp (subtract from x if width goes out of area
|
||||
item.layout([area.x(), area.y(), area.w().min(*w), area.h()])
|
||||
},
|
||||
Self::H(h, item) => {
|
||||
// TODO: axis clamp (subtract from x if width goes out of area
|
||||
item.layout([area.x(), area.y(), area.w(), area.h().min(*h)])
|
||||
},
|
||||
Self::WH(w, h, item) => {
|
||||
item.layout([area.x(), area.y(), area.w().min(*w), area.h().min(*h)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, L: Layout<E>> Layout<E> for Outset<E::Unit, L> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>> {
|
||||
match self {
|
||||
Self::W(w, item) => if area.x() < *w { Ok(None) } else {
|
||||
item.layout([area.x() - *w, area.y(), area.w() + *w, area.h()])
|
||||
},
|
||||
Self::H(h, item) => if area.y() < *h { Ok(None) } else {
|
||||
item.layout([area.x(), area.y() - *h, area.w(), area.h() + *h])
|
||||
},
|
||||
Self::WH(w, h, item) => if area.x() < *w || area.y() < *h { Ok(None) } else {
|
||||
item.layout([area.x()-*w, area.y() - *h, area.w() + *w, area.h() + *h])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, L: Layout<E>> Layout<E> for Inset<E::Unit, L> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>> {
|
||||
match self {
|
||||
Self::W(w, item) => if area.w() < *w { Ok(None) } else {
|
||||
item.layout([area.x() + *w, area.y(), area.w() - *w, area.h()])
|
||||
},
|
||||
Self::H(h, item) => if area.h() < *h { Ok(None) } else {
|
||||
item.layout([area.x(), area.y() + *h, area.w(), area.h() - *h])
|
||||
},
|
||||
Self::WH(w, h, item) => if area.w() < *w || area.h() < *h { Ok(None) } else {
|
||||
item.layout([area.x() - *w, area.y() - *h, area.w() + *w, area.h() + *h])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, L: Layout<E>> Layout<E> for Offset<E::Unit, L> {
|
||||
fn layout (&self, area: impl Rectangle<E::Unit>) -> Perhaps<impl Rectangle<E::Unit>> {
|
||||
match self {
|
||||
Self::X(x, item) => if area.w() < *x { Ok(None) } else {
|
||||
item.layout([area.x() + *x, area.y(), area.w() - *x, area.h()])
|
||||
},
|
||||
Self::Y(y, item) => if area.h() < *y { Ok(None) } else {
|
||||
item.layout([area.x(), area.y() + *y, area.w(), area.h() - *y])
|
||||
},
|
||||
Self::XY(x, y, item) => if area.w() < *x || area.h() < *y { Ok(None) } else {
|
||||
item.layout([area.x() + *x, area.y() + *y, area.w() - *x, area.h() - *y])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@ use better_panic::{Settings, Verbosity};
|
|||
|
||||
submod! {
|
||||
audio
|
||||
component
|
||||
edn
|
||||
engine
|
||||
space
|
||||
|
|
|
|||
|
|
@ -1,3 +1,77 @@
|
|||
use crate::*;
|
||||
|
||||
pub trait Point<N: Number> {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N {
|
||||
self.x()
|
||||
}
|
||||
fn h (&self) -> N {
|
||||
self.y()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Number> Point<N> for (N, N) {
|
||||
fn x (&self) -> N {
|
||||
self.0
|
||||
}
|
||||
fn y (&self) -> N {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Number> Point<N> for [N;2] {
|
||||
fn x (&self) -> N {
|
||||
self[0]
|
||||
}
|
||||
fn y (&self) -> N {
|
||||
self[1]
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Rectangle<N: Number> {
|
||||
fn x (&self) -> N;
|
||||
fn y (&self) -> N;
|
||||
fn w (&self) -> N;
|
||||
fn h (&self) -> N;
|
||||
fn x2 (&self) -> N {
|
||||
self.x() + self.w()
|
||||
}
|
||||
fn y2 (&self) -> N {
|
||||
self.y() + self.h()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Number> Rectangle<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: Number> Rectangle<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]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_axis_common { ($A:ident $T:ty) => {
|
||||
impl $A<$T> {
|
||||
pub fn start_inc (&mut self) -> $T {
|
||||
|
|
@ -19,16 +93,25 @@ macro_rules! impl_axis_common { ($A:ident $T:ty) => {
|
|||
}
|
||||
} }
|
||||
|
||||
pub struct FixedAxis<T> { pub start: T, pub point: Option<T> }
|
||||
pub struct FixedAxis<T> {
|
||||
pub start: T,
|
||||
pub point: Option<T>
|
||||
}
|
||||
|
||||
impl_axis_common!(FixedAxis u16);
|
||||
impl_axis_common!(FixedAxis usize);
|
||||
|
||||
pub struct ScaledAxis<T> { pub start: T, pub scale: T, pub point: Option<T> }
|
||||
pub struct ScaledAxis<T> {
|
||||
pub start: T,
|
||||
pub scale: T,
|
||||
pub point: Option<T>
|
||||
}
|
||||
|
||||
impl_axis_common!(ScaledAxis u16);
|
||||
impl_axis_common!(ScaledAxis usize);
|
||||
|
||||
impl<T: Copy> ScaledAxis<T> {
|
||||
pub fn scale_mut (&mut self, cb: &impl Fn(T)->T) {
|
||||
self.scale = cb(self.scale)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,11 @@ pub struct Tui {
|
|||
area: Rect,
|
||||
}
|
||||
impl Engine for Tui {
|
||||
type Unit = u16;
|
||||
type HandleInput = Self;
|
||||
type Handled = bool;
|
||||
type Handled = bool;
|
||||
type RenderInput = Self;
|
||||
type Rendered = Rect;
|
||||
type Rendered = Rect;
|
||||
fn exited (&self) -> bool {
|
||||
self.exited.fetch_and(true, Ordering::Relaxed)
|
||||
}
|
||||
|
|
@ -46,12 +47,6 @@ impl Engine for Tui {
|
|||
stdout().execute(LeaveAlternateScreen)?;
|
||||
disable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
fn handle (&self, _: &mut impl Handle<Self>) -> Usually<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn render (&mut self, _: &impl Render<Self>) -> Usually<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Tui {
|
||||
/// Run the main loop.
|
||||
|
|
|
|||
|
|
@ -1,35 +1,69 @@
|
|||
use crate::*;
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Offset<u16>, R) {
|
||||
impl Rectangle<u16> for Rect {
|
||||
fn x (&self) -> u16 { self.x }
|
||||
fn y (&self) -> u16 { self.y }
|
||||
fn w (&self) -> u16 { self.width }
|
||||
fn h (&self) -> u16 { self.height }
|
||||
}
|
||||
|
||||
impl<R> Render<Tui> for Min<<Tui as Engine>::Unit, R> where R: Render<Tui> + Layout<Tui> {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0),
|
||||
height.saturating_sub(self.0.1),
|
||||
)))
|
||||
self.layout(engine.area())?
|
||||
.map(|area|engine.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|engine|engine.render(match self {
|
||||
Self::W(_, inner) => inner,
|
||||
Self::H(_, inner) => inner,
|
||||
Self::WH(_, _, inner) => inner,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Inset<u16>, R) {
|
||||
impl<R> Render<Tui> for Max<<Tui as Engine>::Unit, R> where R: Render<Tui> + Layout<Tui> {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x + self.0.0,
|
||||
y + self.0.1,
|
||||
width.saturating_sub(self.0.0 + self.0.2),
|
||||
height.saturating_sub(self.0.1 + self.0.3),
|
||||
)))
|
||||
self.layout(engine.area())?
|
||||
.map(|area|engine.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|engine|engine.render(match self {
|
||||
Self::W(_, inner) => inner,
|
||||
Self::H(_, inner) => inner,
|
||||
Self::WH(_, _, inner) => inner,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Render<Tui>> Render<Tui> for (Outset<u16>, R) {
|
||||
impl<R> Render<Tui> for Inset<<Tui as Engine>::Unit, R> where R: Render<Tui> + Layout<Tui> {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.1.render(engine.alter_area(|x, y, width, height|(
|
||||
x.saturating_sub(self.0.0),
|
||||
y.saturating_sub(self.0.1),
|
||||
width + self.0.0 + self.0.2,
|
||||
height + self.0.1 + self.0.3,
|
||||
)))
|
||||
self.layout(engine.area())?
|
||||
.map(|area|engine.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|engine|engine.render(match self {
|
||||
Self::W(_, inner) => inner,
|
||||
Self::H(_, inner) => inner,
|
||||
Self::WH(_, _, inner) => inner,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Render<Tui> for Outset<<Tui as Engine>::Unit, R> where R: Render<Tui> + Layout<Tui> {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.layout(engine.area())?
|
||||
.map(|area|engine.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|engine|engine.render(match self {
|
||||
Self::W(_, inner) => inner,
|
||||
Self::H(_, inner) => inner,
|
||||
Self::WH(_, _, inner) => inner,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Render<Tui> for Offset<<Tui as Engine>::Unit, R> where R: Render<Tui> + Layout<Tui> {
|
||||
fn render (&self, engine: &mut Tui) -> Perhaps<Rect> {
|
||||
self.layout(engine.area())?
|
||||
.map(|area|engine.with_area(area.x(), area.y(), area.w(), area.h()))
|
||||
.map(|engine|engine.render(match self {
|
||||
Self::X(_, inner) => inner,
|
||||
Self::Y(_, inner) => inner,
|
||||
Self::XY(_, _, inner) => inner,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue