summaryrefslogtreecommitdiff
path: root/src/layout/model.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
commit20fb4e7c379b79b84d9884d5f2c89d781c5793e2 (patch)
treea1eef90680afa2b43cb1ce0a687c837fd78810e7 /src/layout/model.rs
parent0a087cd28bbee5fcdffbb9d49b0ba9f413ad7f92 (diff)
Document everything 📜
Diffstat (limited to 'src/layout/model.rs')
-rw-r--r--src/layout/model.rs75
1 files changed, 56 insertions, 19 deletions
diff --git a/src/layout/model.rs b/src/layout/model.rs
index 2e61b453..1d635f5c 100644
--- a/src/layout/model.rs
+++ b/src/layout/model.rs
@@ -1,3 +1,7 @@
+//! The model layouter layouts models (i.e.
+//! [syntax models](crate::syntax::SyntaxModel) and [functions](crate::func))
+//! by executing commands issued by the models.
+
use std::future::Future;
use std::pin::Pin;
use smallvec::smallvec;
@@ -13,7 +17,7 @@ use super::text::{layout_text, TextContext};
use super::*;
-#[derive(Debug, Clone)]
+/// Performs the model layouting.
pub struct ModelLayouter<'a, 'p> {
ctx: LayoutContext<'a, 'p>,
layouter: LineLayouter,
@@ -21,7 +25,7 @@ pub struct ModelLayouter<'a, 'p> {
errors: Errors,
}
-/// The general context for layouting.
+/// The context for layouting.
#[derive(Debug, Clone)]
pub struct LayoutContext<'a, 'p> {
/// The font loader to retrieve fonts from when typesetting text
@@ -46,53 +50,74 @@ pub struct LayoutContext<'a, 'p> {
pub debug: bool,
}
+/// The result of layouting: Some layouted things and a list of errors.
pub struct Layouted<T> {
+ /// The result of the layouting process.
pub output: T,
+ /// Errors that arose in the process of layouting.
pub errors: Errors,
}
-impl<T> Layouted<T> {
- pub fn map<F, U>(self, f: F) -> Layouted<U> where F: FnOnce(T) -> U {
- Layouted {
- output: f(self.output),
- errors: self.errors,
- }
- }
-}
-
/// A sequence of layouting commands.
pub type Commands<'a> = Vec<Command<'a>>;
-/// Layouting commands from functions to the typesetting engine.
+/// Commands issued to the layouting engine by models.
#[derive(Debug)]
pub enum Command<'a> {
+ /// Layout the given model in the current context (i.e. not nested). The
+ /// content of the model is not laid out into a separate box and then added,
+ /// but simply laid out flat in the active layouting process.
+ ///
+ /// This has the effect that the content fits nicely into the active line
+ /// layouting, enabling functions to e.g. change the style of some piece of
+ /// text while keeping it integrated in the current paragraph.
LayoutSyntaxModel(&'a SyntaxModel),
+ /// Add a already computed layout.
Add(Layout),
+ /// Add multiple layouts, one after another. This is equivalent to multiple
+ /// [Add](Command::Add) commands.
AddMultiple(MultiLayout),
+
+ /// Add spacing of given [kind](super::SpacingKind) along the primary or
+ /// secondary axis. The spacing kind defines how the spacing interacts with
+ /// surrounding spacing.
AddSpacing(Size, SpacingKind, GenericAxis),
- FinishLine,
- FinishSpace,
+ /// Start a new line.
+ BreakLine,
+ /// Start a new paragraph.
BreakParagraph,
+ /// Start a new page, which will exist in the finished layout even if it
+ /// stays empty (since the page break is a _hard_ space break).
BreakPage,
+ /// Update the text style.
SetTextStyle(TextStyle),
+ /// Update the page style.
SetPageStyle(PageStyle),
+
+ /// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlignment),
+ /// Update the layouting axes along which future boxes will be laid out.
+ /// This finishes the current line.
SetAxes(LayoutAxes),
}
+/// Layout a syntax model into a list of boxes.
pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted<MultiLayout> {
let mut layouter = ModelLayouter::new(ctx);
layouter.layout_syntax_model(model).await;
layouter.finish()
}
+/// A dynamic future type which allows recursive invocation of async functions
+/// when used as the return type. This is also how the async trait functions
+/// work internally.
pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>;
impl<'a, 'p> ModelLayouter<'a, 'p> {
- /// Create a new syntax tree layouter.
+ /// Create a new model layouter.
pub fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> {
ModelLayouter {
layouter: LineLayouter::new(LineContext {
@@ -109,10 +134,12 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
}
}
+ /// Flatly layout a model into this layouting process.
pub fn layout<'r>(
&'r mut self,
model: Spanned<&'r dyn Model>
) -> DynFuture<'r, ()> { Box::pin(async move {
+ // Execute the model's layout function which generates the commands.
let layouted = model.v.layout(LayoutContext {
style: &self.style,
spaces: self.layouter.remaining(),
@@ -121,14 +148,16 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
.. self.ctx
}).await;
- let commands = layouted.output;
+ // Add the errors generated by the model to the error list.
self.errors.extend(offset_spans(layouted.errors, model.span.start));
- for command in commands {
+ for command in layouted.output {
self.execute_command(command, model.span).await;
}
}) }
+ /// Layout a syntax model by directly processing the nodes instead of using
+ /// the command based architecture.
pub fn layout_syntax_model<'r>(
&'r mut self,
model: &'r SyntaxModel
@@ -162,6 +191,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
}
}) }
+ /// Compute the finished list of boxes.
pub fn finish(self) -> Layouted<MultiLayout> {
Layouted {
output: self.layouter.finish(),
@@ -169,6 +199,8 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
}
}
+ /// Execute a command issued by a model. When the command is errorful, the
+ /// given span is stored with the error.
fn execute_command<'r>(
&'r mut self,
command: Command<'r>,
@@ -186,8 +218,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
Secondary => self.layouter.add_secondary_spacing(space, kind),
}
- FinishLine => self.layouter.finish_line(),
- FinishSpace => self.layouter.finish_space(true),
+ BreakLine => self.layouter.finish_line(),
BreakParagraph => self.layout_paragraph(),
BreakPage => {
if self.ctx.nested {
@@ -209,6 +240,9 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
} else {
self.style.page = style;
+ // The line layouter has no idea of page styles and thus we
+ // need to recompute the layouting space resulting of the
+ // new page style and update it within the layouter.
let margins = style.margins();
self.ctx.base = style.dimensions.unpadded(margins);
self.layouter.set_spaces(smallvec![
@@ -229,6 +263,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
}
}) }
+ /// Layout a continous piece of text and add it to the line layouter.
async fn layout_text(&mut self, text: &str) {
self.layouter.add(layout_text(text, TextContext {
loader: &self.ctx.loader,
@@ -238,6 +273,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
}).await)
}
+ /// Add the spacing for a syntactic space node.
fn layout_space(&mut self) {
self.layouter.add_primary_spacing(
self.style.text.word_spacing(),
@@ -245,6 +281,7 @@ impl<'a, 'p> ModelLayouter<'a, 'p> {
);
}
+ /// Finish the paragraph and add paragraph spacing.
fn layout_paragraph(&mut self) {
self.layouter.add_secondary_spacing(
self.style.text.paragraph_spacing(),