Pad function 🔲

This commit is contained in:
Laurenz 2021-03-10 17:42:47 +01:00
parent b0446cbdd1
commit 4e5f85aa4a
6 changed files with 108 additions and 76 deletions

View File

@ -80,6 +80,45 @@ impl<'a> ExecContext<'a> {
self.inner.push(node); self.inner.push(node);
} }
/// Push a normal word space.
pub fn push_space(&mut self) {
let em = self.state.font.font_size();
self.push(NodeSpacing {
amount: self.state.par.word_spacing.resolve(em),
softness: Softness::Soft,
});
}
/// Push text into the context.
///
/// The text is split into lines at newlines.
pub fn push_text(&mut self, text: &str) {
let mut newline = false;
for line in text.split_terminator(is_newline) {
if newline {
self.apply_linebreak();
}
let node = self.make_text_node(line.into());
self.push(node);
newline = true;
}
}
/// Execute the body of a function and return the result as a stack node.
pub fn exec_body(&mut self, body: &ValueTemplate, expand: Spec<Expansion>) -> Node {
let dirs = self.state.dirs;
let align = self.state.align;
self.start_group(ContentGroup);
self.start_par_group();
body.exec(self);
self.end_par_group();
let children = self.end_group::<ContentGroup>().1;
NodeStack { dirs, align, expand, children }.into()
}
/// Start a page group based on the active page state. /// Start a page group based on the active page state.
/// ///
/// The `softness` is a hint on whether empty pages should be kept in the /// The `softness` is a hint on whether empty pages should be kept in the
@ -130,22 +169,6 @@ impl<'a> ExecContext<'a> {
group.softness group.softness
} }
/// Start a content group.
///
/// This also starts an inner paragraph.
pub fn start_content_group(&mut self) {
self.start_group(ContentGroup);
self.start_par_group();
}
/// End a content group and return the resulting nodes.
///
/// This also ends an inner paragraph.
pub fn end_content_group(&mut self) -> Vec<Node> {
self.end_par_group();
self.end_group::<ContentGroup>().1
}
/// Start a paragraph group based on the active text state. /// Start a paragraph group based on the active text state.
pub fn start_par_group(&mut self) { pub fn start_par_group(&mut self) {
let em = self.state.font.font_size(); let em = self.state.font.font_size();
@ -218,29 +241,28 @@ impl<'a> ExecContext<'a> {
} }
} }
/// Push a normal word space. /// Set the font to monospace.
pub fn push_space(&mut self) { pub fn apply_monospace(&mut self) {
let families = self.state.font.families_mut();
families.list.insert(0, "monospace".to_string());
families.flatten();
}
/// Apply a forced line break.
pub fn apply_linebreak(&mut self) {
self.end_par_group();
self.start_par_group();
}
/// Apply a forced paragraph break.
pub fn apply_parbreak(&mut self) {
self.end_par_group();
let em = self.state.font.font_size(); let em = self.state.font.font_size();
self.push(NodeSpacing { self.push(NodeSpacing {
amount: self.state.par.word_spacing.resolve(em), amount: self.state.par.par_spacing.resolve(em),
softness: Softness::Soft, softness: Softness::Soft,
}); });
} self.start_par_group();
/// Push text into the context.
///
/// The text is split into lines at newlines.
pub fn push_text(&mut self, text: &str) {
let mut newline = false;
for line in text.split_terminator(is_newline) {
if newline {
self.apply_linebreak();
}
let node = self.make_text_node(line.into());
self.push(node);
newline = true;
}
} }
/// Construct a text node from the given string based on the active text /// Construct a text node from the given string based on the active text
@ -269,30 +291,6 @@ impl<'a> ExecContext<'a> {
variant, variant,
} }
} }
/// Set the font to monospace.
pub fn apply_monospace(&mut self) {
let families = self.state.font.families_mut();
families.list.insert(0, "monospace".to_string());
families.flatten();
}
/// Apply a forced line break.
pub fn apply_linebreak(&mut self) {
self.end_par_group();
self.start_par_group();
}
/// Apply a forced paragraph break.
pub fn apply_parbreak(&mut self) {
self.end_par_group();
let em = self.state.font.font_size();
self.push(NodeSpacing {
amount: self.state.par.par_spacing.resolve(em),
softness: Softness::Soft,
});
self.start_par_group();
}
} }
/// Defines how an item interacts with surrounding items. /// Defines how an item interacts with surrounding items.

View File

@ -7,6 +7,7 @@ mod align;
mod base; mod base;
mod font; mod font;
mod image; mod image;
mod pad;
mod page; mod page;
mod shapes; mod shapes;
mod spacing; mod spacing;
@ -15,6 +16,7 @@ pub use self::image::*;
pub use align::*; pub use align::*;
pub use base::*; pub use base::*;
pub use font::*; pub use font::*;
pub use pad::*;
pub use page::*; pub use page::*;
pub use shapes::*; pub use shapes::*;
pub use spacing::*; pub use spacing::*;
@ -46,6 +48,7 @@ pub fn new() -> Scope {
set!(func: "font", font); set!(func: "font", font);
set!(func: "h", h); set!(func: "h", h);
set!(func: "image", image); set!(func: "image", image);
set!(func: "pad", pad);
set!(func: "page", page); set!(func: "page", page);
set!(func: "pagebreak", pagebreak); set!(func: "pagebreak", pagebreak);
set!(func: "repr", repr); set!(func: "repr", repr);

38
src/library/pad.rs Normal file
View File

@ -0,0 +1,38 @@
use super::*;
/// `pad`: Pad content at the sides.
///
/// # Positional arguments
/// - Padding for all sides: `padding`, of type `linear` relative to sides.
/// - Body: of type `template`.
///
/// # Named arguments
/// - Left padding: `left`, of type `linear` relative to parent width.
/// - Right padding: `right`, of type `linear` relative to parent width.
/// - Top padding: `top`, of type `linear` relative to parent height.
/// - Bottom padding: `bottom`, of type `linear` relative to parent height.
pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
let all = args.find(ctx);
let left = args.get(ctx, "left");
let top = args.get(ctx, "top");
let right = args.get(ctx, "right");
let bottom = args.get(ctx, "bottom");
let body = args.require::<ValueTemplate>(ctx, "body").unwrap_or_default();
let padding = Sides::new(
left.or(all).unwrap_or_default(),
top.or(all).unwrap_or_default(),
right.or(all).unwrap_or_default(),
bottom.or(all).unwrap_or_default(),
);
Value::template("pad", move |ctx| {
let snapshot = ctx.state.clone();
let expand = Spec::uniform(Expansion::Fit);
let child = ctx.exec_body(&body, expand);
ctx.push(NodePad { padding, child });
ctx.state = snapshot;
})
}

View File

@ -24,28 +24,18 @@ pub fn box_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
let main = args.get(ctx, "main-dir"); let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir"); let cross = args.get(ctx, "cross-dir");
let color = args.get(ctx, "color"); let color = args.get(ctx, "color");
let body = args.find::<ValueTemplate>(ctx); let body = args.find::<ValueTemplate>(ctx).unwrap_or_default();
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
Value::template("box", move |ctx| { Value::template("box", move |ctx| {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();
ctx.set_dirs(Gen::new(main, cross)); ctx.set_dirs(Gen::new(main, cross));
let dirs = ctx.state.dirs;
let align = ctx.state.align;
ctx.start_content_group(); let child = ctx.exec_body(&body, expand);
if let Some(body) = &body { let fixed = NodeFixed { width, height, child };
body.exec(ctx);
}
let children = ctx.end_content_group();
let fill_if = |c| if c { Expansion::Fill } else { Expansion::Fit };
let expand = Spec::new(fill_if(width.is_some()), fill_if(height.is_some()));
let fixed = NodeFixed {
width,
height,
child: NodeStack { dirs, align, expand, children }.into(),
};
if let Some(color) = color { if let Some(color) = color {
ctx.push(NodeBackground { ctx.push(NodeBackground {

BIN
tests/ref/library/pad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

View File

@ -0,0 +1,3 @@
#box(color: #9feb52)[
#pad(10pt, box(color: #eb5278, width: 20pt, height: 20pt))
]