summaryrefslogtreecommitdiff
path: root/library/src/layout/container.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /library/src/layout/container.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'library/src/layout/container.rs')
-rw-r--r--library/src/layout/container.rs497
1 files changed, 0 insertions, 497 deletions
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs
deleted file mode 100644
index c79669d0..00000000
--- a/library/src/layout/container.rs
+++ /dev/null
@@ -1,497 +0,0 @@
-use typst::eval::AutoValue;
-
-use super::VElem;
-use crate::layout::Spacing;
-use crate::prelude::*;
-
-/// An inline-level container that sizes content.
-///
-/// All elements except inline math, text, and boxes are block-level and cannot
-/// occur inside of a paragraph. The box function can be used to integrate such
-/// elements into a paragraph. Boxes take the size of their contents by default
-/// but can also be sized explicitly.
-///
-/// ## Example { #example }
-/// ```example
-/// Refer to the docs
-/// #box(
-/// height: 9pt,
-/// image("docs.svg")
-/// )
-/// for more information.
-/// ```
-///
-/// Display: Box
-/// Category: layout
-#[element(Layout)]
-pub struct BoxElem {
- /// The width of the box.
- ///
- /// Boxes can have [fractional]($type/fraction) widths, as the example
- /// below demonstrates.
- ///
- /// _Note:_ Currently, only boxes and only their widths might be fractionally
- /// sized within paragraphs. Support for fractionally sized images, shapes,
- /// and more might be added in the future.
- ///
- /// ```example
- /// Line in #box(width: 1fr, line(length: 100%)) between.
- /// ```
- pub width: Sizing,
-
- /// The height of the box.
- pub height: Smart<Rel<Length>>,
-
- /// An amount to shift the box's baseline by.
- ///
- /// ```example
- /// Image: #box(baseline: 40%, image("tiger.jpg", width: 2cm)).
- /// ```
- #[resolve]
- pub baseline: Rel<Length>,
-
- /// The box's background color. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
- pub fill: Option<Paint>,
-
- /// The box's border color. See the
- /// [rectangle's documentation]($func/rect.stroke) for more details.
- #[resolve]
- #[fold]
- pub stroke: Sides<Option<Option<PartialStroke>>>,
-
- /// How much to round the box's corners. See the [rectangle's
- /// documentation]($func/rect.radius) for more details.
- #[resolve]
- #[fold]
- pub radius: Corners<Option<Rel<Length>>>,
-
- /// How much to pad the box's content. See the [rectangle's
- /// documentation]($func/rect.inset) for more details.
- #[resolve]
- #[fold]
- pub inset: Sides<Option<Rel<Length>>>,
-
- /// How much to expand the box's size without affecting the layout.
- ///
- /// This is useful to prevent padding from affecting line layout. For a
- /// generalized version of the example below, see the documentation for the
- /// [raw text's block parameter]($func/raw.block).
- ///
- /// ```example
- /// An inline
- /// #box(
- /// fill: luma(235),
- /// inset: (x: 3pt, y: 0pt),
- /// outset: (y: 3pt),
- /// radius: 2pt,
- /// )[rectangle].
- /// ```
- #[resolve]
- #[fold]
- pub outset: Sides<Option<Rel<Length>>>,
-
- /// Whether to clip the content inside the box.
- #[default(false)]
- pub clip: bool,
-
- /// The contents of the box.
- #[positional]
- pub body: Option<Content>,
-}
-
-impl Layout for BoxElem {
- #[tracing::instrument(name = "BoxElem::layout", skip_all)]
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- let width = match self.width(styles) {
- Sizing::Auto => Smart::Auto,
- Sizing::Rel(rel) => Smart::Custom(rel),
- Sizing::Fr(_) => Smart::Custom(Ratio::one().into()),
- };
-
- // Resolve the sizing to a concrete size.
- let sizing = Axes::new(width, self.height(styles));
- let expand = sizing.as_ref().map(Smart::is_custom);
- let size = sizing
- .resolve(styles)
- .zip(regions.base())
- .map(|(s, b)| s.map(|v| v.relative_to(b)))
- .unwrap_or(regions.base());
-
- // Apply inset.
- let mut body = self.body(styles).unwrap_or_default();
- let inset = self.inset(styles);
- if inset.iter().any(|v| !v.is_zero()) {
- body = body.padded(inset.map(|side| side.map(Length::from)));
- }
-
- // Select the appropriate base and expansion for the child depending
- // on whether it is automatically or relatively sized.
- let pod = Regions::one(size, expand);
- let mut frame = body.layout(vt, styles, pod)?.into_frame();
-
- // Enforce correct size.
- *frame.size_mut() = expand.select(size, frame.size());
-
- // Apply baseline shift.
- let shift = self.baseline(styles).relative_to(frame.height());
- if !shift.is_zero() {
- frame.set_baseline(frame.baseline() - shift);
- }
-
- // Clip the contents
- if self.clip(styles) {
- frame.clip();
- }
-
- // Prepare fill and stroke.
- let fill = self.fill(styles);
- let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default));
-
- // Add fill and/or stroke.
- if fill.is_some() || stroke.iter().any(Option::is_some) {
- let outset = self.outset(styles);
- let radius = self.radius(styles);
- frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
- }
-
- // Apply metadata.
- frame.meta(styles, false);
-
- Ok(Fragment::frame(frame))
- }
-}
-
-/// A block-level container.
-///
-/// Such a container can be used to separate content, size it, and give it a
-/// background or border.
-///
-/// ## Examples { #examples }
-/// With a block, you can give a background to content while still allowing it
-/// to break across multiple pages.
-/// ```example
-/// #set page(height: 100pt)
-/// #block(
-/// fill: luma(230),
-/// inset: 8pt,
-/// radius: 4pt,
-/// lorem(30),
-/// )
-/// ```
-///
-/// Blocks are also useful to force elements that would otherwise be inline to
-/// become block-level, especially when writing show rules.
-/// ```example
-/// #show heading: it => it.body
-/// = Blockless
-/// More text.
-///
-/// #show heading: it => block(it.body)
-/// = Blocky
-/// More text.
-/// ```
-///
-/// Display: Block
-/// Category: layout
-#[element(Layout)]
-pub struct BlockElem {
- /// The block's width.
- ///
- /// ```example
- /// #set align(center)
- /// #block(
- /// width: 60%,
- /// inset: 8pt,
- /// fill: silver,
- /// lorem(10),
- /// )
- /// ```
- pub width: Smart<Rel<Length>>,
-
- /// The block's height. When the height is larger than the remaining space
- /// on a page and [`breakable`]($func/block.breakable) is `{true}`, the
- /// block will continue on the next page with the remaining height.
- ///
- /// ```example
- /// #set page(height: 80pt)
- /// #set align(center)
- /// #block(
- /// width: 80%,
- /// height: 150%,
- /// fill: aqua,
- /// )
- /// ```
- pub height: Smart<Rel<Length>>,
-
- /// Whether the block can be broken and continue on the next page.
- ///
- /// ```example
- /// #set page(height: 80pt)
- /// The following block will
- /// jump to its own page.
- /// #block(
- /// breakable: false,
- /// lorem(15),
- /// )
- /// ```
- #[default(true)]
- pub breakable: bool,
-
- /// The block's background color. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
- pub fill: Option<Paint>,
-
- /// The block's border color. See the
- /// [rectangle's documentation]($func/rect.stroke) for more details.
- #[resolve]
- #[fold]
- pub stroke: Sides<Option<Option<PartialStroke>>>,
-
- /// How much to round the block's corners. See the [rectangle's
- /// documentation]($func/rect.radius) for more details.
- #[resolve]
- #[fold]
- pub radius: Corners<Option<Rel<Length>>>,
-
- /// How much to pad the block's content. See the [rectangle's
- /// documentation]($func/rect.inset) for more details.
- #[resolve]
- #[fold]
- pub inset: Sides<Option<Rel<Length>>>,
-
- /// How much to expand the block's size without affecting the layout. See
- /// the [rectangle's documentation]($func/rect.outset) for more details.
- #[resolve]
- #[fold]
- pub outset: Sides<Option<Rel<Length>>>,
-
- /// The spacing around this block. This is shorthand to set `above` and
- /// `below` to the same value.
- ///
- /// ```example
- /// #set align(center)
- /// #show math.equation: set block(above: 8pt, below: 16pt)
- ///
- /// This sum of $x$ and $y$:
- /// $ x + y = z $
- /// A second paragraph.
- /// ```
- #[external]
- #[default(Em::new(1.2).into())]
- pub spacing: Spacing,
-
- /// The spacing between this block and its predecessor. Takes precedence
- /// over `spacing`. Can be used in combination with a show rule to adjust
- /// the spacing around arbitrary block-level elements.
- #[external]
- #[default(Em::new(1.2).into())]
- pub above: Spacing,
- #[internal]
- #[parse(
- let spacing = args.named("spacing")?;
- args.named("above")?
- .map(VElem::block_around)
- .or_else(|| spacing.map(VElem::block_spacing))
- )]
- #[default(VElem::block_spacing(Em::new(1.2).into()))]
- pub above: VElem,
-
- /// The spacing between this block and its successor. Takes precedence
- /// over `spacing`.
- #[external]
- #[default(Em::new(1.2).into())]
- pub below: Spacing,
- #[internal]
- #[parse(
- args.named("below")?
- .map(VElem::block_around)
- .or_else(|| spacing.map(VElem::block_spacing))
- )]
- #[default(VElem::block_spacing(Em::new(1.2).into()))]
- pub below: VElem,
-
- /// Whether to clip the content inside the block.
- #[default(false)]
- pub clip: bool,
-
- /// The contents of the block.
- #[positional]
- pub body: Option<Content>,
-
- /// Whether this block must stick to the following one.
- ///
- /// Use this to prevent page breaks between e.g. a heading and its body.
- #[internal]
- #[default(false)]
- pub sticky: bool,
-}
-
-impl Layout for BlockElem {
- #[tracing::instrument(name = "BlockElem::layout", skip_all)]
- fn layout(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- regions: Regions,
- ) -> SourceResult<Fragment> {
- // Apply inset.
- let mut body = self.body(styles).unwrap_or_default();
- let inset = self.inset(styles);
- if inset.iter().any(|v| !v.is_zero()) {
- body = body.clone().padded(inset.map(|side| side.map(Length::from)));
- }
-
- // Resolve the sizing to a concrete size.
- let sizing = Axes::new(self.width(styles), self.height(styles));
- let mut expand = sizing.as_ref().map(Smart::is_custom);
- let mut size = sizing
- .resolve(styles)
- .zip(regions.base())
- .map(|(s, b)| s.map(|v| v.relative_to(b)))
- .unwrap_or(regions.base());
-
- // Layout the child.
- let mut frames = if self.breakable(styles) {
- // Measure to ensure frames for all regions have the same width.
- if sizing.x == Smart::Auto {
- let pod = Regions::one(size, Axes::splat(false));
- let frame = body.measure(vt, styles, pod)?.into_frame();
- size.x = frame.width();
- expand.x = true;
- }
-
- let mut pod = regions;
- pod.size.x = size.x;
- pod.expand = expand;
-
- if expand.y {
- pod.full = size.y;
- }
-
- // Generate backlog for fixed height.
- let mut heights = vec![];
- if sizing.y.is_custom() {
- let mut remaining = size.y;
- for region in regions.iter() {
- let limited = region.y.min(remaining);
- heights.push(limited);
- remaining -= limited;
- if Abs::zero().fits(remaining) {
- break;
- }
- }
-
- if let Some(last) = heights.last_mut() {
- *last += remaining;
- }
-
- pod.size.y = heights[0];
- pod.backlog = &heights[1..];
- pod.last = None;
- }
-
- let mut frames = body.layout(vt, styles, pod)?.into_frames();
- for (frame, &height) in frames.iter_mut().zip(&heights) {
- *frame.size_mut() =
- expand.select(Size::new(size.x, height), frame.size());
- }
- frames
- } else {
- let pod = Regions::one(size, expand);
- let mut frames = body.layout(vt, styles, pod)?.into_frames();
- *frames[0].size_mut() = expand.select(size, frames[0].size());
- frames
- };
-
- // Clip the contents
- if self.clip(styles) {
- for frame in frames.iter_mut() {
- frame.clip();
- }
- }
-
- // Prepare fill and stroke.
- let fill = self.fill(styles);
- let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default));
-
- // Add fill and/or stroke.
- if fill.is_some() || stroke.iter().any(Option::is_some) {
- let mut skip = false;
- if let [first, rest @ ..] = frames.as_slice() {
- skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
- }
-
- let outset = self.outset(styles);
- let radius = self.radius(styles);
- for frame in frames.iter_mut().skip(skip as usize) {
- frame.fill_and_stroke(
- fill.clone(),
- stroke.clone(),
- outset,
- radius,
- self.span(),
- );
- }
- }
-
- // Apply metadata.
- for frame in &mut frames {
- frame.meta(styles, false);
- }
-
- Ok(Fragment::frames(frames))
- }
-}
-
-/// Defines how to size a grid cell along an axis.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Sizing {
- /// A track that fits its cell's contents.
- Auto,
- /// A track size specified in absolute terms and relative to the parent's
- /// size.
- Rel(Rel<Length>),
- /// A track size specified as a fraction of the remaining free space in the
- /// parent.
- Fr(Fr),
-}
-
-impl Sizing {
- /// Whether this is fractional sizing.
- pub fn is_fractional(self) -> bool {
- matches!(self, Self::Fr(_))
- }
-}
-
-impl Default for Sizing {
- fn default() -> Self {
- Self::Auto
- }
-}
-
-impl<T: Into<Spacing>> From<T> for Sizing {
- fn from(spacing: T) -> Self {
- match spacing.into() {
- Spacing::Rel(rel) => Self::Rel(rel),
- Spacing::Fr(fr) => Self::Fr(fr),
- }
- }
-}
-
-cast! {
- Sizing,
- self => match self {
- Self::Auto => Value::Auto,
- Self::Rel(rel) => rel.into_value(),
- Self::Fr(fr) => fr.into_value(),
- },
- _: AutoValue => Self::Auto,
- v: Rel<Length> => Self::Rel(v),
- v: Fr => Self::Fr(v),
-}