Tidy up styling

This commit is contained in:
Laurenz 2022-01-07 10:46:55 +01:00
parent 5fd9c0b0d7
commit af014cfe5e
9 changed files with 77 additions and 61 deletions

View File

@ -66,7 +66,7 @@ impl Class {
let mut styles = StyleMap::new(); let mut styles = StyleMap::new();
self.set(args, &mut styles)?; self.set(args, &mut styles)?;
let node = (self.construct)(ctx, args)?; let node = (self.construct)(ctx, args)?;
Ok(node.styled(styles)) Ok(node.styled_with_map(styles))
} }
/// Execute the class's set rule. /// Execute the class's set rule.

View File

@ -5,7 +5,7 @@ use std::iter::Sum;
use std::mem; use std::mem;
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use super::{StyleMap, Styled}; use super::{Property, StyleMap, Styled};
use crate::diag::StrResult; use crate::diag::StrResult;
use crate::geom::SpecAxis; use crate::geom::SpecAxis;
use crate::layout::{Layout, PackedNode, RootNode}; use crate::layout::{Layout, PackedNode, RootNode};
@ -84,14 +84,33 @@ impl Node {
Self::Block(node.pack()) Self::Block(node.pack())
} }
/// Style this node. /// Style this node with a single property.
pub fn styled(self, styles: StyleMap) -> Self { pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
if let Self::Sequence(vec) = &mut self {
if let [styled] = vec.as_mut_slice() {
styled.map.set(key, value);
return self;
}
}
self.styled_with_map(StyleMap::with(key, value))
}
/// Style this node with a full style map.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
if let Self::Sequence(vec) = &mut self {
if let [styled] = vec.as_mut_slice() {
styled.map.apply(&styles);
return self;
}
}
Self::Sequence(vec![Styled::new(self, styles)]) Self::Sequence(vec![Styled::new(self, styles)])
} }
/// Style this node in monospace. /// Style this node in monospace.
pub fn monospaced(self) -> Self { pub fn monospaced(self) -> Self {
self.styled(StyleMap::with(TextNode::MONOSPACE, true)) self.styled(TextNode::MONOSPACE, true)
} }
/// Lift to a type-erased block-level node. /// Lift to a type-erased block-level node.

View File

@ -106,7 +106,6 @@ impl StyleMap {
/// `outer`. The ones from `self` take precedence over the ones from /// `outer`. The ones from `self` take precedence over the ones from
/// `outer`. For folded properties `self` contributes the inner value. /// `outer`. For folded properties `self` contributes the inner value.
pub fn chain<'a>(&'a self, outer: &'a StyleChain<'a>) -> StyleChain<'a> { pub fn chain<'a>(&'a self, outer: &'a StyleChain<'a>) -> StyleChain<'a> {
// No need to chain an empty map.
if self.is_empty() { if self.is_empty() {
*outer *outer
} else { } else {
@ -182,7 +181,9 @@ impl PartialEq for StyleMap {
/// matches further up the chain. /// matches further up the chain.
#[derive(Clone, Copy, Hash)] #[derive(Clone, Copy, Hash)]
pub struct StyleChain<'a> { pub struct StyleChain<'a> {
/// The first map in the chain.
inner: &'a StyleMap, inner: &'a StyleMap,
/// The remaining maps in the chain.
outer: Option<&'a Self>, outer: Option<&'a Self>,
} }
@ -238,14 +239,13 @@ impl<'a> StyleChain<'a> {
/// entry for it. /// entry for it.
pub fn get_cloned<P: Property>(self, key: P) -> P::Value { pub fn get_cloned<P: Property>(self, key: P) -> P::Value {
if let Some(value) = self.find(key).cloned() { if let Some(value) = self.find(key).cloned() {
if P::FOLDABLE { if !P::FOLDABLE {
if let Some(outer) = self.outer { return value;
P::fold(value, outer.get_cloned(key)) }
} else {
P::fold(value, P::default()) match self.outer {
} Some(outer) => P::fold(value, outer.get_cloned(key)),
} else { None => P::fold(value, P::default()),
value
} }
} else if let Some(outer) = self.outer { } else if let Some(outer) = self.outer {
outer.get_cloned(key) outer.get_cloned(key)

View File

@ -15,9 +15,9 @@ pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
Node::Text(text.into()) Node::Text(text.into())
}); });
let mut passed = StyleMap::new(); let mut map = StyleMap::new();
passed.set(TextNode::LINK, Some(url.clone())); map.set(TextNode::LINK, Some(url.clone()));
passed.set(ImageNode::LINK, Some(url.clone())); map.set(ImageNode::LINK, Some(url.clone()));
passed.set(ShapeNode::LINK, Some(url)); map.set(ShapeNode::LINK, Some(url));
Ok(Value::Node(body.styled(passed))) Ok(Value::Node(body.styled_with_map(map)))
} }

View File

@ -61,8 +61,8 @@ prelude! {
pub use crate::diag::{At, TypResult}; pub use crate::diag::{At, TypResult};
pub use crate::eval::{ pub use crate::eval::{
Args, Construct, EvalContext, Node, Property, Set, Smart, StyleChain, StyleMap, Args, Construct, EvalContext, Node, Property, Scope, Set, Smart, StyleChain,
Styled, Value, StyleMap, Styled, Value,
}; };
pub use crate::frame::*; pub use crate::frame::*;
pub use crate::geom::*; pub use crate::geom::*;
@ -73,53 +73,50 @@ prelude! {
pub use crate::util::{EcoString, OptionExt}; pub use crate::util::{EcoString, OptionExt};
} }
use crate::eval::Scope;
use prelude::*; use prelude::*;
/// Construct a scope containing all standard library definitions. /// Construct a scope containing all standard library definitions.
pub fn new() -> Scope { pub fn new() -> Scope {
let mut std = Scope::new(); let mut std = Scope::new();
// Classes. // Structure and semantics.
std.def_class::<PageNode>("page"); std.def_class::<PageNode>("page");
std.def_class::<ParNode>("par"); std.def_class::<ParNode>("par");
std.def_class::<TextNode>("text"); std.def_class::<TextNode>("text");
std.def_class::<HeadingNode>("heading");
std.def_class::<ListNode<Unordered>>("list");
std.def_class::<ListNode<Ordered>>("enum");
// Text functions.
std.def_func("underline", underline); std.def_func("underline", underline);
std.def_func("strike", strike); std.def_func("strike", strike);
std.def_func("overline", overline); std.def_func("overline", overline);
std.def_func("link", link); std.def_func("link", link);
std.def_class::<HeadingNode>("heading");
// Break and spacing functions. std.def_class::<ListNode<Unordered>>("list");
std.def_func("pagebreak", pagebreak); std.def_class::<ListNode<Ordered>>("enum");
std.def_func("colbreak", colbreak);
std.def_func("parbreak", parbreak);
std.def_func("linebreak", linebreak);
std.def_func("h", h);
std.def_func("v", v);
// Layout functions.
std.def_func("box", box_);
std.def_func("block", block);
std.def_func("stack", stack);
std.def_func("grid", grid);
std.def_func("pad", pad);
std.def_func("columns", columns);
std.def_func("align", align);
std.def_func("place", place);
std.def_func("move", move_);
std.def_func("scale", scale);
std.def_func("rotate", rotate);
std.def_func("image", image); std.def_func("image", image);
std.def_func("rect", rect); std.def_func("rect", rect);
std.def_func("square", square); std.def_func("square", square);
std.def_func("ellipse", ellipse); std.def_func("ellipse", ellipse);
std.def_func("circle", circle); std.def_func("circle", circle);
// Layout.
std.def_func("h", h);
std.def_func("v", v);
std.def_func("box", box_);
std.def_func("block", block);
std.def_func("align", align);
std.def_func("pad", pad);
std.def_func("place", place);
std.def_func("move", move_);
std.def_func("scale", scale);
std.def_func("rotate", rotate);
std.def_func("stack", stack);
std.def_func("grid", grid);
std.def_func("columns", columns);
// Breaks.
std.def_func("pagebreak", pagebreak);
std.def_func("colbreak", colbreak);
std.def_func("parbreak", parbreak);
std.def_func("linebreak", linebreak);
// Utility functions. // Utility functions.
std.def_func("assert", assert); std.def_func("assert", assert);
std.def_func("type", type_); std.def_func("type", type_);

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use super::prelude::*; use super::prelude::*;
use super::{ColumnsNode, PadNode}; use super::ColumnsNode;
/// Layouts its child onto one or multiple pages. /// Layouts its child onto one or multiple pages.
#[derive(Clone, PartialEq, Hash)] #[derive(Clone, PartialEq, Hash)]
@ -111,7 +111,7 @@ impl PageNode {
} }
// Realize margins with padding node. // Realize margins with padding node.
child = PadNode { child, padding }.pack(); child = child.padded(padding);
// Layout the child. // Layout the child.
let expand = size.map(Length::is_finite); let expand = size.map(Length::is_finite);

View File

@ -29,7 +29,10 @@ impl ParNode {
impl Construct for ParNode { impl Construct for ParNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
// Lift to a block so that it doesn't merge with adjacent stuff. // The paragraph constructor is special: It doesn't create a paragraph
// since that happens automatically through markup. Instead, it just
// lifts the passed body to the block level so that it won't merge with
// adjacent stuff and it styles the contained paragraphs.
Ok(Node::Block(args.expect("body")?)) Ok(Node::Block(args.expect("body")?))
} }
} }

View File

@ -96,10 +96,10 @@ impl TextNode {
impl Construct for TextNode { impl Construct for TextNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
// We don't need to do anything more here because the whole point of the // The text constructor is special: It doesn't create a text node.
// text constructor is to apply the styles and that happens // Instead, it leaves the passed argument structurally unchanged, but
// automatically during class construction. // styles all text in it.
args.expect::<Node>("body") args.expect("body")
} }
} }
@ -404,9 +404,7 @@ fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
let extent = args.named("extent")?.unwrap_or_default(); let extent = args.named("extent")?.unwrap_or_default();
let body: Node = args.expect("body")?; let body: Node = args.expect("body")?;
let deco = LineDecoration { kind, stroke, thickness, offset, extent }; let deco = LineDecoration { kind, stroke, thickness, offset, extent };
Ok(Value::Node( Ok(Value::Node(body.styled(TextNode::LINES, vec![deco])))
body.styled(StyleMap::with(TextNode::LINES, vec![deco])),
))
} }
/// Defines a line that is positioned over, under or on top of text. /// Defines a line that is positioned over, under or on top of text.

View File

@ -12,8 +12,7 @@ Hello *{x}*
#let fruit = [ #let fruit = [
- Apple - Apple
- Orange - Orange
#set list(body-indent: 10pt) #list(body-indent: 10pt, [Pear])
- Pear
] ]
- Fruit - Fruit