use crate::*; /// Defines an enum that transforms its content /// along either the X axis, the Y axis, or both. /// /// The `_Unused` variant wraps the `Output` type /// using `PhantomData` to permit the double generic. macro_rules! transform_xy { ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$area:expr) => { pub enum $Enum { X(T), Y(T), XY(T) } 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<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { Some(if let Some((Token { value: Value::Key($x), .. }, _)) = iter.next() { Self::x(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) } else if let Some((Token { value: Value::Key($y), .. }, _)) = iter.next() { Self::y(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) } else if let Some((Token { value: Value::Key($xy), .. }, _)) = iter.next() { Self::xy(state.get_content(&iter.next().expect("no content").0.value).expect("no content")) } else { return None }) } } impl> Content for $Enum { fn content (&self) -> impl Render { match self { Self::X(item) => item, Self::Y(item) => item, Self::XY(item) => item, } } fn layout (&$self, $to: ::Area) -> ::Area { use $Enum::*; $area } } } } /// Defines an enum that parametrically transforms its content /// along either the X axis, the Y axis, or both. macro_rules! transform_xy_unit { ($x:literal $y:literal $xy:literal |$self:ident : $Enum:ident, $to:ident|$layout:expr) => { pub enum $Enum { X(U, T), Y(U, T), XY(U, U, T), } impl $Enum { pub fn x (x: U, item: T) -> Self { Self::X(x, item) } pub fn y (y: U, item: T) -> Self { Self::Y(y, item) } pub fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { Some(if let Some((Token { value: Value::Key($x), .. }, _)) = iter.next() { let x = state.get(&iter.next().expect("no x").0.value).expect("no x"); let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); Self::x(x, c) } else if let Some((Token { value: Value::Key($y), .. }, _)) = iter.next() { let y = state.get(&iter.next().expect("no y").0.value).expect("no y"); let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); Self::y(y, c) } else if let Some((Token { value: Value::Key($xy), .. }, _)) = iter.next() { let x = state.get(&iter.next().expect("no x").0.value).expect("no x"); let y = state.get(&iter.next().expect("no y").0.value).expect("no y"); let c = state.get_content(&iter.next().expect("no content").0.value).expect("no content"); Self::xy(x, y, c) } else { return None }) } } impl> Content for $Enum { fn content (&self) -> impl Render { Some(match self { Self::X(_, content) => content, Self::Y(_, content) => content, Self::XY(_, _, content) => content, }) } fn layout (&$self, $to: E::Area) -> E::Area { $layout.into() } } impl $Enum { pub fn dx (&self) -> U { match self { Self::X(x, _) => *x, Self::Y(_, _) => 0.into(), Self::XY(x, _, _) => *x, } } pub fn dy (&self) -> U { match self { Self::X(_, _) => 0.into(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y, } } } } } transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{ let [x0, y0, wmax, hmax] = to.xywh(); let [x, y, w, h] = self.content().layout(to).xywh(); match self { X(_) => [x0, y, wmax, h], Y(_) => [x, y0, w, hmax], XY(_) => [x0, y0, wmax, hmax], }.into() }); transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ let [x, y, w, h] = area.xywh(); let fixed_area = match self { Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], }; let [x, y, w, h] = Render::layout(&self.content(), fixed_area.into()).xywh(); let fixed_area = match self { Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], }; fixed_area }); transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{ let area = Render::layout(&self.content(), area); match self { Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()], Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)], Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)], }}); transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{ let [x, y, w, h] = area.xywh(); Render::layout(&self.content(), match self { Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], }.into())}); transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|Render::layout( &self.content(), [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( &self.content(), [area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()].into())); transform_xy_unit!("push/x" "push/y" "push/xy"|self: Push, area|{ let area = Render::layout(&self.content(), area); [area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()] }); transform_xy_unit!("pull/x" "pull/y" "pull/xy"|self: Pull, area|{ let area = Render::layout(&self.content(), area); [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|{ let area = Render::layout(&self.content(), area); let dx = self.dx(); let dy = self.dy(); [area.x().minus(dx), area.y().minus(dy), area.w() + dy + dy, area.h() + dy + dy] }); transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ let area = Render::layout(&self.content(), area); let dx = self.dx(); let dy = self.dy(); [area.x() + dx, area.y() + dy, area.w().minus(dy + dy), area.h().minus(dy + dy), ] });