summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/layout/repeat.rs41
-rw-r--r--tests/ref/repeat-gap.pngbin0 -> 293 bytes
-rw-r--r--tests/ref/repeat-no-justify-align.pngbin0 -> 246 bytes
-rw-r--r--tests/ref/repeat-no-justify.pngbin0 -> 246 bytes
-rw-r--r--tests/suite/layout/repeat.typ15
5 files changed, 43 insertions, 13 deletions
diff --git a/crates/typst/src/layout/repeat.rs b/crates/typst/src/layout/repeat.rs
index 30fc314d..60a3e4c7 100644
--- a/crates/typst/src/layout/repeat.rs
+++ b/crates/typst/src/layout/repeat.rs
@@ -5,7 +5,7 @@ use crate::foundations::{
};
use crate::introspection::Locator;
use crate::layout::{
- Abs, AlignElem, Axes, BlockElem, Frame, Point, Region, Regions, Size,
+ Abs, AlignElem, Axes, BlockElem, Frame, Length, Point, Region, Regions, Size,
};
use crate::utils::Numeric;
@@ -14,7 +14,7 @@ use crate::utils::Numeric;
/// This can be useful when implementing a custom index, reference, or outline.
///
/// Space may be inserted between the instances of the body parameter, so be
-/// sure to include negative space if you need the instances to overlap.
+/// sure to adjust the [`justify`]($repeat.justify) parameter accordingly.
///
/// Errors if there no bounds on the available space, as it would create
/// infinite content.
@@ -35,6 +35,15 @@ pub struct RepeatElem {
/// The content to repeat.
#[required]
pub body: Content,
+
+ /// The gap between each instance of the body.
+ #[default]
+ pub gap: Length,
+
+ /// Whether to increase the gap between instances to completely fill the
+ /// available space.
+ #[default(true)]
+ pub justify: bool,
}
impl Show for Packed<RepeatElem> {
@@ -56,15 +65,6 @@ fn layout_repeat(
) -> SourceResult<Frame> {
let pod = Regions::one(region.size, Axes::new(false, false));
let piece = elem.body().layout(engine, locator, styles, pod)?.into_frame();
-
- let align = AlignElem::alignment_in(styles).resolve(styles);
-
- let fill = region.size.x;
- let width = piece.width();
- let count = (fill / width).floor();
- let remaining = fill % width;
- let apart = remaining / (count - 1.0);
-
let size = Size::new(region.size.x, piece.height());
if !size.is_finite() {
@@ -76,15 +76,30 @@ fn layout_repeat(
frame.set_baseline(piece.baseline());
}
+ let mut gap = elem.gap(styles).resolve(styles);
+ let fill = region.size.x;
+ let width = piece.width();
+
+ // count * width + (count - 1) * gap = fill, but count is an integer so
+ // we need to round down and get the remainder.
+ let count = ((fill + gap) / (width + gap)).floor();
+ let remaining = (fill + gap) % (width + gap);
+
+ let justify = elem.justify(styles);
+ if justify {
+ gap += remaining / (count - 1.0);
+ }
+
+ let align = AlignElem::alignment_in(styles).resolve(styles);
let mut offset = Abs::zero();
- if count == 1.0 {
+ if count == 1.0 || !justify {
offset += align.x.position(remaining);
}
if width > Abs::zero() {
for _ in 0..(count as usize).min(1000) {
frame.push_frame(Point::with_x(offset), piece.clone());
- offset += piece.width() + apart;
+ offset += piece.width() + gap;
}
}
diff --git a/tests/ref/repeat-gap.png b/tests/ref/repeat-gap.png
new file mode 100644
index 00000000..40f7f29a
--- /dev/null
+++ b/tests/ref/repeat-gap.png
Binary files differ
diff --git a/tests/ref/repeat-no-justify-align.png b/tests/ref/repeat-no-justify-align.png
new file mode 100644
index 00000000..1cb79736
--- /dev/null
+++ b/tests/ref/repeat-no-justify-align.png
Binary files differ
diff --git a/tests/ref/repeat-no-justify.png b/tests/ref/repeat-no-justify.png
new file mode 100644
index 00000000..820aec09
--- /dev/null
+++ b/tests/ref/repeat-no-justify.png
Binary files differ
diff --git a/tests/suite/layout/repeat.typ b/tests/suite/layout/repeat.typ
index 5c82fc19..a46bf6d2 100644
--- a/tests/suite/layout/repeat.typ
+++ b/tests/suite/layout/repeat.typ
@@ -42,3 +42,18 @@ A#box(width: 1fr, repeat(rect(width: 6em, height: 0.7em)))B
// Error: 2:2-2:13 repeat with no size restrictions
#set page(width: auto)
#repeat(".")
+
+--- repeat-gap ---
+// Test repeat with custom gap.
+A#box(width: 1fr, repeat(rect(width: 2em, height: 1em), gap: 1em))B
+
+--- repeat-no-justify ---
+// Test repeat with disabled justification.
+#set repeat(justify: false)
+A#box(width: 1fr, repeat(rect(width: 2em, height: 1em), gap: 1em))B
+
+--- repeat-no-justify-align ---
+// Test repeat with alignment and disabled justification.
+#set repeat(justify: false)
+#set align(right)
+A#box(width: 1fr, repeat(rect(width: 2em, height: 1em), gap: 1em))B