mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
184 lines
7 KiB
Rust
184 lines
7 KiB
Rust
use crate::*;
|
|
|
|
/// Defines an enum that wraps the same renderable item
|
|
/// but discriminates on the variant which wraps it.
|
|
///
|
|
/// It also has a special `_Unused` variant which wraps
|
|
/// the engine type using `PhantomData` to permit the
|
|
/// double generic.
|
|
macro_rules! content_enum {
|
|
($Enum:ident: $($Variant:ident),+ $(,)?) => {
|
|
pub enum $Enum<E: Engine, T: Content<E>> {
|
|
_Unused(PhantomData<E>), $($Variant(T)),+
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Defines an enum that transforms its content
|
|
/// along either the X axis, the Y axis, or both.
|
|
macro_rules! transform_xy {
|
|
($self:ident : $Enum:ident |$to:ident|$area:expr) => {
|
|
content_enum!($Enum: X, Y, XY);
|
|
impl<E: Engine, T: Content<E>> $Enum<E, T> {
|
|
pub fn x (item: T) -> Self { Self::X(item) }
|
|
pub fn y (item: T) -> Self { Self::Y(item) }
|
|
pub fn xy (item: T) -> Self { Self::XY(item) }
|
|
}
|
|
impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
|
|
fn content (&self) -> Option<impl Content<E>> {
|
|
Some(match self {
|
|
Self::X(item) => item,
|
|
Self::Y(item) => item,
|
|
Self::XY(item) => item,
|
|
_ => unreachable!()
|
|
})
|
|
}
|
|
fn area (&$self, $to: <E as Engine>::Area) -> <E as Engine>::Area {
|
|
$area
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
transform_xy!(self: Fill |to|{
|
|
let [x0, y0, wmax, hmax] = to.xywh();
|
|
let [x, y, w, h] = self.content().unwrap().area(to).xywh();
|
|
return match self {
|
|
Self::X(_) => [x0, y, wmax, h],
|
|
Self::Y(_) => [x, y0, w, hmax],
|
|
Self::XY(_) => [x0, y0, wmax, hmax],
|
|
_ => unreachable!()
|
|
}.into()
|
|
});
|
|
|
|
/// Defines an enum that transforms its content parametrically
|
|
/// along either the X axis, the Y axis, or both
|
|
macro_rules! transform_xy_unit {
|
|
(|$self:ident : $Enum:ident, $to:ident|$area:expr) => {
|
|
pub enum $Enum<E: Engine, T: Content<E>> {
|
|
X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T),
|
|
}
|
|
impl<E: Engine, T: Content<E>> $Enum<E, T> {
|
|
pub fn x (x: E::Unit, item: T) -> Self { Self::X(x, item) }
|
|
pub fn y (y: E::Unit, item: T) -> Self { Self::Y(y, item) }
|
|
pub fn xy (x: E::Unit, y: E::Unit, item: T) -> Self { Self::XY(x, y, item) }
|
|
pub fn dx (&self) -> E::Unit {
|
|
match self {
|
|
Self::X(x, _) => *x,
|
|
Self::Y(_, _) => E::Unit::zero(),
|
|
Self::XY(x, _, _) => *x,
|
|
}
|
|
}
|
|
pub fn dy (&self) -> E::Unit {
|
|
match self {
|
|
Self::X(_, _) => E::Unit::zero(),
|
|
Self::Y(y, _) => *y,
|
|
Self::XY(_, y, _) => *y,
|
|
}
|
|
}
|
|
}
|
|
impl<E: Engine, T: Content<E>> Content<E> for $Enum<E, T> {
|
|
fn content (&self) -> Option<impl Content<E>> {
|
|
Some(match self {
|
|
Self::X(_, content) => content,
|
|
Self::Y(_, content) => content,
|
|
Self::XY(_, _, content) => content,
|
|
})
|
|
}
|
|
fn area (&$self, $to: E::Area) -> E::Area {
|
|
$area.into()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
transform_xy_unit!(|self: Fixed, to|match self {
|
|
Self::X(fw, _) => [to.x(), to.y(), *fw, to.h()],
|
|
Self::Y(fh, _) => [to.x(), to.y(), to.w(), *fh],
|
|
Self::XY(fw, fh, _) => [to.x(), to.y(), *fw, *fh], // tagn
|
|
});
|
|
|
|
transform_xy_unit!(|self: Shrink, to|
|
|
[to.x(), to.y(), to.w().minus(self.dx()), to.h().minus(self.dy())]);
|
|
|
|
transform_xy_unit!(|self: Expand, to|
|
|
[to.x(), to.y(), to.w() + self.dx(), to.h() + self.dy()]);
|
|
|
|
transform_xy_unit!(|self: Min, to|match self {
|
|
Self::X(mw, _) => [to.x(), to.y(), to.w().max(*mw), to.h()],
|
|
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().max(*mh)],
|
|
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().max(*mw), to.h().max(*mh)]
|
|
});
|
|
|
|
transform_xy_unit!(|self: Max, to|match self {
|
|
Self::X(mw, _) => [to.x(), to.y(), to.w().min(*mw), to.h()],
|
|
Self::Y(mh, _) => [to.x(), to.y(), to.w(), to.h().min(*mh)],
|
|
Self::XY(mw, mh, _) => [to.x(), to.y(), to.w().min(*mw), to.h().min(*mh)],
|
|
});
|
|
|
|
transform_xy_unit!(|self: Push, to|
|
|
[to.x() + self.dx(), to.y() + self.dy(), to.w(), to.h()]);
|
|
|
|
transform_xy_unit!(|self: Pull, to|
|
|
[to.x().minus(self.dx()), to.y().minus(self.dy()), to.w(), to.h()]);
|
|
|
|
transform_xy_unit!(|self: Margin, to|{
|
|
let dx = self.dx();
|
|
let dy = self.dy();
|
|
[to.x().minus(dx), to.y().minus(dy), to.w() + dy + dy, to.h() + dy + dy]
|
|
});
|
|
|
|
transform_xy_unit!(|self: Padding, to|{
|
|
let dx = self.dx();
|
|
let dy = self.dy();
|
|
[to.x() + dx, to.y() + dy, to.w().minus(dy + dy), to.h().minus(dy + dy), ]
|
|
});
|
|
|
|
content_enum!(Align: Center, X, Y, NW, N, NE, E, SE, S, SW, W);
|
|
impl<E: Engine, T: Content<E>> Align<E, T> {
|
|
pub fn c (w: T) -> Self { Self::Center(w) }
|
|
pub fn x (w: T) -> Self { Self::X(w) }
|
|
pub fn y (w: T) -> Self { Self::Y(w) }
|
|
pub fn n (w: T) -> Self { Self::N(w) }
|
|
pub fn s (w: T) -> Self { Self::S(w) }
|
|
pub fn e (w: T) -> Self { Self::E(w) }
|
|
pub fn w (w: T) -> Self { Self::W(w) }
|
|
pub fn nw (w: T) -> Self { Self::NW(w) }
|
|
pub fn sw (w: T) -> Self { Self::SW(w) }
|
|
pub fn ne (w: T) -> Self { Self::NE(w) }
|
|
pub fn se (w: T) -> Self { Self::SE(w) }
|
|
}
|
|
|
|
fn align<E: Engine, T: Content<E>, N: Coordinate, R: Area<N> + From<[N;4]>> (align: &Align<E, T>, outer: R, content: R) -> Option<R> {
|
|
if outer.w() < content.w() || outer.h() < content.h() {
|
|
None
|
|
} else {
|
|
let [ox, oy, ow, oh] = outer.xywh();
|
|
let [ix, iy, iw, ih] = content.xywh();
|
|
Some(match align {
|
|
Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(),
|
|
Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(),
|
|
Align::Y(_) => [ix, oy + (oh - ih) / 2.into(), iw, ih,].into(),
|
|
Align::NW(_) => [ox, oy, iw, ih,].into(),
|
|
Align::N(_) => [ox + (ow - iw) / 2.into(), oy, iw, ih,].into(),
|
|
Align::NE(_) => [ox + ow - iw, oy, iw, ih,].into(),
|
|
Align::W(_) => [ox, oy + (oh - ih) / 2.into(), iw, ih,].into(),
|
|
Align::E(_) => [ox + ow - iw, oy + (oh - ih) / 2.into(), iw, ih,].into(),
|
|
Align::SW(_) => [ox, oy + oh - ih, iw, ih,].into(),
|
|
Align::S(_) => [ox + (ow - iw) / 2.into(), oy + oh - ih, iw, ih,].into(),
|
|
Align::SE(_) => [ox + ow - iw, oy + oh - ih, iw, ih,].into(),
|
|
_ => unreachable!()
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<E: Engine, T: Content<E>> Content<E> for Align<E, T> {
|
|
fn render (&self, to: &mut E::Output) {
|
|
let outer_area = to.area();
|
|
let content = self.content();
|
|
let inner_area = content.area(outer_area);
|
|
if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) {
|
|
to.place(aligned, &content)
|
|
}
|
|
}
|
|
}
|