diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-11-15 13:05:25 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-11-15 13:05:25 +0100 |
| commit | 9473ae61e9cfc419eb6b48fb0ed15441cc7ad48b (patch) | |
| tree | da7f23a8a653b41b5395a4756f1ee0472f5b35e0 /src/layout/mod.rs | |
| parent | 40c879e3c03794a52cabcf31dc4216afc53c6ec5 (diff) | |
Generalize stack layouter 🎲
Diffstat (limited to 'src/layout/mod.rs')
| -rw-r--r-- | src/layout/mod.rs | 124 |
1 files changed, 100 insertions, 24 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 34c9b35a..f0160f31 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -3,6 +3,8 @@ use std::borrow::Cow; use std::io::{self, Write}; +use smallvec::SmallVec; + use toddle::query::{FontClass, SharedFontLoader}; use toddle::Error as FontError; @@ -133,35 +135,38 @@ impl<'a> IntoIterator for &'a MultiLayout { } /// The general context for layouting. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct LayoutContext<'a, 'p> { /// The font loader to retrieve fonts from when typesetting text /// using [`layout_text`]. pub loader: &'a SharedFontLoader<'p>, + /// The style to set text with. This includes sizes and font classes /// which determine which font from the loaders selection is used. pub style: &'a TextStyle, - /// The alignment to use for the content. - pub alignment: Alignment, - /// How to stack the context. - pub flow: Flow, - /// The primary space to layout in. - pub space: LayoutSpace, - /// The additional spaces which are used when the primary space - /// cannot fit the whole content. - pub followup_spaces: Option<LayoutSpace>, - /// Whether to shrink the dimensions to fit the content or the keep the - /// dimensions from the layout spaces. - pub shrink_to_fit: bool, + + /// The spaces to layout in. + pub spaces: LayoutSpaces, + + /// The axes to flow on. + pub axes: LayoutAxes, } +/// A possibly stack-allocated vector of layout spaces. +pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>; + /// Spacial layouting constraints. #[derive(Debug, Copy, Clone)] pub struct LayoutSpace { /// The maximum size of the box to layout in. pub dimensions: Size2D, + /// Padding that should be respected on each side. pub padding: SizeBox, + + /// Whether to shrink the space to fit the content or to keep + /// the original dimensions. + pub shrink_to_fit: bool, } impl LayoutSpace { @@ -170,28 +175,99 @@ impl LayoutSpace { self.dimensions.unpadded(self.padding) } - /// A layout without padding and dimensions reduced by the padding. - pub fn usable_space(&self) -> LayoutSpace { + /// The offset from the origin to the start of content, that is, + /// `(padding.left, padding.top)`. + pub fn start(&self) -> Size2D { + Size2D::new(self.padding.left, self.padding.right) + } + + /// A layout space without padding and dimensions reduced by the padding. + pub fn usable_space(&self, shrink_to_fit: bool) -> LayoutSpace { LayoutSpace { dimensions: self.usable(), padding: SizeBox::zero(), + shrink_to_fit, } } } -/// Where to align content. +/// The axes along which the content is laid out. +#[derive(Debug, Copy, Clone)] +pub struct LayoutAxes { + pub primary: AlignedAxis, + pub secondary: AlignedAxis, +} + +impl LayoutAxes { + /// The position of the anchor specified by the two aligned axes + /// in the given generalized space. + pub fn anchor(&self, area: Size2D) -> Size2D { + Size2D::new(self.primary.anchor(area.x), self.secondary.anchor(area.y)) + } +} + +/// An axis with an alignment. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Alignment { - Left, - Right, - Center, +pub struct AlignedAxis { + pub axis: Axis, + pub alignment: Alignment, +} + +impl AlignedAxis { + /// Returns an aligned axis if the alignment is compatible with the axis. + pub fn new(axis: Axis, alignment: Alignment) -> AlignedAxis { + AlignedAxis { axis, alignment } + } + + /// The pair of axis and alignment. + pub fn pair(&self) -> (Axis, Alignment) { + (self.axis, self.alignment) + } + + /// The position of the anchor specified by this axis on the given line. + pub fn anchor(&self, line: Size) -> Size { + use Alignment::*; + match (self.axis.is_positive(), self.alignment) { + (true, Origin) | (false, End) => Size::zero(), + (_, Center) => line / 2, + (true, End) | (false, Origin) => line, + } + } +} + +/// Where to put content. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Axis { + LeftToRight, + RightToLeft, + TopToBottom, + BottomToTop, } -/// The flow of content. +impl Axis { + /// Whether this is a horizontal axis. + pub fn is_horizontal(&self) -> bool { + match self { + Axis::LeftToRight | Axis::RightToLeft => true, + Axis::TopToBottom | Axis::BottomToTop => false, + } + } + + /// Whether this axis points into the positive coordinate direction. + pub fn is_positive(&self) -> bool { + match self { + Axis::LeftToRight | Axis::TopToBottom => true, + Axis::RightToLeft | Axis::BottomToTop => false, + } + } +} + +/// Where to align content. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Flow { - Vertical, - Horizontal, +pub enum Alignment { + Origin, + Center, + End, } /// The error type for layouting. |
