mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refined expansion model 🔛
This commit is contained in:
parent
1dafe2c2ea
commit
f24e9b44e0
@ -43,7 +43,7 @@ struct PartialLine {
|
|||||||
usable: Size,
|
usable: Size,
|
||||||
content: Vec<(Size, Layout)>,
|
content: Vec<(Size, Layout)>,
|
||||||
dimensions: Size2D,
|
dimensions: Size2D,
|
||||||
space: Option<(Size, bool)>,
|
space: Option<Size>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialLine {
|
impl PartialLine {
|
||||||
@ -64,7 +64,7 @@ impl PartialLine {
|
|||||||
pub struct FlexContext {
|
pub struct FlexContext {
|
||||||
pub spaces: LayoutSpaces,
|
pub spaces: LayoutSpaces,
|
||||||
pub axes: LayoutAxes,
|
pub axes: LayoutAxes,
|
||||||
pub shrink_to_fit: bool,
|
pub expand: bool,
|
||||||
pub flex_spacing: Size,
|
pub flex_spacing: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +74,11 @@ impl FlexLayouter {
|
|||||||
let stack = StackLayouter::new(StackContext {
|
let stack = StackLayouter::new(StackContext {
|
||||||
spaces: ctx.spaces,
|
spaces: ctx.spaces,
|
||||||
axes: ctx.axes,
|
axes: ctx.axes,
|
||||||
shrink_to_fit: ctx.shrink_to_fit,
|
expand: ctx.expand,
|
||||||
});
|
});
|
||||||
|
|
||||||
let usable = stack.primary_usable();
|
let usable = stack.primary_usable();
|
||||||
|
|
||||||
FlexLayouter {
|
FlexLayouter {
|
||||||
stack,
|
stack,
|
||||||
|
|
||||||
@ -107,9 +108,11 @@ impl FlexLayouter {
|
|||||||
self.units.push(FlexUnit::Space(space, soft));
|
self.units.push(FlexUnit::Space(space, soft));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_secondary_space(&mut self, space: Size) -> LayoutResult<()> {
|
pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
|
||||||
self.finish_run()?;
|
if !self.run_is_empty() {
|
||||||
Ok(self.stack.add_space(space))
|
self.finish_run()?;
|
||||||
|
}
|
||||||
|
Ok(self.stack.add_space(space, soft))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
||||||
@ -120,28 +123,26 @@ impl FlexLayouter {
|
|||||||
if replace_empty && self.run_is_empty() && self.stack.space_is_empty() {
|
if replace_empty && self.run_is_empty() && self.stack.space_is_empty() {
|
||||||
self.stack.set_spaces(spaces, true);
|
self.stack.set_spaces(spaces, true);
|
||||||
self.start_run();
|
self.start_run();
|
||||||
|
|
||||||
// let usable = self.stack.primary_usable();
|
|
||||||
// self.line = FlexLine::new(usable);
|
|
||||||
|
|
||||||
// // self.total_usable = self.stack.primary_usable();
|
|
||||||
// // self.usable = self.total_usable;
|
|
||||||
// // self.space = None;
|
|
||||||
} else {
|
} else {
|
||||||
self.stack.set_spaces(spaces, false);
|
self.stack.set_spaces(spaces, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remaining(&self) -> LayoutResult<(LayoutSpaces, LayoutSpaces)> {
|
pub fn remaining(&self) -> LayoutResult<(LayoutSpaces, Option<LayoutSpaces>)> {
|
||||||
let mut future = self.clone();
|
if self.run_is_empty() {
|
||||||
future.finish_run()?;
|
Ok((self.stack.remaining(), None))
|
||||||
|
} else {
|
||||||
|
let mut future = self.clone();
|
||||||
|
let remaining_run = future.finish_run()?;
|
||||||
|
|
||||||
let stack_spaces = future.stack.remaining();
|
let stack_spaces = future.stack.remaining();
|
||||||
let mut flex_spaces = stack_spaces.clone();
|
let mut flex_spaces = stack_spaces.clone();
|
||||||
flex_spaces[0].dimensions.x = future.last_run_remaining.x;
|
flex_spaces[0].dimensions.x = remaining_run.x;
|
||||||
flex_spaces[0].dimensions.y += future.last_run_remaining.y;
|
flex_spaces[0].dimensions.y += remaining_run.y;
|
||||||
|
|
||||||
|
Ok((flex_spaces, Some(stack_spaces)))
|
||||||
|
}
|
||||||
|
|
||||||
Ok((flex_spaces, stack_spaces))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_is_empty(&self) -> bool {
|
pub fn run_is_empty(&self) -> bool {
|
||||||
@ -149,7 +150,7 @@ impl FlexLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_last_is_space(&self) -> bool {
|
pub fn run_last_is_space(&self) -> bool {
|
||||||
matches!(self.units.last(), Some(FlexUnit::Space(_)))
|
matches!(self.units.last(), Some(FlexUnit::Space(_, _)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(mut self) -> LayoutResult<MultiLayout> {
|
pub fn finish(mut self) -> LayoutResult<MultiLayout> {
|
||||||
@ -158,32 +159,65 @@ impl FlexLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> {
|
pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> {
|
||||||
self.finish_run()?;
|
if !self.run_is_empty() {
|
||||||
|
self.finish_run()?;
|
||||||
|
}
|
||||||
Ok(self.stack.finish_space(hard))
|
Ok(self.stack.finish_space(hard))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_run(&mut self) -> LayoutResult<()> {
|
pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
|
||||||
let units = std::mem::replace(&mut self.units, vec![]);
|
let units = std::mem::replace(&mut self.units, vec![]);
|
||||||
for unit in units {
|
for unit in units {
|
||||||
match unit {
|
match unit {
|
||||||
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
||||||
FlexUnit::Space(space, soft) => {
|
FlexUnit::Space(space, soft) => self.layout_space(space, soft),
|
||||||
self.layout_space();
|
|
||||||
self.space = Some(space);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlexUnit::Break => {
|
|
||||||
self.space = None;
|
|
||||||
self.finish_line()?;
|
|
||||||
},
|
|
||||||
|
|
||||||
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
|
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
|
||||||
|
FlexUnit::Break => self.layout_break(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_line()?;
|
self.finish_line()
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
fn finish_line(&mut self) -> LayoutResult<Size2D> {
|
||||||
|
self.finish_partial_line();
|
||||||
|
|
||||||
|
self.stack.add(Layout {
|
||||||
|
dimensions: self.axes.specialize(self.line.combined_dimensions),
|
||||||
|
actions: self.line.actions.into_vec(),
|
||||||
|
debug_render: false,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let remaining = self.axes.specialize(Size2D {
|
||||||
|
x: self.line.usable - self.line.combined_dimensions.x,
|
||||||
|
y: self.line.combined_dimensions.y,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.line = FlexLine::new(self.stack.primary_usable());
|
||||||
|
|
||||||
|
Ok(remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_partial_line(&mut self) {
|
||||||
|
let part = self.line.part;
|
||||||
|
|
||||||
|
let factor = self.axes.primary.axis.factor();
|
||||||
|
let anchor =
|
||||||
|
self.axes.primary.anchor(self.line.usable)
|
||||||
|
- self.axes.primary.anchor(part.dimensions.x);
|
||||||
|
|
||||||
|
for (offset, layout) in part.content {
|
||||||
|
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
|
||||||
|
self.line.actions.add_layout(pos, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.line.combined_dimensions.x.max_eq(part.dimensions.x);
|
||||||
|
self.line.part = PartialLine::new(self.line.usable - part.dimensions.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_run(&mut self) {
|
||||||
|
let usable = self.stack.primary_usable();
|
||||||
|
self.line = FlexLine::new(usable);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
|
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
|
||||||
@ -215,7 +249,7 @@ impl FlexLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_space(&mut self) {
|
fn layout_space(&mut self, space: Size, soft: bool) {
|
||||||
if let Some(space) = self.space.take() {
|
if let Some(space) = self.space.take() {
|
||||||
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
|
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
|
||||||
self.run.size.x += space;
|
self.run.size.x += space;
|
||||||
@ -227,19 +261,19 @@ impl FlexLayouter {
|
|||||||
if axes.primary != self.axes.primary {
|
if axes.primary != self.axes.primary {
|
||||||
self.finish_partial_line();
|
self.finish_partial_line();
|
||||||
|
|
||||||
self.usable = match axes.primary.alignment {
|
// self.usable = match axes.primary.alignment {
|
||||||
Alignment::Origin =>
|
// Alignment::Origin =>
|
||||||
if self.max_extent == Size::zero() {
|
// if self.max_extent == Size::zero() {
|
||||||
self.total_usable
|
// self.total_usable
|
||||||
} else {
|
// } else {
|
||||||
Size::zero()
|
// Size::zero()
|
||||||
},
|
// },
|
||||||
Alignment::Center => crate::size::max(
|
// Alignment::Center => crate::size::max(
|
||||||
self.total_usable - 2 * self.max_extent,
|
// self.total_usable - 2 * self.max_extent,
|
||||||
Size::zero()
|
// Size::zero()
|
||||||
),
|
// ),
|
||||||
Alignment::End => self.total_usable - self.max_extent,
|
// Alignment::End => self.total_usable - self.max_extent,
|
||||||
};
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
if axes.secondary != self.axes.secondary {
|
if axes.secondary != self.axes.secondary {
|
||||||
@ -249,57 +283,8 @@ impl FlexLayouter {
|
|||||||
self.axes = axes;
|
self.axes = axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_line(&mut self) -> LayoutResult<()> {
|
fn layout_break(&mut self) {
|
||||||
self.finish_partial_line();
|
|
||||||
|
|
||||||
if self.merged_dimensions.y == Size::zero() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let actions = std::mem::replace(&mut self.merged_actions, LayoutActionList::new());
|
|
||||||
self.stack.add(Layout {
|
|
||||||
dimensions: self.axes.specialize(self.merged_dimensions),
|
|
||||||
actions: actions.into_vec(),
|
|
||||||
debug_render: false,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.merged_dimensions = Size2D::zero();
|
|
||||||
self.max_extent = Size::zero();
|
|
||||||
self.usable = self.total_usable;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_partial_line(&mut self) {
|
|
||||||
if self.run.content.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let factor = if self.axes.primary.axis.is_positive() { 1 } else { -1 };
|
|
||||||
let anchor = self.axes.primary.anchor(self.total_usable)
|
|
||||||
- self.axes.primary.anchor(self.run.size.x);
|
|
||||||
|
|
||||||
self.max_extent = crate::size::max(self.max_extent, anchor + factor * self.run.size.x);
|
|
||||||
|
|
||||||
for (offset, layout) in self.run.content.drain(..) {
|
|
||||||
let general_position = Size2D::with_x(anchor + factor * offset);
|
|
||||||
let position = self.axes.specialize(general_position);
|
|
||||||
|
|
||||||
self.merged_actions.add_layout(position, layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.merged_dimensions.x = match self.axes.primary.alignment {
|
|
||||||
Alignment::Origin => self.run.size.x,
|
|
||||||
Alignment::Center | Alignment::End => self.total_usable,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.merged_dimensions.y = crate::size::max(
|
|
||||||
self.merged_dimensions.y,
|
|
||||||
self.run.size.y + self.flex_spacing,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.last_run_remaining = Size2D::new(self.size_left(), self.merged_dimensions.y);
|
|
||||||
self.run.size = Size2D::zero();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_left(&self) -> Size {
|
fn size_left(&self) -> Size {
|
||||||
|
@ -147,9 +147,9 @@ pub struct LayoutContext<'a, 'p> {
|
|||||||
/// The axes to flow on.
|
/// The axes to flow on.
|
||||||
pub axes: LayoutAxes,
|
pub axes: LayoutAxes,
|
||||||
|
|
||||||
/// Whether to shrink the spaces to fit the content or to keep
|
/// Whether layouts should expand to the full dimensions of the space
|
||||||
/// the original dimensions.
|
/// they lie on or whether should tightly fit the content.
|
||||||
pub shrink_to_fit: bool,
|
pub expand: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A possibly stack-allocated vector of layout spaces.
|
/// A possibly stack-allocated vector of layout spaces.
|
||||||
@ -219,6 +219,14 @@ impl LayoutAxes {
|
|||||||
pub fn anchor(&self, space: Size2D) -> Size2D {
|
pub fn anchor(&self, space: Size2D) -> Size2D {
|
||||||
Size2D::new(self.primary.anchor(space.x), self.secondary.anchor(space.y))
|
Size2D::new(self.primary.anchor(space.x), self.secondary.anchor(space.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This axes with `expand` set to the given value for both axes.
|
||||||
|
pub fn expanding(&self, expand: bool) -> LayoutAxes {
|
||||||
|
LayoutAxes {
|
||||||
|
primary: self.primary.expanding(expand),
|
||||||
|
secondary: self.secondary.expanding(expand),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An axis with an alignment.
|
/// An axis with an alignment.
|
||||||
@ -226,17 +234,13 @@ impl LayoutAxes {
|
|||||||
pub struct AlignedAxis {
|
pub struct AlignedAxis {
|
||||||
pub axis: Axis,
|
pub axis: Axis,
|
||||||
pub alignment: Alignment,
|
pub alignment: Alignment,
|
||||||
|
pub expand: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlignedAxis {
|
impl AlignedAxis {
|
||||||
/// Returns an aligned axis if the alignment is compatible with the axis.
|
/// Creates an aligned axis from its three components.
|
||||||
pub fn new(axis: Axis, alignment: Alignment) -> AlignedAxis {
|
pub fn new(axis: Axis, alignment: Alignment, expand: bool) -> AlignedAxis {
|
||||||
AlignedAxis { axis, alignment }
|
AlignedAxis { axis, alignment, expand }
|
||||||
}
|
|
||||||
|
|
||||||
/// The pair of axis and alignment.
|
|
||||||
pub fn pair(&self) -> (Axis, Alignment) {
|
|
||||||
(self.axis, self.alignment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The position of the anchor specified by this axis on the given line.
|
/// The position of the anchor specified by this axis on the given line.
|
||||||
@ -248,6 +252,16 @@ impl AlignedAxis {
|
|||||||
(true, End) | (false, Origin) => line,
|
(true, End) | (false, Origin) => line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This axis with `expand` set to the given value.
|
||||||
|
pub fn expanding(&self, expand: bool) -> AlignedAxis {
|
||||||
|
AlignedAxis { expand, ..*self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this axis needs expansion.
|
||||||
|
pub fn needs_expansion(&self) -> bool {
|
||||||
|
self.expand || self.alignment != Alignment::Origin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where to put content.
|
/// Where to put content.
|
||||||
|
@ -8,28 +8,31 @@ pub struct StackLayouter {
|
|||||||
|
|
||||||
space: usize,
|
space: usize,
|
||||||
hard: bool,
|
hard: bool,
|
||||||
start: Size2D,
|
|
||||||
actions: LayoutActionList,
|
actions: LayoutActionList,
|
||||||
combined_dimensions: Size2D,
|
combined_dimensions: Size2D, // <- specialized
|
||||||
|
|
||||||
sub: Subspace,
|
sub: Subspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Subspace {
|
struct Subspace {
|
||||||
|
origin: Size2D, // <- specialized
|
||||||
usable: Size2D,
|
usable: Size2D,
|
||||||
anchor: Size2D,
|
anchor: Size2D, // <- generic
|
||||||
factor: i32,
|
factor: i32,
|
||||||
dimensions: Size2D,
|
dimensions: Size2D, // <- generic
|
||||||
|
space: Option<Size>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subspace {
|
impl Subspace {
|
||||||
fn new(usable: Size2D, axes: LayoutAxes) -> Subspace {
|
fn new(origin: Size2D, usable: Size2D, axes: LayoutAxes) -> Subspace {
|
||||||
Subspace {
|
Subspace {
|
||||||
usable,
|
origin,
|
||||||
|
usable: axes.generalize(usable),
|
||||||
anchor: axes.anchor(usable),
|
anchor: axes.anchor(usable),
|
||||||
factor: axes.secondary.axis.factor(),
|
factor: axes.secondary.axis.factor(),
|
||||||
dimensions: Size2D::zero(),
|
dimensions: Size2D::zero(),
|
||||||
|
space: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +44,7 @@ impl Subspace {
|
|||||||
pub struct StackContext {
|
pub struct StackContext {
|
||||||
pub spaces: LayoutSpaces,
|
pub spaces: LayoutSpaces,
|
||||||
pub axes: LayoutAxes,
|
pub axes: LayoutAxes,
|
||||||
pub shrink_to_fit: bool,
|
pub expand: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackLayouter {
|
impl StackLayouter {
|
||||||
@ -49,7 +52,6 @@ impl StackLayouter {
|
|||||||
pub fn new(ctx: StackContext) -> StackLayouter {
|
pub fn new(ctx: StackContext) -> StackLayouter {
|
||||||
let axes = ctx.axes;
|
let axes = ctx.axes;
|
||||||
let space = ctx.spaces[0];
|
let space = ctx.spaces[0];
|
||||||
let usable = ctx.axes.generalize(space.usable());
|
|
||||||
|
|
||||||
StackLayouter {
|
StackLayouter {
|
||||||
ctx,
|
ctx,
|
||||||
@ -57,15 +59,16 @@ impl StackLayouter {
|
|||||||
|
|
||||||
space: 0,
|
space: 0,
|
||||||
hard: true,
|
hard: true,
|
||||||
start: space.start(),
|
|
||||||
actions: LayoutActionList::new(),
|
actions: LayoutActionList::new(),
|
||||||
combined_dimensions: Size2D::zero(),
|
combined_dimensions: Size2D::zero(),
|
||||||
|
|
||||||
sub: Subspace::new(usable, axes),
|
sub: Subspace::new(space.start(), space.usable(), axes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
|
||||||
|
self.layout_space();
|
||||||
|
|
||||||
let size = self.ctx.axes.generalize(layout.dimensions);
|
let size = self.ctx.axes.generalize(layout.dimensions);
|
||||||
let mut new_dimensions = merge(self.sub.dimensions, size);
|
let mut new_dimensions = merge(self.sub.dimensions, size);
|
||||||
|
|
||||||
@ -81,10 +84,9 @@ impl StackLayouter {
|
|||||||
let offset = self.sub.dimensions.y;
|
let offset = self.sub.dimensions.y;
|
||||||
let anchor = self.ctx.axes.anchor(size);
|
let anchor = self.ctx.axes.anchor(size);
|
||||||
|
|
||||||
let pos = self.ctx.axes.specialize(
|
let pos = self.sub.origin + self.ctx.axes.specialize(
|
||||||
self.start
|
(self.sub.anchor - anchor)
|
||||||
+ (self.sub.anchor - anchor)
|
+ Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
|
||||||
+ Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.actions.add_layout(pos, layout);
|
self.actions.add_layout(pos, layout);
|
||||||
@ -100,19 +102,16 @@ impl StackLayouter {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_space(&mut self, space: Size) {
|
pub fn add_space(&mut self, space: Size, soft: bool) {
|
||||||
if self.sub.dimensions.y + space > self.sub.usable.y {
|
self.sub.space = Some(space);
|
||||||
self.finish_space(false);
|
if !soft {
|
||||||
} else {
|
self.layout_space();
|
||||||
self.sub.dimensions.y += space;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
pub fn set_axes(&mut self, axes: LayoutAxes) {
|
||||||
if axes != self.ctx.axes {
|
if axes != self.ctx.axes {
|
||||||
self.finish_subspace();
|
self.finish_subspace(axes);
|
||||||
self.ctx.axes = axes;
|
|
||||||
self.sub = Subspace::new(self.remaining_subspace(), axes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +127,7 @@ impl StackLayouter {
|
|||||||
|
|
||||||
pub fn remaining(&self) -> LayoutSpaces {
|
pub fn remaining(&self) -> LayoutSpaces {
|
||||||
let mut spaces = smallvec![LayoutSpace {
|
let mut spaces = smallvec![LayoutSpace {
|
||||||
dimensions: self.ctx.axes.specialize(self.remaining_subspace()),
|
dimensions: self.remaining_subspace().1,
|
||||||
padding: SizeBox::zero(),
|
padding: SizeBox::zero(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@ -161,13 +160,13 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_space(&mut self, hard: bool) {
|
pub fn finish_space(&mut self, hard: bool) {
|
||||||
self.finish_subspace();
|
self.finish_subspace(self.ctx.axes);
|
||||||
|
|
||||||
let space = self.ctx.spaces[self.space];
|
let space = self.ctx.spaces[self.space];
|
||||||
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
|
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
|
||||||
|
|
||||||
self.layouts.add(Layout {
|
self.layouts.add(Layout {
|
||||||
dimensions: match self.ctx.shrink_to_fit {
|
dimensions: match self.ctx.expand {
|
||||||
true => self.combined_dimensions.padded(space.padding),
|
true => self.combined_dimensions.padded(space.padding),
|
||||||
false => space.dimensions,
|
false => space.dimensions,
|
||||||
},
|
},
|
||||||
@ -180,27 +179,60 @@ impl StackLayouter {
|
|||||||
|
|
||||||
fn start_space(&mut self, space: usize, hard: bool) {
|
fn start_space(&mut self, space: usize, hard: bool) {
|
||||||
self.space = space;
|
self.space = space;
|
||||||
|
|
||||||
let space = self.ctx.spaces[space];
|
let space = self.ctx.spaces[space];
|
||||||
let usable = self.ctx.axes.generalize(space.usable());
|
|
||||||
|
|
||||||
self.hard = hard;
|
self.hard = hard;
|
||||||
self.start = space.start();
|
|
||||||
self.combined_dimensions = Size2D::zero();
|
self.combined_dimensions = Size2D::zero();
|
||||||
self.sub = Subspace::new(usable, self.ctx.axes);
|
self.sub = Subspace::new(space.start(), space.usable(), self.ctx.axes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_space(&self) -> usize {
|
fn next_space(&self) -> usize {
|
||||||
(self.space + 1).min(self.ctx.spaces.len() - 1)
|
(self.space + 1).min(self.ctx.spaces.len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_subspace(&mut self) {
|
fn finish_subspace(&mut self, new_axes: LayoutAxes) {
|
||||||
let dims = self.ctx.axes.specialize(self.sub.dimensions);
|
if self.ctx.axes.primary.needs_expansion() {
|
||||||
self.combined_dimensions = merge(self.combined_dimensions, dims);
|
self.sub.dimensions.x = self.sub.usable.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ctx.axes.secondary.needs_expansion() {
|
||||||
|
self.sub.dimensions.y = self.sub.usable.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (new_origin, new_usable) = self.remaining_subspace();
|
||||||
|
|
||||||
|
let origin = self.sub.origin;
|
||||||
|
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
|
||||||
|
let space = self.ctx.spaces[self.space];
|
||||||
|
self.combined_dimensions.max_eq(origin - space.start() + dimensions);
|
||||||
|
|
||||||
|
self.ctx.axes = new_axes;
|
||||||
|
self.sub = Subspace::new(new_origin, new_usable, new_axes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remaining_subspace(&self) -> Size2D {
|
fn remaining_subspace(&self) -> (Size2D, Size2D) {
|
||||||
Size2D::new(self.sub.usable.x, self.sub.usable.y - self.sub.dimensions.y)
|
let used = self.ctx.axes.specialize(self.sub.usable);
|
||||||
|
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
|
||||||
|
|
||||||
|
let new_usable = self.ctx.axes.specialize(Size2D {
|
||||||
|
x: self.sub.usable.x,
|
||||||
|
y: self.sub.usable.y - self.sub.dimensions.y,
|
||||||
|
});
|
||||||
|
|
||||||
|
let new_origin = self.sub.origin
|
||||||
|
+ Size2D::with_y(self.ctx.axes.specialize(self.sub.dimensions).y);
|
||||||
|
|
||||||
|
(new_origin, new_usable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_space(&mut self) {
|
||||||
|
if let Some(space) = self.sub.space.take() {
|
||||||
|
if self.sub.dimensions.y + space > self.sub.usable.y {
|
||||||
|
self.finish_space(false);
|
||||||
|
} else {
|
||||||
|
self.sub.dimensions.y += space;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
flex_spacing: flex_spacing(&ctx.text_style),
|
flex_spacing: flex_spacing(&ctx.text_style),
|
||||||
spaces: ctx.spaces.clone(),
|
spaces: ctx.spaces.clone(),
|
||||||
axes: ctx.axes,
|
axes: ctx.axes,
|
||||||
shrink_to_fit: ctx.shrink_to_fit,
|
expand: ctx.expand,
|
||||||
}),
|
}),
|
||||||
style: ctx.text_style.clone(),
|
style: ctx.text_style.clone(),
|
||||||
ctx,
|
ctx,
|
||||||
@ -66,24 +66,25 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
/// Layout a function.
|
/// Layout a function.
|
||||||
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
||||||
let (flex_spaces, stack_spaces) = self.flex.remaining()?;
|
let (first, second) = self.flex.remaining()?;
|
||||||
|
|
||||||
let ctx = |spaces| LayoutContext {
|
let ctx = |spaces| LayoutContext {
|
||||||
|
loader: self.ctx.loader,
|
||||||
top_level: false,
|
top_level: false,
|
||||||
text_style: &self.style,
|
text_style: &self.style,
|
||||||
|
page_style: self.ctx.page_style,
|
||||||
spaces,
|
spaces,
|
||||||
shrink_to_fit: true,
|
axes: self.ctx.axes.expanding(false),
|
||||||
.. self.ctx
|
expand: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try putting it in the flex space first, but if that is not enough
|
let commands = match func.body.val.layout(ctx(first)) {
|
||||||
// space, use the other space.
|
|
||||||
let commands = match func.body.val.layout(ctx(flex_spaces)) {
|
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(LayoutError::NotEnoughSpace(_)) => {
|
Err(e) => match (e, second) {
|
||||||
func.body.val.layout(ctx(stack_spaces))?
|
(LayoutError::NotEnoughSpace(_), Some(space))
|
||||||
|
=> func.body.val.layout(ctx(space))?,
|
||||||
|
_ => Err(e)?,
|
||||||
},
|
},
|
||||||
e => e?,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for command in commands {
|
for command in commands {
|
||||||
@ -101,10 +102,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
Command::AddMultiple(layouts) => self.flex.add_multiple(layouts),
|
Command::AddMultiple(layouts) => self.flex.add_multiple(layouts),
|
||||||
|
|
||||||
Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, false),
|
Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, false),
|
||||||
Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space)?,
|
Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space, false)?,
|
||||||
|
|
||||||
Command::FinishLine => self.flex.add_break(),
|
Command::FinishLine => self.flex.add_break(),
|
||||||
Command::FinishRun => self.flex.finish_run()?,
|
Command::FinishRun => { self.flex.finish_run()?; },
|
||||||
Command::FinishSpace => self.flex.finish_space(true)?,
|
Command::FinishSpace => self.flex.finish_space(true)?,
|
||||||
|
|
||||||
Command::BreakParagraph => self.break_paragraph()?,
|
Command::BreakParagraph => self.break_paragraph()?,
|
||||||
@ -140,7 +141,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
|
|
||||||
/// Finish the current flex layout and add space after it.
|
/// Finish the current flex layout and add space after it.
|
||||||
fn break_paragraph(&mut self) -> LayoutResult<()> {
|
fn break_paragraph(&mut self) -> LayoutResult<()> {
|
||||||
self.flex.add_secondary_space(paragraph_spacing(&self.style))
|
self.flex.add_secondary_space(paragraph_spacing(&self.style), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,10 +106,10 @@ impl<'p> Typesetter<'p> {
|
|||||||
padding: self.page_style.margins,
|
padding: self.page_style.margins,
|
||||||
}],
|
}],
|
||||||
axes: LayoutAxes {
|
axes: LayoutAxes {
|
||||||
primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin),
|
primary: AlignedAxis::new(Axis::LeftToRight, Alignment::Origin, false),
|
||||||
secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin),
|
secondary: AlignedAxis::new(Axis::TopToBottom, Alignment::Origin, false),
|
||||||
},
|
},
|
||||||
shrink_to_fit: false,
|
expand: true,
|
||||||
},
|
},
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub struct ParagraphBreak;
|
|||||||
function! {
|
function! {
|
||||||
data: ParagraphBreak,
|
data: ParagraphBreak,
|
||||||
parse: plain,
|
parse: plain,
|
||||||
layout(_, _) { Ok(commands![FinishRun]) }
|
layout(_, _) { Ok(commands![BreakParagraph]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! space_func {
|
macro_rules! space_func {
|
||||||
|
14
src/size.rs
14
src/size.rs
@ -95,6 +95,12 @@ impl Size {
|
|||||||
pub fn to_inches(&self) -> f32 {
|
pub fn to_inches(&self) -> f32 {
|
||||||
self.points * 0.0138889
|
self.points * 0.0138889
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set this size to the maximum of itself and the other size.
|
||||||
|
#[inline]
|
||||||
|
pub fn max_eq(&mut self, other: Size) {
|
||||||
|
*self = max(*self, other);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Size2D {
|
impl Size2D {
|
||||||
@ -146,6 +152,14 @@ impl Size2D {
|
|||||||
pub fn fits(&self, other: Size2D) -> bool {
|
pub fn fits(&self, other: Size2D) -> bool {
|
||||||
self.x >= other.x && self.y >= other.y
|
self.x >= other.x && self.y >= other.y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set this size to the maximum of itself and the other size
|
||||||
|
/// (for both dimensions).
|
||||||
|
#[inline]
|
||||||
|
pub fn max_eq(&mut self, other: Size2D) {
|
||||||
|
self.x.max_eq(other.x);
|
||||||
|
self.y.max_eq(other.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SizeBox {
|
impl SizeBox {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user