Split up and document shapes
@ -6,25 +6,25 @@ use crate::prelude::*;
|
|||||||
///
|
///
|
||||||
/// Tables are used to arrange content in cells. Cells can contain arbitray
|
/// Tables are used to arrange content in cells. Cells can contain arbitray
|
||||||
/// content, including multiple paragraphs and are specified in row-major order.
|
/// content, including multiple paragraphs and are specified in row-major order.
|
||||||
/// Because tables are [grids](@grid) with configurable cell strokes and
|
/// Because tables are just grids with configurable cell properties, refer to
|
||||||
/// padding, refer to the [grid](@grid) docs for more information on how to size
|
/// the [grid documentation](@grid) for more information on how to size the
|
||||||
/// the table tracks.
|
/// table tracks.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
/// #table(
|
/// #table(
|
||||||
/// columns: (1fr, auto, auto),
|
/// columns: (1fr, auto, auto),
|
||||||
/// padding: 10pt,
|
/// inset: 10pt,
|
||||||
/// align: horizon,
|
/// align: horizon,
|
||||||
/// [], [*Area*], [*Parameters*],
|
/// [], [*Area*], [*Parameters*],
|
||||||
/// image("cylinder.svg", fit: "contain"),
|
/// image("cylinder.svg"),
|
||||||
/// $ pi h (D^2 - d^2) / 4 $,
|
/// $ pi h (D^2 - d^2) / 4 $,
|
||||||
/// [
|
/// [
|
||||||
/// $h$: height \
|
/// $h$: height \
|
||||||
/// $D$: outer radius \
|
/// $D$: outer radius \
|
||||||
/// $d$: inner radius
|
/// $d$: inner radius
|
||||||
/// ],
|
/// ],
|
||||||
/// image("tetrahedron.svg", fit: "contain"),
|
/// image("tetrahedron.svg"),
|
||||||
/// $ sqrt(2) / 12 a^3 $,
|
/// $ sqrt(2) / 12 a^3 $,
|
||||||
/// [$a$: edge length]
|
/// [$a$: edge length]
|
||||||
/// )
|
/// )
|
||||||
@ -36,33 +36,23 @@ use crate::prelude::*;
|
|||||||
///
|
///
|
||||||
/// - rows: TrackSizings (named)
|
/// - rows: TrackSizings (named)
|
||||||
/// Defines the row sizes.
|
/// Defines the row sizes.
|
||||||
///
|
/// See the [grid documentation](@grid) for more information on track sizing.
|
||||||
/// See [the respective `grid` argument](@grid/rows) for more information
|
|
||||||
/// on sizing tracks.
|
|
||||||
///
|
///
|
||||||
/// - columns: TrackSizings (named)
|
/// - columns: TrackSizings (named)
|
||||||
/// Defines the column sizes.
|
/// Defines the column sizes.
|
||||||
///
|
/// See the [grid documentation](@grid) for more information on track sizing.
|
||||||
/// See [the respective `grid` argument](@grid/columns) for more information
|
|
||||||
/// on sizing tracks.
|
|
||||||
///
|
///
|
||||||
/// - gutter: TrackSizings (named)
|
/// - gutter: TrackSizings (named)
|
||||||
/// Defines the gaps between rows & columns.
|
/// Defines the gaps between rows & columns.
|
||||||
///
|
/// See the [grid documentation](@grid) for more information on gutters.
|
||||||
/// See [the respective `grid` argument](@grid/gutter) for more information
|
|
||||||
/// on gutter.
|
|
||||||
///
|
///
|
||||||
/// - column-gutter: TrackSizings (named)
|
/// - column-gutter: TrackSizings (named)
|
||||||
/// Defines the gaps between columns. Takes precedence over `gutter`.
|
/// Defines the gaps between columns. Takes precedence over `gutter`.
|
||||||
///
|
/// See the [grid documentation](@grid) for more information on gutters.
|
||||||
/// See [the respective `grid` argument](@grid/column-gutter) for more information
|
|
||||||
/// on gutter.
|
|
||||||
///
|
///
|
||||||
/// - row-gutter: TrackSizings (named)
|
/// - row-gutter: TrackSizings (named)
|
||||||
/// Defines the gaps between rows. Takes precedence over `gutter`.
|
/// Defines the gaps between rows. Takes precedence over `gutter`.
|
||||||
///
|
/// See the [grid documentation](@grid) for more information on gutters.
|
||||||
/// See [the respective `grid` argument](@grid/row-gutter) for more information
|
|
||||||
/// on gutter.
|
|
||||||
///
|
///
|
||||||
/// ## Category
|
/// ## Category
|
||||||
/// basics
|
/// basics
|
||||||
@ -82,14 +72,14 @@ pub struct TableNode {
|
|||||||
impl TableNode {
|
impl TableNode {
|
||||||
/// How to fill the cells.
|
/// How to fill the cells.
|
||||||
///
|
///
|
||||||
/// This can either be a color or a function that returns a color. The
|
/// This can be a color or a function that returns a color. The function is
|
||||||
/// function is passed the cell's column and row index, starting at zero.
|
/// passed the cell's column and row index, starting at zero. This can be
|
||||||
/// This can be used to implement striped tables.
|
/// used to implement striped tables.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// #table(
|
/// #table(
|
||||||
/// fill: (col, _) => if odd(col) { luma(240) } else { luma(255) },
|
/// fill: (col, _) => if odd(col) { luma(240) } else { white },
|
||||||
/// align: (col, row) =>
|
/// align: (col, row) =>
|
||||||
/// if row == 0 { center }
|
/// if row == 0 { center }
|
||||||
/// else if col == 0 { left }
|
/// else if col == 0 { left }
|
||||||
@ -120,7 +110,9 @@ impl TableNode {
|
|||||||
pub const STROKE: Option<PartialStroke> = Some(PartialStroke::default());
|
pub const STROKE: Option<PartialStroke> = Some(PartialStroke::default());
|
||||||
|
|
||||||
/// How much to pad the cells's content.
|
/// 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> {
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
|
let TrackSizings(columns) = args.named("columns")?.unwrap_or_default();
|
||||||
@ -162,7 +154,7 @@ impl Layout for TableNode {
|
|||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let fill = styles.get(Self::FILL);
|
let fill = styles.get(Self::FILL);
|
||||||
let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);
|
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 align = styles.get(Self::ALIGN);
|
||||||
|
|
||||||
let cols = self.tracks.x.len().max(1);
|
let cols = self.tracks.x.len().max(1);
|
||||||
@ -172,7 +164,7 @@ impl Layout for TableNode {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, child)| {
|
.map(|(i, child)| {
|
||||||
let mut child = child.padded(Sides::splat(padding));
|
let mut child = child.padded(Sides::splat(inset));
|
||||||
|
|
||||||
let x = i % cols;
|
let x = i % cols;
|
||||||
let y = i / cols;
|
let y = i / cols;
|
||||||
|
@ -134,10 +134,14 @@ impl Inline for BoxNode {}
|
|||||||
/// The spacing between this block and its predecessor. Takes precedence over
|
/// The spacing between this block and its predecessor. Takes precedence over
|
||||||
/// `spacing`.
|
/// `spacing`.
|
||||||
///
|
///
|
||||||
|
/// The default value is `{1.2em}`.
|
||||||
|
///
|
||||||
/// - below: Spacing (named, settable)
|
/// - below: Spacing (named, settable)
|
||||||
/// The spacing between this block and its successor. Takes precedence
|
/// The spacing between this block and its successor. Takes precedence
|
||||||
/// over `spacing`.
|
/// over `spacing`.
|
||||||
///
|
///
|
||||||
|
/// The default value is `{1.2em}`.
|
||||||
|
///
|
||||||
/// ## Category
|
/// ## Category
|
||||||
/// layout
|
/// layout
|
||||||
#[func]
|
#[func]
|
||||||
|
@ -7,28 +7,31 @@ use super::Spacing;
|
|||||||
///
|
///
|
||||||
/// The grid element allows you to arrange content in a grid. You can define the
|
/// 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.
|
/// 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,
|
/// There are multiple sizing modes for columns and rows that can be used to
|
||||||
/// that can be used to create complex layouts.
|
/// create complex layouts.
|
||||||
///
|
///
|
||||||
/// The sizing of the grid is determined by the track sizes specified in the
|
/// 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
|
/// arguments. Because each of the sizing parameters accepts the same values, we
|
||||||
/// will explain them here. Each sizing argument accepts an array of track
|
/// will explain them just once, here. Each sizing argument accepts an array of
|
||||||
/// sizes. A track size is either:
|
/// 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
|
/// 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
|
/// which, and together they claim more than the available space, the `{auto}`
|
||||||
/// resized to fit the available space.
|
/// tracks will fairly distribute the available space among themselves.
|
||||||
/// - a fractional length (e.g. `{1fr}`). Once all other tracks have been sized,
|
///
|
||||||
|
/// - 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
|
/// 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
|
/// with a fraction of `{1fr}`, they will each take up half of the remaining
|
||||||
/// space.
|
/// space.
|
||||||
///
|
///
|
||||||
/// To specify a single track, the array can be omitted in favor of a single
|
/// 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
|
/// 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)}`.
|
/// `columns:` `{(auto, auto, auto)}`.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
|
@ -4,9 +4,9 @@ use crate::prelude::*;
|
|||||||
/// Hide content without affecting layout.
|
/// Hide content without affecting layout.
|
||||||
///
|
///
|
||||||
/// The `hide` function allows you to hide content while the layout still 'sees'
|
/// 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
|
/// it. This is useful to create whitespace that is exactly as large as some
|
||||||
/// as some content. It may also be useful to redact content because its
|
/// content. It may also be useful to redact content because its arguments are
|
||||||
/// arguments are not included in the output.
|
/// not included in the output.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -42,7 +42,7 @@ pub struct PageNode(pub Content);
|
|||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl PageNode {
|
impl PageNode {
|
||||||
/// The unflipped width of the page.
|
/// The width of the page.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
@ -58,7 +58,7 @@ impl PageNode {
|
|||||||
#[property(resolve)]
|
#[property(resolve)]
|
||||||
pub const WIDTH: Smart<Length> = Smart::Custom(Paper::A4.width().into());
|
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
|
/// If this is set to `{auto}`, page breaks can only be triggered manually
|
||||||
/// by inserting a [page break](@pagebreak). Most examples throughout this
|
/// 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.
|
/// - A single length: The same margin on all sides.
|
||||||
/// - `{auto}`: The margin is set to the default value for the page's size.
|
/// - `{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.
|
/// - A dictionary: With a dictionary, the margins can be set individually.
|
||||||
/// The dictionary can contain the following keys in order of precedence:
|
/// The dictionary can contain the following keys in order of precedence:
|
||||||
/// - `top`: The top margin.
|
/// - `top`: The top margin.
|
||||||
@ -101,7 +100,7 @@ impl PageNode {
|
|||||||
/// - `left`: The left margin.
|
/// - `left`: The left margin.
|
||||||
/// - `x`: The horizontal margins.
|
/// - `x`: The horizontal margins.
|
||||||
/// - `y`: The vertical 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.
|
/// dictionary explicitly sets a size.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -197,7 +196,7 @@ impl PageNode {
|
|||||||
/// text(8pt, numbering("I", i))
|
/// text(8pt, numbering("I", i))
|
||||||
/// )
|
/// )
|
||||||
/// )
|
/// )
|
||||||
///
|
///
|
||||||
/// #lorem(18)
|
/// #lorem(18)
|
||||||
/// ```
|
/// ```
|
||||||
#[property(referenced)]
|
#[property(referenced)]
|
||||||
|
@ -13,7 +13,6 @@ use crate::prelude::*;
|
|||||||
/// ```
|
/// ```
|
||||||
/// #stack(
|
/// #stack(
|
||||||
/// dir: ttb,
|
/// dir: ttb,
|
||||||
///
|
|
||||||
/// rect(width: 40pt),
|
/// rect(width: 40pt),
|
||||||
/// rect(width: 120pt),
|
/// rect(width: 120pt),
|
||||||
/// rect(width: 90pt),
|
/// rect(width: 90pt),
|
||||||
|
@ -11,17 +11,15 @@ use crate::prelude::*;
|
|||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```
|
/// ```
|
||||||
/// #rect(
|
/// #rect(inset: 0pt, move(
|
||||||
/// move(
|
/// dx: 6pt, dy: 6pt,
|
||||||
/// dx: 6pt, dy: 6pt,
|
/// rect(
|
||||||
/// rect(
|
/// inset: 8pt,
|
||||||
/// inset: 8pt,
|
/// fill: white,
|
||||||
/// fill: white,
|
/// stroke: black,
|
||||||
/// stroke: black,
|
/// [Abra cadabra]
|
||||||
/// [Abra cadabra]
|
|
||||||
/// )
|
|
||||||
/// )
|
/// )
|
||||||
/// )
|
/// ))
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Parameters
|
/// ## Parameters
|
||||||
|
@ -242,7 +242,9 @@ impl TextNode {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
|
/// #set rect(inset: 0pt)
|
||||||
/// #set text(size: 20pt)
|
/// #set text(size: 20pt)
|
||||||
|
///
|
||||||
/// #set text(top-edge: "ascender")
|
/// #set text(top-edge: "ascender")
|
||||||
/// #rect(fill: aqua)[Typst]
|
/// #rect(fill: aqua)[Typst]
|
||||||
///
|
///
|
||||||
@ -256,7 +258,9 @@ impl TextNode {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
|
/// #set rect(inset: 0pt)
|
||||||
/// #set text(size: 20pt)
|
/// #set text(size: 20pt)
|
||||||
|
///
|
||||||
/// #set text(bottom-edge: "baseline")
|
/// #set text(bottom-edge: "baseline")
|
||||||
/// #rect(fill: aqua)[Typst]
|
/// #rect(fill: aqua)[Typst]
|
||||||
///
|
///
|
||||||
|
@ -65,7 +65,7 @@ use crate::prelude::*;
|
|||||||
/// // that retains the correct baseline.
|
/// // that retains the correct baseline.
|
||||||
/// #show raw.where(block: false): rect.with(
|
/// #show raw.where(block: false): rect.with(
|
||||||
/// fill: luma(240),
|
/// fill: luma(240),
|
||||||
/// inset: (x: 3pt),
|
/// inset: (x: 3pt, y: 0pt),
|
||||||
/// outset: (y: 3pt),
|
/// outset: (y: 3pt),
|
||||||
/// radius: 2pt,
|
/// radius: 2pt,
|
||||||
/// )
|
/// )
|
||||||
|
@ -3,98 +3,167 @@ use std::f64::consts::SQRT_2;
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// # Rectangle
|
/// # 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
|
/// ## Parameters
|
||||||
/// - body: Content (positional)
|
/// - 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)
|
/// - width: Rel<Length> (named)
|
||||||
/// The shape's width.
|
/// The rectangle's width, relative to its parent container.
|
||||||
///
|
///
|
||||||
/// - height: Rel<Length> (named)
|
/// - height: Rel<Length> (named)
|
||||||
/// The shape's height.
|
/// The rectangle's height, relative to its parent container.
|
||||||
///
|
|
||||||
/// - 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.
|
|
||||||
///
|
///
|
||||||
/// ## Category
|
/// ## Category
|
||||||
/// visualize
|
/// visualize
|
||||||
#[func]
|
#[func]
|
||||||
#[capable(Layout, Inline)]
|
#[capable(Layout, Inline)]
|
||||||
#[derive(Debug, Hash)]
|
#[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>;
|
|
||||||
|
|
||||||
/// A rectangle with optional content.
|
|
||||||
pub type RectNode = ShapeNode<RECT>;
|
|
||||||
|
|
||||||
/// A circle with optional content.
|
|
||||||
pub type CircleNode = ShapeNode<CIRCLE>;
|
|
||||||
|
|
||||||
/// A ellipse with optional content.
|
|
||||||
pub type EllipseNode = ShapeNode<ELLIPSE>;
|
|
||||||
|
|
||||||
#[node]
|
#[node]
|
||||||
impl<const S: ShapeKind> ShapeNode<S> {
|
impl RectNode {
|
||||||
/// How to fill the shape.
|
/// 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;
|
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.
|
/// 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)]
|
#[property(resolve, fold)]
|
||||||
pub const INSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
|
pub const STROKE: Smart<Sides<Option<Option<PartialStroke>>>> = Smart::Auto;
|
||||||
/// How much to extend the shape's dimensions beyond the allocated space.
|
|
||||||
|
/// 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());
|
||||||
|
|
||||||
|
/// 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)]
|
#[property(resolve, fold)]
|
||||||
pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
|
pub const OUTSET: Sides<Option<Rel<Length>>> = Sides::splat(Rel::zero());
|
||||||
|
|
||||||
/// How much to round the shape's corners.
|
|
||||||
#[property(skip, resolve, fold)]
|
|
||||||
pub const RADIUS: Corners<Option<Rel<Length>>> = Corners::splat(Rel::zero());
|
|
||||||
|
|
||||||
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
|
||||||
let size = match S {
|
let width = args.named("width")?;
|
||||||
SQUARE => args.named::<Length>("size")?.map(Rel::from),
|
let height = args.named("height")?;
|
||||||
CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Rel::from(r)),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let width = match size {
|
|
||||||
None => args.named("width")?,
|
|
||||||
size => size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let height = match size {
|
|
||||||
None => args.named("height")?,
|
|
||||||
size => size,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self(args.eat()?).pack().boxed(Axes::new(width, height)))
|
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> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
"body" => match &self.0 {
|
"body" => match &self.0 {
|
||||||
@ -106,124 +175,484 @@ impl<const S: ShapeKind> ShapeNode<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const S: ShapeKind> Layout for ShapeNode<S> {
|
impl Layout for RectNode {
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&self,
|
||||||
vt: &mut Vt,
|
vt: &mut Vt,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
let mut frame;
|
layout(
|
||||||
if let Some(child) = &self.0 {
|
vt,
|
||||||
let mut inset = styles.get(Self::INSET);
|
ShapeKind::Rect,
|
||||||
if is_round(S) {
|
&self.0,
|
||||||
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
|
styles.get(Self::FILL),
|
||||||
}
|
styles.get(Self::STROKE),
|
||||||
|
styles.get(Self::INSET),
|
||||||
// Pad the child.
|
styles.get(Self::OUTSET),
|
||||||
let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
|
styles.get(Self::RADIUS),
|
||||||
|
styles,
|
||||||
let mut pod = Regions::one(regions.first, regions.base, regions.expand);
|
regions,
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame = Frame::new(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let outset = styles.get(Self::OUTSET).relative_to(frame.size());
|
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
|
||||||
|
|
||||||
let radius = styles
|
|
||||||
.get(Self::RADIUS)
|
|
||||||
.map(|side| side.relative_to(size.x.min(size.y) / 2.0));
|
|
||||||
|
|
||||||
let pos = Point::new(-outset.left, -outset.top);
|
|
||||||
|
|
||||||
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))),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply metadata.
|
|
||||||
frame.meta(styles);
|
|
||||||
|
|
||||||
Ok(Fragment::frame(frame))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const S: ShapeKind> Inline for ShapeNode<S> {}
|
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 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 square. See the [rectangle's
|
||||||
|
/// documentation](@rect/stroke) for more details.
|
||||||
|
#[property(resolve, fold)]
|
||||||
|
pub const STROKE: Smart<Sides<Option<Option<PartialStroke>>>> = Smart::Auto;
|
||||||
|
|
||||||
|
/// 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());
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = match size {
|
||||||
|
None => args.named("height")?,
|
||||||
|
size => size,
|
||||||
|
};
|
||||||
|
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 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 {
|
||||||
|
"body" => match &self.0 {
|
||||||
|
Some(body) => Some(Value::Content(body.clone())),
|
||||||
|
None => Some(Value::None),
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout for EllipseNode {
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
vt: &mut Vt,
|
||||||
|
styles: StyleChain,
|
||||||
|
regions: Regions,
|
||||||
|
) -> SourceResult<Fragment> {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inline for EllipseNode {}
|
||||||
|
|
||||||
|
/// # 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>);
|
||||||
|
|
||||||
|
#[node]
|
||||||
|
impl CircleNode {
|
||||||
|
/// How to fill the circle. See the [rectangle's documentation](@rect/fill)
|
||||||
|
/// for more details.
|
||||||
|
pub const FILL: Option<Paint> = None;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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());
|
||||||
|
|
||||||
|
/// 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());
|
||||||
|
|
||||||
|
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 height = match size {
|
||||||
|
None => args.named("height")?,
|
||||||
|
size => size,
|
||||||
|
};
|
||||||
|
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 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 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply metadata.
|
||||||
|
frame.meta(styles);
|
||||||
|
|
||||||
|
Ok(Fragment::frame(frame))
|
||||||
|
}
|
||||||
|
|
||||||
/// A category of shape.
|
/// A category of shape.
|
||||||
pub type ShapeKind = usize;
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ShapeKind {
|
||||||
/// A rectangle with equal side lengths.
|
/// A rectangle with equal side lengths.
|
||||||
const SQUARE: ShapeKind = 0;
|
Square,
|
||||||
|
/// A quadrilateral with four right angles.
|
||||||
/// A quadrilateral with four right angles.
|
Rect,
|
||||||
const RECT: ShapeKind = 1;
|
/// An ellipse with coinciding foci.
|
||||||
|
Circle,
|
||||||
/// An ellipse with coinciding foci.
|
/// A curve around two focal points.
|
||||||
const CIRCLE: ShapeKind = 2;
|
Ellipse,
|
||||||
|
|
||||||
/// A curve around two focal points.
|
|
||||||
const ELLIPSE: ShapeKind = 3;
|
|
||||||
|
|
||||||
/// Whether a shape kind is curvy.
|
|
||||||
fn is_round(kind: ShapeKind) -> bool {
|
|
||||||
matches!(kind, CIRCLE | ELLIPSE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a shape kind has equal side length.
|
impl ShapeKind {
|
||||||
fn is_quadratic(kind: ShapeKind) -> bool {
|
/// Whether this shape kind is curvy.
|
||||||
matches!(kind, SQUARE | CIRCLE)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
where
|
||||||
T: Cast + Default + Copy,
|
T: Cast + Copy,
|
||||||
{
|
{
|
||||||
fn is(value: &Value) -> bool {
|
fn is(value: &Value) -> bool {
|
||||||
matches!(value, Value::Dict(_)) || T::is(value)
|
matches!(value, Value::Dict(_)) || T::is(value)
|
||||||
@ -439,9 +439,9 @@ where
|
|||||||
|
|
||||||
dict.finish(&["left", "top", "right", "bottom", "x", "y", "rest"])?;
|
dict.finish(&["left", "top", "right", "bottom", "x", "y", "rest"])?;
|
||||||
|
|
||||||
Ok(sides.map(Option::unwrap_or_default))
|
Ok(sides)
|
||||||
} else if T::is(&value) {
|
} else if T::is(&value) {
|
||||||
Ok(Self::splat(T::cast(value)?))
|
Ok(Self::splat(Some(T::cast(value)?)))
|
||||||
} else {
|
} else {
|
||||||
<Self as Cast>::error(value)
|
<Self as Cast>::error(value)
|
||||||
}
|
}
|
||||||
@ -452,9 +452,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Cast for Corners<T>
|
impl<T> Cast for Corners<Option<T>>
|
||||||
where
|
where
|
||||||
T: Cast + Default + Copy,
|
T: Cast + Copy,
|
||||||
{
|
{
|
||||||
fn is(value: &Value) -> bool {
|
fn is(value: &Value) -> bool {
|
||||||
matches!(value, Value::Dict(_)) || T::is(value)
|
matches!(value, Value::Dict(_)) || T::is(value)
|
||||||
@ -488,9 +488,9 @@ where
|
|||||||
"rest",
|
"rest",
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
Ok(corners.map(Option::unwrap_or_default))
|
Ok(corners)
|
||||||
} else if T::is(&value) {
|
} else if T::is(&value) {
|
||||||
Ok(Self::splat(T::cast(value)?))
|
Ok(Self::splat(Some(T::cast(value)?)))
|
||||||
} else {
|
} else {
|
||||||
<Self as Cast>::error(value)
|
<Self as Cast>::error(value)
|
||||||
}
|
}
|
||||||
|
@ -914,57 +914,45 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Axes<Option<GenAlign>> {
|
impl<T> Fold for Axes<Option<T>>
|
||||||
type Output = Axes<GenAlign>;
|
where
|
||||||
|
T: Fold,
|
||||||
|
{
|
||||||
|
type Output = Axes<T::Output>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::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
|
where
|
||||||
T: Fold,
|
T: Fold,
|
||||||
{
|
{
|
||||||
type Output = Sides<T::Output>;
|
type Output = Sides<T::Output>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::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>>> {
|
impl<T> Fold for Corners<Option<T>>
|
||||||
type Output = Sides<Rel<Abs>>;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for Sides<Option<Smart<Rel<Length>>>> {
|
|
||||||
type Output = Sides<Smart<Rel<Length>>>;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Fold for Corners<T>
|
|
||||||
where
|
where
|
||||||
T: Fold,
|
T: Fold,
|
||||||
{
|
{
|
||||||
type Output = Corners<T::Output>;
|
type Output = Corners<T::Output>;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::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 Corners<Option<Rel<Abs>>> {
|
|
||||||
type Output = Corners<Rel<Abs>>;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.zip(outer).map(|(inner, outer)| inner.unwrap_or(outer))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,3 +966,27 @@ impl Fold for PartialStroke<Abs> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Fold for Rel<Length> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn fold(self, _: Self::Output) -> Self::Output {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold for Rel<Abs> {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn fold(self, _: Self::Output) -> Self::Output {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fold for GenAlign {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn fold(self, _: Self::Output) -> Self::Output {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -39,7 +39,7 @@ No: list \
|
|||||||
// Test grid like show rule.
|
// Test grid like show rule.
|
||||||
#show desc: it => table(
|
#show desc: it => table(
|
||||||
columns: 2,
|
columns: 2,
|
||||||
padding: 3pt,
|
inset: 3pt,
|
||||||
..it.items.map(item => (emph(item(0)), item(1))).flatten(),
|
..it.items.map(item => (emph(item(0)), item(1))).flatten(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
#rect(width: 1cm, fill: c.negate())
|
#rect(width: 1cm, fill: c.negate())
|
||||||
|
|
||||||
#for x in range(0, 11) {
|
#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) {
|
#for x in range(0, 11) {
|
||||||
square(width: 9pt, fill: c.darken(x * 10%))
|
square(size: 9pt, fill: c.darken(x * 10%))
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#show raw.where(block: false): rect.with(
|
#show raw.where(block: false): rect.with(
|
||||||
radius: 2pt,
|
radius: 2pt,
|
||||||
outset: (y: 3pt),
|
outset: (y: 3pt),
|
||||||
inset: (x: 3pt),
|
inset: (x: 3pt, y: 0pt),
|
||||||
fill: luma(230),
|
fill: luma(230),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +27,9 @@ with selectors and justification.
|
|||||||
code!("it");
|
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: 1): set text(red)
|
||||||
#show heading.where(level: 2): set text(blue)
|
#show heading.where(level: 2): set text(blue)
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
#let cell(width, color) = rect(width: width, height: 2cm, fill: color)
|
#let cell(width, color) = rect(width: width, height: 2cm, fill: color)
|
||||||
|
|
||||||
#set page(width: 100pt, height: 140pt)
|
#set page(width: 100pt, height: 140pt)
|
||||||
#grid(
|
#grid(
|
||||||
columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%),
|
columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%),
|
||||||
@ -21,6 +20,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
#set rect(inset: 0pt)
|
||||||
#grid(
|
#grid(
|
||||||
columns: (auto, auto, 40%),
|
columns: (auto, auto, 40%),
|
||||||
column-gutter: 1fr,
|
column-gutter: 1fr,
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
row-gutter: 10pt,
|
row-gutter: 10pt,
|
||||||
column-gutter: (0pt, 10%),
|
column-gutter: (0pt, 10%),
|
||||||
align(top, image("/res/rhino.png")),
|
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],
|
[rofl],
|
||||||
[\ A] * 3,
|
[\ A] * 3,
|
||||||
[Ha!\ ] * 3,
|
[Ha!\ ] * 3,
|
||||||
@ -55,7 +55,7 @@
|
|||||||
column-gutter: (0pt, 10%),
|
column-gutter: (0pt, 10%),
|
||||||
[A], [B], [C], [D],
|
[A], [B], [C], [D],
|
||||||
grid(columns: 2, [A], [B], [C\ ]*3, [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],
|
[rofl],
|
||||||
[E\ ]*4,
|
[E\ ]*4,
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#pad(left: 10pt, [Indented!])
|
#pad(left: 10pt, [Indented!])
|
||||||
|
|
||||||
// All sides together.
|
// All sides together.
|
||||||
|
#set rect(inset: 0pt)
|
||||||
#rect(fill: conifer,
|
#rect(fill: conifer,
|
||||||
pad(10pt, right: 20pt,
|
pad(10pt, right: 20pt,
|
||||||
rect(width: 20pt, height: 20pt, fill: rgb("eb5278"))
|
rect(width: 20pt, height: 20pt, fill: rgb("eb5278"))
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
#let column(title, linebreaks, hyphenate) = {
|
#let column(title, linebreaks, hyphenate) = {
|
||||||
rect(width: 132pt, fill: rgb("eee"))[
|
rect(inset: 0pt, width: 132pt, fill: rgb("eee"))[
|
||||||
#set par(linebreaks: linebreaks)
|
#set par(linebreaks: linebreaks)
|
||||||
#set text(hyphenate: hyphenate)
|
#set text(hyphenate: hyphenate)
|
||||||
#strong(title) \ #story
|
#strong(title) \ #story
|
||||||
|
@ -19,6 +19,5 @@ World! 🌍
|
|||||||
#set text(white)
|
#set text(white)
|
||||||
#rect(fill: forest)[
|
#rect(fill: forest)[
|
||||||
#v(1fr)
|
#v(1fr)
|
||||||
#h(1fr) Hi you! #h(5pt)
|
#h(1fr) Hi you!
|
||||||
#v(5pt)
|
|
||||||
]
|
]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#set text("Latin Modern Roman")
|
#set text("Latin Modern Roman")
|
||||||
#show <table>: it => table(
|
#show <table>: it => table(
|
||||||
columns: 2,
|
columns: 2,
|
||||||
padding: 8pt,
|
inset: 8pt,
|
||||||
..it.text
|
..it.text
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map(line => (text(10pt, raw(line, lang: "typ")), eval(line) + [ ]))
|
.map(line => (text(10pt, raw(line, lang: "typ")), eval(line) + [ ]))
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#set page(width: 160pt)
|
#set page(width: 160pt)
|
||||||
#set text(size: 8pt)
|
#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)
|
#set text("IBM Plex Mono", top-edge: top, bottom-edge: bottom)
|
||||||
From #top to #bottom
|
From #top to #bottom
|
||||||
]
|
]
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#set page(width: 130pt, margin: 15pt)
|
#set page(width: 130pt, margin: 15pt)
|
||||||
#set par(justify: true, linebreaks: "simple")
|
#set par(justify: true, linebreaks: "simple")
|
||||||
#set text(size: 9pt)
|
#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
|
This is a little bit of text that builds up to
|
||||||
hang-ing hyphens and dash---es and then, you know,
|
hang-ing hyphens and dash---es and then, you know,
|
||||||
some punctuation in the margin.
|
some punctuation in the margin.
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#table(
|
#table(
|
||||||
columns: 3,
|
columns: 3,
|
||||||
[Typo.], [Fallb.], [Synth],
|
[Typo.], [Fallb.], [Synth],
|
||||||
[x#super[1]], [x#super[5n]], [x#super[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(width: 6pt)]],
|
[x#sub[1]], [x#sub[5n]], [x#sub[2 #square(size: 6pt)]],
|
||||||
)
|
)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -4,9 +4,13 @@
|
|||||||
// Test relative width and height and size that is smaller
|
// Test relative width and height and size that is smaller
|
||||||
// than default size.
|
// than default size.
|
||||||
#set page(width: 120pt, height: 70pt)
|
#set page(width: 120pt, height: 70pt)
|
||||||
#square(width: 50%, align(bottom)[A])
|
#set align(center + horizon)
|
||||||
|
#square(width: 50%, [A])
|
||||||
#square(height: 50%)
|
#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.
|
// Test alignment in automatically sized square and circle.
|
||||||
@ -37,7 +41,7 @@
|
|||||||
// Test square that is overflowing due to its aspect ratio.
|
// Test square that is overflowing due to its aspect ratio.
|
||||||
#set page(width: 40pt, height: 20pt, margin: 5pt)
|
#set page(width: 40pt, height: 20pt, margin: 5pt)
|
||||||
#square(width: 100%) #parbreak()
|
#square(width: 100%) #parbreak()
|
||||||
#square(width: 100%)[Hello]
|
#square(width: 100%)[Hey]
|
||||||
|
|
||||||
---
|
---
|
||||||
// Size cannot be relative because we wouldn't know
|
// Size cannot be relative because we wouldn't know
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test auto sizing.
|
// Test auto sizing.
|
||||||
|
#set circle(inset: 0pt)
|
||||||
|
|
||||||
Auto-sized circle. \
|
Auto-sized circle. \
|
||||||
#circle(fill: rgb("eb5278"), stroke: 2pt + black,
|
#circle(fill: rgb("eb5278"), stroke: 2pt + black,
|
||||||
@ -33,13 +34,14 @@ Expanded by height.
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Ensure circle directly in rect works.
|
// 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.
|
// Test relative sizing.
|
||||||
#let centered(body) = align(center + horizon, body)
|
#let centered(body) = align(center + horizon, body)
|
||||||
#set text(fill: white)
|
#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(radius: 10pt, fill: eastern, centered[A]) // D=20pt
|
||||||
#circle(height: 60%, fill: eastern, centered[B]) // D=30pt
|
#circle(height: 60%, fill: eastern, centered[B]) // D=30pt
|
||||||
#circle(width: 20% + 20pt, fill: eastern, centered[C]) // D=40pt
|
#circle(width: 20% + 20pt, fill: eastern, centered[C]) // D=40pt
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
#ellipse()
|
#ellipse()
|
||||||
|
|
||||||
---
|
---
|
||||||
|
#set rect(inset: 0pt)
|
||||||
|
#set ellipse(inset: 0pt)
|
||||||
|
|
||||||
Rect in ellipse in fixed rect. \
|
Rect in ellipse in fixed rect. \
|
||||||
#rect(width: 3cm, height: 2cm, fill: rgb("2a631a"),
|
#rect(width: 3cm, height: 2cm, fill: rgb("2a631a"),
|
||||||
ellipse(fill: forest, width: 100%, height: 100%,
|
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.
|
An inline #ellipse(width: 8pt, height: 6pt, outset: (top: 3pt, rest: 5.5pt)) ellipse.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#set page(width: 150pt)
|
#set page(width: 150pt)
|
||||||
|
|
||||||
// Fit to text.
|
// Fit to text.
|
||||||
#rect(fill: conifer, inset: 3pt)[Textbox]
|
#rect(fill: conifer)[Textbox]
|
||||||
|
|
||||||
// Empty with fixed width and height.
|
// Empty with fixed width and height.
|
||||||
#block(rect(
|
#block(rect(
|
||||||
@ -18,7 +18,7 @@
|
|||||||
))
|
))
|
||||||
|
|
||||||
// Fixed width, text height.
|
// 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.
|
// Page width, fixed height.
|
||||||
#rect(height: 1cm, width: 100%, fill: rgb("734ced"))[Topleft]
|
#rect(height: 1cm, width: 100%, fill: rgb("734ced"))[Topleft]
|
||||||
@ -44,18 +44,6 @@
|
|||||||
#rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt))
|
#rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt))
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
|
||||||
// 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"
|
// 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))
|
#rect(radius: (left: 10pt, cake: 5pt))
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
// Test auto-sized square.
|
// Test auto-sized square.
|
||||||
#square(fill: eastern, inset: 5pt)[
|
#square(fill: eastern)[
|
||||||
#set text(fill: white, weight: "bold")
|
#set text(fill: white, weight: "bold")
|
||||||
Typst
|
Typst
|
||||||
]
|
]
|
||||||
|