diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-10-01 15:35:09 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-10-01 15:35:09 +0200 |
| commit | f8770d2b2a8ac389704897f92f2753398352835b (patch) | |
| tree | e522fb7b52f780d3040e71990cf8e765fa7669df /src/layout/primitive.rs | |
| parent | e676ab53ddbab367179ee2ab214bb41ff2ee0c11 (diff) | |
Generalize layouting primitives 🛤
Diffstat (limited to 'src/layout/primitive.rs')
| -rw-r--r-- | src/layout/primitive.rs | 320 |
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, } } } |
