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> { _Unused(PhantomData), $($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> $Enum { 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> Content for $Enum { fn content (&self) -> Option> { Some(match self { Self::X(item) => item, Self::Y(item) => item, Self::XY(item) => item, _ => unreachable!() }) } fn area (&$self, $to: ::Area) -> ::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> { X(E::Unit, T), Y(E::Unit, T), XY(E::Unit, E::Unit, T), } impl> $Enum { 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> Content for $Enum { fn content (&self) -> Option> { 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> Align { 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, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { 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> Content for Align { 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) } } }