summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-28 00:26:24 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-28 00:26:24 +0200
commit90be79dc864c776a4d6f6739af671d4ea8407549 (patch)
tree27de49da69a388d244c3786bd326d79694cd8e83
parent1a7ce3da02a25900dcdc09c110fe00229fd193d4 (diff)
Page background and foreground
-rw-r--r--src/frame.rs88
-rw-r--r--src/library/layout/page.rs35
-rw-r--r--tests/ref/layout/locate-group.pngbin3881 -> 42394 bytes
-rw-r--r--tests/ref/layout/page-marginals.pngbin68057 -> 69787 bytes
-rw-r--r--tests/typ/layout/locate-group.typ35
-rw-r--r--tests/typ/layout/page-marginals.typ3
6 files changed, 112 insertions, 49 deletions
diff --git a/src/frame.rs b/src/frame.rs
index 2a16b6f2..1bd1f454 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -37,6 +37,34 @@ impl Frame {
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.
pub fn prepend(&mut self, pos: Point, element: Element) {
self.elements.insert(0, (pos, element));
@@ -50,15 +78,14 @@ impl Frame {
self.elements.splice(0 .. 0, insert);
}
- /// Add an element at a position in the foreground.
- pub fn push(&mut self, pos: Point, element: Element) {
- self.elements.push((pos, element));
- }
-
- /// 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()
+ /// Add a frame at a position in the background.
+ pub fn prepend_frame(&mut self, pos: Point, frame: impl FrameRepr) {
+ if self.elements.is_empty() || frame.as_ref().is_light() {
+ frame.inline(self, 0, pos);
+ } else {
+ self.elements
+ .insert(0, (pos, Element::Group(Group::new(frame.share()))));
+ }
}
/// Insert an element at the given layer in the frame.
@@ -68,18 +95,6 @@ impl Frame {
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
/// given alignments.
pub fn resize(&mut self, target: Size, aligns: Spec<Align>) {
@@ -153,7 +168,7 @@ pub trait FrameRepr: AsRef<Frame> {
fn share(self) -> Arc<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 {
@@ -161,16 +176,18 @@ impl FrameRepr for Frame {
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 sink.elements.is_empty() {
sink.elements = self.elements;
} else {
- sink.elements.extend(self.elements);
+ sink.elements.splice(layer .. layer, self.elements);
}
} else {
- sink.elements
- .extend(self.elements.into_iter().map(|(p, e)| (p + offset, e)));
+ sink.elements.splice(
+ layer .. layer,
+ self.elements.into_iter().map(|(p, e)| (p + offset, e)),
+ );
}
}
}
@@ -180,12 +197,15 @@ impl FrameRepr for Arc<Frame> {
self
}
- fn inline(self, sink: &mut Frame, offset: Point) {
+ fn inline(self, sink: &mut Frame, layer: usize, offset: Point) {
match Arc::try_unwrap(self) {
- Ok(frame) => frame.inline(sink, offset),
- Err(rc) => sink
- .elements
- .extend(rc.elements.iter().cloned().map(|(p, e)| (p + offset, e))),
+ Ok(frame) => frame.inline(sink, layer, offset),
+ Err(rc) => {
+ sink.elements.splice(
+ 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 {
- Self::Owned(owned) => owned.inline(sink, offset),
- Self::Shared(shared) => shared.inline(sink, offset),
+ Self::Owned(owned) => owned.inline(sink, layer, offset),
+ Self::Shared(shared) => shared.inline(sink, layer, offset),
}
}
}
diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs
index 7d91aa6d..8435e510 100644
--- a/src/library/layout/page.rs
+++ b/src/library/layout/page.rs
@@ -34,6 +34,12 @@ impl PageNode {
/// The page's footer.
#[property(referenced)]
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> {
Ok(Content::Page(Self(args.expect("body")?)))
@@ -95,22 +101,29 @@ impl PageNode {
let header = styles.get(Self::HEADER);
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 {
let size = frame.size;
- let padding = padding.resolve(styles).relative_to(size);
- for (y, h, marginal) in [
- (Length::zero(), padding.top, header),
- (size.y - padding.bottom, padding.bottom, footer),
+ let pad = padding.resolve(styles).relative_to(size);
+ let pw = size.x - pad.left - pad.right;
+ let py = size.y - pad.bottom;
+ 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)? {
- let pos = Point::new(padding.left, y);
- 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 pod = Regions::one(area, area, Spec::splat(true));
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)]
pub enum Marginal {
/// Nothing,
diff --git a/tests/ref/layout/locate-group.png b/tests/ref/layout/locate-group.png
index e38a4a32..c9fe4065 100644
--- a/tests/ref/layout/locate-group.png
+++ b/tests/ref/layout/locate-group.png
Binary files differ
diff --git a/tests/ref/layout/page-marginals.png b/tests/ref/layout/page-marginals.png
index 734973b6..78dd4591 100644
--- a/tests/ref/layout/page-marginals.png
+++ b/tests/ref/layout/page-marginals.png
Binary files differ
diff --git a/tests/typ/layout/locate-group.typ b/tests/typ/layout/locate-group.typ
index 5c022f5a..c3a5ddab 100644
--- a/tests/typ/layout/locate-group.typ
+++ b/tests/typ/layout/locate-group.typ
@@ -32,10 +32,9 @@
..{
let seen = ()
for item in all {
- if item.value not in seen {
- seen.push(item.value)
- (num(all, item.value), item.value)
- }
+ if item.value in seen { continue }
+ (num(all, item.value), item.value)
+ seen.push(item.value)
}
}
))}
@@ -43,6 +42,34 @@
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`.
// Ref: false
#show it: heading as group("headings").entry(
diff --git a/tests/typ/layout/page-marginals.typ b/tests/typ/layout/page-marginals.typ
index 6e8e3d85..9b323692 100644
--- a/tests/typ/layout/page-marginals.typ
+++ b/tests/typ/layout/page-marginals.typ
@@ -7,6 +7,9 @@
text(0.8em)[_Chapter 1_]
}),
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