summaryrefslogtreecommitdiff
path: root/src/library/structure
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-30 14:12:28 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-30 14:12:28 +0200
commitf9e115daf54c29358f890b137f50a33a781af680 (patch)
tree496de52246629ea8039db6beea94eb779ed2851d /src/library/structure
parentf7c67cde72e6a67f45180856b332bae9863243bd (diff)
New block spacing model
Diffstat (limited to 'src/library/structure')
-rw-r--r--src/library/structure/heading.rs51
-rw-r--r--src/library/structure/list.rs48
-rw-r--r--src/library/structure/table.rs31
3 files changed, 76 insertions, 54 deletions
diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs
index a6c87912..4f6c54f3 100644
--- a/src/library/structure/heading.rs
+++ b/src/library/structure/heading.rs
@@ -1,3 +1,4 @@
+use crate::library::layout::BlockSpacing;
use crate::library::prelude::*;
use crate::library::text::{FontFamily, TextNode, TextSize, Toggle};
@@ -6,7 +7,7 @@ use crate::library::text::{FontFamily, TextNode, TextSize, Toggle};
pub struct HeadingNode {
/// The logical nesting depth of the section, starting from one. In the
/// default style, this controls the text size of the heading.
- pub level: usize,
+ pub level: NonZeroUsize,
/// The heading's contents.
pub body: Content,
}
@@ -22,8 +23,12 @@ impl HeadingNode {
/// The size of text in the heading.
#[property(referenced)]
pub const SIZE: Leveled<TextSize> = Leveled::Mapping(|level| {
- let upscale = (1.6 - 0.1 * level as f64).max(0.75);
- TextSize(Em::new(upscale).into())
+ let size = match level.get() {
+ 1 => 1.4,
+ 2 => 1.2,
+ _ => 1.0,
+ };
+ TextSize(Em::new(size).into())
});
/// Whether text in the heading is strengthend.
@@ -36,21 +41,24 @@ impl HeadingNode {
#[property(referenced)]
pub const UNDERLINE: Leveled<bool> = Leveled::Value(false);
- /// The extra padding above the heading.
- #[property(referenced)]
- pub const ABOVE: Leveled<RawLength> = Leveled::Value(Length::zero().into());
- /// The extra padding below the heading.
- #[property(referenced)]
- pub const BELOW: Leveled<RawLength> = Leveled::Value(Length::zero().into());
-
- /// Whether the heading is block-level.
- #[property(referenced)]
- pub const BLOCK: Leveled<bool> = Leveled::Value(true);
+ /// The spacing above the heading.
+ #[property(referenced, shorthand(around))]
+ pub const ABOVE: Leveled<Option<BlockSpacing>> = Leveled::Mapping(|level| {
+ let ratio = match level.get() {
+ 1 => 1.5,
+ _ => 1.2,
+ };
+ Some(Ratio::new(ratio).into())
+ });
+ /// The spacing below the heading.
+ #[property(referenced, shorthand(around))]
+ pub const BELOW: Leveled<Option<BlockSpacing>> =
+ Leveled::Value(Some(Ratio::new(0.55).into()));
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self {
body: args.expect("body")?,
- level: args.named("level")?.unwrap_or(1),
+ level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()),
}))
}
}
@@ -58,13 +66,13 @@ impl HeadingNode {
impl Show for HeadingNode {
fn encode(&self) -> Dict {
dict! {
- "level" => Value::Int(self.level as i64),
+ "level" => Value::Int(self.level.get() as i64),
"body" => Value::Content(self.body.clone()),
}
}
fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
- Ok(self.body.clone())
+ Ok(Content::block(self.body.clone()))
}
fn finalize(
@@ -103,11 +111,6 @@ impl Show for HeadingNode {
}
realized = realized.styled_with_map(map);
-
- if resolve!(Self::BLOCK) {
- realized = Content::block(realized);
- }
-
realized = realized.spaced(
resolve!(Self::ABOVE).resolve(styles),
resolve!(Self::BELOW).resolve(styles),
@@ -123,19 +126,19 @@ pub enum Leveled<T> {
/// A bare value.
Value(T),
/// A simple mapping from a heading level to a value.
- Mapping(fn(usize) -> T),
+ Mapping(fn(NonZeroUsize) -> T),
/// A closure mapping from a heading level to a value.
Func(Func, Span),
}
impl<T: Cast + Clone> Leveled<T> {
/// Resolve the value based on the level.
- pub fn resolve(&self, ctx: &mut Context, level: usize) -> TypResult<T> {
+ pub fn resolve(&self, ctx: &mut Context, level: NonZeroUsize) -> TypResult<T> {
Ok(match self {
Self::Value(value) => value.clone(),
Self::Mapping(mapping) => mapping(level),
Self::Func(func, span) => {
- let args = Args::from_values(*span, [Value::Int(level as i64)]);
+ let args = Args::from_values(*span, [Value::Int(level.get() as i64)]);
func.call(ctx, args)?.cast().at(*span)?
}
})
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
index ac705156..4356ffb4 100644
--- a/src/library/structure/list.rs
+++ b/src/library/structure/list.rs
@@ -2,7 +2,7 @@ use std::fmt::Write;
use unscanny::Scanner;
-use crate::library::layout::{GridNode, TrackSizing};
+use crate::library::layout::{BlockSpacing, GridNode, TrackSizing};
use crate::library::prelude::*;
use crate::library::text::ParNode;
use crate::library::utility::Numbering;
@@ -12,9 +12,10 @@ use crate::library::utility::Numbering;
pub struct ListNode<const L: ListKind = UNORDERED> {
/// Where the list starts.
pub start: usize,
- /// If false, there is paragraph spacing between the items, if true
- /// there is list spacing between the items.
+ /// If true, the items are separated by leading instead of list spacing.
pub tight: bool,
+ /// If true, the spacing above the list is leading instead of above spacing.
+ pub attached: bool,
/// The individual bulleted or numbered items.
pub items: StyleVec<ListItem>,
}
@@ -38,10 +39,6 @@ impl<const L: ListKind> ListNode<L> {
/// How the list is labelled.
#[property(referenced)]
pub const LABEL: Label = Label::Default;
-
- /// The spacing between the list items of a non-wide list.
- #[property(resolve)]
- pub const SPACING: RawLength = RawLength::zero();
/// The indentation of each item's label.
#[property(resolve)]
pub const INDENT: RawLength = RawLength::zero();
@@ -49,17 +46,21 @@ impl<const L: ListKind> ListNode<L> {
#[property(resolve)]
pub const BODY_INDENT: RawLength = Em::new(0.5).into();
- /// The extra padding above the list.
- #[property(resolve)]
- pub const ABOVE: RawLength = RawLength::zero();
- /// The extra padding below the list.
+ /// The spacing above the list.
+ #[property(resolve, shorthand(around))]
+ pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
+ /// The spacing below the list.
+ #[property(resolve, shorthand(around))]
+ pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
+ /// The spacing between the items of a wide (non-tight) list.
#[property(resolve)]
- pub const BELOW: RawLength = RawLength::zero();
+ pub const SPACING: BlockSpacing = Ratio::one().into();
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self {
start: args.named("start")?.unwrap_or(1),
tight: args.named("tight")?.unwrap_or(true),
+ attached: args.named("attached")?.unwrap_or(false),
items: args
.all()?
.into_iter()
@@ -78,6 +79,7 @@ impl<const L: ListKind> Show for ListNode<L> {
dict! {
"start" => Value::Int(self.start as i64),
"tight" => Value::Bool(self.tight),
+ "attached" => Value::Bool(self.attached),
"items" => Value::Array(
self.items
.items()
@@ -103,14 +105,12 @@ impl<const L: ListKind> Show for ListNode<L> {
number += 1;
}
- let leading = styles.get(ParNode::LEADING);
- let spacing = if self.tight {
- styles.get(Self::SPACING)
+ let gutter = if self.tight {
+ styles.get(ParNode::LEADING)
} else {
- styles.get(ParNode::SPACING)
+ styles.get(Self::SPACING)
};
- let gutter = leading + spacing;
let indent = styles.get(Self::INDENT);
let body_indent = styles.get(Self::BODY_INDENT);
@@ -132,7 +132,19 @@ impl<const L: ListKind> Show for ListNode<L> {
styles: StyleChain,
realized: Content,
) -> TypResult<Content> {
- Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
+ let mut above = styles.get(Self::ABOVE);
+ let mut below = styles.get(Self::BELOW);
+
+ if self.attached {
+ if above.is_some() {
+ above = Some(styles.get(ParNode::LEADING));
+ }
+ if below.is_some() {
+ below = Some(styles.get(ParNode::SPACING));
+ }
+ }
+
+ Ok(realized.spaced(above, below))
}
}
diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs
index 191d3dd3..7b3f2ac5 100644
--- a/src/library/structure/table.rs
+++ b/src/library/structure/table.rs
@@ -1,4 +1,4 @@
-use crate::library::layout::{GridNode, TrackSizing};
+use crate::library::layout::{BlockSpacing, GridNode, TrackSizing};
use crate::library::prelude::*;
/// A table of items.
@@ -15,16 +15,24 @@ pub struct TableNode {
#[node(showable)]
impl TableNode {
/// The primary cell fill color.
+ #[property(shorthand(fill))]
pub const PRIMARY: Option<Paint> = None;
/// The secondary cell fill color.
+ #[property(shorthand(fill))]
pub const SECONDARY: Option<Paint> = None;
/// How to stroke the cells.
#[property(resolve, fold)]
pub const STROKE: Option<RawStroke> = Some(RawStroke::default());
-
/// How much to pad the cells's content.
pub const PADDING: Relative<RawLength> = Length::pt(5.0).into();
+ /// The spacing above the table.
+ #[property(resolve, shorthand(around))]
+ pub const ABOVE: Option<BlockSpacing> = Some(Ratio::one().into());
+ /// The spacing below the table.
+ #[property(resolve, shorthand(around))]
+ pub const BELOW: Option<BlockSpacing> = Some(Ratio::one().into());
+
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let columns = args.named("columns")?.unwrap_or_default();
let rows = args.named("rows")?.unwrap_or_default();
@@ -40,16 +48,6 @@ impl TableNode {
cells: args.all()?,
}))
}
-
- fn set(args: &mut Args) -> TypResult<StyleMap> {
- let mut styles = StyleMap::new();
- let fill = args.named("fill")?;
- styles.set_opt(Self::PRIMARY, args.named("primary")?.or(fill));
- styles.set_opt(Self::SECONDARY, args.named("secondary")?.or(fill));
- styles.set_opt(Self::STROKE, args.named("stroke")?);
- styles.set_opt(Self::PADDING, args.named("padding")?);
- Ok(styles)
- }
}
impl Show for TableNode {
@@ -99,4 +97,13 @@ impl Show for TableNode {
cells,
}))
}
+
+ fn finalize(
+ &self,
+ _: &mut Context,
+ styles: StyleChain,
+ realized: Content,
+ ) -> TypResult<Content> {
+ Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
+ }
}