Page background and foreground

This commit is contained in:
Laurenz 2022-05-28 00:26:24 +02:00
parent 1a7ce3da02
commit 90be79dc86
6 changed files with 112 additions and 49 deletions

View File

@ -37,6 +37,34 @@ impl Frame {
self.baseline.unwrap_or(self.size.y) self.baseline.unwrap_or(self.size.y)
} }
/// The layer the next item will be added on. This corresponds to the number
/// of elements in the frame.
pub fn layer(&self) -> usize {
self.elements.len()
}
/// Whether the frame has comparatively few elements.
pub fn is_light(&self) -> bool {
self.elements.len() <= 5
}
/// Add an element at a position in the foreground.
pub fn push(&mut self, pos: Point, element: Element) {
self.elements.push((pos, element));
}
/// Add a frame.
///
/// Automatically decides whether to inline the frame or to include it as a
/// group based on the number of elements in the frame.
pub fn push_frame(&mut self, pos: Point, frame: impl FrameRepr) {
if self.elements.is_empty() || frame.as_ref().is_light() {
frame.inline(self, self.layer(), pos);
} else {
self.elements.push((pos, Element::Group(Group::new(frame.share()))));
}
}
/// Add an element at a position in the background. /// Add an element at a position in the background.
pub fn prepend(&mut self, pos: Point, element: Element) { pub fn prepend(&mut self, pos: Point, element: Element) {
self.elements.insert(0, (pos, element)); self.elements.insert(0, (pos, element));
@ -50,15 +78,14 @@ impl Frame {
self.elements.splice(0 .. 0, insert); self.elements.splice(0 .. 0, insert);
} }
/// Add an element at a position in the foreground. /// Add a frame at a position in the background.
pub fn push(&mut self, pos: Point, element: Element) { pub fn prepend_frame(&mut self, pos: Point, frame: impl FrameRepr) {
self.elements.push((pos, element)); if self.elements.is_empty() || frame.as_ref().is_light() {
} frame.inline(self, 0, pos);
} else {
/// The layer the next item will be added on. This corresponds to the number self.elements
/// of elements in the frame. .insert(0, (pos, Element::Group(Group::new(frame.share()))));
pub fn layer(&self) -> usize { }
self.elements.len()
} }
/// Insert an element at the given layer in the frame. /// Insert an element at the given layer in the frame.
@ -68,18 +95,6 @@ impl Frame {
self.elements.insert(layer, (pos, element)); self.elements.insert(layer, (pos, element));
} }
/// Add a frame.
///
/// Automatically decides whether to inline the frame or to include it as a
/// group based on the number of elements in the frame.
pub fn push_frame(&mut self, pos: Point, frame: impl FrameRepr) {
if self.elements.is_empty() || frame.as_ref().elements.len() <= 5 {
frame.inline(self, pos);
} else {
self.elements.push((pos, Element::Group(Group::new(frame.share()))));
}
}
/// Resize the frame to a new size, distributing new space according to the /// Resize the frame to a new size, distributing new space according to the
/// given alignments. /// given alignments.
pub fn resize(&mut self, target: Size, aligns: Spec<Align>) { pub fn resize(&mut self, target: Size, aligns: Spec<Align>) {
@ -153,7 +168,7 @@ pub trait FrameRepr: AsRef<Frame> {
fn share(self) -> Arc<Frame>; fn share(self) -> Arc<Frame>;
/// Inline `self` into the sink frame. /// Inline `self` into the sink frame.
fn inline(self, sink: &mut Frame, offset: Point); fn inline(self, sink: &mut Frame, layer: usize, offset: Point);
} }
impl FrameRepr for Frame { impl FrameRepr for Frame {
@ -161,16 +176,18 @@ impl FrameRepr for Frame {
Arc::new(self) Arc::new(self)
} }
fn inline(self, sink: &mut Frame, offset: Point) { fn inline(self, sink: &mut Frame, layer: usize, offset: Point) {
if offset.is_zero() { if offset.is_zero() {
if sink.elements.is_empty() { if sink.elements.is_empty() {
sink.elements = self.elements; sink.elements = self.elements;
} else { } else {
sink.elements.extend(self.elements); sink.elements.splice(layer .. layer, self.elements);
} }
} else { } else {
sink.elements sink.elements.splice(
.extend(self.elements.into_iter().map(|(p, e)| (p + offset, e))); layer .. layer,
self.elements.into_iter().map(|(p, e)| (p + offset, e)),
);
} }
} }
} }
@ -180,12 +197,15 @@ impl FrameRepr for Arc<Frame> {
self self
} }
fn inline(self, sink: &mut Frame, offset: Point) { fn inline(self, sink: &mut Frame, layer: usize, offset: Point) {
match Arc::try_unwrap(self) { match Arc::try_unwrap(self) {
Ok(frame) => frame.inline(sink, offset), Ok(frame) => frame.inline(sink, layer, offset),
Err(rc) => sink Err(rc) => {
.elements sink.elements.splice(
.extend(rc.elements.iter().cloned().map(|(p, e)| (p + offset, e))), layer .. layer,
rc.elements.iter().cloned().map(|(p, e)| (p + offset, e)),
);
}
} }
} }
} }
@ -198,10 +218,10 @@ impl FrameRepr for MaybeShared<Frame> {
} }
} }
fn inline(self, sink: &mut Frame, offset: Point) { fn inline(self, sink: &mut Frame, layer: usize, offset: Point) {
match self { match self {
Self::Owned(owned) => owned.inline(sink, offset), Self::Owned(owned) => owned.inline(sink, layer, offset),
Self::Shared(shared) => shared.inline(sink, offset), Self::Shared(shared) => shared.inline(sink, layer, offset),
} }
} }
} }

View File

@ -34,6 +34,12 @@ impl PageNode {
/// The page's footer. /// The page's footer.
#[property(referenced)] #[property(referenced)]
pub const FOOTER: Marginal = Marginal::None; pub const FOOTER: Marginal = Marginal::None;
/// Content in the page's background.
#[property(referenced)]
pub const BACKGROUND: Marginal = Marginal::None;
/// Content in the page's foreground.
#[property(referenced)]
pub const FOREGROUND: Marginal = Marginal::None;
fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> { fn construct(_: &mut Machine, args: &mut Args) -> TypResult<Content> {
Ok(Content::Page(Self(args.expect("body")?))) Ok(Content::Page(Self(args.expect("body")?)))
@ -95,22 +101,29 @@ impl PageNode {
let header = styles.get(Self::HEADER); let header = styles.get(Self::HEADER);
let footer = styles.get(Self::FOOTER); let footer = styles.get(Self::FOOTER);
let foreground = styles.get(Self::FOREGROUND);
let background = styles.get(Self::BACKGROUND);
// Realize header and footer. // Realize overlays.
for frame in &mut frames { for frame in &mut frames {
let size = frame.size; let size = frame.size;
let padding = padding.resolve(styles).relative_to(size); let pad = padding.resolve(styles).relative_to(size);
for (y, h, marginal) in [ let pw = size.x - pad.left - pad.right;
(Length::zero(), padding.top, header), let py = size.y - pad.bottom;
(size.y - padding.bottom, padding.bottom, footer), for (marginal, pos, area) in [
(header, Point::with_x(pad.left), Size::new(pw, pad.top)),
(footer, Point::new(pad.left, py), Size::new(pw, pad.bottom)),
(foreground, Point::zero(), size),
(background, Point::zero(), size),
] { ] {
if let Some(content) = marginal.resolve(ctx, page)? { if let Some(content) = marginal.resolve(ctx, page)? {
let pos = Point::new(padding.left, y); let pod = Regions::one(area, area, Spec::splat(true));
let w = size.x - padding.left - padding.right;
let area = Size::new(w, h);
let pod = Regions::one(area, area, area.map(Length::is_finite));
let sub = content.layout(ctx, &pod, styles)?.remove(0); let sub = content.layout(ctx, &pod, styles)?.remove(0);
Arc::make_mut(frame).push_frame(pos, sub); if std::ptr::eq(marginal, background) {
Arc::make_mut(frame).prepend_frame(pos, sub);
} else {
Arc::make_mut(frame).push_frame(pos, sub);
}
} }
} }
@ -140,7 +153,7 @@ impl PagebreakNode {
} }
} }
/// A header or footer definition. /// A header, footer, foreground or background definition.
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum Marginal { pub enum Marginal {
/// Nothing, /// Nothing,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -32,16 +32,43 @@
..{ ..{
let seen = () let seen = ()
for item in all { for item in all {
if item.value not in seen { if item.value in seen { continue }
seen.push(item.value) (num(all, item.value), item.value)
(num(all, item.value), item.value) seen.push(item.value)
}
} }
} }
))} ))}
As shown in #cite("abc") and #cite("def") and #cite("abc") ... As shown in #cite("abc") and #cite("def") and #cite("abc") ...
---
// Test lovely sidebar.
#let lovely = group("lovely")
#let words = ("Juliet", "soft", "fair", "maid")
#let regex = regex(words.map(p => "(" + p + ")").join("|"))
#show word: regex as underline(word) + lovely.entry(_ => {})
#set page(
paper: "a8",
margins: (left: 25pt, rest: 15pt),
foreground: lovely.all(entries => {
let seen = ()
for y in entries.map(it => it.y) {
if y in seen { continue }
let line = entries.filter(it => it.y == y)
for i, it in line {
let x = 10pt - 4pt * (line.len() - i - 1)
place(dx: x, dy: it.y - 8pt, [💗])
}
seen.push(y)
}
}),
)
But, soft! what light through yonder window breaks? It is the east, and Juliet
is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and
pale with grief, That thou her maid art far more fair than she: Be not her maid,
since she is envious.
--- ---
// Test that `all` contains `me`. // Test that `all` contains `me`.
// Ref: false // Ref: false

View File

@ -7,6 +7,9 @@
text(0.8em)[_Chapter 1_] text(0.8em)[_Chapter 1_]
}), }),
footer: page => v(5pt) + align(center)[\~ #page \~], footer: page => v(5pt) + align(center)[\~ #page \~],
background: n => if n <= 2 {
place(center + horizon, circle(radius: 1cm, fill: luma(90%)))
}
) )
But, soft! what light through yonder window breaks? It is the east, and Juliet But, soft! what light through yonder window breaks? It is the east, and Juliet