mirror of
https://github.com/typst/typst
synced 2025-06-28 08:12:53 +08:00
Layout improvements
This commit is contained in:
parent
304d9dd110
commit
393d74f9bb
@ -6,7 +6,7 @@ use std::ops::{Add, AddAssign};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Paint, Sides, Size};
|
use crate::geom::{Align, Dir, Length, Linear, Paint, Sides, Size, SpecAxis};
|
||||||
use crate::layout::{Layout, PackedNode};
|
use crate::layout::{Layout, PackedNode};
|
||||||
use crate::library::{
|
use crate::library::{
|
||||||
Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode,
|
Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode,
|
||||||
@ -33,7 +33,7 @@ enum TemplateNode {
|
|||||||
/// Plain text.
|
/// Plain text.
|
||||||
Text(EcoString),
|
Text(EcoString),
|
||||||
/// Spacing.
|
/// Spacing.
|
||||||
Spacing(GenAxis, Spacing),
|
Spacing(SpecAxis, Spacing),
|
||||||
/// A decorated template.
|
/// A decorated template.
|
||||||
Decorated(Decoration, Template),
|
Decorated(Decoration, Template),
|
||||||
/// An inline node builder.
|
/// An inline node builder.
|
||||||
@ -108,7 +108,7 @@ impl Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add spacing along an axis.
|
/// Add spacing along an axis.
|
||||||
pub fn spacing(&mut self, axis: GenAxis, spacing: Spacing) {
|
pub fn spacing(&mut self, axis: SpecAxis, spacing: Spacing) {
|
||||||
self.make_mut().push(TemplateNode::Spacing(axis, spacing));
|
self.make_mut().push(TemplateNode::Spacing(axis, spacing));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,13 +349,13 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Push spacing into the active paragraph or flow depending on the `axis`.
|
/// Push spacing into the active paragraph or flow depending on the `axis`.
|
||||||
fn spacing(&mut self, axis: GenAxis, spacing: Spacing) {
|
fn spacing(&mut self, axis: SpecAxis, spacing: Spacing) {
|
||||||
match axis {
|
match axis {
|
||||||
GenAxis::Block => {
|
SpecAxis::Vertical => {
|
||||||
self.flow.finish_par(&self.style);
|
self.flow.finish_par(&self.style);
|
||||||
self.flow.push_hard(FlowChild::Spacing(spacing));
|
self.flow.push_hard(FlowChild::Spacing(spacing));
|
||||||
}
|
}
|
||||||
GenAxis::Inline => {
|
SpecAxis::Horizontal => {
|
||||||
self.flow.par.push_hard(ParChild::Spacing(spacing));
|
self.flow.par.push_hard(ParChild::Spacing(spacing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
src/frame.rs
54
src/frame.rs
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::font::FaceId;
|
use crate::font::FaceId;
|
||||||
use crate::geom::{Em, Length, Paint, Path, Point, Size, Transform};
|
use crate::geom::{Align, Em, Length, Paint, Path, Point, Size, Spec, Transform};
|
||||||
use crate::image::ImageId;
|
use crate::image::ImageId;
|
||||||
|
|
||||||
/// A finished layout with elements at fixed positions.
|
/// A finished layout with elements at fixed positions.
|
||||||
@ -23,9 +23,9 @@ pub struct Frame {
|
|||||||
impl Frame {
|
impl Frame {
|
||||||
/// Create a new, empty frame.
|
/// Create a new, empty frame.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(size: Size, baseline: Length) -> Self {
|
pub fn new(size: Size) -> Self {
|
||||||
assert!(size.is_finite());
|
assert!(size.is_finite());
|
||||||
Self { size, baseline, elements: vec![] }
|
Self { size, baseline: size.h, elements: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an element at a position in the background.
|
/// Add an element at a position in the background.
|
||||||
@ -55,12 +55,46 @@ impl Frame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move all elements in the frame by an offset.
|
/// Resize the frame to a new size, distributing new space according to the
|
||||||
|
/// given alignments.
|
||||||
|
pub fn resize(&mut self, new: Size, aligns: Spec<Align>) {
|
||||||
|
let offset = Point::new(
|
||||||
|
aligns.x.resolve(new.w - self.size.w),
|
||||||
|
aligns.y.resolve(new.h - self.size.h),
|
||||||
|
);
|
||||||
|
self.size = new;
|
||||||
|
self.baseline += offset.y;
|
||||||
|
self.translate(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the contents of the frame by an offset.
|
||||||
pub fn translate(&mut self, offset: Point) {
|
pub fn translate(&mut self, offset: Point) {
|
||||||
for (point, _) in &mut self.elements {
|
for (point, _) in &mut self.elements {
|
||||||
*point += offset;
|
*point += offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Arbitrarily transform the contents of the frame.
|
||||||
|
pub fn transform(&mut self, transform: Transform) {
|
||||||
|
self.group(|g| g.transform = transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clip the contents of a frame to its size.
|
||||||
|
pub fn clip(&mut self) {
|
||||||
|
self.group(|g| g.clips = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap the frame's contents in a group and modify that group with `f`.
|
||||||
|
pub fn group<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Group),
|
||||||
|
{
|
||||||
|
let mut wrapper = Frame { elements: vec![], ..*self };
|
||||||
|
let mut group = Group::new(Rc::new(std::mem::take(self)));
|
||||||
|
f(&mut group);
|
||||||
|
wrapper.push(Point::zero(), Element::Group(group));
|
||||||
|
*self = wrapper;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Frame {
|
impl Debug for Frame {
|
||||||
@ -116,18 +150,6 @@ impl Group {
|
|||||||
clips: false,
|
clips: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the group's transform.
|
|
||||||
pub fn transform(mut self, transform: Transform) -> Self {
|
|
||||||
self.transform = transform;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set whether the group should be a clipping boundary.
|
|
||||||
pub fn clips(mut self, clips: bool) -> Self {
|
|
||||||
self.clips = clips;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A run of shaped text.
|
/// A run of shaped text.
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// A container with an inline and a block component.
|
/// A container with a main and cross component.
|
||||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Gen<T> {
|
pub struct Gen<T> {
|
||||||
/// The inline component.
|
/// The main component.
|
||||||
pub inline: T,
|
pub cross: T,
|
||||||
/// The block component.
|
/// The cross component.
|
||||||
pub block: T,
|
pub main: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Gen<T> {
|
impl<T> Gen<T> {
|
||||||
/// Create a new instance from the two components.
|
/// Create a new instance from the two components.
|
||||||
pub const fn new(inline: T, block: T) -> Self {
|
pub const fn new(cross: T, main: T) -> Self {
|
||||||
Self { inline, block }
|
Self { cross, main }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new instance with two equal components.
|
/// Create a new instance with two equal components.
|
||||||
@ -20,7 +20,7 @@ impl<T> Gen<T> {
|
|||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
{
|
{
|
||||||
Self { inline: value.clone(), block: value }
|
Self { cross: value.clone(), main: value }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps the individual fields with `f`.
|
/// Maps the individual fields with `f`.
|
||||||
@ -28,17 +28,14 @@ impl<T> Gen<T> {
|
|||||||
where
|
where
|
||||||
F: FnMut(T) -> U,
|
F: FnMut(T) -> U,
|
||||||
{
|
{
|
||||||
Gen {
|
Gen { cross: f(self.cross), main: f(self.main) }
|
||||||
inline: f(self.inline),
|
|
||||||
block: f(self.block),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to the specific representation, given the current block axis.
|
/// Convert to the specific representation, given the current block axis.
|
||||||
pub fn to_spec(self, block: SpecAxis) -> Spec<T> {
|
pub fn to_spec(self, main: SpecAxis) -> Spec<T> {
|
||||||
match block {
|
match main {
|
||||||
SpecAxis::Horizontal => Spec::new(self.block, self.inline),
|
SpecAxis::Horizontal => Spec::new(self.main, self.cross),
|
||||||
SpecAxis::Vertical => Spec::new(self.inline, self.block),
|
SpecAxis::Vertical => Spec::new(self.cross, self.main),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,19 +44,19 @@ impl Gen<Length> {
|
|||||||
/// The zero value.
|
/// The zero value.
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inline: Length::zero(),
|
cross: Length::zero(),
|
||||||
block: Length::zero(),
|
main: Length::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to a point.
|
/// Convert to a point.
|
||||||
pub fn to_point(self, block: SpecAxis) -> Point {
|
pub fn to_point(self, main: SpecAxis) -> Point {
|
||||||
self.to_spec(block).to_point()
|
self.to_spec(main).to_point()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to a size.
|
/// Convert to a size.
|
||||||
pub fn to_size(self, block: SpecAxis) -> Size {
|
pub fn to_size(self, main: SpecAxis) -> Size {
|
||||||
self.to_spec(block).to_size()
|
self.to_spec(main).to_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +64,8 @@ impl<T> Gen<Option<T>> {
|
|||||||
/// Unwrap the individual fields.
|
/// Unwrap the individual fields.
|
||||||
pub fn unwrap_or(self, other: Gen<T>) -> Gen<T> {
|
pub fn unwrap_or(self, other: Gen<T>) -> Gen<T> {
|
||||||
Gen {
|
Gen {
|
||||||
inline: self.inline.unwrap_or(other.inline),
|
cross: self.cross.unwrap_or(other.cross),
|
||||||
block: self.block.unwrap_or(other.block),
|
main: self.main.unwrap_or(other.main),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,40 +75,40 @@ impl<T> Get<GenAxis> for Gen<T> {
|
|||||||
|
|
||||||
fn get(self, axis: GenAxis) -> T {
|
fn get(self, axis: GenAxis) -> T {
|
||||||
match axis {
|
match axis {
|
||||||
GenAxis::Inline => self.inline,
|
GenAxis::Cross => self.cross,
|
||||||
GenAxis::Block => self.block,
|
GenAxis::Main => self.main,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mut(&mut self, axis: GenAxis) -> &mut T {
|
fn get_mut(&mut self, axis: GenAxis) -> &mut T {
|
||||||
match axis {
|
match axis {
|
||||||
GenAxis::Inline => &mut self.inline,
|
GenAxis::Cross => &mut self.cross,
|
||||||
GenAxis::Block => &mut self.block,
|
GenAxis::Main => &mut self.main,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug> Debug for Gen<T> {
|
impl<T: Debug> Debug for Gen<T> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "Gen({:?}, {:?})", self.inline, self.block)
|
write!(f, "Gen({:?}, {:?})", self.cross, self.main)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The two generic layouting axes.
|
/// Two generic axes of a container.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum GenAxis {
|
pub enum GenAxis {
|
||||||
/// The axis words and lines are set along.
|
/// The minor axis.
|
||||||
Inline,
|
Cross,
|
||||||
/// The axis paragraphs and pages are set along.
|
/// The major axis.
|
||||||
Block,
|
Main,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenAxis {
|
impl GenAxis {
|
||||||
/// The other axis.
|
/// The other axis.
|
||||||
pub fn other(self) -> Self {
|
pub fn other(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Inline => Self::Block,
|
Self::Cross => Self::Main,
|
||||||
Self::Block => Self::Inline,
|
Self::Main => Self::Cross,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ impl Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to the generic representation.
|
/// Convert to the generic representation.
|
||||||
pub const fn to_gen(self, block: SpecAxis) -> Gen<Length> {
|
pub const fn to_gen(self, main: SpecAxis) -> Gen<Length> {
|
||||||
match block {
|
match main {
|
||||||
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
||||||
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,8 @@ impl Size {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to the generic representation.
|
/// Convert to the generic representation.
|
||||||
pub const fn to_gen(self, block: SpecAxis) -> Gen<Length> {
|
pub const fn to_gen(self, main: SpecAxis) -> Gen<Length> {
|
||||||
match block {
|
match main {
|
||||||
SpecAxis::Horizontal => Gen::new(self.h, self.w),
|
SpecAxis::Horizontal => Gen::new(self.h, self.w),
|
||||||
SpecAxis::Vertical => Gen::new(self.w, self.h),
|
SpecAxis::Vertical => Gen::new(self.w, self.h),
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,8 @@ impl<T> Spec<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to the generic representation.
|
/// Convert to the generic representation.
|
||||||
pub fn to_gen(self, block: SpecAxis) -> Gen<T> {
|
pub fn to_gen(self, main: SpecAxis) -> Gen<T> {
|
||||||
match block {
|
match main {
|
||||||
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
||||||
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::Regions;
|
||||||
use crate::frame::Frame;
|
use crate::frame::Frame;
|
||||||
use crate::geom::{Length, Linear, Size, Spec};
|
use crate::geom::{Length, Linear, Size, Spec};
|
||||||
|
|
||||||
@ -59,6 +60,17 @@ impl Constraints {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create tight constraints for a region.
|
||||||
|
pub fn tight(regions: &Regions) -> Self {
|
||||||
|
Self {
|
||||||
|
min: Spec::default(),
|
||||||
|
max: Spec::default(),
|
||||||
|
exact: regions.current.to_spec().map(Some),
|
||||||
|
base: regions.base.to_spec().map(Some),
|
||||||
|
expand: regions.expand,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether the constraints are fullfilled in a region with the given
|
/// Check whether the constraints are fullfilled in a region with the given
|
||||||
/// properties.
|
/// properties.
|
||||||
pub fn check(&self, current: Size, base: Size, expand: Spec<bool>) -> bool {
|
pub fn check(&self, current: Size, base: Size, expand: Spec<bool>) -> bool {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::geom::{Size, Spec};
|
use crate::geom::{Length, Size, Spec};
|
||||||
|
|
||||||
/// A sequence of regions to layout into.
|
/// A sequence of regions to layout into.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -13,9 +13,6 @@ pub struct Regions {
|
|||||||
pub last: Option<Size>,
|
pub last: Option<Size>,
|
||||||
/// Whether nodes should expand to fill the regions instead of shrinking to
|
/// Whether nodes should expand to fill the regions instead of shrinking to
|
||||||
/// fit the content.
|
/// fit the content.
|
||||||
///
|
|
||||||
/// This property is only handled by nodes that have the ability to control
|
|
||||||
/// their own size.
|
|
||||||
pub expand: Spec<bool>,
|
pub expand: Spec<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,13 +49,26 @@ impl Regions {
|
|||||||
regions
|
regions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether `current` is a fully sized (untouched) copy of the last region.
|
/// Whether the current region is full and a region break is called for.
|
||||||
|
pub fn is_full(&self) -> bool {
|
||||||
|
Length::zero().fits(self.current.h) && !self.in_last()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether `current` is the last usable region.
|
||||||
///
|
///
|
||||||
/// If this is true, calling `next()` will have no effect.
|
/// If this is true, calling `next()` will have no effect.
|
||||||
pub fn in_full_last(&self) -> bool {
|
pub fn in_last(&self) -> bool {
|
||||||
self.backlog.len() == 0 && self.last.map_or(true, |size| self.current == size)
|
self.backlog.len() == 0 && self.last.map_or(true, |size| self.current == size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advance to the next region if there is any.
|
||||||
|
pub fn next(&mut self) {
|
||||||
|
if let Some(size) = self.backlog.next().or(self.last) {
|
||||||
|
self.current = size;
|
||||||
|
self.base = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator that returns pairs of `(current, base)` that are equivalent
|
/// An iterator that returns pairs of `(current, base)` that are equivalent
|
||||||
/// to what would be produced by calling [`next()`](Self::next) repeatedly
|
/// to what would be produced by calling [`next()`](Self::next) repeatedly
|
||||||
/// until all regions are exhausted.
|
/// until all regions are exhausted.
|
||||||
@ -69,14 +79,6 @@ impl Regions {
|
|||||||
first.chain(backlog.chain(last).map(|&s| (s, s)))
|
first.chain(backlog.chain(last).map(|&s| (s, s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance to the next region if there is any.
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
if let Some(size) = self.backlog.next().or(self.last) {
|
|
||||||
self.current = size;
|
|
||||||
self.base = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutate all contained sizes in place.
|
/// Mutate all contained sizes in place.
|
||||||
pub fn mutate<F>(&mut self, mut f: F)
|
pub fn mutate<F>(&mut self, mut f: F)
|
||||||
where
|
where
|
||||||
|
@ -34,28 +34,29 @@ impl Layout for AlignNode {
|
|||||||
pod.expand.x &= self.aligns.x.is_none();
|
pod.expand.x &= self.aligns.x.is_none();
|
||||||
pod.expand.y &= self.aligns.y.is_none();
|
pod.expand.y &= self.aligns.y.is_none();
|
||||||
|
|
||||||
|
// Layout the child.
|
||||||
let mut frames = self.child.layout(ctx, &pod);
|
let mut frames = self.child.layout(ctx, &pod);
|
||||||
for (Constrained { item: frame, cts }, (current, _)) in
|
|
||||||
|
for (Constrained { item: frame, cts }, (current, base)) in
|
||||||
frames.iter_mut().zip(regions.iter())
|
frames.iter_mut().zip(regions.iter())
|
||||||
{
|
{
|
||||||
let canvas = Size::new(
|
// The possibly larger size in which we align the frame.
|
||||||
|
let new = Size::new(
|
||||||
if regions.expand.x { current.w } else { frame.size.w },
|
if regions.expand.x { current.w } else { frame.size.w },
|
||||||
if regions.expand.y { current.h } else { frame.size.h },
|
if regions.expand.y { current.h } else { frame.size.h },
|
||||||
);
|
);
|
||||||
|
|
||||||
let aligns = self.aligns.unwrap_or(Spec::new(Align::Left, Align::Top));
|
let aligns = self.aligns.unwrap_or(Spec::new(Align::Left, Align::Top));
|
||||||
let offset = Point::new(
|
Rc::make_mut(frame).resize(new, aligns);
|
||||||
aligns.x.resolve(canvas.w - frame.size.w),
|
|
||||||
aligns.y.resolve(canvas.h - frame.size.h),
|
|
||||||
);
|
|
||||||
|
|
||||||
let frame = Rc::make_mut(frame);
|
|
||||||
frame.size = canvas;
|
|
||||||
frame.baseline += offset.y;
|
|
||||||
frame.translate(offset);
|
|
||||||
|
|
||||||
|
// Set constraints.
|
||||||
cts.expand = regions.expand;
|
cts.expand = regions.expand;
|
||||||
cts.exact = current.to_spec().map(Some);
|
cts.base.x.and_set(Some(base.w));
|
||||||
|
cts.base.y.and_set(Some(base.h));
|
||||||
|
cts.exact = Spec::new(
|
||||||
|
regions.expand.x.then(|| current.w),
|
||||||
|
regions.expand.y.then(|| current.h),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
frames
|
||||||
|
@ -52,7 +52,7 @@ impl Layout for FlowNode {
|
|||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
FlowLayouter::new(self, regions.clone()).layout(ctx)
|
FlowLayouter::new(self, regions).layout(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ struct FlowLayouter<'a> {
|
|||||||
children: &'a [FlowChild],
|
children: &'a [FlowChild],
|
||||||
/// Whether the flow should expand to fill the region.
|
/// Whether the flow should expand to fill the region.
|
||||||
expand: Spec<bool>,
|
expand: Spec<bool>,
|
||||||
/// The region to layout into.
|
/// The regions to layout children into.
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
/// The full size of `regions.current` that was available before we started
|
/// The full size of `regions.current` that was available before we started
|
||||||
/// subtracting.
|
/// subtracting.
|
||||||
@ -109,15 +109,18 @@ enum FlowItem {
|
|||||||
|
|
||||||
impl<'a> FlowLayouter<'a> {
|
impl<'a> FlowLayouter<'a> {
|
||||||
/// Create a new flow layouter.
|
/// Create a new flow layouter.
|
||||||
fn new(flow: &'a FlowNode, mut regions: Regions) -> Self {
|
fn new(flow: &'a FlowNode, regions: &Regions) -> Self {
|
||||||
// Disable vertical expansion for children.
|
|
||||||
let expand = regions.expand;
|
let expand = regions.expand;
|
||||||
|
let full = regions.current;
|
||||||
|
|
||||||
|
// Disable vertical expansion for children.
|
||||||
|
let mut regions = regions.clone();
|
||||||
regions.expand.y = false;
|
regions.expand.y = false;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
children: &flow.children,
|
children: &flow.children,
|
||||||
expand,
|
expand,
|
||||||
full: regions.current,
|
full,
|
||||||
regions,
|
regions,
|
||||||
used: Size::zero(),
|
used: Size::zero(),
|
||||||
fr: Fractional::zero(),
|
fr: Fractional::zero(),
|
||||||
@ -129,6 +132,10 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
/// Layout all children.
|
/// Layout all children.
|
||||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
for child in self.children {
|
for child in self.children {
|
||||||
|
if self.regions.is_full() {
|
||||||
|
self.finish_region();
|
||||||
|
}
|
||||||
|
|
||||||
match *child {
|
match *child {
|
||||||
FlowChild::Spacing(Spacing::Linear(v)) => {
|
FlowChild::Spacing(Spacing::Linear(v)) => {
|
||||||
self.layout_absolute(v);
|
self.layout_absolute(v);
|
||||||
@ -215,7 +222,7 @@ impl<'a> FlowLayouter<'a> {
|
|||||||
size.h = self.full.h;
|
size.h = self.full.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Frame::new(size, size.h);
|
let mut output = Frame::new(size);
|
||||||
let mut before = Length::zero();
|
let mut before = Length::zero();
|
||||||
let mut ruler = Align::Top;
|
let mut ruler = Align::Top;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
@ -99,11 +99,11 @@ struct GridLayouter<'a> {
|
|||||||
cols: Vec<TrackSizing>,
|
cols: Vec<TrackSizing>,
|
||||||
/// The row tracks including gutter tracks.
|
/// The row tracks including gutter tracks.
|
||||||
rows: Vec<TrackSizing>,
|
rows: Vec<TrackSizing>,
|
||||||
/// The regions to layout into.
|
/// The regions to layout children into.
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
/// Resolved column sizes.
|
/// Resolved column sizes.
|
||||||
rcols: Vec<Length>,
|
rcols: Vec<Length>,
|
||||||
/// The full block size of the current region.
|
/// The full height of the current region.
|
||||||
full: Length,
|
full: Length,
|
||||||
/// The used-up size of the current region. The horizontal size is
|
/// The used-up size of the current region. The horizontal size is
|
||||||
/// determined once after columns are resolved and not touched again.
|
/// determined once after columns are resolved and not touched again.
|
||||||
@ -353,6 +353,12 @@ impl<'a> GridLayouter<'a> {
|
|||||||
/// Layout the grid row-by-row.
|
/// Layout the grid row-by-row.
|
||||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
for y in 0 .. self.rows.len() {
|
for y in 0 .. self.rows.len() {
|
||||||
|
// Skip to next region if current one is full, but only for content
|
||||||
|
// rows, not for gutter rows.
|
||||||
|
if y % 2 == 0 && self.regions.is_full() {
|
||||||
|
self.finish_region(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
match self.rows[y] {
|
match self.rows[y] {
|
||||||
TrackSizing::Auto => self.layout_auto_row(ctx, y),
|
TrackSizing::Auto => self.layout_auto_row(ctx, y),
|
||||||
TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y),
|
TrackSizing::Linear(v) => self.layout_linear_row(ctx, v, y),
|
||||||
@ -368,8 +374,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.finished
|
self.finished
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with automatic size along the block axis. Such a row may
|
/// Layout a row with automatic height. Such a row may break across multiple
|
||||||
/// break across multiple regions.
|
/// regions.
|
||||||
fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) {
|
fn layout_auto_row(&mut self, ctx: &mut LayoutContext, y: usize) {
|
||||||
let mut resolved: Vec<Length> = vec![];
|
let mut resolved: Vec<Length> = vec![];
|
||||||
|
|
||||||
@ -388,10 +394,14 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let mut sizes =
|
let mut sizes =
|
||||||
node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h);
|
node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h);
|
||||||
|
|
||||||
|
// For each region, we want to know the maximum height any
|
||||||
|
// column requires.
|
||||||
for (target, size) in resolved.iter_mut().zip(&mut sizes) {
|
for (target, size) in resolved.iter_mut().zip(&mut sizes) {
|
||||||
target.set_max(size);
|
target.set_max(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New heights are maximal by virtue of being new. Note that
|
||||||
|
// this extend only uses the rest of the sizes iterator.
|
||||||
resolved.extend(sizes);
|
resolved.extend(sizes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,16 +441,16 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with linear sizing along the block axis. Such a row cannot
|
/// Layout a row with linear height. Such a row cannot break across multiple
|
||||||
/// break across multiple regions, but it may force a region break.
|
/// regions, but it may force a region break.
|
||||||
fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) {
|
fn layout_linear_row(&mut self, ctx: &mut LayoutContext, v: Linear, y: usize) {
|
||||||
let resolved = v.resolve(self.regions.base.h);
|
let resolved = v.resolve(self.regions.base.h);
|
||||||
let frame = self.layout_single_row(ctx, resolved, y);
|
let frame = self.layout_single_row(ctx, resolved, y);
|
||||||
|
|
||||||
// Skip to fitting region.
|
// Skip to fitting region.
|
||||||
let length = frame.size.h;
|
let height = frame.size.h;
|
||||||
while !self.regions.current.h.fits(length) && !self.regions.in_full_last() {
|
while !self.regions.current.h.fits(height) && !self.regions.in_last() {
|
||||||
self.cts.max.y = Some(self.used.h + length);
|
self.cts.max.y = Some(self.used.h + height);
|
||||||
self.finish_region(ctx);
|
self.finish_region(ctx);
|
||||||
|
|
||||||
// Don't skip multiple regions for gutter and don't push a row.
|
// Don't skip multiple regions for gutter and don't push a row.
|
||||||
@ -452,14 +462,14 @@ impl<'a> GridLayouter<'a> {
|
|||||||
self.push_row(frame);
|
self.push_row(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a row with a fixed size along the block axis and return its frame.
|
/// Layout a row with fixed height and return its frame.
|
||||||
fn layout_single_row(
|
fn layout_single_row(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
height: Length,
|
height: Length,
|
||||||
y: usize,
|
y: usize,
|
||||||
) -> Frame {
|
) -> Frame {
|
||||||
let mut output = Frame::new(Size::new(self.used.w, height), height);
|
let mut output = Frame::new(Size::new(self.used.w, height));
|
||||||
let mut pos = Point::zero();
|
let mut pos = Point::zero();
|
||||||
|
|
||||||
for (x, &rcol) in self.rcols.iter().enumerate() {
|
for (x, &rcol) in self.rcols.iter().enumerate() {
|
||||||
@ -496,7 +506,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// Prepare frames.
|
// Prepare frames.
|
||||||
let mut outputs: Vec<_> = heights
|
let mut outputs: Vec<_> = heights
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&h| Frame::new(Size::new(self.used.w, h), h))
|
.map(|&h| Frame::new(Size::new(self.used.w, h)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Prepare regions.
|
// Prepare regions.
|
||||||
@ -553,7 +563,7 @@ impl<'a> GridLayouter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The frame for the region.
|
// The frame for the region.
|
||||||
let mut output = Frame::new(size, size.h);
|
let mut output = Frame::new(size);
|
||||||
let mut pos = Point::zero();
|
let mut pos = Point::zero();
|
||||||
|
|
||||||
// Place finished rows and layout fractional rows.
|
// Place finished rows and layout fractional rows.
|
||||||
|
@ -43,12 +43,12 @@ impl Layout for ImageNode {
|
|||||||
let &Regions { current, expand, .. } = regions;
|
let &Regions { current, expand, .. } = regions;
|
||||||
|
|
||||||
let img = ctx.images.get(self.id);
|
let img = ctx.images.get(self.id);
|
||||||
let pixel_w = img.width() as f64;
|
let pxw = img.width() as f64;
|
||||||
let pixel_h = img.height() as f64;
|
let pxh = img.height() as f64;
|
||||||
|
|
||||||
let region_ratio = current.w / current.h;
|
let pixel_ratio = pxw / pxh;
|
||||||
let pixel_ratio = pixel_w / pixel_h;
|
let current_ratio = current.w / current.h;
|
||||||
let wide = region_ratio < pixel_ratio;
|
let wide = pixel_ratio > current_ratio;
|
||||||
|
|
||||||
// The space into which the image will be placed according to its fit.
|
// The space into which the image will be placed according to its fit.
|
||||||
let canvas = if expand.x && expand.y {
|
let canvas = if expand.x && expand.y {
|
||||||
@ -58,7 +58,7 @@ impl Layout for ImageNode {
|
|||||||
} else if current.h.is_finite() {
|
} else if current.h.is_finite() {
|
||||||
Size::new(current.w.min(current.h * pixel_ratio), current.h)
|
Size::new(current.w.min(current.h * pixel_ratio), current.h)
|
||||||
} else {
|
} else {
|
||||||
Size::new(Length::pt(pixel_w), Length::pt(pixel_h))
|
Size::new(Length::pt(pxw), Length::pt(pxh))
|
||||||
};
|
};
|
||||||
|
|
||||||
// The actual size of the fitted image.
|
// The actual size of the fitted image.
|
||||||
@ -73,26 +73,19 @@ impl Layout for ImageNode {
|
|||||||
ImageFit::Stretch => canvas,
|
ImageFit::Stretch => canvas,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Position the image so that it is centered in the canvas.
|
// First, place the image in a frame of exactly its size and then resize
|
||||||
let mut frame = Frame::new(canvas, canvas.h);
|
// the frame to the canvas size, center aligning the image in the
|
||||||
frame.push(
|
// process.
|
||||||
(canvas - size).to_point() / 2.0,
|
let mut frame = Frame::new(size);
|
||||||
Element::Image(self.id, size),
|
frame.push(Point::zero(), Element::Image(self.id, size));
|
||||||
);
|
frame.resize(canvas, Spec::new(Align::Center, Align::Horizon));
|
||||||
|
|
||||||
// Create a clipping group if the image mode is `cover`.
|
// Create a clipping group if the fit mode is "cover".
|
||||||
if self.fit == ImageFit::Cover {
|
if self.fit == ImageFit::Cover {
|
||||||
let mut wrapper = Frame::new(canvas, canvas.h);
|
frame.clip();
|
||||||
wrapper.push(
|
|
||||||
Point::zero(),
|
|
||||||
Element::Group(Group::new(Rc::new(frame)).clips(true)),
|
|
||||||
);
|
|
||||||
frame = wrapper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cts = Constraints::new(regions.expand);
|
vec![frame.constrain(Constraints::tight(regions))]
|
||||||
cts.exact = regions.current.to_spec().map(Some);
|
|
||||||
vec![frame.constrain(cts)]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,12 +241,17 @@ impl<'a> ParLayouter<'a> {
|
|||||||
starts.push((range.start, deco));
|
starts.push((range.start, deco));
|
||||||
}
|
}
|
||||||
ParChild::Undecorate => {
|
ParChild::Undecorate => {
|
||||||
let (start, deco) = starts.pop().unwrap();
|
if let Some((start, deco)) = starts.pop() {
|
||||||
decos.push((start .. range.end, deco));
|
decos.push((start .. range.end, deco));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (start, deco) in starts {
|
||||||
|
decos.push((start .. bidi.text.len(), deco));
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
align: par.align,
|
align: par.align,
|
||||||
leading: par.leading,
|
leading: par.leading,
|
||||||
@ -307,7 +312,7 @@ impl<'a> ParLayouter<'a> {
|
|||||||
|
|
||||||
// If the line does not fit vertically, we start a new region.
|
// If the line does not fit vertically, we start a new region.
|
||||||
while !stack.regions.current.h.fits(line.size.h) {
|
while !stack.regions.current.h.fits(line.size.h) {
|
||||||
if stack.regions.in_full_last() {
|
if stack.regions.in_last() {
|
||||||
stack.overflowing = true;
|
stack.overflowing = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -487,8 +492,9 @@ impl<'a> LineLayout<'a> {
|
|||||||
let size = Size::new(self.size.w.max(width), self.size.h);
|
let size = Size::new(self.size.w.max(width), self.size.h);
|
||||||
let remaining = size.w - self.size.w;
|
let remaining = size.w - self.size.w;
|
||||||
|
|
||||||
let mut output = Frame::new(size, self.baseline);
|
let mut output = Frame::new(size);
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
|
output.baseline = self.baseline;
|
||||||
|
|
||||||
for (range, item) in self.reordered() {
|
for (range, item) in self.reordered() {
|
||||||
let mut position = |mut frame: Frame| {
|
let mut position = |mut frame: Frame| {
|
||||||
@ -621,7 +627,7 @@ impl<'a> LineStack<'a> {
|
|||||||
self.cts.exact = self.full.to_spec().map(Some);
|
self.cts.exact = self.full.to_spec().map(Some);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Frame::new(self.size, self.size.h);
|
let mut output = Frame::new(self.size);
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
|
@ -165,12 +165,16 @@ impl Layout for ShapeNode {
|
|||||||
if regions.expand.y { regions.current.h } else { default },
|
if regions.expand.y { regions.current.h } else { default },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Don't overflow the region.
|
||||||
|
size.w = size.w.min(regions.current.w);
|
||||||
|
size.h = size.h.min(regions.current.h);
|
||||||
|
|
||||||
if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) {
|
if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) {
|
||||||
size.w = size.w.min(size.h);
|
size.w = size.w.min(size.h);
|
||||||
size.h = size.w;
|
size.h = size.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
Frame::new(size, size.h)
|
Frame::new(size)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add fill and/or stroke.
|
// Add fill and/or stroke.
|
||||||
@ -197,9 +201,6 @@ impl Layout for ShapeNode {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Return tight constraints for now.
|
// Return tight constraints for now.
|
||||||
let mut cts = Constraints::new(regions.expand);
|
vec![frame.constrain(Constraints::tight(regions))]
|
||||||
cts.exact = regions.current.to_spec().map(Some);
|
|
||||||
cts.base = regions.base.to_spec().map(Some);
|
|
||||||
vec![frame.constrain(cts)]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,6 @@ impl Layout for SizedNode {
|
|||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
// Resolve width and height relative to the region's base.
|
|
||||||
let width = self.sizing.x.map(|w| w.resolve(regions.base.w));
|
|
||||||
let height = self.sizing.y.map(|h| h.resolve(regions.base.h));
|
|
||||||
|
|
||||||
// Generate constraints.
|
// Generate constraints.
|
||||||
let mut cts = Constraints::new(regions.expand);
|
let mut cts = Constraints::new(regions.expand);
|
||||||
cts.set_base_if_linear(regions.base, self.sizing);
|
cts.set_base_if_linear(regions.base, self.sizing);
|
||||||
@ -56,6 +52,10 @@ impl Layout for SizedNode {
|
|||||||
cts.base.y = Some(regions.base.h);
|
cts.base.y = Some(regions.base.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve width and height relative to the region's base.
|
||||||
|
let width = self.sizing.x.map(|w| w.resolve(regions.base.w));
|
||||||
|
let height = self.sizing.y.map(|h| h.resolve(regions.base.h));
|
||||||
|
|
||||||
// The "pod" is the region into which the child will be layouted.
|
// The "pod" is the region into which the child will be layouted.
|
||||||
let pod = {
|
let pod = {
|
||||||
let size = Size::new(
|
let size = Size::new(
|
||||||
|
@ -3,14 +3,14 @@ use super::prelude::*;
|
|||||||
/// `h`: Horizontal spacing.
|
/// `h`: Horizontal spacing.
|
||||||
pub fn h(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn h(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let mut template = Template::new();
|
let mut template = Template::new();
|
||||||
template.spacing(GenAxis::Inline, args.expect("spacing")?);
|
template.spacing(SpecAxis::Horizontal, args.expect("spacing")?);
|
||||||
Ok(Value::Template(template))
|
Ok(Value::Template(template))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `v`: Vertical spacing.
|
/// `v`: Vertical spacing.
|
||||||
pub fn v(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
pub fn v(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||||
let mut template = Template::new();
|
let mut template = Template::new();
|
||||||
template.spacing(GenAxis::Block, args.expect("spacing")?);
|
template.spacing(SpecAxis::Vertical, args.expect("spacing")?);
|
||||||
Ok(Value::Template(template))
|
Ok(Value::Template(template))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ impl Layout for StackNode {
|
|||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
StackLayouter::new(self, regions.clone()).layout(ctx)
|
StackLayouter::new(self, regions).layout(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ struct StackLayouter<'a> {
|
|||||||
axis: SpecAxis,
|
axis: SpecAxis,
|
||||||
/// Whether the stack should expand to fill the region.
|
/// Whether the stack should expand to fill the region.
|
||||||
expand: Spec<bool>,
|
expand: Spec<bool>,
|
||||||
/// The region to layout into.
|
/// The regions to layout children into.
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
/// The full size of `regions.current` that was available before we started
|
/// The full size of `regions.current` that was available before we started
|
||||||
/// subtracting.
|
/// subtracting.
|
||||||
@ -123,17 +123,21 @@ enum StackItem {
|
|||||||
|
|
||||||
impl<'a> StackLayouter<'a> {
|
impl<'a> StackLayouter<'a> {
|
||||||
/// Create a new stack layouter.
|
/// Create a new stack layouter.
|
||||||
fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
|
fn new(stack: &'a StackNode, regions: &Regions) -> Self {
|
||||||
// Disable expansion along the block axis for children.
|
|
||||||
let axis = stack.dir.axis();
|
let axis = stack.dir.axis();
|
||||||
let expand = regions.expand;
|
let expand = regions.expand;
|
||||||
|
let full = regions.current;
|
||||||
|
|
||||||
|
|
||||||
|
// Disable expansion along the block axis for children.
|
||||||
|
let mut regions = regions.clone();
|
||||||
regions.expand.set(axis, false);
|
regions.expand.set(axis, false);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
stack,
|
stack,
|
||||||
axis,
|
axis,
|
||||||
expand,
|
expand,
|
||||||
full: regions.current,
|
full,
|
||||||
regions,
|
regions,
|
||||||
used: Gen::zero(),
|
used: Gen::zero(),
|
||||||
fr: Fractional::zero(),
|
fr: Fractional::zero(),
|
||||||
@ -145,6 +149,10 @@ impl<'a> StackLayouter<'a> {
|
|||||||
/// Layout all children.
|
/// Layout all children.
|
||||||
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
for child in &self.stack.children {
|
for child in &self.stack.children {
|
||||||
|
if self.regions.is_full() {
|
||||||
|
self.finish_region();
|
||||||
|
}
|
||||||
|
|
||||||
match *child {
|
match *child {
|
||||||
StackChild::Spacing(Spacing::Linear(v)) => {
|
StackChild::Spacing(Spacing::Linear(v)) => {
|
||||||
self.layout_absolute(v);
|
self.layout_absolute(v);
|
||||||
@ -167,10 +175,10 @@ impl<'a> StackLayouter<'a> {
|
|||||||
fn layout_absolute(&mut self, amount: Linear) {
|
fn layout_absolute(&mut self, amount: Linear) {
|
||||||
// Resolve the linear, limiting it to the remaining available space.
|
// Resolve the linear, limiting it to the remaining available space.
|
||||||
let remaining = self.regions.current.get_mut(self.axis);
|
let remaining = self.regions.current.get_mut(self.axis);
|
||||||
let resolved = amount.resolve(self.full.get(self.axis));
|
let resolved = amount.resolve(self.regions.base.get(self.axis));
|
||||||
let limited = resolved.min(*remaining);
|
let limited = resolved.min(*remaining);
|
||||||
*remaining -= limited;
|
*remaining -= limited;
|
||||||
self.used.block += limited;
|
self.used.main += limited;
|
||||||
self.items.push(StackItem::Absolute(resolved));
|
self.items.push(StackItem::Absolute(resolved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +195,9 @@ impl<'a> StackLayouter<'a> {
|
|||||||
for (i, frame) in frames.into_iter().enumerate() {
|
for (i, frame) in frames.into_iter().enumerate() {
|
||||||
// Grow our size, shrink the region and save the frame for later.
|
// Grow our size, shrink the region and save the frame for later.
|
||||||
let size = frame.item.size.to_gen(self.axis);
|
let size = frame.item.size.to_gen(self.axis);
|
||||||
self.used.block += size.block;
|
self.used.main += size.main;
|
||||||
self.used.inline.set_max(size.inline);
|
self.used.cross.set_max(size.cross);
|
||||||
*self.regions.current.get_mut(self.axis) -= size.block;
|
*self.regions.current.get_mut(self.axis) -= size.main;
|
||||||
self.items.push(StackItem::Frame(frame.item, align));
|
self.items.push(StackItem::Frame(frame.item, align));
|
||||||
|
|
||||||
if i + 1 < len {
|
if i + 1 < len {
|
||||||
@ -210,13 +218,13 @@ impl<'a> StackLayouter<'a> {
|
|||||||
|
|
||||||
// Expand fully if there are fr spacings.
|
// Expand fully if there are fr spacings.
|
||||||
let full = self.full.get(self.axis);
|
let full = self.full.get(self.axis);
|
||||||
let remaining = full - self.used.block;
|
let remaining = full - self.used.main;
|
||||||
if self.fr.get() > 0.0 && full.is_finite() {
|
if self.fr.get() > 0.0 && full.is_finite() {
|
||||||
self.used.block = full;
|
self.used.main = full;
|
||||||
size.set(self.axis, full);
|
size.set(self.axis, full);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Frame::new(size, size.h);
|
let mut output = Frame::new(size);
|
||||||
let mut before = Length::zero();
|
let mut before = Length::zero();
|
||||||
let mut ruler: Align = self.stack.dir.start().into();
|
let mut ruler: Align = self.stack.dir.start().into();
|
||||||
|
|
||||||
@ -239,11 +247,11 @@ impl<'a> StackLayouter<'a> {
|
|||||||
// Align along the block axis.
|
// Align along the block axis.
|
||||||
let parent = size.get(self.axis);
|
let parent = size.get(self.axis);
|
||||||
let child = frame.size.get(self.axis);
|
let child = frame.size.get(self.axis);
|
||||||
let block = ruler.resolve(parent - self.used.block)
|
let block = ruler.resolve(parent - self.used.main)
|
||||||
+ if self.stack.dir.is_positive() {
|
+ if self.stack.dir.is_positive() {
|
||||||
before
|
before
|
||||||
} else {
|
} else {
|
||||||
self.used.block - child - before
|
self.used.main - child - before
|
||||||
};
|
};
|
||||||
|
|
||||||
let pos = Gen::new(Length::zero(), block).to_point(self.axis);
|
let pos = Gen::new(Length::zero(), block).to_point(self.axis);
|
||||||
|
@ -305,8 +305,9 @@ pub struct ShapedGlyph {
|
|||||||
impl<'a> ShapedText<'a> {
|
impl<'a> ShapedText<'a> {
|
||||||
/// Build the shaped text's frame.
|
/// Build the shaped text's frame.
|
||||||
pub fn build(&self) -> Frame {
|
pub fn build(&self) -> Frame {
|
||||||
let mut frame = Frame::new(self.size, self.baseline);
|
let mut frame = Frame::new(self.size);
|
||||||
let mut offset = Length::zero();
|
let mut offset = Length::zero();
|
||||||
|
frame.baseline = self.baseline;
|
||||||
|
|
||||||
for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) {
|
for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) {
|
||||||
let pos = Point::new(offset, self.baseline);
|
let pos = Point::new(offset, self.baseline);
|
||||||
|
@ -62,13 +62,7 @@ impl Layout for TransformNode {
|
|||||||
let transform = Transform::translation(x, y)
|
let transform = Transform::translation(x, y)
|
||||||
.pre_concat(self.transform)
|
.pre_concat(self.transform)
|
||||||
.pre_concat(Transform::translation(-x, -y));
|
.pre_concat(Transform::translation(-x, -y));
|
||||||
|
Rc::make_mut(frame).transform(transform);
|
||||||
let mut wrapper = Frame::new(frame.size, frame.baseline);
|
|
||||||
wrapper.push(
|
|
||||||
Point::zero(),
|
|
||||||
Element::Group(Group::new(std::mem::take(frame)).transform(transform)),
|
|
||||||
);
|
|
||||||
*frame = Rc::new(wrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frames
|
frames
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
// Syntax sugar for function definitions.
|
// Syntax sugar for function definitions.
|
||||||
#let fill = conifer
|
#let fill = conifer
|
||||||
#let rect(body) = rect(width: 2cm, fill: fill, pad(5pt, body))
|
#let rect(body) = rect(width: 2cm, fill: fill, padding: 5pt, body)
|
||||||
#rect[Hi!]
|
#rect[Hi!]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -7,6 +7,6 @@
|
|||||||
#let d = 3
|
#let d = 3
|
||||||
#let value = [hi]
|
#let value = [hi]
|
||||||
#let item(a, b) = a + b
|
#let item(a, b) = a + b
|
||||||
#let fn(body) = rect(fill: conifer, pad(5pt, body))
|
#let fn = rect with (fill: conifer, padding: 5pt)
|
||||||
|
|
||||||
Some _includable_ text.
|
Some _includable_ text.
|
||||||
|
@ -16,7 +16,7 @@ Auto-sized circle. \
|
|||||||
Center-aligned rect in auto-sized circle.
|
Center-aligned rect in auto-sized circle.
|
||||||
#circle(fill: forest, stroke: conifer,
|
#circle(fill: forest, stroke: conifer,
|
||||||
align(center + horizon,
|
align(center + horizon,
|
||||||
rect(fill: conifer, pad(5pt)[But, soft!])
|
rect(fill: conifer, padding: 5pt)[But, soft!]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
))
|
))
|
||||||
|
|
||||||
// Fixed width, text height.
|
// Fixed width, text height.
|
||||||
#rect(width: 2cm, fill: rgb("9650d6"), pad(5pt)[Fixed and padded])
|
#rect(width: 2cm, fill: rgb("9650d6"), padding: 5pt)[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]
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
cell(100%, rgb("00ff00")),
|
cell(100%, rgb("00ff00")),
|
||||||
)
|
)
|
||||||
|
|
||||||
#grid()
|
|
||||||
|
|
||||||
---
|
---
|
||||||
#grid(
|
#grid(
|
||||||
columns: (auto, auto, 40%),
|
columns: (auto, auto, 40%),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user