mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Create basic box and line-break functions 📦
This commit is contained in:
parent
f22f9513ae
commit
1987e5861c
@ -119,6 +119,7 @@ pub enum Command<'a> {
|
||||
SetAlignment(Alignment),
|
||||
SetStyle(TextStyle),
|
||||
FinishLayout,
|
||||
FinishFlexRun,
|
||||
}
|
||||
|
||||
macro_rules! commands {
|
||||
|
@ -73,6 +73,8 @@ enum FlexUnit {
|
||||
/// is only present if there was no flow break in between the two
|
||||
/// surrounding boxes.
|
||||
Glue(Size2D),
|
||||
/// A forced break of the current flex run.
|
||||
Break,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -114,6 +116,11 @@ impl FlexLayouter {
|
||||
self.units.push(FlexUnit::Glue(glue));
|
||||
}
|
||||
|
||||
/// Add a forced line break.
|
||||
pub fn add_break(&mut self) {
|
||||
self.units.push(FlexUnit::Break);
|
||||
}
|
||||
|
||||
/// Compute the justified layout.
|
||||
///
|
||||
/// The layouter is not consumed by this to prevent ownership problems
|
||||
@ -127,6 +134,7 @@ impl FlexLayouter {
|
||||
match unit {
|
||||
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
||||
FlexUnit::Glue(glue) => self.layout_glue(glue),
|
||||
FlexUnit::Break => self.layout_break()?,
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,14 +165,12 @@ impl FlexLayouter {
|
||||
}
|
||||
|
||||
self.finish_run()?;
|
||||
} else {
|
||||
// Only add the glue if we did not move to a new line.
|
||||
self.flush_glue();
|
||||
}
|
||||
|
||||
self.flush_glue();
|
||||
|
||||
let dimensions = boxed.dimensions;
|
||||
self.run.content.push((self.run.size.x, boxed));
|
||||
|
||||
self.grow_run(dimensions);
|
||||
|
||||
Ok(())
|
||||
@ -174,20 +180,12 @@ impl FlexLayouter {
|
||||
self.cached_glue = Some(glue);
|
||||
}
|
||||
|
||||
fn flush_glue(&mut self) {
|
||||
if let Some(glue) = self.cached_glue.take() {
|
||||
let new_line_width = self.run.size.x + glue.x;
|
||||
if !self.overflows_line(new_line_width) {
|
||||
self.grow_run(glue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grow_run(&mut self, dimensions: Size2D) {
|
||||
self.run.size.x += dimensions.x;
|
||||
self.run.size.y = crate::size::max(self.run.size.y, dimensions.y);
|
||||
fn layout_break(&mut self) -> LayoutResult<()> {
|
||||
self.cached_glue = None;
|
||||
self.finish_run()
|
||||
}
|
||||
|
||||
/// Finish the current flex run.
|
||||
fn finish_run(&mut self) -> LayoutResult<()> {
|
||||
self.run.size.y += self.ctx.flex_spacing;
|
||||
|
||||
@ -208,6 +206,19 @@ impl FlexLayouter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_glue(&mut self) {
|
||||
if let Some(glue) = self.cached_glue.take() {
|
||||
if self.run.size.x > Size::zero() && !self.overflows_line(self.run.size.x + glue.x) {
|
||||
self.grow_run(glue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn grow_run(&mut self, dimensions: Size2D) {
|
||||
self.run.size.x += dimensions.x;
|
||||
self.run.size.y = crate::size::max(self.run.size.y, dimensions.y);
|
||||
}
|
||||
|
||||
/// Whether this layouter contains any items.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.units.is_empty()
|
||||
|
@ -88,6 +88,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
||||
// Finish the current flex layout on a copy to find out how
|
||||
// much space would be remaining if we finished.
|
||||
|
||||
let mut lookahead_stack = self.stack.clone();
|
||||
let layouts = self.flex.clone().finish()?;
|
||||
lookahead_stack.add_many(layouts)?;
|
||||
@ -106,9 +107,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
|
||||
for command in commands {
|
||||
match command {
|
||||
Command::Layout(tree) => {
|
||||
self.layout(tree)?;
|
||||
}
|
||||
Command::Layout(tree) => self.layout(tree)?,
|
||||
|
||||
Command::Add(layout) => {
|
||||
self.finish_flex()?;
|
||||
@ -130,15 +129,15 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
self.start_new_flex();
|
||||
}
|
||||
|
||||
Command::SetStyle(style) => {
|
||||
*self.style.to_mut() = style;
|
||||
}
|
||||
Command::SetStyle(style) => *self.style.to_mut() = style,
|
||||
|
||||
Command::FinishLayout => {
|
||||
self.finish_flex()?;
|
||||
self.stack.finish_layout(true)?;
|
||||
self.start_new_flex();
|
||||
}
|
||||
|
||||
Command::FinishFlexRun => self.flex.add_break(),
|
||||
}
|
||||
}
|
||||
|
||||
|
29
src/library/boxed.rs
Normal file
29
src/library/boxed.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use super::prelude::*;
|
||||
|
||||
/// Wraps content into a box.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BoxFunc {
|
||||
body: SyntaxTree
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if let Some(body) = body {
|
||||
Ok(BoxFunc {
|
||||
body: parse(body, ctx)?
|
||||
})
|
||||
} else {
|
||||
err("box: expected body")
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList> {
|
||||
let layout = layout_tree(&self.body, ctx)?;
|
||||
Ok(commands![Command::AddMany(layout)])
|
||||
}
|
||||
}
|
@ -1,5 +1,28 @@
|
||||
use super::prelude::*;
|
||||
|
||||
/// Ends the current line.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct LinebreakFunc;
|
||||
|
||||
impl Function for LinebreakFunc {
|
||||
fn parse(header: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult<Self>
|
||||
where Self: Sized {
|
||||
if has_arguments(header) {
|
||||
return err("linebreak: expected no arguments");
|
||||
}
|
||||
|
||||
if body.is_some() {
|
||||
return err("linebreak: expected no body");
|
||||
}
|
||||
|
||||
Ok(LinebreakFunc)
|
||||
}
|
||||
|
||||
fn layout(&self, _: LayoutContext) -> LayoutResult<CommandList> {
|
||||
Ok(commands![Command::FinishFlexRun])
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends the current page.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PagebreakFunc;
|
||||
|
@ -3,8 +3,9 @@
|
||||
use crate::func::Scope;
|
||||
|
||||
mod align;
|
||||
mod styles;
|
||||
mod boxed;
|
||||
mod breaks;
|
||||
mod styles;
|
||||
|
||||
/// Useful imports for creating your own functions.
|
||||
pub mod prelude {
|
||||
@ -17,17 +18,21 @@ pub mod prelude {
|
||||
}
|
||||
|
||||
pub use align::AlignFunc;
|
||||
pub use breaks::PagebreakFunc;
|
||||
pub use boxed::BoxFunc;
|
||||
pub use breaks::{LinebreakFunc, PagebreakFunc};
|
||||
pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc};
|
||||
|
||||
/// Create a scope with all standard functions.
|
||||
pub fn std() -> Scope {
|
||||
let mut std = Scope::new();
|
||||
std.add::<AlignFunc>("align");
|
||||
std.add::<BoxFunc>("box");
|
||||
std.add::<LinebreakFunc>("linebreak");
|
||||
std.add::<LinebreakFunc>("n");
|
||||
std.add::<PagebreakFunc>("pagebreak");
|
||||
std.add::<BoldFunc>("bold");
|
||||
std.add::<ItalicFunc>("italic");
|
||||
std.add::<MonospaceFunc>("mono");
|
||||
std.add::<AlignFunc>("align");
|
||||
std.add::<PagebreakFunc>("pagebreak");
|
||||
std
|
||||
}
|
||||
|
||||
|
10
tests/layouts/boxes.typ
Normal file
10
tests/layouts/boxes.typ
Normal file
@ -0,0 +1,10 @@
|
||||
{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]
|
@ -17,3 +17,10 @@ This is not italic anymore, but still bold.
|
||||
[bold]
|
||||
|
||||
This is completely reset. 😀
|
||||
|
||||
[box][
|
||||
[italic]
|
||||
Styles are scoped by boxes.
|
||||
]
|
||||
|
||||
Outside of the box: No effect.
|
||||
|
Loading…
x
Reference in New Issue
Block a user