summaryrefslogtreecommitdiff
path: root/src/layout/primitive.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-01 15:35:09 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-01 15:35:09 +0200
commitf8770d2b2a8ac389704897f92f2753398352835b (patch)
treee522fb7b52f780d3040e71990cf8e765fa7669df /src/layout/primitive.rs
parente676ab53ddbab367179ee2ab214bb41ff2ee0c11 (diff)
Generalize layouting primitives 🛤
Diffstat (limited to 'src/layout/primitive.rs')
-rw-r--r--src/layout/primitive.rs320
1 files changed, 151 insertions, 169 deletions
diff --git a/src/layout/primitive.rs b/src/layout/primitive.rs
index 6b5df7db..cdbe63c2 100644
--- a/src/layout/primitive.rs
+++ b/src/layout/primitive.rs
@@ -2,46 +2,33 @@
use std::fmt::{self, Display, Formatter};
-use super::prelude::*;
-
-/// Specifies the axes along content is laid out.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct LayoutAxes {
- pub primary: Dir,
- pub secondary: Dir,
-}
+/// 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<Dir>;
-impl LayoutAxes {
- /// Create a new instance from the two directions.
- ///
- /// # Panics
- /// This function panics if the directions are aligned, i.e. if they are
- /// on the same axis.
- pub fn new(primary: Dir, secondary: Dir) -> Self {
- if primary.axis() == secondary.axis() {
- panic!("directions {} and {} are aligned", primary, secondary);
- }
- Self { primary, secondary }
+impl Default for LayoutSystem {
+ fn default() -> Self {
+ Self::new(Dir::LTR, Dir::TTB)
}
+}
- /// Return the direction of the specified generic axis.
- pub fn get(self, axis: GenAxis) -> Dir {
- match axis {
- Primary => self.primary,
- Secondary => self.secondary,
- }
- }
+/// Specifies where to align a layout in a parent container.
+pub type LayoutAlign = Gen2<GenAlign>;
- /// Borrow the direction of the specified generic axis mutably.
- pub fn get_mut(&mut self, axis: GenAxis) -> &mut Dir {
- match axis {
- Primary => &mut self.primary,
- Secondary => &mut self.secondary,
- }
- }
+impl LayoutAlign {
+ /// The layout alignment that has both components set to `Start`.
+ pub const START: Self = Self {
+ primary: GenAlign::Start,
+ secondary: GenAlign::Start,
+ };
}
-/// Directions along which content is laid out.
+/// Whether to expand a layout to an area's full size or shrink it to fit its content.
+pub type LayoutExpansion = Spec2<bool>;
+
+/// The four directions into which content can be laid out.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dir {
/// Left to right.
@@ -55,11 +42,31 @@ pub enum Dir {
}
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 {
- LTR | RTL => Horizontal,
- TTB | BTT => Vertical,
+ Self::LTR | Self::RTL => SpecAxis::Horizontal,
+ Self::TTB | Self::BTT => SpecAxis::Vertical,
}
}
@@ -68,8 +75,8 @@ impl Dir {
/// The positive directions are left-to-right and top-to-bottom.
pub fn is_positive(self) -> bool {
match self {
- LTR | TTB => true,
- RTL | BTT => false,
+ Self::LTR | Self::TTB => true,
+ Self::RTL | Self::BTT => false,
}
}
@@ -84,10 +91,10 @@ impl Dir {
/// The inverse direction.
pub fn inv(self) -> Self {
match self {
- LTR => RTL,
- RTL => LTR,
- TTB => BTT,
- BTT => TTB,
+ Self::LTR => Self::RTL,
+ Self::RTL => Self::LTR,
+ Self::TTB => Self::BTT,
+ Self::BTT => Self::TTB,
}
}
}
@@ -95,10 +102,10 @@ impl Dir {
impl Display for Dir {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
- LTR => "ltr",
- RTL => "rtl",
- TTB => "ttb",
- BTT => "btt",
+ Self::LTR => "ltr",
+ Self::RTL => "rtl",
+ Self::TTB => "ttb",
+ Self::BTT => "btt",
})
}
}
@@ -106,24 +113,24 @@ impl Display for Dir {
/// The two generic layouting axes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum GenAxis {
- /// The primary layouting direction along which text and lines flow.
+ /// The primary layouting direction into which text and lines flow.
Primary,
- /// The secondary layouting direction along which paragraphs grow.
+ /// The secondary layouting direction into which paragraphs grow.
Secondary,
}
impl GenAxis {
- /// The specific version of this axis in the given system of axes.
- pub fn to_specific(self, axes: LayoutAxes) -> SpecAxis {
- axes.get(self).axis()
+ /// 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 {
- Primary => "primary",
- Secondary => "secondary",
+ Self::Primary => "primary",
+ Self::Secondary => "secondary",
})
}
}
@@ -138,12 +145,12 @@ pub enum SpecAxis {
}
impl SpecAxis {
- /// The generic version of this axis in the given system of axes.
- pub fn to_generic(self, axes: LayoutAxes) -> GenAxis {
- if self == axes.primary.axis() {
- Primary
+ /// 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 {
- Secondary
+ GenAxis::Secondary
}
}
}
@@ -151,40 +158,19 @@ impl SpecAxis {
impl Display for SpecAxis {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
- Horizontal => "horizontal",
- Vertical => "vertical",
+ Self::Horizontal => "horizontal",
+ Self::Vertical => "vertical",
})
}
}
-/// Specifies where to align a layout in a parent container.
+/// A side of a container.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct LayoutAlign {
- pub primary: GenAlign,
- pub secondary: GenAlign,
-}
-
-impl LayoutAlign {
- /// Create a new instance from the two alignments.
- pub fn new(primary: GenAlign, secondary: GenAlign) -> Self {
- Self { primary, secondary }
- }
-
- /// Return the alignment for the specified generic axis.
- pub fn get(self, axis: GenAxis) -> GenAlign {
- match axis {
- Primary => self.primary,
- Secondary => self.secondary,
- }
- }
-
- /// Borrow the alignment for the specified generic axis mutably.
- pub fn get_mut(&mut self, axis: GenAxis) -> &mut GenAlign {
- match axis {
- Primary => &mut self.primary,
- Secondary => &mut self.secondary,
- }
- }
+pub enum Side {
+ Left,
+ Top,
+ Right,
+ Bottom,
}
/// Where to align content along an axis in a generic context.
@@ -199,9 +185,9 @@ impl GenAlign {
/// The inverse alignment.
pub fn inv(self) -> Self {
match self {
- Start => End,
- Center => Center,
- End => Start,
+ Self::Start => Self::End,
+ Self::Center => Self::Center,
+ Self::End => Self::Start,
}
}
}
@@ -209,9 +195,9 @@ impl GenAlign {
impl Display for GenAlign {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
- Start => "start",
- Center => "center",
- End => "end",
+ Self::Start => "start",
+ Self::Center => "center",
+ Self::End => "end",
})
}
}
@@ -232,30 +218,29 @@ impl SpecAlign {
/// Returns `None` if this is `Center` since the axis is unknown.
pub fn axis(self) -> Option<SpecAxis> {
match self {
- Self::Left => Some(Horizontal),
- Self::Right => Some(Horizontal),
- Self::Top => Some(Vertical),
- Self::Bottom => Some(Vertical),
+ 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 system of axes.
- pub fn to_generic(self, axes: LayoutAxes) -> GenAlign {
- let get = |spec: SpecAxis, align: GenAlign| {
- let axis = spec.to_generic(axes);
- if axes.get(axis).is_positive() {
- align
+ /// 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 {
- align.inv()
+ positive.inv()
}
};
match self {
- Self::Left => get(Horizontal, Start),
- Self::Right => get(Horizontal, End),
- Self::Top => get(Vertical, Start),
- Self::Bottom => get(Vertical, End),
+ 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,
}
}
@@ -273,85 +258,82 @@ impl Display for SpecAlign {
}
}
-/// Specifies whether to expand a layout to the full size of the space it is
-/// laid out in or to shrink it to fit the content.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct LayoutExpansion {
- /// Whether to expand on the horizontal axis.
- pub horizontal: bool,
- /// Whether to expand on the vertical axis.
- pub vertical: bool,
+/// A generic container with two components for the two generic axes.
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Gen2<T> {
+ /// The primary component.
+ pub primary: T,
+ /// The secondary component.
+ pub secondary: T,
}
-impl LayoutExpansion {
- /// Create a new instance from the two values.
- pub fn new(horizontal: bool, vertical: bool) -> Self {
- Self { horizontal, vertical }
+impl<T> Gen2<T> {
+ /// Create a new instance from the two components.
+ pub fn new(primary: T, secondary: T) -> Self {
+ Self { primary, secondary }
}
- /// Return the expansion value for the given specific axis.
- pub fn get(self, axis: SpecAxis) -> bool {
+ /// Return the component for the specified generic axis.
+ pub fn get(self, axis: GenAxis) -> T {
match axis {
- Horizontal => self.horizontal,
- Vertical => self.vertical,
+ GenAxis::Primary => self.primary,
+ GenAxis::Secondary => self.secondary,
}
}
- /// Borrow the expansion value for the given specific axis mutably.
- pub fn get_mut(&mut self, axis: SpecAxis) -> &mut bool {
+ /// Borrow the component for the specified generic axis.
+ pub fn get_ref(&mut self, axis: GenAxis) -> &T {
match axis {
- Horizontal => &mut self.horizontal,
- Vertical => &mut self.vertical,
+ GenAxis::Primary => &mut self.primary,
+ GenAxis::Secondary => &mut self.secondary,
}
}
-}
-/// Defines how spacing interacts with surrounding spacing.
-///
-/// There are two options for interaction: Hard and soft spacing. Typically,
-/// hard spacing is used when a fixed amount of space needs to be inserted no
-/// matter what. In contrast, soft spacing can be used to insert a default
-/// spacing between e.g. two words or paragraphs that can still be overridden by
-/// a hard space.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum SpacingKind {
- /// Hard spaces are always laid out and consume surrounding soft space.
- Hard,
- /// Soft spaces are not laid out if they are touching a hard space and
- /// consume neighbouring soft spaces with higher levels.
- Soft(u32),
+ /// 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,
+ }
+ }
}
-impl SpacingKind {
- /// The standard spacing kind used for paragraph spacing.
- pub const PARAGRAPH: Self = Self::Soft(1);
+/// A generic container with two components for the two specific axes.
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Spec2<T> {
+ /// The horizontal component.
+ pub horizontal: T,
+ /// The vertical component.
+ pub vertical: T,
+}
- /// The standard spacing kind used for line spacing.
- pub const LINE: Self = Self::Soft(2);
+impl<T> Spec2<T> {
+ /// Create a new instance from the two components.
+ pub fn new(horizontal: T, vertical: T) -> Self {
+ Self { horizontal, vertical }
+ }
- /// The standard spacing kind used for word spacing.
- pub const WORD: Self = Self::Soft(1);
-}
+ /// 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,
+ }
+ }
-/// The spacing kind of the most recently inserted item in a layouting process.
-///
-/// Since the last inserted item may not be spacing at all, this can be `None`.
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub(crate) enum LastSpacing {
- /// The last item was hard spacing.
- Hard,
- /// The last item was soft spacing with the given width and level.
- Soft(f64, u32),
- /// The last item wasn't spacing.
- None,
-}
+ /// 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,
+ }
+ }
-impl LastSpacing {
- /// The width of the soft space if this is a soft space or zero otherwise.
- pub fn soft_or_zero(self) -> f64 {
- match self {
- LastSpacing::Soft(space, _) => space,
- _ => 0.0,
+ /// 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,
}
}
}