mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Completed stack and flex refactoring 🔋
This commit is contained in:
parent
f24e9b44e0
commit
863a1a7a00
@ -133,9 +133,10 @@ impl LayoutActionList {
|
|||||||
self.actions.is_empty()
|
self.actions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the list of actions as a vector.
|
/// Return the list of actions as a vector, leaving an empty
|
||||||
pub fn into_vec(self) -> Vec<LayoutAction> {
|
/// vector in its position.
|
||||||
self.actions
|
pub fn to_vec(&mut self) -> Vec<LayoutAction> {
|
||||||
|
std::mem::replace(&mut self.actions, vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a cached move action if one is cached.
|
/// Append a cached move action if one is cached.
|
||||||
|
@ -2,13 +2,13 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FlexLayouter {
|
pub struct FlexLayouter {
|
||||||
stack: StackLayouter,
|
|
||||||
|
|
||||||
axes: LayoutAxes,
|
axes: LayoutAxes,
|
||||||
flex_spacing: Size,
|
flex_spacing: Size,
|
||||||
|
stack: StackLayouter,
|
||||||
|
|
||||||
units: Vec<FlexUnit>,
|
units: Vec<FlexUnit>,
|
||||||
line: FlexLine,
|
line: FlexLine,
|
||||||
|
part: PartialLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -24,7 +24,6 @@ struct FlexLine {
|
|||||||
usable: Size,
|
usable: Size,
|
||||||
actions: LayoutActionList,
|
actions: LayoutActionList,
|
||||||
combined_dimensions: Size2D,
|
combined_dimensions: Size2D,
|
||||||
part: PartialLine,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlexLine {
|
impl FlexLine {
|
||||||
@ -33,7 +32,6 @@ impl FlexLine {
|
|||||||
usable,
|
usable,
|
||||||
actions: LayoutActionList::new(),
|
actions: LayoutActionList::new(),
|
||||||
combined_dimensions: Size2D::zero(),
|
combined_dimensions: Size2D::zero(),
|
||||||
part: PartialLine::new(usable),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +42,7 @@ struct PartialLine {
|
|||||||
content: Vec<(Size, Layout)>,
|
content: Vec<(Size, Layout)>,
|
||||||
dimensions: Size2D,
|
dimensions: Size2D,
|
||||||
space: Option<Size>,
|
space: Option<Size>,
|
||||||
|
last_was_space: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialLine {
|
impl PartialLine {
|
||||||
@ -53,6 +52,7 @@ impl PartialLine {
|
|||||||
content: vec![],
|
content: vec![],
|
||||||
dimensions: Size2D::zero(),
|
dimensions: Size2D::zero(),
|
||||||
space: None,
|
space: None,
|
||||||
|
last_was_space: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,13 +80,13 @@ impl FlexLayouter {
|
|||||||
let usable = stack.primary_usable();
|
let usable = stack.primary_usable();
|
||||||
|
|
||||||
FlexLayouter {
|
FlexLayouter {
|
||||||
stack,
|
|
||||||
|
|
||||||
axes: ctx.axes,
|
axes: ctx.axes,
|
||||||
flex_spacing: ctx.flex_spacing,
|
flex_spacing: ctx.flex_spacing,
|
||||||
|
stack,
|
||||||
|
|
||||||
units: vec![],
|
units: vec![],
|
||||||
line: FlexLine::new(usable)
|
line: FlexLine::new(usable),
|
||||||
|
part: PartialLine::new(usable),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ impl FlexLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_primary_space(&mut self, space: Size, soft: bool) {
|
pub fn add_primary_space(&mut self, space: Size, soft: bool) {
|
||||||
self.units.push(FlexUnit::Space(space, soft));
|
self.units.push(FlexUnit::Space(space, soft))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
|
pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
|
||||||
@ -122,7 +122,7 @@ impl FlexLayouter {
|
|||||||
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
|
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
|
||||||
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_line();
|
||||||
} else {
|
} else {
|
||||||
self.stack.set_spaces(spaces, false);
|
self.stack.set_spaces(spaces, false);
|
||||||
}
|
}
|
||||||
@ -142,7 +142,6 @@ impl FlexLayouter {
|
|||||||
|
|
||||||
Ok((flex_spaces, Some(stack_spaces)))
|
Ok((flex_spaces, Some(stack_spaces)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_is_empty(&self) -> bool {
|
pub fn run_is_empty(&self) -> bool {
|
||||||
@ -162,7 +161,9 @@ impl FlexLayouter {
|
|||||||
if !self.run_is_empty() {
|
if !self.run_is_empty() {
|
||||||
self.finish_run()?;
|
self.finish_run()?;
|
||||||
}
|
}
|
||||||
Ok(self.stack.finish_space(hard))
|
|
||||||
|
self.stack.finish_space(hard);
|
||||||
|
Ok(self.start_line())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
|
pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
|
||||||
@ -172,7 +173,7 @@ impl FlexLayouter {
|
|||||||
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
|
||||||
FlexUnit::Space(space, soft) => self.layout_space(space, soft),
|
FlexUnit::Space(space, soft) => self.layout_space(space, soft),
|
||||||
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
|
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
|
||||||
FlexUnit::Break => self.layout_break(),
|
FlexUnit::Break => { self.finish_line()?; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,77 +184,91 @@ impl FlexLayouter {
|
|||||||
self.finish_partial_line();
|
self.finish_partial_line();
|
||||||
|
|
||||||
self.stack.add(Layout {
|
self.stack.add(Layout {
|
||||||
dimensions: self.axes.specialize(self.line.combined_dimensions),
|
dimensions: self.axes.specialize(Size2D {
|
||||||
actions: self.line.actions.into_vec(),
|
x: self.line.usable,
|
||||||
|
y: self.line.combined_dimensions.y + self.flex_spacing,
|
||||||
|
}),
|
||||||
|
actions: self.line.actions.to_vec(),
|
||||||
debug_render: false,
|
debug_render: false,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let remaining = self.axes.specialize(Size2D {
|
let remaining = self.axes.specialize(Size2D {
|
||||||
x: self.line.usable - self.line.combined_dimensions.x,
|
x: self.part.usable
|
||||||
|
- self.part.dimensions.x
|
||||||
|
- self.part.space.unwrap_or(Size::zero()),
|
||||||
y: self.line.combined_dimensions.y,
|
y: self.line.combined_dimensions.y,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.line = FlexLine::new(self.stack.primary_usable());
|
self.start_line();
|
||||||
|
|
||||||
Ok(remaining)
|
Ok(remaining)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_partial_line(&mut self) {
|
fn start_line(&mut self) {
|
||||||
let part = self.line.part;
|
let usable = self.stack.primary_usable();
|
||||||
|
self.line = FlexLine::new(usable);
|
||||||
|
self.part = PartialLine::new(usable);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_partial_line(&mut self) {
|
||||||
let factor = self.axes.primary.axis.factor();
|
let factor = self.axes.primary.axis.factor();
|
||||||
let anchor =
|
let anchor =
|
||||||
self.axes.primary.anchor(self.line.usable)
|
self.axes.primary.anchor(self.line.usable)
|
||||||
- self.axes.primary.anchor(part.dimensions.x);
|
- self.axes.primary.anchor(self.part.dimensions.x);
|
||||||
|
|
||||||
for (offset, layout) in part.content {
|
for (offset, layout) in self.part.content.drain(..) {
|
||||||
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
|
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
|
||||||
self.line.actions.add_layout(pos, layout);
|
self.line.actions.add_layout(pos, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.line.combined_dimensions.x.max_eq(part.dimensions.x);
|
self.line.combined_dimensions.x = anchor + factor * self.part.dimensions.x;
|
||||||
self.line.part = PartialLine::new(self.line.usable - part.dimensions.x);
|
self.line.combined_dimensions.y.max_eq(self.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<()> {
|
||||||
let size = self.axes.generalize(boxed.dimensions);
|
let size = self.axes.generalize(boxed.dimensions);
|
||||||
|
|
||||||
if size.x > self.size_left() {
|
let new_dimension = self.part.dimensions.x
|
||||||
self.space = None;
|
+ self.part.space.unwrap_or(Size::zero());
|
||||||
|
|
||||||
|
if new_dimension > self.part.usable {
|
||||||
self.finish_line()?;
|
self.finish_line()?;
|
||||||
|
|
||||||
while size.x > self.usable {
|
while size.x > self.line.usable {
|
||||||
if self.stack.space_is_last() {
|
if self.stack.space_is_last() {
|
||||||
Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?;
|
Err(LayoutError::NotEnoughSpace("failed to add box to flex run"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_space(true);
|
self.stack.finish_space(true);
|
||||||
self.total_usable = self.stack.primary_usable();
|
|
||||||
self.usable = self.total_usable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.layout_space();
|
if let Some(space) = self.part.space.take() {
|
||||||
|
self.layout_space(space, false);
|
||||||
|
}
|
||||||
|
|
||||||
let offset = self.run.size.x;
|
let offset = self.part.dimensions.x;
|
||||||
self.run.content.push((offset, boxed));
|
self.part.content.push((offset, boxed));
|
||||||
|
|
||||||
self.run.size.x += size.x;
|
self.part.dimensions.x += size.x;
|
||||||
self.run.size.y = crate::size::max(self.run.size.y, size.y);
|
self.part.dimensions.y.max_eq(size.y);
|
||||||
|
self.part.last_was_space = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_space(&mut self, space: Size, soft: bool) {
|
fn layout_space(&mut self, space: Size, soft: bool) {
|
||||||
if let Some(space) = self.space.take() {
|
if soft {
|
||||||
if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
|
if !self.part.last_was_space {
|
||||||
self.run.size.x += space;
|
self.part.space = Some(space);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if self.part.dimensions.x + space > self.part.usable {
|
||||||
|
self.part.dimensions.x = self.part.usable;
|
||||||
|
} else {
|
||||||
|
self.part.dimensions.x += space;
|
||||||
|
}
|
||||||
|
self.part.last_was_space = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,19 +276,17 @@ 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 {
|
let extent = self.line.combined_dimensions.x;
|
||||||
// Alignment::Origin =>
|
let usable = self.line.usable;
|
||||||
// if self.max_extent == Size::zero() {
|
|
||||||
// self.total_usable
|
let new_usable = match axes.primary.alignment {
|
||||||
// } else {
|
Alignment::Origin if extent == Size::zero() => usable,
|
||||||
// Size::zero()
|
Alignment::Center if extent < usable / 2 => usable - 2 * extent,
|
||||||
// },
|
Alignment::End => usable - extent,
|
||||||
// Alignment::Center => crate::size::max(
|
_ => Size::zero(),
|
||||||
// self.total_usable - 2 * self.max_extent,
|
};
|
||||||
// Size::zero()
|
|
||||||
// ),
|
self.part = PartialLine::new(new_usable);
|
||||||
// Alignment::End => self.total_usable - self.max_extent,
|
|
||||||
// };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if axes.secondary != self.axes.secondary {
|
if axes.secondary != self.axes.secondary {
|
||||||
@ -282,13 +295,4 @@ impl FlexLayouter {
|
|||||||
|
|
||||||
self.axes = axes;
|
self.axes = axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_break(&mut self) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_left(&self) -> Size {
|
|
||||||
let space = self.space.unwrap_or(Size::zero());
|
|
||||||
self.usable - (self.run.size.x + space)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,22 +6,38 @@ pub struct StackLayouter {
|
|||||||
ctx: StackContext,
|
ctx: StackContext,
|
||||||
layouts: MultiLayout,
|
layouts: MultiLayout,
|
||||||
|
|
||||||
space: usize,
|
space: Space,
|
||||||
hard: bool,
|
|
||||||
actions: LayoutActionList,
|
|
||||||
combined_dimensions: Size2D, // <- specialized
|
|
||||||
|
|
||||||
sub: Subspace,
|
sub: Subspace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Space {
|
||||||
|
index: usize,
|
||||||
|
hard: bool,
|
||||||
|
actions: LayoutActionList,
|
||||||
|
combined_dimensions: Size2D,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Space {
|
||||||
|
fn new(index: usize, hard: bool) -> Space {
|
||||||
|
Space {
|
||||||
|
index,
|
||||||
|
hard,
|
||||||
|
actions: LayoutActionList::new(),
|
||||||
|
combined_dimensions: Size2D::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Subspace {
|
struct Subspace {
|
||||||
origin: Size2D, // <- specialized
|
origin: Size2D,
|
||||||
usable: Size2D,
|
usable: Size2D,
|
||||||
anchor: Size2D, // <- generic
|
anchor: Size2D,
|
||||||
factor: i32,
|
factor: i32,
|
||||||
dimensions: Size2D, // <- generic
|
dimensions: Size2D,
|
||||||
space: Option<Size>,
|
space: Option<Size>,
|
||||||
|
last_was_space: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subspace {
|
impl Subspace {
|
||||||
@ -33,6 +49,7 @@ impl Subspace {
|
|||||||
factor: axes.secondary.axis.factor(),
|
factor: axes.secondary.axis.factor(),
|
||||||
dimensions: Size2D::zero(),
|
dimensions: Size2D::zero(),
|
||||||
space: None,
|
space: None,
|
||||||
|
last_was_space: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,29 +73,30 @@ impl StackLayouter {
|
|||||||
StackLayouter {
|
StackLayouter {
|
||||||
ctx,
|
ctx,
|
||||||
layouts: MultiLayout::new(),
|
layouts: MultiLayout::new(),
|
||||||
|
space: Space::new(0, true),
|
||||||
space: 0,
|
|
||||||
hard: true,
|
|
||||||
actions: LayoutActionList::new(),
|
|
||||||
combined_dimensions: Size2D::zero(),
|
|
||||||
|
|
||||||
sub: Subspace::new(space.start(), space.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();
|
if let Some(space) = self.sub.space.take() {
|
||||||
|
self.add_space(space, false);
|
||||||
|
}
|
||||||
|
|
||||||
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 = Size2D {
|
||||||
|
x: crate::size::max(self.sub.dimensions.x, size.x),
|
||||||
|
y: self.sub.dimensions.y + size.y
|
||||||
|
};
|
||||||
|
|
||||||
while !self.sub.usable.fits(new_dimensions) {
|
while !self.sub.usable.fits(new_dimensions) {
|
||||||
if self.space_is_empty() {
|
if self.space_is_last() && self.space_is_empty() {
|
||||||
Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?;
|
Err(LayoutError::NotEnoughSpace("failed to add box to stack"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_space(true);
|
self.finish_space(true);
|
||||||
new_dimensions = merge(self.sub.dimensions, size);
|
new_dimensions = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.sub.dimensions.y;
|
let offset = self.sub.dimensions.y;
|
||||||
@ -86,11 +104,12 @@ impl StackLayouter {
|
|||||||
|
|
||||||
let pos = self.sub.origin + self.ctx.axes.specialize(
|
let pos = self.sub.origin + self.ctx.axes.specialize(
|
||||||
(self.sub.anchor - anchor)
|
(self.sub.anchor - anchor)
|
||||||
+ Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
|
+ Size2D::with_y(self.space.combined_dimensions.y + self.sub.factor * offset)
|
||||||
);
|
);
|
||||||
|
|
||||||
self.actions.add_layout(pos, layout);
|
self.space.actions.add_layout(pos, layout);
|
||||||
self.sub.dimensions = new_dimensions;
|
self.sub.dimensions = new_dimensions;
|
||||||
|
self.sub.last_was_space = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -103,24 +122,36 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_space(&mut self, space: Size, soft: bool) {
|
pub fn add_space(&mut self, space: Size, soft: bool) {
|
||||||
|
if soft {
|
||||||
|
if !self.sub.last_was_space {
|
||||||
self.sub.space = Some(space);
|
self.sub.space = Some(space);
|
||||||
if !soft {
|
}
|
||||||
self.layout_space();
|
} else {
|
||||||
|
if self.sub.dimensions.y + space > self.sub.usable.y {
|
||||||
|
self.sub.dimensions.y = self.sub.usable.y;
|
||||||
|
self.finish_space(false);
|
||||||
|
} else {
|
||||||
|
self.sub.dimensions.y += space;
|
||||||
|
}
|
||||||
|
self.sub.last_was_space = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(axes);
|
self.finish_subspace();
|
||||||
|
let (origin, usable) = self.remaining_subspace();
|
||||||
|
self.ctx.axes = axes;
|
||||||
|
self.sub = Subspace::new(origin, usable, axes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
|
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
|
||||||
if replace_empty && self.space_is_empty() {
|
if replace_empty && self.space_is_empty() {
|
||||||
self.ctx.spaces = spaces;
|
self.ctx.spaces = spaces;
|
||||||
self.start_space(0, self.hard);
|
self.start_space(0, self.space.hard);
|
||||||
} else {
|
} else {
|
||||||
self.ctx.spaces.truncate(self.space + 1);
|
self.ctx.spaces.truncate(self.space.index + 1);
|
||||||
self.ctx.spaces.extend(spaces);
|
self.ctx.spaces.extend(spaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,34 +174,33 @@ impl StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn space_is_empty(&self) -> bool {
|
pub fn space_is_empty(&self) -> bool {
|
||||||
self.combined_dimensions == Size2D::zero()
|
self.space.combined_dimensions == Size2D::zero()
|
||||||
|
&& self.space.actions.is_empty()
|
||||||
&& self.sub.dimensions == Size2D::zero()
|
&& self.sub.dimensions == Size2D::zero()
|
||||||
&& self.actions.is_empty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn space_is_last(&self) -> bool {
|
pub fn space_is_last(&self) -> bool {
|
||||||
self.space == self.ctx.spaces.len() - 1
|
self.space.index == self.ctx.spaces.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(mut self) -> MultiLayout {
|
pub fn finish(mut self) -> MultiLayout {
|
||||||
if self.hard || !self.space_is_empty() {
|
if self.space.hard || !self.space_is_empty() {
|
||||||
self.finish_space(false);
|
self.finish_space(false);
|
||||||
}
|
}
|
||||||
self.layouts
|
self.layouts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_space(&mut self, hard: bool) {
|
pub fn finish_space(&mut self, hard: bool) {
|
||||||
self.finish_subspace(self.ctx.axes);
|
self.finish_subspace();
|
||||||
|
|
||||||
let space = self.ctx.spaces[self.space];
|
let space = self.ctx.spaces[self.space.index];
|
||||||
let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
|
|
||||||
|
|
||||||
self.layouts.add(Layout {
|
self.layouts.add(Layout {
|
||||||
dimensions: match self.ctx.expand {
|
dimensions: match self.ctx.expand {
|
||||||
true => self.combined_dimensions.padded(space.padding),
|
true => self.space.combined_dimensions.padded(space.padding),
|
||||||
false => space.dimensions,
|
false => space.dimensions,
|
||||||
},
|
},
|
||||||
actions: actions.into_vec(),
|
actions: self.space.actions.to_vec(),
|
||||||
debug_render: true,
|
debug_render: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -178,19 +208,17 @@ 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::new(space, hard);
|
||||||
let space = self.ctx.spaces[space];
|
|
||||||
|
|
||||||
self.hard = hard;
|
let space = self.ctx.spaces[space];
|
||||||
self.combined_dimensions = Size2D::zero();
|
|
||||||
self.sub = Subspace::new(space.start(), space.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.index + 1).min(self.ctx.spaces.len() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_subspace(&mut self, new_axes: LayoutAxes) {
|
fn finish_subspace(&mut self) {
|
||||||
if self.ctx.axes.primary.needs_expansion() {
|
if self.ctx.axes.primary.needs_expansion() {
|
||||||
self.sub.dimensions.x = self.sub.usable.x;
|
self.sub.dimensions.x = self.sub.usable.x;
|
||||||
}
|
}
|
||||||
@ -199,46 +227,23 @@ impl StackLayouter {
|
|||||||
self.sub.dimensions.y = self.sub.usable.y;
|
self.sub.dimensions.y = self.sub.usable.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (new_origin, new_usable) = self.remaining_subspace();
|
let space = self.ctx.spaces[self.space.index];
|
||||||
|
|
||||||
let origin = self.sub.origin;
|
let origin = self.sub.origin;
|
||||||
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
|
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
|
||||||
let space = self.ctx.spaces[self.space];
|
self.space.combined_dimensions.max_eq(origin - space.start() + dimensions);
|
||||||
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, Size2D) {
|
fn remaining_subspace(&self) -> (Size2D, Size2D) {
|
||||||
let used = self.ctx.axes.specialize(self.sub.usable);
|
let new_origin = self.sub.origin + match self.ctx.axes.secondary.axis.is_positive() {
|
||||||
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
|
true => self.ctx.axes.specialize(Size2D::with_y(self.sub.dimensions.y)),
|
||||||
|
false => Size2D::zero(),
|
||||||
|
};
|
||||||
|
|
||||||
let new_usable = self.ctx.axes.specialize(Size2D {
|
let new_usable = self.ctx.axes.specialize(Size2D {
|
||||||
x: self.sub.usable.x,
|
x: self.sub.usable.x,
|
||||||
y: self.sub.usable.y - self.sub.dimensions.y,
|
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)
|
(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge(a: Size2D, b: Size2D) -> Size2D {
|
|
||||||
Size2D {
|
|
||||||
x: crate::size::max(a.x, b.x),
|
|
||||||
y: a.y + b.y
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
|
|||||||
|
|
||||||
Ok(Layout {
|
Ok(Layout {
|
||||||
dimensions: Size2D::new(self.width, self.ctx.style.font_size),
|
dimensions: Size2D::new(self.width, self.ctx.style.font_size),
|
||||||
actions: self.actions.into_vec(),
|
actions: self.actions.to_vec(),
|
||||||
debug_render: false,
|
debug_render: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
/// Layouts syntax trees into boxes.
|
|
||||||
pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> {
|
pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> {
|
||||||
let mut layouter = TreeLayouter::new(ctx);
|
let mut layouter = TreeLayouter::new(ctx);
|
||||||
layouter.layout(tree)?;
|
layouter.layout(tree)?;
|
||||||
@ -16,7 +15,7 @@ struct TreeLayouter<'a, 'p> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'p> TreeLayouter<'a, 'p> {
|
impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||||
/// Create a new layouter.
|
/// Create a new syntax tree layouter.
|
||||||
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
|
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
|
||||||
TreeLayouter {
|
TreeLayouter {
|
||||||
flex: FlexLayouter::new(FlexContext {
|
flex: FlexLayouter::new(FlexContext {
|
||||||
@ -30,28 +29,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a syntax tree.
|
|
||||||
fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> {
|
fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> {
|
||||||
for node in &tree.nodes {
|
for node in &tree.nodes {
|
||||||
match &node.val {
|
match &node.val {
|
||||||
Node::Text(text) => {
|
Node::Text(text) => self.layout_text(text)?,
|
||||||
self.flex.add(layout_text(text, TextContext {
|
|
||||||
loader: &self.ctx.loader,
|
|
||||||
style: &self.style,
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Space => {
|
Node::Space => self.layout_space(),
|
||||||
if !self.flex.run_is_empty() && !self.flex.run_last_is_space() {
|
Node::Newline => self.layout_paragraph()?,
|
||||||
let space = self.style.word_spacing * self.style.font_size;
|
|
||||||
self.flex.add_primary_space(space, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Node::Newline => {
|
|
||||||
if !self.flex.run_is_empty() {
|
|
||||||
self.break_paragraph()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::ToggleItalics => self.style.toggle_class(FontClass::Italic),
|
Node::ToggleItalics => self.style.toggle_class(FontClass::Italic),
|
||||||
Node::ToggleBold => self.style.toggle_class(FontClass::Bold),
|
Node::ToggleBold => self.style.toggle_class(FontClass::Bold),
|
||||||
@ -64,11 +48,33 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a function.
|
fn layout_text(&mut self, text: &str) -> LayoutResult<()> {
|
||||||
|
let layout = layout_text(text, TextContext {
|
||||||
|
loader: &self.ctx.loader,
|
||||||
|
style: &self.style,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(self.flex.add(layout))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_space(&mut self) {
|
||||||
|
if !self.flex.run_is_empty() {
|
||||||
|
self.flex.add_primary_space(word_spacing(&self.style), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout_paragraph(&mut self) -> LayoutResult<()> {
|
||||||
|
if !self.flex.run_is_empty() {
|
||||||
|
self.flex.add_secondary_space(paragraph_spacing(&self.style), true)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
|
||||||
let (first, second) = self.flex.remaining()?;
|
let (first, second) = self.flex.remaining()?;
|
||||||
|
|
||||||
let ctx = |spaces| LayoutContext {
|
let ctx = |spaces| {
|
||||||
|
LayoutContext {
|
||||||
loader: self.ctx.loader,
|
loader: self.ctx.loader,
|
||||||
top_level: false,
|
top_level: false,
|
||||||
text_style: &self.style,
|
text_style: &self.style,
|
||||||
@ -76,15 +82,19 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
spaces,
|
spaces,
|
||||||
axes: self.ctx.axes.expanding(false),
|
axes: self.ctx.axes.expanding(false),
|
||||||
expand: false,
|
expand: false,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let commands = match func.body.val.layout(ctx(first)) {
|
let commands = match func.body.val.layout(ctx(first)) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => match (e, second) {
|
Err(e) => {
|
||||||
(LayoutError::NotEnoughSpace(_), Some(space))
|
match (e, second) {
|
||||||
=> func.body.val.layout(ctx(space))?,
|
(LayoutError::NotEnoughSpace(_), Some(space)) => {
|
||||||
_ => Err(e)?,
|
func.body.val.layout(ctx(space))?
|
||||||
},
|
}
|
||||||
|
(e, _) => Err(e)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for command in commands {
|
for command in commands {
|
||||||
@ -108,7 +118,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
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.layout_paragraph()?,
|
||||||
|
|
||||||
Command::SetTextStyle(style) => self.style = style,
|
Command::SetTextStyle(style) => self.style = style,
|
||||||
Command::SetPageStyle(style) => {
|
Command::SetPageStyle(style) => {
|
||||||
@ -134,15 +144,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the layout.
|
|
||||||
fn finish(self) -> LayoutResult<MultiLayout> {
|
fn finish(self) -> LayoutResult<MultiLayout> {
|
||||||
self.flex.finish()
|
self.flex.finish()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Finish the current flex layout and add space after it.
|
fn word_spacing(style: &TextStyle) -> Size {
|
||||||
fn break_paragraph(&mut self) -> LayoutResult<()> {
|
style.word_spacing * style.font_size
|
||||||
self.flex.add_secondary_space(paragraph_spacing(&self.style), true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flex_spacing(style: &TextStyle) -> Size {
|
fn flex_spacing(style: &TextStyle) -> Size {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user