diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-07-02 19:59:52 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-07-02 20:07:43 +0200 |
| commit | ebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch) | |
| tree | 2bbc24ddb4124c4bb14dec0e536129d4de37b056 /library/src/layout/container.rs | |
| parent | 3ab19185093d7709f824b95b979060ce125389d8 (diff) | |
Move everything into `crates/` directory
Diffstat (limited to 'library/src/layout/container.rs')
| -rw-r--r-- | library/src/layout/container.rs | 497 |
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), -} |
