summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-21 16:19:46 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-21 16:19:46 +0100
commit31f904a2c406953cbce334e02b37a712b9b9d016 (patch)
treec23f6e063864d97afb9b3d58c10ce4c2877106c5
parent4af7b9118c5ce612b3d9d7dd06118ce23b731d9c (diff)
Split up and document shapes
-rw-r--r--library/src/basics/table.rs48
-rw-r--r--library/src/layout/container.rs4
-rw-r--r--library/src/layout/grid.rs25
-rw-r--r--library/src/layout/hide.rs6
-rw-r--r--library/src/layout/page.rs9
-rw-r--r--library/src/layout/stack.rs1
-rw-r--r--library/src/layout/transform.rs18
-rw-r--r--library/src/text/mod.rs4
-rw-r--r--library/src/text/raw.rs2
-rw-r--r--library/src/visualize/shape.rs715
-rw-r--r--src/model/cast.rs16
-rw-r--r--src/model/styles.rs68
-rw-r--r--tests/ref/compiler/show-selector.pngbin12923 -> 20689 bytes
-rw-r--r--tests/ref/visualize/shape-aspect.pngbin4501 -> 4751 bytes
-rw-r--r--tests/ref/visualize/shape-circle.pngbin39277 -> 39582 bytes
-rw-r--r--tests/ref/visualize/shape-rect.pngbin16269 -> 9426 bytes
-rw-r--r--tests/ref/visualize/shape-square.pngbin18290 -> 18061 bytes
-rw-r--r--tests/typ/basics/desc.typ2
-rw-r--r--tests/typ/compiler/methods-color.typ4
-rw-r--r--tests/typ/compiler/show-selector.typ5
-rw-r--r--tests/typ/layout/grid-1.typ2
-rw-r--r--tests/typ/layout/grid-3.typ4
-rw-r--r--tests/typ/layout/pad.typ1
-rw-r--r--tests/typ/layout/par-knuth.typ2
-rw-r--r--tests/typ/layout/stack-2.typ3
-rw-r--r--tests/typ/math/syntax.typ2
-rw-r--r--tests/typ/text/edge.typ2
-rw-r--r--tests/typ/text/microtype.typ2
-rw-r--r--tests/typ/text/shift.typ4
-rw-r--r--tests/typ/visualize/shape-aspect.typ10
-rw-r--r--tests/typ/visualize/shape-circle.typ6
-rw-r--r--tests/typ/visualize/shape-ellipse.typ5
-rw-r--r--tests/typ/visualize/shape-rect.typ16
-rw-r--r--tests/typ/visualize/shape-square.typ2
34 files changed, 714 insertions, 274 deletions
diff --git a/library/src/basics/table.rs b/library/src/basics/table.rs
index 3c618b3f..411a31fc 100644
--- a/library/src/basics/table.rs
+++ b/library/src/basics/table.rs
@@ -6,25 +6,25 @@ use crate::prelude::*;
///
/// Tables are used to arrange content in cells. Cells can contain arbitray
/// content, including multiple paragraphs and are specified in row-major order.
-/// Because tables are [grids](@grid) with configurable cell strokes and
-/// padding, refer to the [grid](@grid) docs for more information on how to size
-/// the table tracks.
+/// Because tables are just grids with configurable cell properties, refer to
+/// the [grid documentation](@grid) for more information on how to size the
+/// table tracks.
///
/// ## Example
/// ```
/// #table(
/// columns: (1fr, auto, auto),
-/// padding: 10pt,
+/// inset: 10pt,
/// align: horizon,
/// [], [*Area*], [*Parameters*],
-/// image("cylinder.svg", fit: "contain"),
+/// image("cylinder.svg"),
/// $ pi h (D^2 - d^2) / 4 $,
/// [
/// $h$: height \
/// $D$: outer radius \
/// $d$: inner radius
/// ],
-/// image("tetrahedron.svg", fit: "contain"),
+/// image("tetrahedron.svg"),
/// $ sqrt(2) / 12 a^3 $,
/// [$a$: edge length]
/// )
@@ -36,33 +36,23 @@ use crate::prelude::*;
///
/// - rows: TrackSizings (named)
/// Defines the row sizes.
-///
-/// See [the respective `grid` argument](@grid/rows) for more information
-/// on sizing tracks.
+/// See the [grid documentation](@grid) for more information on track sizing.
///
/// - columns: TrackSizings (named)
/// Defines the column sizes.
-///
-/// See [the respective `grid` argument](@grid/columns) for more information
-/// on sizing tracks.
+/// See the [grid documentation](@grid) for more information on track sizing.
///
/// - gutter: TrackSizings (named)
/// Defines the gaps between rows & columns.
-///
-/// See [the respective `grid` argument](@grid/gutter) for more information
-/// on gutter.
+/// See the [grid documentation](@grid) for more information on gutters.
///
/// - column-gutter: TrackSizings (named)
/// Defines the gaps between columns. Takes precedence over `gutter`.
-///
-/// See [the respective `grid` argument](@grid/column-gutter) for more information
-/// on gutter.
+/// See the [grid documentation](@grid) for more information on gutters.
///
/// - row-gutter: TrackSizings (named)
/// Defines the gaps between rows. Takes precedence over `gutter`.
-///
-/// See [the respective `grid` argument](@grid/row-gutter) for more information
-/// on gutter.
+/// See the [grid documentation](@grid) for more information on gutters.
///
/// ## Category
/// basics
@@ -82,14 +72,14 @@ pub struct TableNode {
impl TableNode {
/// How to fill the cells.
///
- /// This can either be a color or a function that returns a color. The
- /// function is passed the cell's column and row index, starting at zero.
- /// This can be used to implement striped tables.
+ /// This can be a color or a function that returns a color. The function is
+ /// passed the cell's column and row index, starting at zero. This can be
+ /// used to implement striped tables.
///
/// # Example
/// ```
/// #table(
- /// fill: (col, _) => if odd(col) { luma(240) } else { luma(255) },
+ /// fill: (col, _) => if odd(col) { luma(240) } else { white },
/// align: (col, row) =>
/// if row == 0 { center }
/// else if col == 0 { left }
@@ -120,7 +110,9 @@ impl TableNode {
pub const STROKE: Option<PartialStroke> = Some(PartialStroke::default());
/// How much to pad the cells's content.
- pub const PADDING: Rel<Length> = Abs::pt(5.0).into();
+ ///
+ /// The default value is `{5pt}`.
+ pub const INSET: Rel<Length> = Abs::pt(5.0).into();
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
@@ -162,7 +154,7 @@ impl Layout for TableNode {
) -> SourceResult<Fragment> {
let fill = styles.get(Self::FILL);
let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
- let padding = styles.get(Self::PADDING);
+ let inset = styles.get(Self::INSET);
let align = styles.get(Self::ALIGN);
let cols = self.tracks.x.len().max(1);
@@ -172,7 +164,7 @@ impl Layout for TableNode {
.cloned()
.enumerate()
.map(|(i, child)| {
- let mut child = child.padded(Sides::splat(padding));
+ let mut child = child.padded(Sides::splat(inset));
let x = i % cols;
let y = i / cols;
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
index 5bff050b..8a772f2d 100644
--- a/library/src/layout/container.rs
+++ b/library/src/layout/container.rs
@@ -134,10 +134,14 @@ impl Inline for BoxNode {}
/// The spacing between this block and its predecessor. Takes precedence over
/// `spacing`.
///
+/// The default value is `{1.2em}`.
+///
/// - below: Spacing (named, settable)
/// The spacing between this block and its successor. Takes precedence
/// over `spacing`.
///
+/// The default value is `{1.2em}`.
+///
/// ## Category
/// layout
#[func]
diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs
index 00233cd1..a3ee5df7 100644
--- a/library/src/layout/grid.rs
+++ b/library/src/layout/grid.rs
@@ -7,28 +7,31 @@ use super::Spacing;
///
/// The grid element allows you to arrange content in a grid. You can define the
/// number of rows and columns, as well as the size of the gutters between them.
-/// There are multiple sizing modes for columns and rows, including fixed sizes,
-/// that can be used to create complex layouts.
+/// There are multiple sizing modes for columns and rows that can be used to
+/// create complex layouts.
///
/// The sizing of the grid is determined by the track sizes specified in the
/// arguments. Because each of the sizing parameters accepts the same values, we
-/// will explain them here. Each sizing argument accepts an array of track
-/// sizes. A track size is either:
+/// will explain them just once, here. Each sizing argument accepts an array of
+/// individual track sizes. A track size is either:
///
-/// - a fixed length (e.g. `{10pt}`). The track will be exactly this size.
-/// - `{auto}`. The track will be sized to fit its contents. It will be at most
+/// - `{auto}`: The track will be sized to fit its contents. It will be at most
/// as large as the remaining space. If there is more than one `{auto}` track
-/// which, together, claim more than the available space, the tracks will be
-/// resized to fit the available space.
-/// - a fractional length (e.g. `{1fr}`). Once all other tracks have been sized,
+/// which, and together they claim more than the available space, the `{auto}`
+/// tracks will fairly distribute the available space among themselves.
+///
+/// - A fixed or relative length (e.g. `{10pt}` or `{20% - 1cm}`): The track
+/// will be exactly of this size.
+///
+/// - A fractional length (e.g. `{1fr}`): Once all other tracks have been sized,
/// the remaining space will be divided among the fractional tracks according
-/// to their fraction. For example, if there are two fractional tracks, each
+/// to their fractions. For example, if there are two fractional tracks, each
/// with a fraction of `{1fr}`, they will each take up half of the remaining
/// space.
///
/// To specify a single track, the array can be omitted in favor of a single
/// value. To specify multiple `{auto}` tracks, enter the number of tracks
-/// instead of a value. For example, `columns:` `{3}` is equivalent to
+/// instead of an array. For example, `columns:` `{3}` is equivalent to
/// `columns:` `{(auto, auto, auto)}`.
///
/// ## Example
diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs
index a63a4974..01ae48c5 100644
--- a/library/src/layout/hide.rs
+++ b/library/src/layout/hide.rs
@@ -4,9 +4,9 @@ use crate::prelude::*;
/// Hide content without affecting layout.
///
/// The `hide` function allows you to hide content while the layout still 'sees'
-/// it. This is useful to create to create whitespace that is exactly as large
-/// as some content. It may also be useful to redact content because its
-/// arguments are not included in the output.
+/// it. This is useful to create whitespace that is exactly as large as some
+/// content. It may also be useful to redact content because its arguments are
+/// not included in the output.
///
/// ## Example
/// ```
diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs
index 43a7768e..c1ac6118 100644
--- a/library/src/layout/page.rs
+++ b/library/src/layout/page.rs
@@ -42,7 +42,7 @@ pub struct PageNode(pub Content);
#[node]
impl PageNode {
- /// The unflipped width of the page.
+ /// The width of the page.
///
/// # Example
/// ```
@@ -58,7 +58,7 @@ impl PageNode {
#[property(resolve)]
pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width().into());
- /// The unflipped height of the page.
+ /// The height of the page.
///
/// If this is set to `{auto}`, page breaks can only be triggered manually
/// by inserting a [page break](@pagebreak). Most examples throughout this
@@ -92,7 +92,6 @@ impl PageNode {
///
/// - A single length: The same margin on all sides.
/// - `{auto}`: The margin is set to the default value for the page's size.
- /// - `{none}`: The page will be stripped of its margins.
/// - A dictionary: With a dictionary, the margins can be set individually.
/// The dictionary can contain the following keys in order of precedence:
/// - `top`: The top margin.
@@ -101,7 +100,7 @@ impl PageNode {
/// - `left`: The left margin.
/// - `x`: The horizontal margins.
/// - `y`: The vertical margins.
- /// - `rest`: The margins on all sides except the sides for which the
+ /// - `rest`: The margins on all sides except those for which the
/// dictionary explicitly sets a size.
///
/// # Example
@@ -197,7 +196,7 @@ impl PageNode {
/// text(8pt, numbering("I", i))
/// )
/// )
- ///
+ ///
/// #lorem(18)
/// ```
#[property(referenced)]
diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs
index 39e040b3..3287df3a 100644
--- a/library/src/layout/stack.rs
+++ b/library/src/layout/stack.rs
@@ -13,7 +13,6 @@ use crate::prelude::*;
/// ```
/// #stack(
/// dir: ttb,
-///
/// rect(width: 40pt),
/// rect(width: 120pt),
/// rect(width: 90pt),
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs
index 451656ae..c843ad4e 100644
--- a/library/src/layout/transform.rs
+++ b/library/src/layout/transform.rs
@@ -11,17 +11,15 @@ use crate::prelude::*;
///
/// ## Example
/// ```
-/// #rect(
-/// move(
-/// dx: 6pt, dy: 6pt,
-/// rect(
-/// inset: 8pt,
-/// fill: white,
-/// stroke: black,
-/// [Abra cadabra]
-/// )
+/// #rect(inset: 0pt, move(
+/// dx: 6pt, dy: 6pt,
+/// rect(
+/// inset: 8pt,
+/// fill: white,
+/// stroke: black,
+/// [Abra cadabra]
/// )
-/// )
+/// ))
/// ```
///
/// ## Parameters
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index 4a25197d..3424cce6 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -242,7 +242,9 @@ impl TextNode {
///
/// # Example
/// ```
+ /// #set rect(inset: 0pt)
/// #set text(size: 20pt)
+ ///
/// #set text(top-edge: "ascender")
/// #rect(fill: aqua)[Typst]
///
@@ -256,7 +258,9 @@ impl TextNode {
///
/// # Example
/// ```
+ /// #set rect(inset: 0pt)
/// #set text(size: 20pt)
+ ///
/// #set text(bottom-edge: "baseline")
/// #rect(fill: aqua)[Typst]
///
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 3cf7e8e7..c6026f89 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -65,7 +65,7 @@ use crate::prelude::*;
/// // that retains the correct baseline.
/// #show raw.where(block: false): rect.with(
/// fill: luma(240),
-/// inset: (x: 3pt),
+/// inset: (x: 3pt, y: 0pt),
/// outset: (y: 3pt),
/// radius: 2pt,
/// )
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index d1ced9cd..9bf2bbab 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -3,72 +3,278 @@ use std::f64::consts::SQRT_2;
use crate::prelude::*;
/// # Rectangle
-/// A sizable and fillable shape with optional content.
+/// A rectangle with optional content.
+///
+/// ## Example
+/// ```
+/// // Without content.
+/// #rect(width: 35%, height: 30pt)
+///
+/// // With content.
+/// #rect[
+/// Automatically sized \
+/// to fit the content.
+/// ]
+/// ```
///
/// ## Parameters
/// - body: Content (positional)
-/// The content to place into the shape.
+/// The content to place into the rectangle.
+///
+/// When this is omitted, the rectangle takes on a default size of at most
+/// `{45pt}` by `{30pt}`.
///
/// - width: Rel<Length> (named)
-/// The shape's width.
+/// The rectangle's width, relative to its parent container.
///
/// - height: Rel<Length> (named)
-/// The shape's height.
-///
-/// - size: Length (named)
-/// The square's side length.
-///
-/// - radius: Length (named)
-/// The circle's radius.
-///
-/// - stroke: Smart<Sides<Option<PartialStroke>>> (named)
-/// How to stroke the shape.
+/// The rectangle's height, relative to its parent container.
///
/// ## Category
/// visualize
#[func]
#[capable(Layout, Inline)]
#[derive(Debug, Hash)]
-pub struct ShapeNode<const S: ShapeKind>(pub Option<Content>);
+pub struct RectNode(pub Option<Content>);
-/// A square with optional content.
-pub type SquareNode = ShapeNode<SQUARE>;
+#[node]
+impl RectNode {
+ /// How to fill the rectangle.
+ ///
+ /// When setting a fill, the default stroke disappears. To create a
+ /// rectangle with both fill and stroke, you have to configure both.
+ ///
+ /// # Example
+ /// ```
+ /// #rect(fill: blue)
+ /// ```
+ pub const FILL: Option<Paint> = None;
-/// A rectangle with optional content.
-pub type RectNode = ShapeNode<RECT>;
+ /// How to stroke the rectangle. This can be:
+ ///
+ /// - `{none}` to disable the stroke.
+ /// - `{auto}` for a stroke of `{1pt}` black if and if only if no fill is
+ /// given.
+ /// - A length specifying the stroke's thickness. The color is inherited,
+ /// defaulting to black.
+ /// - A color to use for the stroke. The thickness is inherited, defaulting
+ /// to `{1pt}`.
+ /// - A stroke combined from color and thickness using the `+` operator as
+ /// in `{2pt + red}`.
+ /// - A dictionary: With a dictionary, the stroke for each side can be set
+ /// individually. The dictionary can contain the following keys in order
+ /// of precedence:
+ /// - `top`: The top stroke.
+ /// - `right`: The right stroke.
+ /// - `bottom`: The bottom stroke.
+ /// - `left`: The left stroke.
+ /// - `x`: The horizontal stroke.
+ /// - `y`: The vertical stroke.
+ /// - `rest`: The stroke on all sides except those for which the
+ /// dictionary explicitly sets a size.
+ ///
+ /// # Example
+ /// ```
+ /// #stack(
+ /// dir: ltr,
+ /// spacing: 1fr,
+ /// rect(stroke: red),
+ /// rect(stroke: 2pt),
+ /// rect(stroke: 2pt + red),
+ /// )
+ /// ```
+ #[property(resolve, fold)]
+ pub const STROKE: Smart<Sides<Option<Option<PartialStroke>>>> = Smart::Auto;
+
+ /// How much to round the rectangle's corners, relative to the minimum of
+ /// the width and height divided by two. This can be:
+ ///
+ /// - A relative length for a uniform corner radius.
+ /// - A dictionary: With a dictionary, the stroke for each side can be set
+ /// individually. The dictionary can contain the following keys in order
+ /// of precedence:
+ /// - `top-left`: The top-left corner radius.
+ /// - `top-right`: The top-right corner radius.
+ /// - `bottom-right`: The bottom-right corner radius.
+ /// - `bottom-left`: The bottom-left corner radius.
+ /// - `left`: The top-left and bottom-left corner radii.
+ /// - `top`: The top-left and top-right corner radii.
+ /// - `right`: The top-right and bottom-right corner radii.
+ /// - `bottom`: The bottom-left and bottom-right corner radii.
+ /// - `rest`: The radii for all corners except those for which the
+ /// dictionary explicitly sets a size.
+ ///
+ /// # Example
+ /// ```
+ /// #set rect(stroke: 4pt)
+ /// #rect(
+ /// radius: (
+ /// left: 5pt,
+ /// top-right: 20pt,
+ /// bottom-right: 10pt,
+ /// ),
+ /// stroke: (
+ /// left: red,
+ /// top: yellow,
+ /// right: green,
+ /// bottom: blue,
+ /// ),
+ /// )
+ /// ```
+ #[property(resolve, fold)]
+ pub const RADIUS: Corners<Option<Rel<Length>>> = Corners::splat(Rel::zero());
-/// A circle with optional content.
-pub type CircleNode = ShapeNode<CIRCLE>;
+ /// How much to pad the rectangle's content.
+ ///
+ /// The default value is `{5pt}`.
+ ///
+ /// _Note:_ When the rectangle contains text, its exact size depends on the
+ /// current [text edges](@text/top-edge).
+ ///
+ /// # Example
+ /// ```
+ /// A #rect(inset: 0pt)[tight] fit.
+ /// ```
+ #[property(resolve, fold)]
+ pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Abs::pt(5.0).into());
+
+ /// How much to expand the rectangle's size without affecting the layout.
+ ///
+ /// This is, for instance, useful to prevent an inline rectangle from
+ /// affecting line layout. For a generalized version of the example below,
+ /// see the documentation for the [raw text's block parameter](@raw/block).
+ ///
+ /// # Example
+ /// ```
+ /// This
+ /// #rect(
+ /// fill: luma(235),
+ /// inset: (x: 3pt, y: 0pt),
+ /// outset: (y: 3pt),
+ /// radius: 2pt,
+ /// )[rectangle]
+ /// is inline.
+ /// ```
+ #[property(resolve, fold)]
+ pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
-/// A ellipse with optional content.
-pub type EllipseNode = ShapeNode<ELLIPSE>;
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ Ok(Self(args.eat()?).pack().boxed(Axes::new(width, height)))
+ }
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => match &self.0 {
+ Some(body) => Some(Value::Content(body.clone())),
+ None => Some(Value::None),
+ },
+ _ => None,
+ }
+ }
+}
+
+impl Layout for RectNode {
+ fn layout(
+ &self,
+ vt: &mut Vt,
+ styles: StyleChain,
+ regions: Regions,
+ ) -> SourceResult<Fragment> {
+ layout(
+ vt,
+ ShapeKind::Rect,
+ &self.0,
+ styles.get(Self::FILL),
+ styles.get(Self::STROKE),
+ styles.get(Self::INSET),
+ styles.get(Self::OUTSET),
+ styles.get(Self::RADIUS),
+ styles,
+ regions,
+ )
+ }
+}
+
+impl Inline for RectNode {}
+
+/// # Square
+/// A square with optional content.
+///
+/// ## Example
+/// ```
+/// // Without content.
+/// #square(size: 30pt)
+///
+/// // With content.
+/// #square[
+/// Automatically \
+/// sized to fit.
+/// ]
+/// ```
+///
+/// ## Parameters
+/// - body: Content (positional)
+/// The content to place into the square. The square expands to fit this
+/// content, keeping the 1-1 aspect ratio.
+///
+/// When this is omitted, the square takes on a default size of at most
+/// `{30pt}`.
+///
+/// - size: Length (named)
+/// The square's side length. This is mutually exclusive with `width` and
+/// `height`.
+///
+/// - width: Rel<Length> (named)
+/// The square's width. This is mutually exclusive with `size` and `height`.
+///
+/// In contrast to `size`, this can be relative to the parent container's
+/// width.
+///
+/// - height: Rel<Length> (named)
+/// The square's height. This is mutually exclusive with `size` and `width`.
+///
+/// In contrast to `size`, this can be relative to the parent container's
+/// height.
+///
+/// ## Category
+/// visualize
+#[func]
+#[capable(Layout, Inline)]
+#[derive(Debug, Hash)]
+pub struct SquareNode(pub Option<Content>);
#[node]
-impl<const S: ShapeKind> ShapeNode<S> {
- /// How to fill the shape.
+impl SquareNode {
+ /// How to fill the square. See the [rectangle's documentation](@rect/fill)
+ /// for more details.
pub const FILL: Option<Paint> = None;
- /// How to stroke the shape.
- #[property(skip, resolve, fold)]
- pub const STROKE: Smart<Sides<Option<PartialStroke>>> = Smart::Auto;
- /// How much to pad the shape's content.
- #[property(resolve, fold)]
- pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
- /// How much to extend the shape's dimensions beyond the allocated space.
+ /// How to stroke the square. See the [rectangle's
+ /// documentation](@rect/stroke) for more details.
#[property(resolve, fold)]
- pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
+ pub const STROKE: Smart<Sides<Option<Option<PartialStroke>>>> = Smart::Auto;
- /// How much to round the shape's corners.
- #[property(skip, resolve, fold)]
+ /// How much to round the square's corners. See the [rectangle's
+ /// documentation](@rect/radius) for more details.
+ #[property(resolve, fold)]
pub const RADIUS: Corners<Option<Rel<Length>>> = Corners::splat(Rel::zero());
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let size = match S {
- SQUARE => args.named::<Length>("size")?.map(Rel::from),
- CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Rel::from(r)),
- _ => None,
- };
+ /// How much to pad the square's content. See the [rectangle's
+ /// documentation](@rect/inset) for more details.
+ ///
+ /// The default value is `{5pt}`.
+ #[property(resolve, fold)]
+ pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Abs::pt(5.0).into());
+ /// How much to expand the square's size without affecting the layout. See
+ /// the [rectangle's documentation](@rect/outset) for more details.
+ #[property(resolve, fold)]
+ pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
+
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let size = args.named::<Length>("size")?.map(Rel::from);
let width = match size {
None => args.named("width")?,
size => size,
@@ -78,22 +284,108 @@ impl<const S: ShapeKind> ShapeNode<S> {
None => args.named("height")?,
size => size,
};
-
Ok(Self(args.eat()?).pack().boxed(Axes::new(width, height)))
}
- fn set(...) {
- if is_round(S) {
- styles.set_opt(
- Self::STROKE,
- args.named::<Smart<Option<PartialStroke>>>("stroke")?
- .map(|some| some.map(Sides::splat)),
- );
- } else {
- styles.set_opt(Self::STROKE, args.named("stroke")?);
- styles.set_opt(Self::RADIUS, args.named("radius")?);
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => match &self.0 {
+ Some(body) => Some(Value::Content(body.clone())),
+ None => Some(Value::None),
+ },
+ _ => None,
}
}
+}
+
+impl Layout for SquareNode {
+ fn layout(
+ &self,
+ vt: &mut Vt,
+ styles: StyleChain,
+ regions: Regions,
+ ) -> SourceResult<Fragment> {
+ layout(
+ vt,
+ ShapeKind::Square,
+ &self.0,
+ styles.get(Self::FILL),
+ styles.get(Self::STROKE),
+ styles.get(Self::INSET),
+ styles.get(Self::OUTSET),
+ styles.get(Self::RADIUS),
+ styles,
+ regions,
+ )
+ }
+}
+
+impl Inline for SquareNode {}
+
+/// # Ellipse
+/// An ellipse with optional content.
+///
+/// ## Example
+/// ```
+/// // Without content.
+/// #ellipse(width: 35%, height: 30pt)
+///
+/// // With content.
+/// #ellipse[
+/// #set align(center)
+/// Automatically sized \
+/// to fit the content.
+/// ]
+/// ```
+///
+/// ## Parameters
+/// - body: Content (positional)
+/// The content to place into the ellipse.
+///
+/// When this is omitted, the ellipse takes on a default size of at most
+/// `{45pt}` by `{30pt}`.
+///
+/// - width: Rel<Length> (named)
+/// The ellipse's width, relative to its parent container.
+///
+/// - height: Rel<Length> (named)
+/// The ellipse's height, relative to its parent container.
+///
+/// ## Category
+/// visualize
+#[func]
+#[capable(Layout, Inline)]
+#[derive(Debug, Hash)]
+pub struct EllipseNode(pub Option<Content>);
+
+#[node]
+impl EllipseNode {
+ /// How to fill the ellipse. See the [rectangle's documentation](@rect/fill)
+ /// for more details.
+ pub const FILL: Option<Paint> = None;
+
+ /// How to stroke the ellipse. See the [rectangle's
+ /// documentation](@rect/stroke) for more details.
+ #[property(resolve, fold)]
+ pub const STROKE: Smart<Option<PartialStroke>> = Smart::Auto;
+
+ /// How much to pad the ellipse's content. See the [rectangle's
+ /// documentation](@rect/inset) for more details.
+ ///
+ /// The default value is `{5pt}`.
+ #[property(resolve, fold)]
+ pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Abs::pt(5.0).into());
+
+ /// How much to expand the ellipse's size without affecting the layout. See
+ /// the [rectangle's documentation](@rect/outset) for more details.
+ #[property(resolve, fold)]
+ pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
+
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ Ok(Self(args.eat()?).pack().boxed(Axes::new(width, height)))
+ }
fn field(&self, name: &str) -> Option<Value> {
match name {
@@ -106,124 +398,261 @@ impl<const S: ShapeKind> ShapeNode<S> {
}
}
-impl<const S: ShapeKind> Layout for ShapeNode<S> {
+impl Layout for EllipseNode {
fn layout(
&self,
vt: &mut Vt,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let mut frame;
- if let Some(child) = &self.0 {
- let mut inset = styles.get(Self::INSET);
- if is_round(S) {
- inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
- }
+ layout(
+ vt,
+ ShapeKind::Ellipse,
+ &self.0,
+ styles.get(Self::FILL),
+ styles.get(Self::STROKE).map(Sides::splat),
+ styles.get(Self::INSET),
+ styles.get(Self::OUTSET),
+ Corners::splat(Rel::zero()),
+ styles,
+ regions,
+ )
+ }
+}
- // Pad the child.
- let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
+impl Inline for EllipseNode {}
- let mut pod = Regions::one(regions.first, regions.base, regions.expand);
- frame = child.layout(vt, styles, pod)?.into_frame();
+/// # Circle
+/// A circle with optional content.
+///
+/// ## Example
+/// ```
+/// // Without content.
+/// #circle(radius: 15pt)
+///
+/// // With content.
+/// #circle[
+/// #set align(center + horizon)
+/// Automatically \
+/// sized to fit.
+/// ]
+/// ```
+///
+/// ## Parameters
+/// - body: Content (positional)
+/// The content to place into the circle. The circle expands to fit this
+/// content, keeping the 1-1 aspect ratio.
+///
+/// - radius: Length (named)
+/// The circle's radius. This is mutually exclusive with `width` and
+/// `height`.
+///
+/// - width: Rel<Length> (named)
+/// The circle's width. This is mutually exclusive with `radius` and `height`.
+///
+/// In contrast to `size`, this can be relative to the parent container's
+/// width.
+///
+/// - height: Rel<Length> (named)
+/// The circle's height.This is mutually exclusive with `radius` and `width`.
+///
+/// In contrast to `size`, this can be relative to the parent container's
+/// height.
+///
+/// ## Category
+/// visualize
+#[func]
+#[capable(Layout, Inline)]
+#[derive(Debug, Hash)]
+pub struct CircleNode(pub Option<Content>);
- // Relayout with full expansion into square region to make sure
- // the result is really a square or circle.
- if is_quadratic(S) {
- let length = if regions.expand.x || regions.expand.y {
- let target = regions.expand.select(regions.first, Size::zero());
- target.x.max(target.y)
- } else {
- let size = frame.size();
- let desired = size.x.max(size.y);
- desired.min(regions.first.x).min(regions.first.y)
- };
-
- pod.first = Size::splat(length);
- pod.expand = Axes::splat(true);
- frame = child.layout(vt, styles, pod)?.into_frame();
- }
- } else {
- // The default size that a shape takes on if it has no child and
- // enough space.
- let mut size = Size::new(Abs::pt(45.0), Abs::pt(30.0)).min(regions.first);
-
- if is_quadratic(S) {
- let length = if regions.expand.x || regions.expand.y {
- let target = regions.expand.select(regions.first, Size::zero());
- target.x.max(target.y)
- } else {
- size.x.min(size.y)
- };
- size = Size::splat(length);
- } else {
- size = regions.expand.select(regions.first, size);
- }
+#[node]
+impl CircleNode {
+ /// How to fill the circle. See the [rectangle's documentation](@rect/fill)
+ /// for more details.
+ pub const FILL: Option<Paint> = None;
- frame = Frame::new(size);
- }
+ /// How to stroke the circle. See the [rectangle's
+ /// documentation](@rect/stroke) for more details.
+ #[property(resolve, fold)]
+ pub const STROKE: Smart<Option<PartialStroke>> = Smart::Auto;
- // Add fill and/or stroke.
- let fill = styles.get(Self::FILL);
- let stroke = match styles.get(Self::STROKE) {
- Smart::Auto if fill.is_none() => Sides::splat(Some(Stroke::default())),
- Smart::Auto => Sides::splat(None),
- Smart::Custom(strokes) => {
- strokes.map(|s| s.map(PartialStroke::unwrap_or_default))
- }
- };
+ /// How much to pad the circle's content. See the [rectangle's
+ /// documentation](@rect/inset) for more details.
+ ///
+ /// The default value is `{5pt}`.
+ #[property(resolve, fold)]
+ pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Abs::pt(5.0).into());
- let outset = styles.get(Self::OUTSET).relative_to(frame.size());
- let size = frame.size() + outset.sum_by_axis();
+ /// How much to expand the circle's size without affecting the layout. See
+ /// the [rectangle's documentation](@rect/outset) for more details.
+ #[property(resolve, fold)]
+ pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
- let radius = styles
- .get(Self::RADIUS)
- .map(|side| side.relative_to(size.x.min(size.y) / 2.0));
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let size = args.named::<Length>("radius")?.map(|r| 2.0 * Rel::from(r));
+ let width = match size {
+ None => args.named("width")?,
+ size => size,
+ };
- let pos = Point::new(-outset.left, -outset.top);
+ let height = match size {
+ None => args.named("height")?,
+ size => size,
+ };
+ Ok(Self(args.eat()?).pack().boxed(Axes::new(width, height)))
+ }
- if fill.is_some() || stroke.iter().any(Option::is_some) {
- if is_round(S) {
- let shape = ellipse(size, fill, stroke.left);
- frame.prepend(pos, Element::Shape(shape));
- } else {
- frame.prepend_multiple(
- rounded_rect(size, radius, fill, stroke)
- .into_iter()
- .map(|x| (pos, Element::Shape(x))),
- )
- }
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "body" => match &self.0 {
+ Some(body) => Some(Value::Content(body.clone())),
+ None => Some(Value::None),
+ },
+ _ => None,
}
+ }
+}
- // Apply metadata.
- frame.meta(styles);
-
- Ok(Fragment::frame(frame))
+impl Layout for CircleNode {
+ fn layout(
+ &self,
+ vt: &mut Vt,
+ styles: StyleChain,
+ regions: Regions,
+ ) -> SourceResult<Fragment> {
+ layout(
+ vt,
+ ShapeKind::Circle,
+ &self.0,
+ styles.get(Self::FILL),
+ styles.get(Self::STROKE).map(Sides::splat),
+ styles.get(Self::INSET),
+ styles.get(Self::OUTSET),
+ Corners::splat(Rel::zero()),
+ styles,
+ regions,
+ )
}
}
-impl<const S: ShapeKind> Inline for ShapeNode<S> {}
+impl Inline for CircleNode {}
+
+/// Layout a shape.
+fn layout(
+ vt: &mut Vt,
+ kind: ShapeKind,
+ body: &Option<Content>,
+ fill: Option<Paint>,
+ stroke: Smart<Sides<Option<PartialStroke<Abs>>>>,
+ mut inset: Sides<Rel<Abs>>,
+ outset: Sides<Rel<Abs>>,
+ radius: Corners<Rel<Abs>>,
+ styles: StyleChain,
+ regions: Regions,
+) -> SourceResult<Fragment> {
+ let mut frame;
+ if let Some(child) = body {
+ if kind.is_round() {
+ inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
+ }
-/// A category of shape.
-pub type ShapeKind = usize;
+ // Pad the child.
+ let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
+
+ let mut pod = Regions::one(regions.first, regions.base, regions.expand);
+ frame = child.layout(vt, styles, pod)?.into_frame();
+
+ // Relayout with full expansion into square region to make sure
+ // the result is really a square or circle.
+ if kind.is_quadratic() {
+ let length = if regions.expand.x || regions.expand.y {
+ let target = regions.expand.select(regions.first, Size::zero());
+ target.x.max(target.y)
+ } else {
+ let size = frame.size();
+ let desired = size.x.max(size.y);
+ desired.min(regions.first.x).min(regions.first.y)
+ };
+
+ pod.first = Size::splat(length);
+ pod.expand = Axes::splat(true);
+ frame = child.layout(vt, styles, pod)?.into_frame();
+ }
+ } else {
+ // The default size that a shape takes on if it has no child and
+ // enough space.
+ let mut size = Size::new(Abs::pt(45.0), Abs::pt(30.0)).min(regions.first);
+
+ if kind.is_quadratic() {
+ let length = if regions.expand.x || regions.expand.y {
+ let target = regions.expand.select(regions.first, Size::zero());
+ target.x.max(target.y)
+ } else {
+ size.x.min(size.y)
+ };
+ size = Size::splat(length);
+ } else {
+ size = regions.expand.select(regions.first, size);
+ }
+
+ frame = Frame::new(size);
+ }
-/// A rectangle with equal side lengths.
-const SQUARE: ShapeKind = 0;
+ // Add fill and/or stroke.
+ let stroke = match stroke {
+ Smart::Auto if fill.is_none() => Sides::splat(Some(Stroke::default())),
+ Smart::Auto => Sides::splat(None),
+ Smart::Custom(strokes) => {
+ strokes.map(|s| s.map(PartialStroke::unwrap_or_default))
+ }
+ };
-/// A quadrilateral with four right angles.
-const RECT: ShapeKind = 1;
+ let outset = outset.relative_to(frame.size());
+ let size = frame.size() + outset.sum_by_axis();
+ let radius = radius.map(|side| side.relative_to(size.x.min(size.y) / 2.0));
+ let pos = Point::new(-outset.left, -outset.top);
-/// An ellipse with coinciding foci.
-const CIRCLE: ShapeKind = 2;
+ if fill.is_some() || stroke.iter().any(Option::is_some) {
+ if kind.is_round() {
+ let shape = ellipse(size, fill, stroke.left);
+ frame.prepend(pos, Element::Shape(shape));
+ } else {
+ frame.prepend_multiple(
+ rounded_rect(size, radius, fill, stroke)
+ .into_iter()
+ .map(|x| (pos, Element::Shape(x))),
+ )
+ }
+ }
-/// A curve around two focal points.
-const ELLIPSE: ShapeKind = 3;
+ // Apply metadata.
+ frame.meta(styles);
-/// Whether a shape kind is curvy.
-fn is_round(kind: ShapeKind) -> bool {
- matches!(kind, CIRCLE | ELLIPSE)
+ Ok(Fragment::frame(frame))
}
-/// Whether a shape kind has equal side length.
-fn is_quadratic(kind: ShapeKind) -> bool {
- matches!(kind, SQUARE | CIRCLE)
+/// A category of shape.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum ShapeKind {
+ /// A rectangle with equal side lengths.
+ Square,
+ /// A quadrilateral with four right angles.
+ Rect,
+ /// An ellipse with coinciding foci.
+ Circle,
+ /// A curve around two focal points.
+ Ellipse,
+}
+
+impl ShapeKind {
+ /// Whether this shape kind is curvy.
+ fn is_round(self) -> bool {
+ matches!(self, Self::Circle | Self::Ellipse)
+ }
+
+ /// Whether this shape kind has equal side length.
+ fn is_quadratic(self) -> bool {
+ matches!(self, Self::Square | Self::Circle)
+ }
}
diff --git a/src/model/cast.rs b/src/model/cast.rs
index 1a5cae45..17ed2d30 100644
--- a/src/model/cast.rs
+++ b/src/model/cast.rs
@@ -415,9 +415,9 @@ impl<T: Cast> Cast for Smart<T> {
}
}
-impl<T> Cast for Sides<T>
+impl<T> Cast for Sides<Option<T>>
where
- T: Cast + Default + Copy,
+ T: Cast + Copy,
{
fn is(value: &Value) -> bool {
matches!(value, Value::Dict(_)) || T::is(value)
@@ -439,9 +439,9 @@ where
dict.finish(&["left", "top", "right", "bottom", "x", "y", "rest"])?;
- Ok(sides.map(Option::unwrap_or_default))
+ Ok(sides)
} else if T::is(&value) {
- Ok(Self::splat(T::cast(value)?))
+ Ok(Self::splat(Some(T::cast(value)?)))
} else {
<Self as Cast>::error(value)
}
@@ -452,9 +452,9 @@ where
}
}
-impl<T> Cast for Corners<T>
+impl<T> Cast for Corners<Option<T>>
where
- T: Cast + Default + Copy,
+ T: Cast + Copy,
{
fn is(value: &Value) -> bool {
matches!(value, Value::Dict(_)) || T::is(value)
@@ -488,9 +488,9 @@ where
"rest",
])?;
- Ok(corners.map(Option::unwrap_or_default))
+ Ok(corners)
} else if T::is(&value) {
- Ok(Self::splat(T::cast(value)?))
+ Ok(Self::splat(Some(T::cast(value)?)))
} else {
<Self as Cast>::error(value)
}
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 1eaf5128..e78d83cd 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -914,67 +914,79 @@ where
}
}
-impl Fold for Axes<Option<GenAlign>> {
- type Output = Axes<GenAlign>;
+impl<T> Fold for Axes<Option<T>>
+where
+ T: Fold,
+{
+ type Output = Axes<T::Output>;
fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
+ self.zip(outer).map(|(inner, outer)| match inner {
+ Some(value) => value.fold(outer),
+ None => outer,
+ })
}
}
-impl<T> Fold for Sides<T>
+impl<T> Fold for Sides<Option<T>>
where
T: Fold,
{
type Output = Sides<T::Output>;
fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.fold(outer))
+ self.zip(outer).map(|(inner, outer)| match inner {
+ Some(value) => value.fold(outer),
+ None => outer,
+ })
}
}
-impl Fold for Sides<Option<Rel<Abs>>> {
- type Output = Sides<Rel<Abs>>;
+impl<T> Fold for Corners<Option<T>>
+where
+ T: Fold,
+{
+ type Output = Corners<T::Output>;
fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
+ self.zip(outer).map(|(inner, outer)| match inner {
+ Some(value) => value.fold(outer),
+ None => outer,
+ })
}
}
-impl Fold for Sides<Option<Smart<Rel<Length>>>> {
- type Output = Sides<Smart<Rel<Length>>>;
+impl Fold for PartialStroke<Abs> {
+ type Output = Self;
fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
+ Self {
+ paint: self.paint.or(outer.paint),
+ thickness: self.thickness.or(outer.thickness),
+ }
}
}
-impl<T> Fold for Corners<T>
-where
- T: Fold,
-{
- type Output = Corners<T::Output>;
+impl Fold for Rel<Length> {
+ type Output = Self;
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.fold(outer))
+ fn fold(self, _: Self::Output) -> Self::Output {
+ self
}
}
-impl Fold for Corners<Option<Rel<Abs>>> {
- type Output = Corners<Rel<Abs>>;
+impl Fold for Rel<Abs> {
+ type Output = Self;
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
+ fn fold(self, _: Self::Output) -> Self::Output {
+ self
}
}
-impl Fold for PartialStroke<Abs> {
+impl Fold for GenAlign {
type Output = Self;
- fn fold(self, outer: Self::Output) -> Self::Output {
- Self {
- paint: self.paint.or(outer.paint),
- thickness: self.thickness.or(outer.thickness),
- }
+ fn fold(self, _: Self::Output) -> Self::Output {
+ self
}
}
diff --git a/tests/ref/compiler/show-selector.png b/tests/ref/compiler/show-selector.png
index 1cdcfa3f..799fcf93 100644
--- a/tests/ref/compiler/show-selector.png
+++ b/tests/ref/compiler/show-selector.png
Binary files differ
diff --git a/tests/ref/visualize/shape-aspect.png b/tests/ref/visualize/shape-aspect.png
index a8e458fb..46cb12ad 100644
--- a/tests/ref/visualize/shape-aspect.png
+++ b/tests/ref/visualize/shape-aspect.png
Binary files differ
diff --git a/tests/ref/visualize/shape-circle.png b/tests/ref/visualize/shape-circle.png
index 6cbbc0e3..acb1d02a 100644
--- a/tests/ref/visualize/shape-circle.png
+++ b/tests/ref/visualize/shape-circle.png
Binary files differ
diff --git a/tests/ref/visualize/shape-rect.png b/tests/ref/visualize/shape-rect.png
index b3311ba5..5eeab37f 100644
--- a/tests/ref/visualize/shape-rect.png
+++ b/tests/ref/visualize/shape-rect.png
Binary files differ
diff --git a/tests/ref/visualize/shape-square.png b/tests/ref/visualize/shape-square.png
index 1f5303b9..3d81f2ed 100644
--- a/tests/ref/visualize/shape-square.png
+++ b/tests/ref/visualize/shape-square.png
Binary files differ
diff --git a/tests/typ/basics/desc.typ b/tests/typ/basics/desc.typ
index 8147b400..a638098b 100644
--- a/tests/typ/basics/desc.typ
+++ b/tests/typ/basics/desc.typ
@@ -39,7 +39,7 @@ No: list \
// Test grid like show rule.
#show desc: it => table(
columns: 2,
- padding: 3pt,
+ inset: 3pt,
..it.items.map(item => (emph(item(0)), item(1))).flatten(),
)
diff --git a/tests/typ/compiler/methods-color.typ b/tests/typ/compiler/methods-color.typ
index 1188030a..3b165d14 100644
--- a/tests/typ/compiler/methods-color.typ
+++ b/tests/typ/compiler/methods-color.typ
@@ -15,8 +15,8 @@
#rect(width: 1cm, fill: c.negate())
#for x in range(0, 11) {
- square(width: 9pt, fill: c.lighten(x * 10%))
+ square(size: 9pt, fill: c.lighten(x * 10%))
}
#for x in range(0, 11) {
- square(width: 9pt, fill: c.darken(x * 10%))
+ square(size: 9pt, fill: c.darken(x * 10%))
}
diff --git a/tests/typ/compiler/show-selector.typ b/tests/typ/compiler/show-selector.typ
index 0e9823a5..56e82769 100644
--- a/tests/typ/compiler/show-selector.typ
+++ b/tests/typ/compiler/show-selector.typ
@@ -5,7 +5,7 @@
#show raw.where(block: false): rect.with(
radius: 2pt,
outset: (y: 3pt),
- inset: (x: 3pt),
+ inset: (x: 3pt, y: 0pt),
fill: luma(230),
)
@@ -27,6 +27,9 @@ with selectors and justification.
code!("it");
```
+You can use the ```rs *const T``` pointer or
+the ```rs &mut T``` reference.
+
---
#show heading.where(level: 1): set text(red)
#show heading.where(level: 2): set text(blue)
diff --git a/tests/typ/layout/grid-1.typ b/tests/typ/layout/grid-1.typ
index dcc9b474..411fd8d0 100644
--- a/tests/typ/layout/grid-1.typ
+++ b/tests/typ/layout/grid-1.typ
@@ -2,7 +2,6 @@
---
#let cell(width, color) = rect(width: width, height: 2cm, fill: color)
-
#set page(width: 100pt, height: 140pt)
#grid(
columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%),
@@ -21,6 +20,7 @@
)
---
+#set rect(inset: 0pt)
#grid(
columns: (auto, auto, 40%),
column-gutter: 1fr,
diff --git a/tests/typ/layout/grid-3.typ b/tests/typ/layout/grid-3.typ
index c55f22a6..8194da73 100644
--- a/tests/typ/layout/grid-3.typ
+++ b/tests/typ/layout/grid-3.typ
@@ -24,7 +24,7 @@
row-gutter: 10pt,
column-gutter: (0pt, 10%),
align(top, image("/res/rhino.png")),
- align(top, rect(fill: eastern, align(right)[LoL])),
+ align(top, rect(inset: 0pt, fill: eastern, align(right)[LoL])),
[rofl],
[\ A] * 3,
[Ha!\ ] * 3,
@@ -55,7 +55,7 @@
column-gutter: (0pt, 10%),
[A], [B], [C], [D],
grid(columns: 2, [A], [B], [C\ ]*3, [D]),
- align(top, rect(fill: eastern, align(right)[LoL])),
+ align(top, rect(inset: 0pt, fill: eastern, align(right)[LoL])),
[rofl],
[E\ ]*4,
)
diff --git a/tests/typ/layout/pad.typ b/tests/typ/layout/pad.typ
index 9791eae4..4f481502 100644
--- a/tests/typ/layout/pad.typ
+++ b/tests/typ/layout/pad.typ
@@ -5,6 +5,7 @@
#pad(left: 10pt, [Indented!])
// All sides together.
+#set rect(inset: 0pt)
#rect(fill: conifer,
pad(10pt, right: 20pt,
rect(width: 20pt, height: 20pt, fill: rgb("eb5278"))
diff --git a/tests/typ/layout/par-knuth.typ b/tests/typ/layout/par-knuth.typ
index 59349738..dbe13fc4 100644
--- a/tests/typ/layout/par-knuth.typ
+++ b/tests/typ/layout/par-knuth.typ
@@ -14,7 +14,7 @@
]
#let column(title, linebreaks, hyphenate) = {
- rect(width: 132pt, fill: rgb("eee"))[
+ rect(inset: 0pt, width: 132pt, fill: rgb("eee"))[
#set par(linebreaks: linebreaks)
#set text(hyphenate: hyphenate)
#strong(title) \ #story
diff --git a/tests/typ/layout/stack-2.typ b/tests/typ/layout/stack-2.typ
index f88f7a58..a06950f1 100644
--- a/tests/typ/layout/stack-2.typ
+++ b/tests/typ/layout/stack-2.typ
@@ -19,6 +19,5 @@ World! 🌍
#set text(white)
#rect(fill: forest)[
#v(1fr)
- #h(1fr) Hi you! #h(5pt)
- #v(5pt)
+ #h(1fr) Hi you!
]
diff --git a/tests/typ/math/syntax.typ b/tests/typ/math/syntax.typ
index 4500d91e..72b4b7c2 100644
--- a/tests/typ/math/syntax.typ
+++ b/tests/typ/math/syntax.typ
@@ -2,7 +2,7 @@
#set text("Latin Modern Roman")
#show <table>: it => table(
columns: 2,
- padding: 8pt,
+ inset: 8pt,
..it.text
.split("\n")
.map(line => (text(10pt, raw(line, lang: "typ")), eval(line) + [ ]))
diff --git a/tests/typ/text/edge.typ b/tests/typ/text/edge.typ
index 26be4aeb..802c0ddb 100644
--- a/tests/typ/text/edge.typ
+++ b/tests/typ/text/edge.typ
@@ -4,7 +4,7 @@
#set page(width: 160pt)
#set text(size: 8pt)
-#let try(top, bottom) = rect(fill: conifer)[
+#let try(top, bottom) = rect(inset: 0pt, fill: conifer)[
#set text("IBM Plex Mono", top-edge: top, bottom-edge: bottom)
From #top to #bottom
]
diff --git a/tests/typ/text/microtype.typ b/tests/typ/text/microtype.typ
index c1f0d344..69b50500 100644
--- a/tests/typ/text/microtype.typ
+++ b/tests/typ/text/microtype.typ
@@ -5,7 +5,7 @@
#set page(width: 130pt, margin: 15pt)
#set par(justify: true, linebreaks: "simple")
#set text(size: 9pt)
-#rect(fill: rgb(0, 0, 0, 0), width: 100%)[
+#rect(inset: 0pt, fill: rgb(0, 0, 0, 0), width: 100%)[
This is a little bit of text that builds up to
hang-ing hyphens and dash---es and then, you know,
some punctuation in the margin.
diff --git a/tests/typ/text/shift.typ b/tests/typ/text/shift.typ
index 01a83f6e..6fe31bec 100644
--- a/tests/typ/text/shift.typ
+++ b/tests/typ/text/shift.typ
@@ -4,8 +4,8 @@
#table(
columns: 3,
[Typo.], [Fallb.], [Synth],
- [x#super[1]], [x#super[5n]], [x#super[2 #square(width: 6pt)]],
- [x#sub[1]], [x#sub[5n]], [x#sub[2 #square(width: 6pt)]],
+ [x#super[1]], [x#super[5n]], [x#super[2 #square(size: 6pt)]],
+ [x#sub[1]], [x#sub[5n]], [x#sub[2 #square(size: 6pt)]],
)
---
diff --git a/tests/typ/visualize/shape-aspect.typ b/tests/typ/visualize/shape-aspect.typ
index f2dd9b51..d2dc1140 100644
--- a/tests/typ/visualize/shape-aspect.typ
+++ b/tests/typ/visualize/shape-aspect.typ
@@ -4,9 +4,13 @@
// Test relative width and height and size that is smaller
// than default size.
#set page(width: 120pt, height: 70pt)
-#square(width: 50%, align(bottom)[A])
+#set align(center + horizon)
+#square(width: 50%, [A])
#square(height: 50%)
-#box(stack(square(size: 10pt), 5pt, square(size: 10pt, [B])))
+#box(stack(
+ square(size: 10pt),
+ square(size: 20pt, [B])
+))
---
// Test alignment in automatically sized square and circle.
@@ -37,7 +41,7 @@
// Test square that is overflowing due to its aspect ratio.
#set page(width: 40pt, height: 20pt, margin: 5pt)
#square(width: 100%) #parbreak()
-#square(width: 100%)[Hello]
+#square(width: 100%)[Hey]
---
// Size cannot be relative because we wouldn't know
diff --git a/tests/typ/visualize/shape-circle.typ b/tests/typ/visualize/shape-circle.typ
index 13ff67de..23c6fcbd 100644
--- a/tests/typ/visualize/shape-circle.typ
+++ b/tests/typ/visualize/shape-circle.typ
@@ -7,6 +7,7 @@
---
// Test auto sizing.
+#set circle(inset: 0pt)
Auto-sized circle. \
#circle(fill: rgb("eb5278"), stroke: 2pt + black,
@@ -33,13 +34,14 @@ Expanded by height.
---
// Ensure circle directly in rect works.
-#rect(width: 40pt, height: 30pt, fill: forest, circle(fill: conifer))
+#rect(width: 40pt, height: 30pt, fill: forest,
+ circle(fill: conifer))
---
// Test relative sizing.
#let centered(body) = align(center + horizon, body)
#set text(fill: white)
-#rect(width: 100pt, height: 50pt, fill: rgb("aaa"), centered[
+#rect(width: 100pt, height: 50pt, inset: 0pt, fill: rgb("aaa"), centered[
#circle(radius: 10pt, fill: eastern, centered[A]) // D=20pt
#circle(height: 60%, fill: eastern, centered[B]) // D=30pt
#circle(width: 20% + 20pt, fill: eastern, centered[C]) // D=40pt
diff --git a/tests/typ/visualize/shape-ellipse.typ b/tests/typ/visualize/shape-ellipse.typ
index ba4d0d0a..6447d1d0 100644
--- a/tests/typ/visualize/shape-ellipse.typ
+++ b/tests/typ/visualize/shape-ellipse.typ
@@ -5,6 +5,9 @@
#ellipse()
---
+#set rect(inset: 0pt)
+#set ellipse(inset: 0pt)
+
Rect in ellipse in fixed rect. \
#rect(width: 3cm, height: 2cm, fill: rgb("2a631a"),
ellipse(fill: forest, width: 100%, height: 100%,
@@ -23,4 +26,4 @@ Auto-sized ellipse. \
]
-An inline #ellipse(width: 8pt, height: 6pt, outset: (top: 3pt, rest: 5.5pt)) ellipse. \ No newline at end of file
+An inline #ellipse(width: 8pt, height: 6pt, outset: (top: 3pt, rest: 5.5pt)) ellipse.
diff --git a/tests/typ/visualize/shape-rect.typ b/tests/typ/visualize/shape-rect.typ
index 9a115e54..c8518bbd 100644
--- a/tests/typ/visualize/shape-rect.typ
+++ b/tests/typ/visualize/shape-rect.typ
@@ -8,7 +8,7 @@
#set page(width: 150pt)
// Fit to text.
-#rect(fill: conifer, inset: 3pt)[Textbox]
+#rect(fill: conifer)[Textbox]
// Empty with fixed width and height.
#block(rect(
@@ -18,7 +18,7 @@
))
// Fixed width, text height.
-#rect(width: 2cm, fill: rgb("9650d6"), inset: 5pt)[Fixed and padded]
+#rect(width: 2cm, fill: rgb("9650d6"))[Fixed and padded]
// Page width, fixed height.
#rect(height: 1cm, width: 100%, fill: rgb("734ced"))[Topleft]
@@ -45,18 +45,6 @@
]
---
-// Outset padding.
-#set raw(lang: "rust")
-#show raw: it => [
- #set text(8pt)
- #h(5.6pt, weak: true)
- #rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), it)
- #h(5.6pt, weak: true)
-]
-
-Use the `*const T` pointer or the `&mut T` reference.
-
----
// Error: 15-38 unexpected key "cake", valid keys are "top-left", "top-right", "bottom-right", "bottom-left", "left", "top", "right", "bottom", and "rest"
#rect(radius: (left: 10pt, cake: 5pt))
diff --git a/tests/typ/visualize/shape-square.typ b/tests/typ/visualize/shape-square.typ
index 622fa9c8..8ac9001f 100644
--- a/tests/typ/visualize/shape-square.typ
+++ b/tests/typ/visualize/shape-square.typ
@@ -7,7 +7,7 @@
---
// Test auto-sized square.
-#square(fill: eastern, inset: 5pt)[
+#square(fill: eastern)[
#set text(fill: white, weight: "bold")
Typst
]