mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +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),
|
SetAlignment(Alignment),
|
||||||
SetStyle(TextStyle),
|
SetStyle(TextStyle),
|
||||||
FinishLayout,
|
FinishLayout,
|
||||||
|
FinishFlexRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! commands {
|
macro_rules! commands {
|
||||||
|
@ -73,6 +73,8 @@ enum FlexUnit {
|
|||||||
/// is only present if there was no flow break in between the two
|
/// is only present if there was no flow break in between the two
|
||||||
/// surrounding boxes.
|
/// surrounding boxes.
|
||||||
Glue(Size2D),
|
Glue(Size2D),
|
||||||
|
/// A forced break of the current flex run.
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -114,6 +116,11 @@ impl FlexLayouter {
|
|||||||
self.units.push(FlexUnit::Glue(glue));
|
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.
|
/// Compute the justified layout.
|
||||||
///
|
///
|
||||||
/// The layouter is not consumed by this to prevent ownership problems
|
/// The layouter is not consumed by this to prevent ownership problems
|
||||||
@ -127,6 +134,7 @@ impl FlexLayouter {
|
|||||||
match unit {
|
match unit {
|
||||||
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
||||||
FlexUnit::Glue(glue) => self.layout_glue(glue),
|
FlexUnit::Glue(glue) => self.layout_glue(glue),
|
||||||
|
FlexUnit::Break => self.layout_break()?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,14 +165,12 @@ impl FlexLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.finish_run()?;
|
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;
|
let dimensions = boxed.dimensions;
|
||||||
self.run.content.push((self.run.size.x, boxed));
|
self.run.content.push((self.run.size.x, boxed));
|
||||||
|
|
||||||
self.grow_run(dimensions);
|
self.grow_run(dimensions);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -174,20 +180,12 @@ impl FlexLayouter {
|
|||||||
self.cached_glue = Some(glue);
|
self.cached_glue = Some(glue);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_glue(&mut self) {
|
fn layout_break(&mut self) -> LayoutResult<()> {
|
||||||
if let Some(glue) = self.cached_glue.take() {
|
self.cached_glue = None;
|
||||||
let new_line_width = self.run.size.x + glue.x;
|
self.finish_run()
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finish the current flex run.
|
||||||
fn finish_run(&mut self) -> LayoutResult<()> {
|
fn finish_run(&mut self) -> LayoutResult<()> {
|
||||||
self.run.size.y += self.ctx.flex_spacing;
|
self.run.size.y += self.ctx.flex_spacing;
|
||||||
|
|
||||||
@ -208,6 +206,19 @@ impl FlexLayouter {
|
|||||||
Ok(())
|
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.
|
/// Whether this layouter contains any items.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.units.is_empty()
|
self.units.is_empty()
|
||||||
|
@ -88,6 +88,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
||||||
// Finish the current flex layout on a copy to find out how
|
// Finish the current flex layout on a copy to find out how
|
||||||
// much space would be remaining if we finished.
|
// much space would be remaining if we finished.
|
||||||
|
|
||||||
let mut lookahead_stack = self.stack.clone();
|
let mut lookahead_stack = self.stack.clone();
|
||||||
let layouts = self.flex.clone().finish()?;
|
let layouts = self.flex.clone().finish()?;
|
||||||
lookahead_stack.add_many(layouts)?;
|
lookahead_stack.add_many(layouts)?;
|
||||||
@ -106,9 +107,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
for command in commands {
|
for command in commands {
|
||||||
match command {
|
match command {
|
||||||
Command::Layout(tree) => {
|
Command::Layout(tree) => self.layout(tree)?,
|
||||||
self.layout(tree)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::Add(layout) => {
|
Command::Add(layout) => {
|
||||||
self.finish_flex()?;
|
self.finish_flex()?;
|
||||||
@ -130,15 +129,15 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
self.start_new_flex();
|
self.start_new_flex();
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::SetStyle(style) => {
|
Command::SetStyle(style) => *self.style.to_mut() = style,
|
||||||
*self.style.to_mut() = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::FinishLayout => {
|
Command::FinishLayout => {
|
||||||
self.finish_flex()?;
|
self.finish_flex()?;
|
||||||
self.stack.finish_layout(true)?;
|
self.stack.finish_layout(true)?;
|
||||||
self.start_new_flex();
|
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::*;
|
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.
|
/// Ends the current page.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PagebreakFunc;
|
pub struct PagebreakFunc;
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
use crate::func::Scope;
|
use crate::func::Scope;
|
||||||
|
|
||||||
mod align;
|
mod align;
|
||||||
mod styles;
|
mod boxed;
|
||||||
mod breaks;
|
mod breaks;
|
||||||
|
mod styles;
|
||||||
|
|
||||||
/// Useful imports for creating your own functions.
|
/// Useful imports for creating your own functions.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
@ -17,17 +18,21 @@ pub mod prelude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use align::AlignFunc;
|
pub use align::AlignFunc;
|
||||||
pub use breaks::PagebreakFunc;
|
pub use boxed::BoxFunc;
|
||||||
|
pub use breaks::{LinebreakFunc, PagebreakFunc};
|
||||||
pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc};
|
pub use styles::{BoldFunc, ItalicFunc, MonospaceFunc};
|
||||||
|
|
||||||
/// Create a scope with all standard functions.
|
/// Create a scope with all standard functions.
|
||||||
pub fn std() -> Scope {
|
pub fn std() -> Scope {
|
||||||
let mut std = Scope::new();
|
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::<BoldFunc>("bold");
|
||||||
std.add::<ItalicFunc>("italic");
|
std.add::<ItalicFunc>("italic");
|
||||||
std.add::<MonospaceFunc>("mono");
|
std.add::<MonospaceFunc>("mono");
|
||||||
std.add::<AlignFunc>("align");
|
|
||||||
std.add::<PagebreakFunc>("pagebreak");
|
|
||||||
std
|
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]
|
[bold]
|
||||||
|
|
||||||
This is completely reset. 😀
|
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