//! Layouting primitives.
use std::fmt::{self, Display, Formatter};
/// Specifies the directions into which content is laid out.
///
/// The primary component defines into which direction text and lines flow and the
/// secondary into which paragraphs and pages grow.
pub type LayoutSystem = Gen2
;
impl Default for LayoutSystem {
fn default() -> Self {
Self::new(Dir::LTR, Dir::TTB)
}
}
/// Specifies where to align a layout in a parent container.
pub type LayoutAlign = Gen2;
impl LayoutAlign {
/// The layout alignment that has both components set to `Start`.
pub const START: Self = Self {
primary: GenAlign::Start,
secondary: GenAlign::Start,
};
}
/// Whether to expand a layout to an area's full size or shrink it to fit its content.
pub type LayoutExpansion = Spec2;
/// The four directions into which content can be laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir {
/// Left to right.
LTR,
/// Right to left.
RTL,
/// Top to bottom.
TTB,
/// Bottom to top.
BTT,
}
impl Dir {
/// The side this direction starts at.
pub fn start(self) -> Side {
match self {
Self::LTR => Side::Left,
Self::RTL => Side::Right,
Self::TTB => Side::Top,
Self::BTT => Side::Bottom,
}
}
/// The side this direction ends at.
pub fn end(self) -> Side {
match self {
Self::LTR => Side::Right,
Self::RTL => Side::Left,
Self::TTB => Side::Bottom,
Self::BTT => Side::Top,
}
}
/// The specific axis this direction belongs to.
pub fn axis(self) -> SpecAxis {
match self {
Self::LTR | Self::RTL => SpecAxis::Horizontal,
Self::TTB | Self::BTT => SpecAxis::Vertical,
}
}
/// Whether this direction points into the positive coordinate direction.
///
/// The positive directions are left-to-right and top-to-bottom.
pub fn is_positive(self) -> bool {
match self {
Self::LTR | Self::TTB => true,
Self::RTL | Self::BTT => false,
}
}
/// The factor for this direction.
///
/// - `1.0` if the direction is positive.
/// - `-1.0` if the direction is negative.
pub fn factor(self) -> f64 {
if self.is_positive() { 1.0 } else { -1.0 }
}
/// The inverse direction.
pub fn inv(self) -> Self {
match self {
Self::LTR => Self::RTL,
Self::RTL => Self::LTR,
Self::TTB => Self::BTT,
Self::BTT => Self::TTB,
}
}
}
impl Display for Dir {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::LTR => "ltr",
Self::RTL => "rtl",
Self::TTB => "ttb",
Self::BTT => "btt",
})
}
}
/// The two generic layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenAxis {
/// The primary layouting direction into which text and lines flow.
Primary,
/// The secondary layouting direction into which paragraphs grow.
Secondary,
}
impl GenAxis {
/// The specific version of this axis in the given layout system.
pub fn to_spec(self, sys: LayoutSystem) -> SpecAxis {
sys.get(self).axis()
}
}
impl Display for GenAxis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Primary => "primary",
Self::Secondary => "secondary",
})
}
}
/// The two specific layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum SpecAxis {
/// The horizontal layouting axis.
Horizontal,
/// The vertical layouting axis.
Vertical,
}
impl SpecAxis {
/// The generic version of this axis in the given layout system.
pub fn to_gen(self, sys: LayoutSystem) -> GenAxis {
if self == sys.primary.axis() {
GenAxis::Primary
} else {
GenAxis::Secondary
}
}
}
impl Display for SpecAxis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Horizontal => "horizontal",
Self::Vertical => "vertical",
})
}
}
/// A side of a container.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Side {
Left,
Top,
Right,
Bottom,
}
/// Where to align content along an axis in a generic context.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GenAlign {
Start,
Center,
End,
}
impl GenAlign {
/// The inverse alignment.
pub fn inv(self) -> Self {
match self {
Self::Start => Self::End,
Self::Center => Self::Center,
Self::End => Self::Start,
}
}
}
impl Display for GenAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Start => "start",
Self::Center => "center",
Self::End => "end",
})
}
}
/// Where to align content along an axis in a specific context.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum SpecAlign {
Left,
Right,
Top,
Bottom,
Center,
}
impl SpecAlign {
/// The specific axis this alignment refers to.
///
/// Returns `None` if this is `Center` since the axis is unknown.
pub fn axis(self) -> Option {
match self {
Self::Left => Some(SpecAxis::Horizontal),
Self::Right => Some(SpecAxis::Horizontal),
Self::Top => Some(SpecAxis::Vertical),
Self::Bottom => Some(SpecAxis::Vertical),
Self::Center => None,
}
}
/// The generic version of this alignment in the given layout system.
pub fn to_gen(self, sys: LayoutSystem) -> GenAlign {
let get = |spec: SpecAxis, positive: GenAlign| {
if sys.get(spec.to_gen(sys)).is_positive() {
positive
} else {
positive.inv()
}
};
match self {
Self::Left => get(SpecAxis::Horizontal, GenAlign::Start),
Self::Right => get(SpecAxis::Horizontal, GenAlign::End),
Self::Top => get(SpecAxis::Vertical, GenAlign::Start),
Self::Bottom => get(SpecAxis::Vertical, GenAlign::End),
Self::Center => GenAlign::Center,
}
}
}
impl Display for SpecAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Left => "left",
Self::Right => "right",
Self::Top => "top",
Self::Bottom => "bottom",
Self::Center => "center",
})
}
}
/// A generic container with two components for the two generic axes.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Gen2 {
/// The primary component.
pub primary: T,
/// The secondary component.
pub secondary: T,
}
impl Gen2 {
/// Create a new instance from the two components.
pub fn new(primary: T, secondary: T) -> Self {
Self { primary, secondary }
}
/// Return the component for the specified generic axis.
pub fn get(self, axis: GenAxis) -> T {
match axis {
GenAxis::Primary => self.primary,
GenAxis::Secondary => self.secondary,
}
}
/// Borrow the component for the specified generic axis.
pub fn get_ref(&mut self, axis: GenAxis) -> &T {
match axis {
GenAxis::Primary => &mut self.primary,
GenAxis::Secondary => &mut self.secondary,
}
}
/// Borrow the component for the specified generic axis mutably.
pub fn get_mut(&mut self, axis: GenAxis) -> &mut T {
match axis {
GenAxis::Primary => &mut self.primary,
GenAxis::Secondary => &mut self.secondary,
}
}
}
/// A generic container with two components for the two specific axes.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Spec2 {
/// The horizontal component.
pub horizontal: T,
/// The vertical component.
pub vertical: T,
}
impl Spec2 {
/// Create a new instance from the two components.
pub fn new(horizontal: T, vertical: T) -> Self {
Self { horizontal, vertical }
}
/// Return the component for the given specific axis.
pub fn get(self, axis: SpecAxis) -> T {
match axis {
SpecAxis::Horizontal => self.horizontal,
SpecAxis::Vertical => self.vertical,
}
}
/// Borrow the component for the given specific axis.
pub fn get_ref(&mut self, axis: SpecAxis) -> &T {
match axis {
SpecAxis::Horizontal => &mut self.horizontal,
SpecAxis::Vertical => &mut self.vertical,
}
}
/// Borrow the component for the given specific axis mutably.
pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
match axis {
SpecAxis::Horizontal => &mut self.horizontal,
SpecAxis::Vertical => &mut self.vertical,
}
}
}