Extend stack layouts from vertical to horizontal flows ➡
This commit is contained in:
parent
1987e5861c
commit
991e879e1d
BIN
fonts/Latin-Modern-Math.otf
Normal file
BIN
fonts/Latin-Modern-Math.otf
Normal file
Binary file not shown.
@ -10,4 +10,5 @@
|
||||
"CMU-Typewriter-Italic.ttf" = ["Computer Modern", "Italic", "Serif", "SansSerif", "Monospace"]
|
||||
"CMU-Typewriter-Bold.ttf" = ["Computer Modern", "Bold", "Serif", "SansSerif", "Monospace"]
|
||||
"CMU-Typewriter-Bold-Italic.ttf" = ["Computer Modern", "Bold", "Italic", "Serif", "SansSerif", "Monospace"]
|
||||
"Latin-Modern-Math.otf" = ["Latin Modern", "Latin Modern Math", "Math", "Regular", "Serif"]
|
||||
"NotoEmoji-Regular.ttf" = ["Noto", "Noto Emoji", "Regular", "SansSerif", "Serif", "Monospace"]
|
||||
|
@ -146,8 +146,10 @@ impl<'d, W: Write> ExportProcess<'d, W> {
|
||||
for index in 0 .. num_fonts {
|
||||
let old_index = new_to_old[&index];
|
||||
let font = font_loader.get_with_index(old_index);
|
||||
let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)?;
|
||||
fonts.push(OwnedFont::from_bytes(subsetted)?);
|
||||
let subsetted = font.subsetted(font_chars[&old_index].iter().cloned(), &SUBSET_TABLES)
|
||||
.map(|bytes| OwnedFont::from_bytes(bytes))
|
||||
.unwrap_or_else(|_| font.to_owned())?;
|
||||
fonts.push(subsetted);
|
||||
}
|
||||
|
||||
Ok((fonts, old_to_new))
|
||||
|
@ -90,7 +90,7 @@ impl FlexLayouter {
|
||||
ctx,
|
||||
units: vec![],
|
||||
|
||||
stack: StackLayouter::new(StackContext::from_flex_ctx(ctx)),
|
||||
stack: StackLayouter::new(StackContext::from_flex_ctx(ctx, Flow::Vertical)),
|
||||
|
||||
usable_width: ctx.space.usable().x,
|
||||
run: FlexRun {
|
||||
|
@ -134,6 +134,8 @@ pub struct LayoutContext<'a, 'p> {
|
||||
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
|
||||
@ -176,6 +178,13 @@ pub enum Alignment {
|
||||
Center,
|
||||
}
|
||||
|
||||
/// The flow of content.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Flow {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
/// The error type for layouting.
|
||||
pub enum LayoutError {
|
||||
/// There is not enough space to add an item.
|
||||
|
@ -26,15 +26,17 @@ pub struct StackContext {
|
||||
pub space: LayoutSpace,
|
||||
pub followup_spaces: Option<LayoutSpace>,
|
||||
pub shrink_to_fit: bool,
|
||||
pub flow: Flow,
|
||||
}
|
||||
|
||||
macro_rules! reuse {
|
||||
($ctx:expr) => {
|
||||
($ctx:expr, $flow:expr) => {
|
||||
StackContext {
|
||||
alignment: $ctx.alignment,
|
||||
space: $ctx.space,
|
||||
followup_spaces: $ctx.followup_spaces,
|
||||
shrink_to_fit: $ctx.shrink_to_fit
|
||||
shrink_to_fit: $ctx.shrink_to_fit,
|
||||
flow: $flow
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -42,12 +44,12 @@ macro_rules! reuse {
|
||||
impl StackContext {
|
||||
/// Create a stack context from a generic layout context.
|
||||
pub fn from_layout_ctx(ctx: LayoutContext) -> StackContext {
|
||||
reuse!(ctx)
|
||||
reuse!(ctx, ctx.flow)
|
||||
}
|
||||
|
||||
/// Create a stack context from a flex context.
|
||||
pub fn from_flex_ctx(ctx: FlexContext) -> StackContext {
|
||||
reuse!(ctx)
|
||||
pub fn from_flex_ctx(ctx: FlexContext, flow: Flow) -> StackContext {
|
||||
reuse!(ctx, flow)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,9 +81,15 @@ impl StackLayouter {
|
||||
self.start_new_space()?;
|
||||
}
|
||||
|
||||
let new_dimensions = Size2D {
|
||||
x: crate::size::max(self.dimensions.x, layout.dimensions.x),
|
||||
y: self.dimensions.y + layout.dimensions.y,
|
||||
let new_dimensions = match self.ctx.flow {
|
||||
Flow::Vertical => Size2D {
|
||||
x: crate::size::max(self.dimensions.x, layout.dimensions.x),
|
||||
y: self.dimensions.y + layout.dimensions.y,
|
||||
},
|
||||
Flow::Horizontal => Size2D {
|
||||
x: self.dimensions.x + layout.dimensions.x,
|
||||
y: crate::size::max(self.dimensions.y, layout.dimensions.y),
|
||||
}
|
||||
};
|
||||
|
||||
if self.overflows(new_dimensions) {
|
||||
@ -104,9 +112,13 @@ impl StackLayouter {
|
||||
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
|
||||
};
|
||||
|
||||
self.cursor.y += layout.dimensions.y;
|
||||
self.dimensions = new_dimensions;
|
||||
|
||||
match self.ctx.flow {
|
||||
Flow::Vertical => self.cursor.y += layout.dimensions.y,
|
||||
Flow::Horizontal => self.cursor.x += layout.dimensions.x,
|
||||
}
|
||||
|
||||
self.actions.add_layout(position, layout);
|
||||
|
||||
Ok(())
|
||||
@ -120,23 +132,26 @@ impl StackLayouter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add vertical space after the last layout.
|
||||
/// Add space after the last layout.
|
||||
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
||||
if !self.started {
|
||||
self.start_new_space()?;
|
||||
}
|
||||
|
||||
let new_dimensions = self.dimensions + Size2D::with_y(space);
|
||||
let new_space = match self.ctx.flow {
|
||||
Flow::Vertical => Size2D::with_y(space),
|
||||
Flow::Horizontal => Size2D::with_x(space),
|
||||
};
|
||||
|
||||
if self.overflows(new_dimensions) {
|
||||
if self.overflows(self.dimensions + new_space) {
|
||||
if self.ctx.followup_spaces.is_some() {
|
||||
self.finish_layout(false)?;
|
||||
} else {
|
||||
return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
|
||||
}
|
||||
} else {
|
||||
self.cursor.y += space;
|
||||
self.dimensions.y += space;
|
||||
self.cursor += new_space;
|
||||
self.dimensions += new_space;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -195,10 +210,17 @@ impl StackLayouter {
|
||||
|
||||
/// The remaining space for new layouts.
|
||||
pub fn remaining(&self) -> Size2D {
|
||||
Size2D {
|
||||
x: self.usable.x,
|
||||
y: self.usable.y - self.dimensions.y,
|
||||
match self.ctx.flow {
|
||||
Flow::Vertical => Size2D {
|
||||
x: self.usable.x,
|
||||
y: self.usable.y - self.dimensions.y,
|
||||
},
|
||||
Flow::Horizontal => Size2D {
|
||||
x: self.usable.x - self.dimensions.x,
|
||||
y: self.usable.y,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Whether the active space of this layouter contains no content.
|
||||
|
@ -96,6 +96,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
|
||||
let mut ctx = self.ctx;
|
||||
ctx.style = &self.style;
|
||||
ctx.flow = Flow::Vertical;
|
||||
ctx.shrink_to_fit = true;
|
||||
ctx.space.dimensions = remaining;
|
||||
ctx.space.padding = SizeBox::zero();
|
||||
|
@ -21,7 +21,7 @@ use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
||||
|
||||
use crate::func::Scope;
|
||||
use crate::layout::{layout_tree, LayoutContext, MultiLayout};
|
||||
use crate::layout::{Alignment, LayoutError, LayoutResult, LayoutSpace};
|
||||
use crate::layout::{Alignment, Flow, LayoutError, LayoutResult, LayoutSpace};
|
||||
use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
|
||||
use crate::style::{PageStyle, TextStyle};
|
||||
use crate::syntax::SyntaxTree;
|
||||
@ -105,6 +105,7 @@ impl<'p> Typesetter<'p> {
|
||||
loader: &self.loader,
|
||||
style: &self.text_style,
|
||||
alignment: Alignment::Left,
|
||||
flow: Flow::Vertical,
|
||||
space,
|
||||
followup_spaces: Some(space),
|
||||
shrink_to_fit: false,
|
||||
|
@ -1,21 +1,39 @@
|
||||
use super::prelude::*;
|
||||
use crate::layout::Flow;
|
||||
|
||||
/// Wraps content into a box.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BoxFunc {
|
||||
body: SyntaxTree
|
||||
body: SyntaxTree,
|
||||
flow: Flow,
|
||||
}
|
||||
|
||||
impl Function for BoxFunc {
|
||||
fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
|
||||
where Self: Sized {
|
||||
if has_arguments(header) {
|
||||
return err("pagebreak: expected no arguments");
|
||||
}
|
||||
let flow = if header.args.is_empty() {
|
||||
Flow::Vertical
|
||||
} else if header.args.len() == 1 {
|
||||
if let Expression::Ident(ident) = &header.args[0] {
|
||||
match ident.as_str() {
|
||||
"vertical" => Flow::Vertical,
|
||||
"horizontal" => Flow::Horizontal,
|
||||
f => return err(format!("invalid flow specifier: '{}'", f)),
|
||||
}
|
||||
} else {
|
||||
return err(format!(
|
||||
"expected alignment specifier, found: '{}'",
|
||||
header.args[0]
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return err("box: expected flow specifier or no arguments");
|
||||
};
|
||||
|
||||
if let Some(body) = body {
|
||||
Ok(BoxFunc {
|
||||
body: parse(body, ctx)?
|
||||
body: parse(body, ctx)?,
|
||||
flow,
|
||||
})
|
||||
} else {
|
||||
err("box: expected body")
|
||||
@ -23,7 +41,11 @@ impl Function for BoxFunc {
|
||||
}
|
||||
|
||||
fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList> {
|
||||
let layout = layout_tree(&self.body, ctx)?;
|
||||
let layout = layout_tree(&self.body, LayoutContext {
|
||||
flow: self.flow,
|
||||
.. ctx
|
||||
})?;
|
||||
|
||||
Ok(commands![Command::AddMany(layout)])
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
{size:400pt*250pt}
|
||||
|
||||
[box][
|
||||
*Technical University Berlin* [n]
|
||||
*Faculty II, Institute for Mathematics* [n]
|
||||
Secretary Example [n]
|
||||
Prof. Dr. Example [n]
|
||||
Assistant #1, Assistant #2, Assistant #3
|
||||
]
|
||||
[align: right][*WiSe 2019/2020* [n] Week 1]
|
31
tests/layouts/coma.typ
Normal file
31
tests/layouts/coma.typ
Normal file
@ -0,0 +1,31 @@
|
||||
{size:420pt*300pt}
|
||||
|
||||
[box: horizontal][
|
||||
*Technical University Berlin* [n]
|
||||
*Faculty II, Institute for Mathematics* [n]
|
||||
Secretary Example [n]
|
||||
Prof. Dr. Example [n]
|
||||
Assistant #1, Assistant #2, Assistant #3
|
||||
|
||||
[align: right][*WiSe 2019/2020* [n] Week 1]
|
||||
]
|
||||
|
||||
// hack for more whitespace
|
||||
[box][]
|
||||
|
||||
[align: center][
|
||||
*3. Ubungsblatt Computerorientierte Mathematik II* [n]
|
||||
*Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) [n]
|
||||
*Alle Antworten sind zu beweisen.*
|
||||
]
|
||||
|
||||
[box: horizontal][
|
||||
*1. Aufgabe* [align: right][(1 + 1 + 2 Punkte)]
|
||||
]
|
||||
|
||||
Ein _Binärbaum_ ist ein Wurzelbaum, in dem jeder Knoten ≤ 2 Kinder hat.
|
||||
Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel
|
||||
zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges
|
||||
von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user