mirror of
https://github.com/typst/typst
synced 2025-06-28 16:22:53 +08:00
Implement function layouting ✒
This commit is contained in:
parent
e39a6efccf
commit
f6fe3b5cdd
@ -48,16 +48,22 @@ fn run() -> Result<(), Box<Error>> {
|
|||||||
let mut src = String::new();
|
let mut src = String::new();
|
||||||
file.read_to_string(&mut src).map_err(|_| "failed to read from source file")?;
|
file.read_to_string(&mut src).map_err(|_| "failed to read from source file")?;
|
||||||
|
|
||||||
// Create a typesetter with a font provider that provides three fonts
|
// Create a typesetter with a font provider that provides the default fonts.
|
||||||
// (two sans-serif fonts and a fallback for the emoji).
|
|
||||||
let mut typesetter = Typesetter::new();
|
let mut typesetter = Typesetter::new();
|
||||||
typesetter.add_font_provider(FileSystemFontProvider::new("fonts", vec![
|
typesetter.add_font_provider(FileSystemFontProvider::new("fonts", vec![
|
||||||
("NotoSans-Regular.ttf", font_info!(["NotoSans", "Noto", SansSerif])),
|
("CMU-SansSerif-Regular.ttf", font_info!(["Computer Modern", SansSerif])),
|
||||||
("NotoSans-Italic.ttf", font_info!(["NotoSans", "Noto", SansSerif], italic)),
|
("CMU-SansSerif-Italic.ttf", font_info!(["Computer Modern", SansSerif], italic)),
|
||||||
("NotoSans-Bold.ttf", font_info!(["NotoSans", "Noto", SansSerif], bold)),
|
("CMU-SansSerif-Bold.ttf", font_info!(["Computer Modern", SansSerif], bold)),
|
||||||
("NotoSans-BoldItalic.ttf", font_info!(["NotoSans", "Noto", SansSerif], italic, bold)),
|
("CMU-SansSerif-Bold-Italic.ttf", font_info!(["Computer Modern", SansSerif], bold, italic)),
|
||||||
("NotoSansMath-Regular.ttf", font_info!(["NotoSansMath", "Noto", SansSerif])),
|
("CMU-Serif-Regular.ttf", font_info!(["Computer Modern", Serif])),
|
||||||
("NotoEmoji-Regular.ttf", font_info!(["NotoEmoji", "Noto", SansSerif, Serif, Monospace])),
|
("CMU-Serif-Italic.ttf", font_info!(["Computer Modern", Serif], italic)),
|
||||||
|
("CMU-Serif-Bold.ttf", font_info!(["Computer Modern", Serif], bold)),
|
||||||
|
("CMU-Serif-Bold-Italic.ttf", font_info!(["Computer Modern", Serif], bold, italic)),
|
||||||
|
("CMU-Typewriter-Regular.ttf", font_info!(["Computer Modern", Monospace])),
|
||||||
|
("CMU-Typewriter-Italic.ttf", font_info!(["Computer Modern", Monospace], italic)),
|
||||||
|
("CMU-Typewriter-Bold.ttf", font_info!(["Computer Modern", Monospace], bold)),
|
||||||
|
("CMU-Typewriter-Bold-Italic.ttf", font_info!(["Computer Modern", Monospace], bold, italic)),
|
||||||
|
("NotoEmoji-Regular.ttf", font_info!(["NotoEmoji", "Noto", SansSerif, Serif, Monospace])),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Typeset the source code.
|
// Typeset the source code.
|
||||||
|
68
src/func.rs
68
src/func.rs
@ -4,9 +4,10 @@ use std::any::Any;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
use crate::layout::{Layout, LayoutContext, LayoutResult};
|
use crate::layout::{layout, Layout, LayoutContext, LayoutResult};
|
||||||
use crate::parsing::{ParseContext, ParseResult};
|
use crate::layout::flex::FlexLayout;
|
||||||
use crate::syntax::FuncHeader;
|
use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
|
||||||
|
use crate::syntax::{SyntaxTree, FuncHeader};
|
||||||
|
|
||||||
|
|
||||||
/// Typesetting function types.
|
/// Typesetting function types.
|
||||||
@ -25,8 +26,7 @@ pub trait Function: FunctionBounds {
|
|||||||
///
|
///
|
||||||
/// Returns optionally the resulting layout and a new context if changes to the context should
|
/// Returns optionally the resulting layout and a new context if changes to the context should
|
||||||
/// be made.
|
/// be made.
|
||||||
fn layout(&self, ctx: &LayoutContext)
|
fn layout(&self, ctx: &LayoutContext) -> LayoutResult<Option<Layout>>;
|
||||||
-> LayoutResult<(Option<Layout>, Option<LayoutContext>)>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for dyn Function {
|
impl PartialEq for dyn Function {
|
||||||
@ -76,9 +76,14 @@ impl Scope {
|
|||||||
Scope { parsers: HashMap::new() }
|
Scope { parsers: HashMap::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new scope with the standard functions contained.
|
/// Create a new scope with the standard functions contained:
|
||||||
|
/// - `italic`
|
||||||
|
/// - `bold`
|
||||||
pub fn with_std() -> Scope {
|
pub fn with_std() -> Scope {
|
||||||
Scope::new()
|
let mut std = Scope::new();
|
||||||
|
std.add::<BoldFunc>("bold");
|
||||||
|
std.add::<ItalicFunc>("italic");
|
||||||
|
std
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a function type to the scope giving it a name.
|
/// Add a function type to the scope giving it a name.
|
||||||
@ -103,3 +108,52 @@ impl Debug for Scope {
|
|||||||
write!(f, "{:?}", self.parsers.keys())
|
write!(f, "{:?}", self.parsers.keys())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates style functions like bold and italic.
|
||||||
|
macro_rules! style_func {
|
||||||
|
($(#[$outer:meta])* pub struct $struct:ident { $name:expr },
|
||||||
|
$new_ctx:ident => $ctx_change:block) => {
|
||||||
|
$(#[$outer])*
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct $struct { body: SyntaxTree }
|
||||||
|
impl Function for $struct {
|
||||||
|
fn parse(header: &FuncHeader, body: Option<&str>, ctx: &ParseContext)
|
||||||
|
-> ParseResult<Self> where Self: Sized {
|
||||||
|
// Accept only invocations without arguments and with body.
|
||||||
|
if header.args.is_empty() && header.kwargs.is_empty() {
|
||||||
|
if let Some(body) = body {
|
||||||
|
Ok($struct { body: parse(body, ctx)? })
|
||||||
|
} else {
|
||||||
|
Err(ParseError::new(format!("expected body for function `{}`", $name)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ParseError::new(format!("unexpected arguments to function `{}`", $name)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(&self, ctx: &LayoutContext) -> LayoutResult<Option<Layout>> {
|
||||||
|
// Change the context.
|
||||||
|
let mut $new_ctx = ctx.clone();
|
||||||
|
$ctx_change
|
||||||
|
|
||||||
|
// Create a box and put it into a flex layout.
|
||||||
|
let boxed = layout(&self.body, &$new_ctx)?;
|
||||||
|
let flex = FlexLayout::from_box(boxed);
|
||||||
|
|
||||||
|
Ok(Some(Layout::Flex(flex)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
style_func! {
|
||||||
|
/// Typesets text in bold.
|
||||||
|
pub struct BoldFunc { "bold" },
|
||||||
|
ctx => { ctx.style.bold = !ctx.style.bold }
|
||||||
|
}
|
||||||
|
|
||||||
|
style_func! {
|
||||||
|
/// Typesets text in italics.
|
||||||
|
pub struct ItalicFunc { "italic" },
|
||||||
|
ctx => { ctx.style.italic = !ctx.style.italic }
|
||||||
|
}
|
||||||
|
@ -86,6 +86,15 @@ impl BoxLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a sublayout at an absolute position.
|
||||||
|
pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) {
|
||||||
|
// Move all actions into this layout and translate absolute positions.
|
||||||
|
self.actions.reset_origin();
|
||||||
|
self.actions.add(TextAction::MoveAbsolute(position));
|
||||||
|
self.actions.set_origin(position);
|
||||||
|
self.actions.extend(layout.actions);
|
||||||
|
}
|
||||||
|
|
||||||
/// Add some space in between two boxes.
|
/// Add some space in between two boxes.
|
||||||
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
|
||||||
// Check whether this space fits.
|
// Check whether this space fits.
|
||||||
@ -100,20 +109,6 @@ impl BoxLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a sublayout at an absolute position.
|
|
||||||
pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) {
|
|
||||||
// Move all actions into this layout and translate absolute positions.
|
|
||||||
self.actions.reset_origin();
|
|
||||||
self.actions.add(TextAction::MoveAbsolute(position));
|
|
||||||
self.actions.set_origin(position);
|
|
||||||
self.actions.extend(layout.actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this layouter contains any items.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.actions.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The remaining space for new boxes.
|
/// The remaining space for new boxes.
|
||||||
pub fn remaining(&self) -> Size2D {
|
pub fn remaining(&self) -> Size2D {
|
||||||
Size2D {
|
Size2D {
|
||||||
@ -122,6 +117,11 @@ impl BoxLayouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this layouter contains any items.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.actions.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Finish the layouting and create a box layout from this.
|
/// Finish the layouting and create a box layout from this.
|
||||||
pub fn finish(self) -> BoxLayout {
|
pub fn finish(self) -> BoxLayout {
|
||||||
BoxLayout {
|
BoxLayout {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Flexible and lazy layouting of boxes.
|
//! Flexible and lazy layouting of boxes.
|
||||||
|
|
||||||
use crate::doc::TextAction;
|
use crate::doc::TextAction;
|
||||||
use crate::size::Size2D;
|
use crate::size::{Size, Size2D};
|
||||||
use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError};
|
use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError};
|
||||||
|
|
||||||
|
|
||||||
@ -10,8 +10,6 @@ use super::{BoxLayout, ActionList, LayoutSpace, LayoutResult, LayoutError};
|
|||||||
pub struct FlexLayout {
|
pub struct FlexLayout {
|
||||||
/// The sublayouts composing this layout.
|
/// The sublayouts composing this layout.
|
||||||
pub units: Vec<FlexUnit>,
|
pub units: Vec<FlexUnit>,
|
||||||
/// The layout space to arrange in.
|
|
||||||
pub ctx: FlexContext,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A unit in a flex layout.
|
/// A unit in a flex layout.
|
||||||
@ -25,14 +23,20 @@ pub enum FlexUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FlexLayout {
|
impl FlexLayout {
|
||||||
/// Create a new flex layouter.
|
/// Create a new flex layout.
|
||||||
pub fn new(ctx: FlexContext) -> FlexLayout {
|
pub fn new() -> FlexLayout {
|
||||||
FlexLayout {
|
FlexLayout {
|
||||||
ctx,
|
|
||||||
units: vec![],
|
units: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new flex layout containing just one box.
|
||||||
|
pub fn from_box(boxed: BoxLayout) -> FlexLayout {
|
||||||
|
FlexLayout {
|
||||||
|
units: vec![FlexUnit::Boxed(boxed)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a sublayout.
|
/// Add a sublayout.
|
||||||
pub fn add_box(&mut self, layout: BoxLayout) {
|
pub fn add_box(&mut self, layout: BoxLayout) {
|
||||||
self.units.push(FlexUnit::Boxed(layout));
|
self.units.push(FlexUnit::Boxed(layout));
|
||||||
@ -54,8 +58,8 @@ impl FlexLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the justified layout.
|
/// Compute the justified layout.
|
||||||
pub fn into_box(self) -> LayoutResult<BoxLayout> {
|
pub fn finish(self, ctx: FlexContext) -> LayoutResult<BoxLayout> {
|
||||||
FlexFinisher::new(self).finish()
|
FlexFinisher::new(self, ctx).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +68,8 @@ impl FlexLayout {
|
|||||||
pub struct FlexContext {
|
pub struct FlexContext {
|
||||||
/// The space to layout the boxes in.
|
/// The space to layout the boxes in.
|
||||||
pub space: LayoutSpace,
|
pub space: LayoutSpace,
|
||||||
/// The flex spacing (like line spacing).
|
/// The flex spacing between two lines of boxes.
|
||||||
pub flex_spacing: f32,
|
pub flex_spacing: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finishes a flex layout by justifying the positions of the individual boxes.
|
/// Finishes a flex layout by justifying the positions of the individual boxes.
|
||||||
@ -82,11 +86,11 @@ struct FlexFinisher {
|
|||||||
|
|
||||||
impl FlexFinisher {
|
impl FlexFinisher {
|
||||||
/// Create the finisher from the layout.
|
/// Create the finisher from the layout.
|
||||||
fn new(layout: FlexLayout) -> FlexFinisher {
|
fn new(layout: FlexLayout, ctx: FlexContext) -> FlexFinisher {
|
||||||
let space = layout.ctx.space;
|
let space = ctx.space;
|
||||||
FlexFinisher {
|
FlexFinisher {
|
||||||
units: layout.units,
|
units: layout.units,
|
||||||
ctx: layout.ctx,
|
ctx,
|
||||||
actions: ActionList::new(),
|
actions: ActionList::new(),
|
||||||
dimensions: Size2D::zero(),
|
dimensions: Size2D::zero(),
|
||||||
usable: space.usable(),
|
usable: space.usable(),
|
||||||
@ -165,11 +169,13 @@ impl FlexFinisher {
|
|||||||
|
|
||||||
/// Move to the next line.
|
/// Move to the next line.
|
||||||
fn newline(&mut self) {
|
fn newline(&mut self) {
|
||||||
self.line.y *= self.ctx.flex_spacing;
|
|
||||||
self.dimensions.x = crate::size::max(self.dimensions.x, self.line.x);
|
self.dimensions.x = crate::size::max(self.dimensions.x, self.line.x);
|
||||||
|
if self.dimensions.y > Size::zero() {
|
||||||
|
self.dimensions.y += self.ctx.flex_spacing;
|
||||||
|
}
|
||||||
self.dimensions.y += self.line.y;
|
self.dimensions.y += self.line.y;
|
||||||
self.cursor.x = self.ctx.space.padding.left;
|
self.cursor.x = self.ctx.space.padding.left;
|
||||||
self.cursor.y += self.line.y;
|
self.cursor.y += self.line.y + self.ctx.flex_spacing;
|
||||||
self.line = Size2D::zero();
|
self.line = Size2D::zero();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ struct Layouter<'a, 'p> {
|
|||||||
flex_layout: FlexLayout,
|
flex_layout: FlexLayout,
|
||||||
flex_ctx: FlexContext,
|
flex_ctx: FlexContext,
|
||||||
text_ctx: TextContext<'a, 'p>,
|
text_ctx: TextContext<'a, 'p>,
|
||||||
|
func_ctx: LayoutContext<'a, 'p>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'p> Layouter<'a, 'p> {
|
impl<'a, 'p> Layouter<'a, 'p> {
|
||||||
@ -85,7 +86,7 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
|||||||
padding: SizeBox::zero(),
|
padding: SizeBox::zero(),
|
||||||
shrink_to_fit: true,
|
shrink_to_fit: true,
|
||||||
},
|
},
|
||||||
flex_spacing: ctx.style.line_spacing,
|
flex_spacing: (ctx.style.line_spacing - 1.0) * Size::points(ctx.style.font_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
// The mutable context for layouting single pieces of text.
|
// The mutable context for layouting single pieces of text.
|
||||||
@ -94,12 +95,24 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
|||||||
style: ctx.style.clone(),
|
style: ctx.style.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The mutable context for layouting single functions.
|
||||||
|
let func_ctx = LayoutContext {
|
||||||
|
loader: &ctx.loader,
|
||||||
|
style: ctx.style.clone(),
|
||||||
|
space: LayoutSpace {
|
||||||
|
dimensions: ctx.space.usable(),
|
||||||
|
padding: SizeBox::zero(),
|
||||||
|
shrink_to_fit: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Layouter {
|
Layouter {
|
||||||
tree,
|
tree,
|
||||||
box_layouter: BoxLayouter::new(box_ctx),
|
box_layouter: BoxLayouter::new(box_ctx),
|
||||||
flex_layout: FlexLayout::new(flex_ctx),
|
flex_layout: FlexLayout::new(),
|
||||||
flex_ctx,
|
flex_ctx,
|
||||||
text_ctx,
|
text_ctx,
|
||||||
|
func_ctx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,28 +137,53 @@ impl<'a, 'p> Layouter<'a, 'p> {
|
|||||||
// Then start a new flex layouting process.
|
// Then start a new flex layouting process.
|
||||||
Node::Newline => {
|
Node::Newline => {
|
||||||
// Finish the current paragraph into a box and add it.
|
// Finish the current paragraph into a box and add it.
|
||||||
self.add_paragraph_spacing()?;
|
let boxed = self.flex_layout.finish(self.flex_ctx)?;
|
||||||
let boxed = self.flex_layout.into_box()?;
|
|
||||||
self.box_layouter.add_box(boxed)?;
|
self.box_layouter.add_box(boxed)?;
|
||||||
|
|
||||||
// Create a fresh flex layout for the next paragraph.
|
// Create a fresh flex layout for the next paragraph.
|
||||||
self.flex_ctx.space.dimensions = self.box_layouter.remaining();
|
self.flex_ctx.space.dimensions = self.box_layouter.remaining();
|
||||||
self.flex_layout = FlexLayout::new(self.flex_ctx);
|
self.flex_layout = FlexLayout::new();
|
||||||
|
self.add_paragraph_spacing()?;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Toggle the text styles.
|
// Toggle the text styles.
|
||||||
Node::ToggleItalics => self.text_ctx.style.italic = !self.text_ctx.style.italic,
|
Node::ToggleItalics => {
|
||||||
Node::ToggleBold => self.text_ctx.style.bold = !self.text_ctx.style.bold,
|
self.text_ctx.style.italic = !self.text_ctx.style.italic;
|
||||||
|
self.func_ctx.style.italic = !self.func_ctx.style.italic;
|
||||||
|
},
|
||||||
|
Node::ToggleBold => {
|
||||||
|
self.text_ctx.style.bold = !self.text_ctx.style.bold;
|
||||||
|
self.func_ctx.style.bold = !self.func_ctx.style.bold;
|
||||||
|
},
|
||||||
|
|
||||||
// Execute a function.
|
// Execute a function.
|
||||||
Node::Func(_) => unimplemented!(),
|
Node::Func(func) => {
|
||||||
|
self.func_ctx.space.dimensions = self.box_layouter.remaining();
|
||||||
|
let layout = func.body.layout(&self.func_ctx)?;
|
||||||
|
|
||||||
|
// Add the potential layout.
|
||||||
|
if let Some(layout) = layout {
|
||||||
|
match layout {
|
||||||
|
Layout::Boxed(boxed) => {
|
||||||
|
// Finish the previous flex run before adding the box.
|
||||||
|
let previous = self.flex_layout.finish(self.flex_ctx)?;
|
||||||
|
self.box_layouter.add_box(previous)?;
|
||||||
|
self.box_layouter.add_box(boxed)?;
|
||||||
|
|
||||||
|
// Create a fresh flex layout for the following content.
|
||||||
|
self.flex_ctx.space.dimensions = self.box_layouter.remaining();
|
||||||
|
self.flex_layout = FlexLayout::new();
|
||||||
|
},
|
||||||
|
Layout::Flex(flex) => self.flex_layout.add_flexible(flex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are remainings, add them to the layout.
|
// If there are remainings, add them to the layout.
|
||||||
if !self.flex_layout.is_empty() {
|
if !self.flex_layout.is_empty() {
|
||||||
self.add_paragraph_spacing()?;
|
let boxed = self.flex_layout.finish(self.flex_ctx)?;
|
||||||
let boxed = self.flex_layout.into_box()?;
|
|
||||||
self.box_layouter.add_box(boxed)?;
|
self.box_layouter.add_box(boxed)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn features() {
|
fn features() {
|
||||||
test("features", r"
|
test("features", r"
|
||||||
**FEATURES TEST PAGE**
|
**Features Test Page**
|
||||||
|
|
||||||
__Multiline:__
|
__Multiline:__
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
||||||
@ -226,7 +226,10 @@ mod test {
|
|||||||
|
|
||||||
__Emoji:__ Hello World! 🌍
|
__Emoji:__ Hello World! 🌍
|
||||||
|
|
||||||
__Styles:__ This is **bold** and that is __italic__!
|
__Styles:__ This is made **bold** and that __italic__ using the built-in syntax!
|
||||||
|
|
||||||
|
__Styles with functions:__ This is in [bold][boldface] and that is in [italic][italics]
|
||||||
|
using library functions!
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,8 +822,7 @@ mod parse_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&self, _: &LayoutContext)
|
fn layout(&self, _: &LayoutContext) -> LayoutResult<Option<Layout>> { Ok(None) }
|
||||||
-> LayoutResult<(Option<Layout>, Option<LayoutContext>)> { Ok((None, None)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A testing function without a body.
|
/// A testing function without a body.
|
||||||
@ -840,8 +839,7 @@ mod parse_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&self, _: &LayoutContext)
|
fn layout(&self, _: &LayoutContext) -> LayoutResult<Option<Layout>> { Ok(None) }
|
||||||
-> LayoutResult<(Option<Layout>, Option<LayoutContext>)> { Ok((None, None)) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ impl Default for TextStyle {
|
|||||||
italic: false,
|
italic: false,
|
||||||
bold: false,
|
bold: false,
|
||||||
font_size: 11.0,
|
font_size: 11.0,
|
||||||
line_spacing: 1.25,
|
line_spacing: 1.2,
|
||||||
paragraph_spacing: 1.5,
|
paragraph_spacing: 1.5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user