mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-02-21 10:39:03 +01:00
161 lines
5.4 KiB
Rust
161 lines
5.4 KiB
Rust
use crate::*;
|
|
|
|
/// Drawing target.
|
|
///
|
|
/// ```
|
|
/// use crate::*;
|
|
/// struct TestOut([u16;4]);
|
|
/// impl Out for TestOut {
|
|
/// type Unit = u16;
|
|
/// type Size = [u16;2];
|
|
/// type Area = [u16;4];
|
|
/// fn area (&self) -> [u16;4] {
|
|
/// self.0
|
|
/// }
|
|
/// fn area_mut (&mut self) -> &mut [u16;4] {
|
|
/// &mut self.0
|
|
/// }
|
|
/// fn place_at <T: Draw<Self> + ?Sized> (&mut self, area: XYWH<u16>, _: &T) {
|
|
/// println!("place_at: {area:?}");
|
|
/// ()
|
|
/// }
|
|
/// }
|
|
/// impl Draw<TestOut> for String {
|
|
/// fn draw (&self, to: &mut TestOut) {
|
|
/// to.area_mut().set_w(self.len() as u16);
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
pub trait Out: Send + Sync + Sized {
|
|
/// Unit of length
|
|
type Unit: Coord;
|
|
/// Current output area
|
|
fn area (&self) -> XYWH<Self::Unit>;
|
|
/// Mutable pointer to area.
|
|
fn area_mut (&mut self) -> &mut XYWH<Self::Unit>;
|
|
/// Render drawable in area specified by `area`
|
|
fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: XYWH<Self::Unit>, content: &'t T);
|
|
/// Render drawable in area specified by `T::layout(self.area())`
|
|
#[inline] fn place <'t, T: Content<Self> + ?Sized> (&mut self, content: &'t T) {
|
|
self.place_at(content.layout(self.area()), content)
|
|
}
|
|
}
|
|
|
|
/// A numeric type that can be used as coordinate.
|
|
///
|
|
/// FIXME: Replace this ad-hoc trait with `num` crate.
|
|
pub trait Coord: 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 plus (self, other: Self) -> Self;
|
|
fn minus (self, other: Self) -> Self {
|
|
if self >= other { self - other } else { 0.into() }
|
|
}
|
|
fn atomic (self) -> AtomicUsize {
|
|
AtomicUsize::new(self.into())
|
|
}
|
|
fn zero () -> Self {
|
|
0.into()
|
|
}
|
|
}
|
|
|
|
/// Drawable with dynamic dispatch.
|
|
pub trait Draw<O: Out> {
|
|
fn draw (&self, to: &mut O);
|
|
}
|
|
|
|
/// FIXME: This is a general implementation: should be called `Eval` and be part of [dizzle].
|
|
/// Matches [Language] expressions to renderings for a given [Output] target.
|
|
pub trait View<O, U> {
|
|
fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually<U> {
|
|
Err(format!("View::view_expr: no exprs defined: {expr:?}").into())
|
|
}
|
|
fn view_word <'a> (&'a self, _output: &mut O, word: &'a impl Symbol) -> Usually<U> {
|
|
Err(format!("View::view_word: no words defined: {word:?}").into())
|
|
}
|
|
fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Language) -> Usually<U> {
|
|
let is_expr = dsl.expr();
|
|
let is_word = dsl.word();
|
|
match (dsl.expr(), dsl.word()) {
|
|
(Ok(Some(e)), _ ) => self.view_expr(output, &e),
|
|
(_, Ok(Some(w))) => self.view_word(output, &w),
|
|
(Err(e), _ ) => Err(format!("invalid view expr:\n{dsl:?}\n{e}").into()),
|
|
(_, Err(w) ) => Err(format!("invalid view word:\n{dsl:?}\n{w}").into()),
|
|
(Ok(None), Ok(None) ) => Err(format!("empty view:\n{dsl:?}").into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Outputs combinator.
|
|
pub trait Lay<O: Out>: Sized {}
|
|
|
|
/// Drawable area of display.
|
|
pub trait Layout<O: Out> {
|
|
fn layout_x (&self, to: XYWH<O::Unit>) -> O::Unit { to.x() }
|
|
fn layout_y (&self, to: XYWH<O::Unit>) -> O::Unit { to.y() }
|
|
fn layout_w_min (&self, _t: XYWH<O::Unit>) -> O::Unit { 0.into() }
|
|
fn layout_w_max (&self, to: XYWH<O::Unit>) -> O::Unit { to.w() }
|
|
fn layout_w (&self, to: XYWH<O::Unit>) -> O::Unit { to.w().max(self.layout_w_min(to)).min(self.layout_w_max(to)) }
|
|
fn layout_h_min (&self, _t: XYWH<O::Unit>) -> O::Unit { 0.into() }
|
|
fn layout_h_max (&self, to: XYWH<O::Unit>) -> O::Unit { to.h() }
|
|
fn layout_h (&self, to: XYWH<O::Unit>) -> O::Unit { to.h().max(self.layout_h_min(to)).min(self.layout_h_max(to)) }
|
|
fn layout (&self, to: XYWH<O::Unit>) -> XYWH<O::Unit> {
|
|
XYWH(self.layout_x(to), self.layout_y(to), self.layout_w(to), self.layout_h(to))
|
|
}
|
|
}
|
|
|
|
pub trait HasContent<O: Out> {
|
|
fn content (&self) -> impl Content<O>;
|
|
}
|
|
|
|
// TODO DOCUMENTME
|
|
pub trait Content<O: Out>: Draw<O> + Layout<O> {}
|
|
|
|
// Something that has an origin point (X, Y).
|
|
pub trait HasXY<N: Coord> {
|
|
fn x (&self) -> N;
|
|
fn y (&self) -> N;
|
|
fn xy (&self) -> XY<N> { XY(self.x(), self.y()) }
|
|
}
|
|
|
|
// Something that has a size (W, H).
|
|
pub trait HasWH<N: Coord> {
|
|
fn w (&self) -> N;
|
|
fn h (&self) -> N;
|
|
fn wh (&self) -> WH<N> { WH(self.w(), self.h()) }
|
|
}
|
|
|
|
// Something that has a 2D bounding box (X, Y, W, H).
|
|
//
|
|
// FIXME: The other way around?
|
|
pub trait HasXYWH<N: Coord>: HasXY<N> + HasWH<N> {
|
|
fn x2 (&self) -> N { self.x().plus(self.w()) }
|
|
fn y2 (&self) -> N { self.y().plus(self.h()) }
|
|
fn xywh (&self) -> XYWH<N> { XYWH(self.x(), self.y(), self.w(), self.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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Something that has a [Measure] of its rendered size.
|
|
pub trait Measured<O: Out> {
|
|
fn measure (&self) -> &Measure<O>;
|
|
fn measure_width (&self) -> O::Unit { self.measure().w() }
|
|
fn measure_height (&self) -> O::Unit { self.measure().h() }
|
|
}
|
|
|
|
pub trait HasPerf {
|
|
fn perf (&self) -> &PerfModel;
|
|
}
|