mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Support fractional width for box
This commit is contained in:
parent
d99359dede
commit
fd90736fb6
@ -44,7 +44,7 @@ pub struct BoxNode {
|
|||||||
/// The content to be sized.
|
/// The content to be sized.
|
||||||
pub body: Content,
|
pub body: Content,
|
||||||
/// The box's width.
|
/// The box's width.
|
||||||
pub width: Smart<Rel<Length>>,
|
pub width: Sizing,
|
||||||
/// The box's height.
|
/// The box's height.
|
||||||
pub height: Smart<Rel<Length>>,
|
pub height: Smart<Rel<Length>>,
|
||||||
/// The box's baseline shift.
|
/// The box's baseline shift.
|
||||||
@ -76,8 +76,14 @@ impl Layout for BoxNode {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
|
let width = match self.width {
|
||||||
|
Sizing::Auto => Smart::Auto,
|
||||||
|
Sizing::Rel(rel) => Smart::Custom(rel),
|
||||||
|
Sizing::Fr(_) => Smart::Custom(Ratio::one().into()),
|
||||||
|
};
|
||||||
|
|
||||||
// Resolve the sizing to a concrete size.
|
// Resolve the sizing to a concrete size.
|
||||||
let sizing = Axes::new(self.width, self.height);
|
let sizing = Axes::new(width, self.height);
|
||||||
let size = sizing
|
let size = sizing
|
||||||
.resolve(styles)
|
.resolve(styles)
|
||||||
.zip(regions.base())
|
.zip(regions.base())
|
||||||
@ -196,3 +202,50 @@ impl Layout for BlockNode {
|
|||||||
self.0.layout(vt, styles, regions)
|
self.0.layout(vt, styles, regions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(self) -> Value {
|
||||||
|
match self {
|
||||||
|
Self::Auto => Value::Auto,
|
||||||
|
Self::Rel(rel) => Spacing::Rel(rel).encode(),
|
||||||
|
Self::Fr(fr) => Spacing::Fr(fr).encode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_slice(vec: &[Sizing]) -> Value {
|
||||||
|
Value::Array(vec.iter().copied().map(Self::encode).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Sizing {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Auto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Spacing> for Sizing {
|
||||||
|
fn from(spacing: Spacing) -> Self {
|
||||||
|
match spacing {
|
||||||
|
Spacing::Rel(rel) => Self::Rel(rel),
|
||||||
|
Spacing::Fr(fr) => Self::Fr(fr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::compute::{Numbering, NumberingPattern};
|
use crate::compute::{Numbering, NumberingPattern};
|
||||||
use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing};
|
use crate::layout::{BlockNode, GridNode, ParNode, Sizing, Spacing};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// # Numbered List
|
/// # Numbered List
|
||||||
@ -193,10 +193,10 @@ impl Layout for EnumNode {
|
|||||||
|
|
||||||
GridNode {
|
GridNode {
|
||||||
tracks: Axes::with_x(vec![
|
tracks: Axes::with_x(vec![
|
||||||
TrackSizing::Relative(indent.into()),
|
Sizing::Rel(indent.into()),
|
||||||
TrackSizing::Auto,
|
Sizing::Auto,
|
||||||
TrackSizing::Relative(body_indent.into()),
|
Sizing::Rel(body_indent.into()),
|
||||||
TrackSizing::Auto,
|
Sizing::Auto,
|
||||||
]),
|
]),
|
||||||
gutter: Axes::with_y(vec![gutter.into()]),
|
gutter: Axes::with_y(vec![gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
|
@ -113,11 +113,11 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
/// Layout vertical spacing.
|
/// Layout vertical spacing.
|
||||||
fn layout_spacing(&mut self, node: VNode, styles: StyleChain) {
|
fn layout_spacing(&mut self, node: VNode, styles: StyleChain) {
|
||||||
self.layout_item(match node.amount {
|
self.layout_item(match node.amount {
|
||||||
Spacing::Relative(v) => FlowItem::Absolute(
|
Spacing::Rel(v) => FlowItem::Absolute(
|
||||||
v.resolve(styles).relative_to(self.full.y),
|
v.resolve(styles).relative_to(self.full.y),
|
||||||
node.weakness > 0,
|
node.weakness > 0,
|
||||||
),
|
),
|
||||||
Spacing::Fractional(v) => FlowItem::Fractional(v),
|
Spacing::Fr(v) => FlowItem::Fractional(v),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::TextNode;
|
use crate::text::TextNode;
|
||||||
|
|
||||||
use super::Spacing;
|
use super::Sizing;
|
||||||
|
|
||||||
/// # Grid
|
/// # Grid
|
||||||
/// Arrange content in a grid.
|
/// Arrange content in a grid.
|
||||||
@ -94,9 +94,9 @@ use super::Spacing;
|
|||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct GridNode {
|
pub struct GridNode {
|
||||||
/// Defines sizing for content rows and columns.
|
/// Defines sizing for content rows and columns.
|
||||||
pub tracks: Axes<Vec<TrackSizing>>,
|
pub tracks: Axes<Vec<Sizing>>,
|
||||||
/// Defines sizing of gutter rows and columns between content.
|
/// Defines sizing of gutter rows and columns between content.
|
||||||
pub gutter: Axes<Vec<TrackSizing>>,
|
pub gutter: Axes<Vec<Sizing>>,
|
||||||
/// The content to be arranged in a grid.
|
/// The content to be arranged in a grid.
|
||||||
pub cells: Vec<Content>,
|
pub cells: Vec<Content>,
|
||||||
}
|
}
|
||||||
@ -122,10 +122,10 @@ impl GridNode {
|
|||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
"columns" => Some(TrackSizing::encode_slice(&self.tracks.x)),
|
"columns" => Some(Sizing::encode_slice(&self.tracks.x)),
|
||||||
"rows" => Some(TrackSizing::encode_slice(&self.tracks.y)),
|
"rows" => Some(Sizing::encode_slice(&self.tracks.y)),
|
||||||
"column-gutter" => Some(TrackSizing::encode_slice(&self.gutter.x)),
|
"column-gutter" => Some(Sizing::encode_slice(&self.gutter.x)),
|
||||||
"row-gutter" => Some(TrackSizing::encode_slice(&self.gutter.y)),
|
"row-gutter" => Some(Sizing::encode_slice(&self.gutter.y)),
|
||||||
"cells" => Some(Value::Array(
|
"cells" => Some(Value::Array(
|
||||||
self.cells.iter().cloned().map(Value::Content).collect(),
|
self.cells.iter().cloned().map(Value::Content).collect(),
|
||||||
)),
|
)),
|
||||||
@ -156,50 +156,14 @@ impl Layout for GridNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines how to size a grid cell along an axis.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum TrackSizing {
|
|
||||||
/// A track that fits its cell's contents.
|
|
||||||
Auto,
|
|
||||||
/// A track size specified in absolute terms and relative to the parent's
|
|
||||||
/// size.
|
|
||||||
Relative(Rel<Length>),
|
|
||||||
/// A track size specified as a fraction of the remaining free space in the
|
|
||||||
/// parent.
|
|
||||||
Fractional(Fr),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrackSizing {
|
|
||||||
pub fn encode(self) -> Value {
|
|
||||||
match self {
|
|
||||||
Self::Auto => Value::Auto,
|
|
||||||
Self::Relative(rel) => Spacing::Relative(rel).encode(),
|
|
||||||
Self::Fractional(fr) => Spacing::Fractional(fr).encode(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode_slice(vec: &[TrackSizing]) -> Value {
|
|
||||||
Value::Array(vec.iter().copied().map(Self::encode).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Spacing> for TrackSizing {
|
|
||||||
fn from(spacing: Spacing) -> Self {
|
|
||||||
match spacing {
|
|
||||||
Spacing::Relative(rel) => Self::Relative(rel),
|
|
||||||
Spacing::Fractional(fr) => Self::Fractional(fr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Track sizing definitions.
|
/// Track sizing definitions.
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct TrackSizings(pub Vec<TrackSizing>);
|
pub struct TrackSizings(pub Vec<Sizing>);
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
TrackSizings,
|
TrackSizings,
|
||||||
sizing: TrackSizing => Self(vec![sizing]),
|
sizing: Sizing => Self(vec![sizing]),
|
||||||
count: NonZeroUsize => Self(vec![TrackSizing::Auto; count.get()]),
|
count: NonZeroUsize => Self(vec![Sizing::Auto; count.get()]),
|
||||||
values: Array => Self(values
|
values: Array => Self(values
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| v.cast().ok())
|
.filter_map(|v| v.cast().ok())
|
||||||
@ -207,10 +171,10 @@ castable! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
TrackSizing,
|
Sizing,
|
||||||
_: AutoValue => Self::Auto,
|
_: AutoValue => Self::Auto,
|
||||||
v: Rel<Length> => Self::Relative(v),
|
v: Rel<Length> => Self::Rel(v),
|
||||||
v: Fr => Self::Fractional(v),
|
v: Fr => Self::Fr(v),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs grid layout.
|
/// Performs grid layout.
|
||||||
@ -224,9 +188,9 @@ struct GridLayouter<'a, 'v> {
|
|||||||
/// Whether this grid has gutters.
|
/// Whether this grid has gutters.
|
||||||
has_gutter: bool,
|
has_gutter: bool,
|
||||||
/// The column tracks including gutter tracks.
|
/// The column tracks including gutter tracks.
|
||||||
cols: Vec<TrackSizing>,
|
cols: Vec<Sizing>,
|
||||||
/// The row tracks including gutter tracks.
|
/// The row tracks including gutter tracks.
|
||||||
rows: Vec<TrackSizing>,
|
rows: Vec<Sizing>,
|
||||||
/// The regions to layout children into.
|
/// The regions to layout children into.
|
||||||
regions: Regions<'a>,
|
regions: Regions<'a>,
|
||||||
/// The inherited styles.
|
/// The inherited styles.
|
||||||
@ -259,8 +223,8 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
/// This prepares grid layout by unifying content and gutter tracks.
|
/// This prepares grid layout by unifying content and gutter tracks.
|
||||||
fn new(
|
fn new(
|
||||||
vt: &'a mut Vt<'v>,
|
vt: &'a mut Vt<'v>,
|
||||||
tracks: Axes<&[TrackSizing]>,
|
tracks: Axes<&[Sizing]>,
|
||||||
gutter: Axes<&[TrackSizing]>,
|
gutter: Axes<&[Sizing]>,
|
||||||
cells: &'a [Content],
|
cells: &'a [Content],
|
||||||
regions: Regions<'a>,
|
regions: Regions<'a>,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
@ -281,8 +245,8 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let has_gutter = gutter.any(|tracks| !tracks.is_empty());
|
let has_gutter = gutter.any(|tracks| !tracks.is_empty());
|
||||||
let auto = TrackSizing::Auto;
|
let auto = Sizing::Auto;
|
||||||
let zero = TrackSizing::Relative(Rel::zero());
|
let zero = Sizing::Rel(Rel::zero());
|
||||||
let get_or = |tracks: &[_], idx, default| {
|
let get_or = |tracks: &[_], idx, default| {
|
||||||
tracks.get(idx).or(tracks.last()).copied().unwrap_or(default)
|
tracks.get(idx).or(tracks.last()).copied().unwrap_or(default)
|
||||||
};
|
};
|
||||||
@ -352,9 +316,9 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.rows[y] {
|
match self.rows[y] {
|
||||||
TrackSizing::Auto => self.layout_auto_row(y)?,
|
Sizing::Auto => self.layout_auto_row(y)?,
|
||||||
TrackSizing::Relative(v) => self.layout_relative_row(v, y)?,
|
Sizing::Rel(v) => self.layout_relative_row(v, y)?,
|
||||||
TrackSizing::Fractional(v) => {
|
Sizing::Fr(v) => {
|
||||||
self.lrows.push(Row::Fr(v, y));
|
self.lrows.push(Row::Fr(v, y));
|
||||||
self.fr += v;
|
self.fr += v;
|
||||||
}
|
}
|
||||||
@ -377,14 +341,14 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
// fractional tracks.
|
// fractional tracks.
|
||||||
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
||||||
match col {
|
match col {
|
||||||
TrackSizing::Auto => {}
|
Sizing::Auto => {}
|
||||||
TrackSizing::Relative(v) => {
|
Sizing::Rel(v) => {
|
||||||
let resolved =
|
let resolved =
|
||||||
v.resolve(self.styles).relative_to(self.regions.base().x);
|
v.resolve(self.styles).relative_to(self.regions.base().x);
|
||||||
*rcol = resolved;
|
*rcol = resolved;
|
||||||
rel += resolved;
|
rel += resolved;
|
||||||
}
|
}
|
||||||
TrackSizing::Fractional(v) => fr += v,
|
Sizing::Fr(v) => fr += v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +382,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
// Determine size of auto columns by laying out all cells in those
|
// Determine size of auto columns by laying out all cells in those
|
||||||
// columns, measuring them and finding the largest one.
|
// columns, measuring them and finding the largest one.
|
||||||
for (x, &col) in self.cols.iter().enumerate() {
|
for (x, &col) in self.cols.iter().enumerate() {
|
||||||
if col != TrackSizing::Auto {
|
if col != Sizing::Auto {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +392,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
// For relative rows, we can already resolve the correct
|
// For relative rows, we can already resolve the correct
|
||||||
// base and for auto and fr we could only guess anyway.
|
// base and for auto and fr we could only guess anyway.
|
||||||
let height = match self.rows[y] {
|
let height = match self.rows[y] {
|
||||||
TrackSizing::Relative(v) => {
|
Sizing::Rel(v) => {
|
||||||
v.resolve(self.styles).relative_to(self.regions.base().y)
|
v.resolve(self.styles).relative_to(self.regions.base().y)
|
||||||
}
|
}
|
||||||
_ => self.regions.base().y,
|
_ => self.regions.base().y,
|
||||||
@ -456,7 +420,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
||||||
if let TrackSizing::Fractional(v) = col {
|
if let Sizing::Fr(v) = col {
|
||||||
*rcol = v.share(fr, remaining);
|
*rcol = v.share(fr, remaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,7 +443,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
for (&col, &rcol) in self.cols.iter().zip(&self.rcols) {
|
for (&col, &rcol) in self.cols.iter().zip(&self.rcols) {
|
||||||
// Remove an auto column if it is not overlarge (rcol <= fair),
|
// Remove an auto column if it is not overlarge (rcol <= fair),
|
||||||
// but also hasn't already been removed (rcol > last).
|
// but also hasn't already been removed (rcol > last).
|
||||||
if col == TrackSizing::Auto && rcol <= fair && rcol > last {
|
if col == Sizing::Auto && rcol <= fair && rcol > last {
|
||||||
redistribute -= rcol;
|
redistribute -= rcol;
|
||||||
overlarge -= 1;
|
overlarge -= 1;
|
||||||
changed = true;
|
changed = true;
|
||||||
@ -489,7 +453,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
|
|
||||||
// Redistribute space fairly among overlarge columns.
|
// Redistribute space fairly among overlarge columns.
|
||||||
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
for (&col, rcol) in self.cols.iter().zip(&mut self.rcols) {
|
||||||
if col == TrackSizing::Auto && *rcol > fair {
|
if col == Sizing::Auto && *rcol > fair {
|
||||||
*rcol = fair;
|
*rcol = fair;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -597,7 +561,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
|
|||||||
if let Some(cell) = self.cell(x, y) {
|
if let Some(cell) = self.cell(x, y) {
|
||||||
let size = Size::new(rcol, height);
|
let size = Size::new(rcol, height);
|
||||||
let mut pod = Regions::one(size, Axes::splat(true));
|
let mut pod = Regions::one(size, Axes::splat(true));
|
||||||
if self.rows[y] == TrackSizing::Auto {
|
if self.rows[y] == Sizing::Auto {
|
||||||
pod.full = self.regions.full;
|
pod.full = self.regions.full;
|
||||||
}
|
}
|
||||||
let frame = cell.layout(self.vt, self.styles, pod)?.into_frame();
|
let frame = cell.layout(self.vt, self.styles, pod)?.into_frame();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::layout::{BlockNode, GridNode, ParNode, Spacing, TrackSizing};
|
use crate::layout::{BlockNode, GridNode, ParNode, Sizing, Spacing};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::TextNode;
|
use crate::text::TextNode;
|
||||||
|
|
||||||
@ -147,10 +147,10 @@ impl Layout for ListNode {
|
|||||||
|
|
||||||
GridNode {
|
GridNode {
|
||||||
tracks: Axes::with_x(vec![
|
tracks: Axes::with_x(vec![
|
||||||
TrackSizing::Relative(indent.into()),
|
Sizing::Rel(indent.into()),
|
||||||
TrackSizing::Auto,
|
Sizing::Auto,
|
||||||
TrackSizing::Relative(body_indent.into()),
|
Sizing::Rel(body_indent.into()),
|
||||||
TrackSizing::Auto,
|
Sizing::Auto,
|
||||||
]),
|
]),
|
||||||
gutter: Axes::with_y(vec![gutter.into()]),
|
gutter: Axes::with_y(vec![gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
|
@ -227,9 +227,16 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
|
|||||||
|
|
||||||
fn accept(
|
fn accept(
|
||||||
&mut self,
|
&mut self,
|
||||||
content: &'a Content,
|
mut content: &'a Content,
|
||||||
styles: StyleChain<'a>,
|
styles: StyleChain<'a>,
|
||||||
) -> SourceResult<()> {
|
) -> SourceResult<()> {
|
||||||
|
if content.has::<dyn LayoutMath>() && !content.is::<FormulaNode>() {
|
||||||
|
content = self
|
||||||
|
.scratch
|
||||||
|
.content
|
||||||
|
.alloc(FormulaNode { body: content.clone(), block: false }.pack());
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare only if this is the first application for this node.
|
// Prepare only if this is the first application for this node.
|
||||||
if let Some(node) = content.with::<dyn Prepare>() {
|
if let Some(node) = content.with::<dyn Prepare>() {
|
||||||
if !content.is_prepared() {
|
if !content.is_prepared() {
|
||||||
@ -470,22 +477,15 @@ impl<'a> ParBuilder<'a> {
|
|||||||
if content.is::<SpaceNode>()
|
if content.is::<SpaceNode>()
|
||||||
|| content.is::<TextNode>()
|
|| content.is::<TextNode>()
|
||||||
|| content.is::<HNode>()
|
|| content.is::<HNode>()
|
||||||
|| content.is::<SmartQuoteNode>()
|
|
||||||
|| content.is::<LinebreakNode>()
|
|| content.is::<LinebreakNode>()
|
||||||
|| content.is::<BoxNode>()
|
|| content.is::<SmartQuoteNode>()
|
||||||
|| content.is::<RepeatNode>()
|
|
||||||
|| content.to::<FormulaNode>().map_or(false, |node| !node.block)
|
|| content.to::<FormulaNode>().map_or(false, |node| !node.block)
|
||||||
|
|| content.is::<BoxNode>()
|
||||||
{
|
{
|
||||||
self.0.push(content.clone(), styles);
|
self.0.push(content.clone(), styles);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !content.is::<FormulaNode>() && content.has::<dyn LayoutMath>() {
|
|
||||||
let formula = FormulaNode { body: content.clone(), block: false }.pack();
|
|
||||||
self.0.push(formula, styles);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ use xi_unicode::LineBreakIterator;
|
|||||||
|
|
||||||
use typst::model::Key;
|
use typst::model::Key;
|
||||||
|
|
||||||
use super::{HNode, RepeatNode, Spacing};
|
use super::{BoxNode, HNode, Sizing, Spacing};
|
||||||
use crate::layout::AlignNode;
|
use crate::layout::AlignNode;
|
||||||
|
use crate::math::FormulaNode;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::{
|
use crate::text::{
|
||||||
shape, LinebreakNode, Quoter, Quotes, ShapedText, SmartQuoteNode, SpaceNode, TextNode,
|
shape, LinebreakNode, Quoter, Quotes, ShapedText, SmartQuoteNode, SpaceNode, TextNode,
|
||||||
@ -330,8 +331,10 @@ enum Segment<'a> {
|
|||||||
Text(usize),
|
Text(usize),
|
||||||
/// Horizontal spacing between other segments.
|
/// Horizontal spacing between other segments.
|
||||||
Spacing(Spacing),
|
Spacing(Spacing),
|
||||||
/// Arbitrary inline-level content.
|
/// A math formula.
|
||||||
Inline(&'a Content),
|
Formula(&'a FormulaNode),
|
||||||
|
/// A box with arbitrary content.
|
||||||
|
Box(&'a BoxNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Segment<'_> {
|
impl Segment<'_> {
|
||||||
@ -340,7 +343,8 @@ impl Segment<'_> {
|
|||||||
match *self {
|
match *self {
|
||||||
Self::Text(len) => len,
|
Self::Text(len) => len,
|
||||||
Self::Spacing(_) => SPACING_REPLACE.len_utf8(),
|
Self::Spacing(_) => SPACING_REPLACE.len_utf8(),
|
||||||
Self::Inline(_) => NODE_REPLACE.len_utf8(),
|
Self::Box(node) if node.width.is_fractional() => SPACING_REPLACE.len_utf8(),
|
||||||
|
Self::Formula(_) | Self::Box(_) => NODE_REPLACE.len_utf8(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,11 +357,9 @@ enum Item<'a> {
|
|||||||
/// Absolute spacing between other items.
|
/// Absolute spacing between other items.
|
||||||
Absolute(Abs),
|
Absolute(Abs),
|
||||||
/// Fractional spacing between other items.
|
/// Fractional spacing between other items.
|
||||||
Fractional(Fr),
|
Fractional(Fr, Option<(&'a BoxNode, StyleChain<'a>)>),
|
||||||
/// Layouted inline-level content.
|
/// Layouted inline-level content.
|
||||||
Frame(Frame),
|
Frame(Frame),
|
||||||
/// A repeating node that fills the remaining space in a line.
|
|
||||||
Repeat(&'a RepeatNode, StyleChain<'a>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Item<'a> {
|
impl<'a> Item<'a> {
|
||||||
@ -373,8 +375,8 @@ impl<'a> Item<'a> {
|
|||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Text(shaped) => shaped.text.len(),
|
Self::Text(shaped) => shaped.text.len(),
|
||||||
Self::Absolute(_) | Self::Fractional(_) => SPACING_REPLACE.len_utf8(),
|
Self::Absolute(_) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(),
|
||||||
Self::Frame(_) | Self::Repeat(_, _) => NODE_REPLACE.len_utf8(),
|
Self::Frame(_) => NODE_REPLACE.len_utf8(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +386,7 @@ impl<'a> Item<'a> {
|
|||||||
Self::Text(shaped) => shaped.width,
|
Self::Text(shaped) => shaped.width,
|
||||||
Self::Absolute(v) => *v,
|
Self::Absolute(v) => *v,
|
||||||
Self::Frame(frame) => frame.width(),
|
Self::Frame(frame) => frame.width(),
|
||||||
Self::Fractional(_) | Self::Repeat(_, _) => Abs::zero(),
|
Self::Fractional(_, _) => Abs::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,8 +475,7 @@ impl<'a> Line<'a> {
|
|||||||
fn fr(&self) -> Fr {
|
fn fr(&self) -> Fr {
|
||||||
self.items()
|
self.items()
|
||||||
.filter_map(|item| match item {
|
.filter_map(|item| match item {
|
||||||
Item::Fractional(fr) => Some(*fr),
|
Item::Fractional(fr, _) => Some(*fr),
|
||||||
Item::Repeat(_, _) => Some(Fr::one()),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.sum()
|
.sum()
|
||||||
@ -530,6 +531,9 @@ fn collect<'a>(
|
|||||||
full.push_str(&node.0);
|
full.push_str(&node.0);
|
||||||
}
|
}
|
||||||
Segment::Text(full.len() - prev)
|
Segment::Text(full.len() - prev)
|
||||||
|
} else if let Some(&node) = child.to::<HNode>() {
|
||||||
|
full.push(SPACING_REPLACE);
|
||||||
|
Segment::Spacing(node.amount)
|
||||||
} else if let Some(node) = child.to::<LinebreakNode>() {
|
} else if let Some(node) = child.to::<LinebreakNode>() {
|
||||||
let c = if node.justify { '\u{2028}' } else { '\n' };
|
let c = if node.justify { '\u{2028}' } else { '\n' };
|
||||||
full.push(c);
|
full.push(c);
|
||||||
@ -557,12 +561,18 @@ fn collect<'a>(
|
|||||||
full.push(if node.double { '"' } else { '\'' });
|
full.push(if node.double { '"' } else { '\'' });
|
||||||
}
|
}
|
||||||
Segment::Text(full.len() - prev)
|
Segment::Text(full.len() - prev)
|
||||||
} else if let Some(&node) = child.to::<HNode>() {
|
} else if let Some(node) = child.to::<FormulaNode>() {
|
||||||
full.push(SPACING_REPLACE);
|
|
||||||
Segment::Spacing(node.amount)
|
|
||||||
} else {
|
|
||||||
full.push(NODE_REPLACE);
|
full.push(NODE_REPLACE);
|
||||||
Segment::Inline(child)
|
Segment::Formula(node)
|
||||||
|
} else if let Some(node) = child.to::<BoxNode>() {
|
||||||
|
full.push(if node.width.is_fractional() {
|
||||||
|
SPACING_REPLACE
|
||||||
|
} else {
|
||||||
|
NODE_REPLACE
|
||||||
|
});
|
||||||
|
Segment::Box(node)
|
||||||
|
} else {
|
||||||
|
panic!("unexpected par child: {child:?}");
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(last) = full.chars().last() {
|
if let Some(last) = full.chars().last() {
|
||||||
@ -614,20 +624,26 @@ fn prepare<'a>(
|
|||||||
shape_range(&mut items, vt, &bidi, cursor..end, styles);
|
shape_range(&mut items, vt, &bidi, cursor..end, styles);
|
||||||
}
|
}
|
||||||
Segment::Spacing(spacing) => match spacing {
|
Segment::Spacing(spacing) => match spacing {
|
||||||
Spacing::Relative(v) => {
|
Spacing::Rel(v) => {
|
||||||
let resolved = v.resolve(styles).relative_to(region.x);
|
let resolved = v.resolve(styles).relative_to(region.x);
|
||||||
items.push(Item::Absolute(resolved));
|
items.push(Item::Absolute(resolved));
|
||||||
}
|
}
|
||||||
Spacing::Fractional(v) => {
|
Spacing::Fr(v) => {
|
||||||
items.push(Item::Fractional(v));
|
items.push(Item::Fractional(v, None));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Segment::Inline(inline) => {
|
Segment::Formula(formula) => {
|
||||||
if let Some(repeat) = inline.to::<RepeatNode>() {
|
let pod = Regions::one(region, Axes::splat(false));
|
||||||
items.push(Item::Repeat(repeat, styles));
|
let mut frame = formula.layout(vt, styles, pod)?.into_frame();
|
||||||
|
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
||||||
|
items.push(Item::Frame(frame));
|
||||||
|
}
|
||||||
|
Segment::Box(node) => {
|
||||||
|
if let Sizing::Fr(v) = node.width {
|
||||||
|
items.push(Item::Fractional(v, Some((node, styles))));
|
||||||
} else {
|
} else {
|
||||||
let pod = Regions::one(region, Axes::splat(false));
|
let pod = Regions::one(region, Axes::splat(false));
|
||||||
let mut frame = inline.layout(vt, styles, pod)?.into_frame();
|
let mut frame = node.layout(vt, styles, pod)?.into_frame();
|
||||||
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
||||||
items.push(Item::Frame(frame));
|
items.push(Item::Frame(frame));
|
||||||
}
|
}
|
||||||
@ -1111,20 +1127,23 @@ fn finalize(
|
|||||||
vt: &mut Vt,
|
vt: &mut Vt,
|
||||||
p: &Preparation,
|
p: &Preparation,
|
||||||
lines: &[Line],
|
lines: &[Line],
|
||||||
mut region: Size,
|
region: Size,
|
||||||
expand: bool,
|
expand: bool,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// Determine the paragraph's width: Full width of the region if we
|
// Determine the paragraph's width: Full width of the region if we
|
||||||
// should expand or there's fractional spacing, fit-to-width otherwise.
|
// should expand or there's fractional spacing, fit-to-width otherwise.
|
||||||
if !region.x.is_finite() || (!expand && lines.iter().all(|line| line.fr().is_zero()))
|
let width = if !region.x.is_finite()
|
||||||
|
|| (!expand && lines.iter().all(|line| line.fr().is_zero()))
|
||||||
{
|
{
|
||||||
region.x = lines.iter().map(|line| line.width).max().unwrap_or_default();
|
lines.iter().map(|line| line.width).max().unwrap_or_default()
|
||||||
}
|
} else {
|
||||||
|
region.x
|
||||||
|
};
|
||||||
|
|
||||||
// Stack the lines into one frame per region.
|
// Stack the lines into one frame per region.
|
||||||
let mut frames: Vec<Frame> = lines
|
let mut frames: Vec<Frame> = lines
|
||||||
.iter()
|
.iter()
|
||||||
.map(|line| commit(vt, p, line, region))
|
.map(|line| commit(vt, p, line, width, region.y))
|
||||||
.collect::<SourceResult<_>>()?;
|
.collect::<SourceResult<_>>()?;
|
||||||
|
|
||||||
// Prevent orphans.
|
// Prevent orphans.
|
||||||
@ -1159,9 +1178,10 @@ fn commit(
|
|||||||
vt: &mut Vt,
|
vt: &mut Vt,
|
||||||
p: &Preparation,
|
p: &Preparation,
|
||||||
line: &Line,
|
line: &Line,
|
||||||
region: Size,
|
width: Abs,
|
||||||
|
full: Abs,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let mut remaining = region.x - line.width;
|
let mut remaining = width - line.width;
|
||||||
let mut offset = Abs::zero();
|
let mut offset = Abs::zero();
|
||||||
|
|
||||||
// Reorder the line from logical to visual order.
|
// Reorder the line from logical to visual order.
|
||||||
@ -1223,8 +1243,17 @@ fn commit(
|
|||||||
Item::Absolute(v) => {
|
Item::Absolute(v) => {
|
||||||
offset += *v;
|
offset += *v;
|
||||||
}
|
}
|
||||||
Item::Fractional(v) => {
|
Item::Fractional(v, node) => {
|
||||||
offset += v.share(fr, remaining);
|
let amount = v.share(fr, remaining);
|
||||||
|
if let Some((node, styles)) = node {
|
||||||
|
let region = Size::new(amount, full);
|
||||||
|
let pod = Regions::one(region, Axes::new(true, false));
|
||||||
|
let mut frame = node.layout(vt, *styles, pod)?.into_frame();
|
||||||
|
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
|
||||||
|
push(&mut offset, frame);
|
||||||
|
} else {
|
||||||
|
offset += amount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Item::Text(shaped) => {
|
Item::Text(shaped) => {
|
||||||
let frame = shaped.build(vt, justification);
|
let frame = shaped.build(vt, justification);
|
||||||
@ -1233,27 +1262,6 @@ fn commit(
|
|||||||
Item::Frame(frame) => {
|
Item::Frame(frame) => {
|
||||||
push(&mut offset, frame.clone());
|
push(&mut offset, frame.clone());
|
||||||
}
|
}
|
||||||
Item::Repeat(repeat, styles) => {
|
|
||||||
let before = offset;
|
|
||||||
let fill = Fr::one().share(fr, remaining);
|
|
||||||
let size = Size::new(fill, region.y);
|
|
||||||
let pod = Regions::one(size, Axes::new(false, false));
|
|
||||||
let frame = repeat.layout(vt, *styles, pod)?.into_frame();
|
|
||||||
let width = frame.width();
|
|
||||||
let count = (fill / width).floor();
|
|
||||||
let remaining = fill % width;
|
|
||||||
let apart = remaining / (count - 1.0);
|
|
||||||
if count == 1.0 {
|
|
||||||
offset += p.align.position(remaining);
|
|
||||||
}
|
|
||||||
if width > Abs::zero() {
|
|
||||||
for _ in 0..(count as usize).min(1000) {
|
|
||||||
push(&mut offset, frame.clone());
|
|
||||||
offset += apart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offset = before + fill;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1262,7 +1270,7 @@ fn commit(
|
|||||||
remaining = Abs::zero();
|
remaining = Abs::zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = Size::new(region.x, top + bottom);
|
let size = Size::new(width, top + bottom);
|
||||||
let mut output = Frame::new(size);
|
let mut output = Frame::new(size);
|
||||||
output.set_baseline(top);
|
output.set_baseline(top);
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use super::AlignNode;
|
||||||
|
|
||||||
/// # Repeat
|
/// # Repeat
|
||||||
/// Repeats content to fill a line.
|
/// Repeats content to the available space.
|
||||||
///
|
///
|
||||||
/// This can be useful when implementing a custom index, reference, or outline.
|
/// This can be useful when implementing a custom index, reference, or outline.
|
||||||
///
|
///
|
||||||
@ -10,7 +12,8 @@ use crate::prelude::*;
|
|||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```example
|
/// ```example
|
||||||
/// Sign on the dotted line: #repeat[.]
|
/// Sign on the dotted line:
|
||||||
|
/// #box(width: 1fr, repeat[.])
|
||||||
///
|
///
|
||||||
/// #set text(10pt)
|
/// #set text(10pt)
|
||||||
/// #v(8pt, weak: true)
|
/// #v(8pt, weak: true)
|
||||||
@ -51,6 +54,34 @@ impl Layout for RepeatNode {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
self.0.layout(vt, styles, regions)
|
let pod = Regions::one(regions.size, Axes::new(false, false));
|
||||||
|
let piece = self.0.layout(vt, styles, pod)?.into_frame();
|
||||||
|
let align = styles.get(AlignNode::ALIGNS).x.resolve(styles);
|
||||||
|
|
||||||
|
let fill = regions.size.x;
|
||||||
|
let width = piece.width();
|
||||||
|
let count = (fill / width).floor();
|
||||||
|
let remaining = fill % width;
|
||||||
|
let apart = remaining / (count - 1.0);
|
||||||
|
|
||||||
|
let size = Size::new(regions.size.x, piece.height());
|
||||||
|
let mut frame = Frame::new(size);
|
||||||
|
if piece.has_baseline() {
|
||||||
|
frame.set_baseline(piece.baseline());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = Abs::zero();
|
||||||
|
if count == 1.0 {
|
||||||
|
offset += align.position(remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
if width > Abs::zero() {
|
||||||
|
for _ in 0..(count as usize).min(1000) {
|
||||||
|
frame.push_frame(Point::with_x(offset), piece.clone());
|
||||||
|
offset += piece.width() + apart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Fragment::frame(frame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,22 +222,22 @@ impl Behave for VNode {
|
|||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Spacing {
|
pub enum Spacing {
|
||||||
/// Spacing specified in absolute terms and relative to the parent's size.
|
/// Spacing specified in absolute terms and relative to the parent's size.
|
||||||
Relative(Rel<Length>),
|
Rel(Rel<Length>),
|
||||||
/// Spacing specified as a fraction of the remaining free space in the
|
/// Spacing specified as a fraction of the remaining free space in the
|
||||||
/// parent.
|
/// parent.
|
||||||
Fractional(Fr),
|
Fr(Fr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spacing {
|
impl Spacing {
|
||||||
/// Whether this is fractional spacing.
|
/// Whether this is fractional spacing.
|
||||||
pub fn is_fractional(self) -> bool {
|
pub fn is_fractional(self) -> bool {
|
||||||
matches!(self, Self::Fractional(_))
|
matches!(self, Self::Fr(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode into a value.
|
/// Encode into a value.
|
||||||
pub fn encode(self) -> Value {
|
pub fn encode(self) -> Value {
|
||||||
match self {
|
match self {
|
||||||
Self::Relative(rel) => {
|
Self::Rel(rel) => {
|
||||||
if rel.rel.is_zero() {
|
if rel.rel.is_zero() {
|
||||||
Value::Length(rel.abs)
|
Value::Length(rel.abs)
|
||||||
} else if rel.abs.is_zero() {
|
} else if rel.abs.is_zero() {
|
||||||
@ -246,28 +246,28 @@ impl Spacing {
|
|||||||
Value::Relative(rel)
|
Value::Relative(rel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Fractional(fr) => Value::Fraction(fr),
|
Self::Fr(fr) => Value::Fraction(fr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Abs> for Spacing {
|
impl From<Abs> for Spacing {
|
||||||
fn from(abs: Abs) -> Self {
|
fn from(abs: Abs) -> Self {
|
||||||
Self::Relative(abs.into())
|
Self::Rel(abs.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Em> for Spacing {
|
impl From<Em> for Spacing {
|
||||||
fn from(em: Em) -> Self {
|
fn from(em: Em) -> Self {
|
||||||
Self::Relative(Rel::new(Ratio::zero(), em.into()))
|
Self::Rel(Rel::new(Ratio::zero(), em.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Spacing {
|
impl PartialOrd for Spacing {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Relative(a), Self::Relative(b)) => a.partial_cmp(b),
|
(Self::Rel(a), Self::Rel(b)) => a.partial_cmp(b),
|
||||||
(Self::Fractional(a), Self::Fractional(b)) => a.partial_cmp(b),
|
(Self::Fr(a), Self::Fr(b)) => a.partial_cmp(b),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,6 +275,6 @@ impl PartialOrd for Spacing {
|
|||||||
|
|
||||||
castable! {
|
castable! {
|
||||||
Spacing,
|
Spacing,
|
||||||
v: Rel<Length> => Self::Relative(v),
|
v: Rel<Length> => Self::Rel(v),
|
||||||
v: Fr => Self::Fractional(v),
|
v: Fr => Self::Fr(v),
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
/// Add spacing along the spacing direction.
|
/// Add spacing along the spacing direction.
|
||||||
fn layout_spacing(&mut self, spacing: Spacing) {
|
fn layout_spacing(&mut self, spacing: Spacing) {
|
||||||
match spacing {
|
match spacing {
|
||||||
Spacing::Relative(v) => {
|
Spacing::Rel(v) => {
|
||||||
// Resolve the spacing and limit it to the remaining space.
|
// Resolve the spacing and limit it to the remaining space.
|
||||||
let resolved = v
|
let resolved = v
|
||||||
.resolve(self.styles)
|
.resolve(self.styles)
|
||||||
@ -213,7 +213,7 @@ impl<'a> StackLayouter<'a> {
|
|||||||
self.used.main += limited;
|
self.used.main += limited;
|
||||||
self.items.push(StackItem::Absolute(resolved));
|
self.items.push(StackItem::Absolute(resolved));
|
||||||
}
|
}
|
||||||
Spacing::Fractional(v) => {
|
Spacing::Fr(v) => {
|
||||||
self.fr += v;
|
self.fr += v;
|
||||||
self.items.push(StackItem::Fractional(v));
|
self.items.push(StackItem::Fractional(v));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::layout::{AlignNode, GridNode, TrackSizing, TrackSizings};
|
use crate::layout::{AlignNode, GridNode, Sizing, TrackSizings};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// # Table
|
/// # Table
|
||||||
@ -63,9 +63,9 @@ use crate::prelude::*;
|
|||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct TableNode {
|
pub struct TableNode {
|
||||||
/// Defines sizing for content rows and columns.
|
/// Defines sizing for content rows and columns.
|
||||||
pub tracks: Axes<Vec<TrackSizing>>,
|
pub tracks: Axes<Vec<Sizing>>,
|
||||||
/// Defines sizing of gutter rows and columns between content.
|
/// Defines sizing of gutter rows and columns between content.
|
||||||
pub gutter: Axes<Vec<TrackSizing>>,
|
pub gutter: Axes<Vec<Sizing>>,
|
||||||
/// The content to be arranged in the table.
|
/// The content to be arranged in the table.
|
||||||
pub cells: Vec<Content>,
|
pub cells: Vec<Content>,
|
||||||
}
|
}
|
||||||
@ -134,10 +134,10 @@ impl TableNode {
|
|||||||
|
|
||||||
fn field(&self, name: &str) -> Option<Value> {
|
fn field(&self, name: &str) -> Option<Value> {
|
||||||
match name {
|
match name {
|
||||||
"columns" => Some(TrackSizing::encode_slice(&self.tracks.x)),
|
"columns" => Some(Sizing::encode_slice(&self.tracks.x)),
|
||||||
"rows" => Some(TrackSizing::encode_slice(&self.tracks.y)),
|
"rows" => Some(Sizing::encode_slice(&self.tracks.y)),
|
||||||
"column-gutter" => Some(TrackSizing::encode_slice(&self.gutter.x)),
|
"column-gutter" => Some(Sizing::encode_slice(&self.gutter.x)),
|
||||||
"row-gutter" => Some(TrackSizing::encode_slice(&self.gutter.y)),
|
"row-gutter" => Some(Sizing::encode_slice(&self.gutter.y)),
|
||||||
"cells" => Some(Value::Array(
|
"cells" => Some(Value::Array(
|
||||||
self.cells.iter().cloned().map(Value::Content).collect(),
|
self.cells.iter().cloned().map(Value::Content).collect(),
|
||||||
)),
|
)),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::layout::{BlockNode, GridNode, HNode, ParNode, Spacing, TrackSizing};
|
use crate::layout::{BlockNode, GridNode, HNode, ParNode, Sizing, Spacing};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::{SpaceNode, TextNode};
|
use crate::text::{SpaceNode, TextNode};
|
||||||
|
|
||||||
@ -136,8 +136,8 @@ impl Layout for TermsNode {
|
|||||||
|
|
||||||
GridNode {
|
GridNode {
|
||||||
tracks: Axes::with_x(vec![
|
tracks: Axes::with_x(vec![
|
||||||
TrackSizing::Relative((indent + body_indent).into()),
|
Sizing::Rel((indent + body_indent).into()),
|
||||||
TrackSizing::Auto,
|
Sizing::Auto,
|
||||||
]),
|
]),
|
||||||
gutter: Axes::with_y(vec![gutter.into()]),
|
gutter: Axes::with_y(vec![gutter.into()]),
|
||||||
cells,
|
cells,
|
||||||
|
@ -277,7 +277,7 @@ impl LayoutMath for Content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(node) = self.to::<HNode>() {
|
if let Some(node) = self.to::<HNode>() {
|
||||||
if let Spacing::Relative(rel) = node.amount {
|
if let Spacing::Rel(rel) = node.amount {
|
||||||
if rel.rel.is_zero() {
|
if rel.rel.is_zero() {
|
||||||
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
|
ctx.push(MathFragment::Spacing(rel.abs.resolve(ctx.styles())));
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use super::HeadingNode;
|
use super::HeadingNode;
|
||||||
use crate::layout::{HNode, HideNode, ParbreakNode, RepeatNode, Spacing};
|
use crate::layout::{
|
||||||
|
BoxNode, HNode, HideNode, ParbreakNode, RepeatNode, Sizing, Spacing,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::{LinebreakNode, SpaceNode, TextNode};
|
use crate::text::{LinebreakNode, SpaceNode, TextNode};
|
||||||
|
|
||||||
@ -180,10 +182,18 @@ impl Show for OutlineNode {
|
|||||||
// Add filler symbols between the section name and page number.
|
// Add filler symbols between the section name and page number.
|
||||||
if let Some(filler) = styles.get(Self::FILL) {
|
if let Some(filler) = styles.get(Self::FILL) {
|
||||||
seq.push(SpaceNode.pack());
|
seq.push(SpaceNode.pack());
|
||||||
seq.push(RepeatNode(filler.clone()).pack());
|
seq.push(
|
||||||
|
BoxNode {
|
||||||
|
body: RepeatNode(filler.clone()).pack(),
|
||||||
|
width: Sizing::Fr(Fr::one()),
|
||||||
|
height: Smart::Auto,
|
||||||
|
baseline: Rel::zero(),
|
||||||
|
}
|
||||||
|
.pack(),
|
||||||
|
);
|
||||||
seq.push(SpaceNode.pack());
|
seq.push(SpaceNode.pack());
|
||||||
} else {
|
} else {
|
||||||
let amount = Spacing::Fractional(Fr::one());
|
let amount = Spacing::Fr(Fr::one());
|
||||||
seq.push(HNode { amount, weak: false }.pack());
|
seq.push(HNode { amount, weak: false }.pack());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ impl Frame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the frame has a non-default baseline.
|
/// Whether the frame has a non-default baseline.
|
||||||
pub fn has_baseline(&mut self) -> bool {
|
pub fn has_baseline(&self) -> bool {
|
||||||
self.baseline.is_some()
|
self.baseline.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 8.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.0 KiB |
@ -9,6 +9,10 @@ Spaced \
|
|||||||
#box(height: 0.5cm) \
|
#box(height: 0.5cm) \
|
||||||
Apart
|
Apart
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test fr box.
|
||||||
|
Hello #box(width: 1fr, rect(height: 0.7em, width: 100%)) World
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test block over multiple pages.
|
// Test block over multiple pages.
|
||||||
|
|
||||||
|
@ -12,28 +12,28 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
#for section in sections [
|
#for section in sections [
|
||||||
#section.at(0) #repeat[.] #section.at(1) \
|
#section.at(0) #box(width: 1fr, repeat[.]) #section.at(1) \
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test dots with RTL.
|
// Test dots with RTL.
|
||||||
#set text(lang: "ar")
|
#set text(lang: "ar")
|
||||||
مقدمة #repeat[.] 15
|
مقدمة #box(width: 1fr, repeat[.]) 15
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test empty repeat.
|
// Test empty repeat.
|
||||||
A #repeat[] B
|
A #box(width: 1fr, repeat[]) B
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test spaceless repeat.
|
// Test unboxed repeat.
|
||||||
A#repeat(rect(width: 2.5em, height: 1em))B
|
#repeat(rect(width: 2em, height: 1em))
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test single repeat in both directions.
|
// Test single repeat in both directions.
|
||||||
A#repeat(rect(width: 6em, height: 0.7em))B
|
A#box(width: 1fr, repeat(rect(width: 6em, height: 0.7em)))B
|
||||||
|
|
||||||
#set align(center)
|
#set align(center)
|
||||||
A#repeat(rect(width: 6em, height: 0.7em))B
|
A#box(width: 1fr, repeat(rect(width: 6em, height: 0.7em)))B
|
||||||
|
|
||||||
#set text(dir: rtl)
|
#set text(dir: rtl)
|
||||||
ريجين#repeat(rect(width: 4em, height: 0.7em))سون
|
ريجين#box(width: 1fr, repeat(rect(width: 4em, height: 0.7em)))سون
|
||||||
|
Loading…
x
Reference in New Issue
Block a user