diff --git a/engine/src/engine.rs b/engine/src/engine.rs index b81fe05a..efc2398e 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -111,32 +111,18 @@ pub trait Area { #[inline] fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] } - fn x2 (&self) -> N { + #[inline] fn x2 (&self) -> N { self.x() + self.w() } - fn y2 (&self) -> N { + #[inline] fn y2 (&self) -> N { self.y() + self.h() } #[inline] fn lrtb (&self) -> [N;4] { [self.x(), self.x2(), self.y(), self.y2()] } - #[inline] fn push_x (&self, x: N) -> [N;4] { - [self.x() + x, self.y(), self.w(), self.h()] - } - #[inline] fn push_y (&self, y: N) -> [N;4] { - [self.x(), self.y() + y, self.w(), self.h()] - } - #[inline] fn shrink_x (&self, x: N) -> [N;4] { - [self.x(), self.y(), self.w().minus(x), self.h()] - } - #[inline] fn shrink_y (&self, y: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().minus(y)] - } - #[inline] fn center (&self) -> [N;2] { [self.x() + self.w() / 2.into(), self.y() + self.h() / 2.into()] } - fn zero () -> [N;4] { [N::zero(), N::zero(), N::zero(), N::zero()] } diff --git a/engine/src/output.rs b/engine/src/output.rs index 0b84022f..78a46ce8 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -22,27 +22,32 @@ pub trait Content: Send + Sync { fn content (&self) -> impl Content { () } - fn area (&self, area: E::Area) -> E::Area { - self.content().area(area) + fn layout (&self, area: E::Area) -> E::Area { + self.content().layout(area) } fn render (&self, output: &mut E::Output) { - self.content().render(output) + output.place(self.layout(output.area()), &self.content()) } } +/// The platonic ideal item of content: emptiness at dead center. impl Content for () { - fn area (&self, _: E::Area) -> E::Area { - [0.into(), 0.into(), 0.into(), 0.into()].into() + fn layout (&self, area: E::Area) -> E::Area { + let [x, y, w, h] = area.xywh(); + let x = x + w / 2.into(); + let y = y + h / 2.into(); + [x, y, 0.into(), 0.into()].into() + } + fn render (&self, _: &mut E::Output) { } - fn render (&self, _: &mut E::Output) {} } impl> Content for &T { fn content (&self) -> impl Content { (*self).content() } - fn area (&self, area: E::Area) -> E::Area { - (*self).area(area) + fn layout (&self, area: E::Area) -> E::Area { + (*self).layout(area) } fn render (&self, output: &mut E::Output) { (*self).render(output) @@ -54,9 +59,9 @@ impl> Content for Option { self.as_ref() .map(|content|content.content()) } - fn area (&self, area: E::Area) -> E::Area { + fn layout (&self, area: E::Area) -> E::Area { self.as_ref() - .map(|content|content.area(area)) + .map(|content|content.layout(area)) .unwrap_or([0.into(), 0.into(), 0.into(), 0.into(),].into()) } fn render (&self, output: &mut E::Output) { diff --git a/engine/src/tui/tui_output.rs b/engine/src/tui/tui_output.rs index 80334616..ceab4c8a 100644 --- a/engine/src/tui/tui_output.rs +++ b/engine/src/tui/tui_output.rs @@ -70,8 +70,11 @@ impl TuiOut { } impl Content for &str { - fn area (&self, to: [u16;4]) -> [u16;4] { - [to[0], to[1], self.chars().count() as u16, 1] + fn layout (&self, to: [u16;4]) -> [u16;4] { + let w = self.chars().count() as u16; + let y = to.y() + to.h() / 2; + let x = (to.x() + to.w() / 2).minus(w / 2); + [x, y, w, 1] } fn render (&self, to: &mut TuiOut) { to.blit(self, to.area.x(), to.area.y(), None) @@ -79,8 +82,11 @@ impl Content for &str { } impl Content for String { - fn area (&self, to: [u16;4]) -> [u16;4] { - [to[0], to[1], self.chars().count() as u16, 1] + fn layout (&self, to: [u16;4]) -> [u16;4] { + let w = self.chars().count() as u16; + let y = to.y() + to.h() / 2; + let x = (to.x() + to.w() / 2).minus(w / 2); + [x, y, w, 1] } fn render (&self, to: &mut TuiOut) { to.blit(self, to.area.x(), to.area.y(), None) diff --git a/layout/README.md b/layout/README.md index ce238f74..82959db0 100644 --- a/layout/README.md +++ b/layout/README.md @@ -1,7 +1,8 @@ # `tek_layout` this crate exposes several layout operators -which are generic over `tek_engine::Engine`. +which work entirely in unsigned coordinates +and are generic over `tek_engine::Engine`. * `Fill` makes the content's dimension equal to the container's. * `Fixed` assigns a fixed dimension to its content. diff --git a/layout/src/align.rs b/layout/src/align.rs index 403d41e5..750ea75d 100644 --- a/layout/src/align.rs +++ b/layout/src/align.rs @@ -20,7 +20,7 @@ impl> Align { } impl> Content for Align { - fn area (&self, outer: E::Area) -> E::Area { + fn layout (&self, outer: E::Area) -> E::Area { align_areas(self.0, outer.xywh(), Content::area(&self.content(), outer).xywh()).into() } fn render (&self, render: &mut E::Output) { @@ -36,20 +36,22 @@ pub fn align_areas(alignment: Alignment, on: [N;4], it: [N;4]) -> let center_y = center(cfy, cmy, it.y()); let east_x = on.x() + on.w().minus(it.w()); let south_y = on.y() + on.h().minus(it.h()); - match alignment { - Alignment::Center => [center_x, center_y, it.w(), it.h()], - Alignment::X => [center_x, it.y(), it.w(), it.h()], - Alignment::Y => [it.x(), center_y, it.w(), it.h()], + let [x, y] = match alignment { + Alignment::Center => [center_x, center_y,], - Alignment::NW => [on.x(), on.y(), it.w(), it.h()], - Alignment::N => [center_x, on.y(), it.w(), it.h()], - Alignment::NE => [east_x, on.y(), it.w(), it.h()], - Alignment::E => [east_x, center_y, it.w(), it.h()], - Alignment::SE => [east_x, south_y, it.w(), it.h()], - Alignment::S => [center_x, south_y, it.w(), it.h()], - Alignment::SW => [on.x(), south_y, it.w(), it.h()], - Alignment::W => [on.x(), center_y, it.w(), it.h()], - } + Alignment::X => [center_x, it.y(), ], + Alignment::Y => [it.x(), center_y,], + + Alignment::NW => [on.x(), on.y(), ], + Alignment::N => [center_x, on.y(), ], + Alignment::NE => [east_x, on.y(), ], + Alignment::E => [east_x, center_y,], + Alignment::SE => [east_x, south_y, ], + Alignment::S => [center_x, south_y, ], + Alignment::SW => [on.x(), south_y, ], + Alignment::W => [on.x(), center_y,], + }; + [x, y, it.w(), it.h()] } //fn align, N: Coordinate, R: Area + From<[N;4]>> (align: &Align, outer: R, content: R) -> Option { diff --git a/layout/src/bsp.rs b/layout/src/bsp.rs index bf2258a6..013aca31 100644 --- a/layout/src/bsp.rs +++ b/layout/src/bsp.rs @@ -69,32 +69,32 @@ impl, Y: Content> Default for Bsp { } impl, Y: Content> Content for Bsp { - fn area (&self, outer: E::Area) -> E::Area { + fn layout (&self, outer: E::Area) -> E::Area { match self { Self::Null(_) => [0.into(), 0.into(), 0.into(), 0.into()].into(), Self::North(_, a, b) => { - let a = a.area(outer); - let b = b.area(North.split_fixed(outer, a.y() + a.h()).1.into()); + let a = a.layout(outer); + let b = b.layout(North.split_fixed(outer, a.y() + a.h()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into() } Self::South(_, a, b) => { - let a = a.area(outer); - let b = b.area(South.split_fixed(outer, a.y() + a.h()).1.into()); + let a = a.layout(outer); + let b = b.layout(South.split_fixed(outer, a.y() + a.h()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h() + b.h()].into() }, Self::East(_, a, b) => { - let a = a.area(outer); - let b = b.area(East.split_fixed(outer, a.x() + a.w()).1.into()); + let a = a.layout(outer); + let b = b.layout(East.split_fixed(outer, a.x() + a.w()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into() }, Self::West(_, a, b) => { - let a = a.area(outer); - let b = b.area(West.split_fixed(outer, a.x() + a.w()).1.into()); + let a = a.layout(outer); + let b = b.layout(West.split_fixed(outer, a.x() + a.w()).1.into()); [a.x().min(b.x()), a.y().min(b.y()), a.w() + b.w(), a.h().max(b.h())].into() }, Self::Above(a, b) | Self::Below(a, b) => { - let a = a.area(outer); - let b = b.area(outer); + let a = a.layout(outer); + let b = b.layout(outer); [a.x().min(b.x()), a.y().min(b.y()), a.w().max(b.w()), a.h().max(b.h())].into() } } @@ -103,38 +103,38 @@ impl, Y: Content> Content for Bsp { let area = to.area().clone(); match self { Self::North(_, a, b) => { - let area_a = a.area(area); - let area_b = b.area(North.split_fixed(area, area_a.y() + area_a.h()).1.into()); + let area_a = a.layout(area); + let area_b = b.layout(North.split_fixed(area, area_a.y() + area_a.h()).1.into()); to.place(area_a, a); to.place(area_b, b); }, Self::South(_, a, b) => { - let area_a = a.area(area).clone(); - let area_b = b.area(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone(); + let area_a = a.layout(area).clone(); + let area_b = b.layout(South.split_fixed(area, area_a.y() + area_a.h()).1.into()).clone(); to.place(area_a, a); to.place(area_b, b); }, Self::East(_, a, b) => { - let area_a = a.area(area); - let area_b = b.area(East.split_fixed(area, area_a.x() + area_a.w()).1.into()); + let area_a = a.layout(area); + let area_b = b.layout(East.split_fixed(area, area_a.x() + area_a.w()).1.into()); to.place(area_a, a); to.place(area_b, b); }, Self::West(_, a, b) => { - let area_a = a.area(area); - let area_b = b.area(West.split_fixed(area, area_a.x() + area_a.w()).1.into()); + let area_a = a.layout(area); + let area_b = b.layout(West.split_fixed(area, area_a.x() + area_a.w()).1.into()); to.place(area_a, a); to.place(area_b, b); }, Self::Above(a, b) => { - let area_a = a.area(area); - let area_b = b.area(area); + let area_a = a.layout(area); + let area_b = b.layout(area); to.place(area_b, b); to.place(area_a, a); }, Self::Below(a, b) => { - let area_a = a.area(area); - let area_b = b.area(area); + let area_a = a.layout(area); + let area_b = b.layout(area); to.place(area_a, a); to.place(area_b, b); }, diff --git a/layout/src/ops.rs b/layout/src/ops.rs index baadfab9..6d093b58 100644 --- a/layout/src/ops.rs +++ b/layout/src/ops.rs @@ -45,11 +45,11 @@ pub struct OptR, R: Content>(Option, F, PhantomDa pub struct When(bool, A, PhantomData); impl> Content for When { - fn area (&self, to: E::Area) -> E::Area { + fn layout (&self, to: E::Area) -> E::Area { let Self(cond, item, ..) = self; let mut area = E::Area::zero(); if *cond { - let item_area = item.area(to); + let item_area = item.layout(to); area[0] = item_area.x(); area[1] = item_area.y(); area[2] = item_area.w(); @@ -67,9 +67,9 @@ impl> Content for When { pub struct Either(bool, A, B, PhantomData); impl, B: Content> Content for Either { - fn area (&self, to: E::Area) -> E::Area { + fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b, ..) = self; - if *cond { a.area(to) } else { b.area(to) } + if *cond { a.layout(to) } else { b.layout(to) } } fn render (&self, to: &mut E::Output) { let Self(cond, a, b, ..) = self; diff --git a/layout/src/transform_xy.rs b/layout/src/transform_xy.rs index 7ede48d8..ef082d0b 100644 --- a/layout/src/transform_xy.rs +++ b/layout/src/transform_xy.rs @@ -22,7 +22,7 @@ macro_rules! transform_xy { _ => unreachable!() } } - fn area (&$self, $to: ::Area) -> ::Area { + fn layout (&$self, $to: ::Area) -> ::Area { $area } } @@ -31,7 +31,7 @@ macro_rules! transform_xy { transform_xy!(self: Fill |to|{ let [x0, y0, wmax, hmax] = to.xywh(); - let [x, y, w, h] = Content::area(&self.content(), to).xywh(); + let [x, y, w, h] = Content::layout(&self.content(), to).xywh(); return match self { Self::X(_) => [x0, y, wmax, h], Self::Y(_) => [x, y0, w, hmax],