mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Allow center alignment 📐
This commit is contained in:
parent
f2f05e07b0
commit
58693486f9
@ -149,10 +149,7 @@ pub struct LayoutSpace {
|
||||
impl LayoutSpace {
|
||||
/// The actually usable area (dimensions minus padding).
|
||||
pub fn usable(&self) -> Size2D {
|
||||
Size2D {
|
||||
x: self.dimensions.x - self.padding.left - self.padding.right,
|
||||
y: self.dimensions.y - self.padding.top - self.padding.bottom,
|
||||
}
|
||||
self.dimensions.unpadded(self.padding)
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,6 +158,7 @@ impl LayoutSpace {
|
||||
pub enum Alignment {
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
}
|
||||
|
||||
/// The error type for layouting.
|
||||
|
@ -73,6 +73,7 @@ impl StackLayouter {
|
||||
let position = match self.space.alignment {
|
||||
Alignment::Left => self.cursor,
|
||||
Alignment::Right => self.cursor - Size2D::with_x(layout.dimensions.x),
|
||||
Alignment::Center => self.cursor - Size2D::with_x(layout.dimensions.x / 2),
|
||||
};
|
||||
|
||||
self.cursor.y += layout.dimensions.y;
|
||||
@ -172,9 +173,9 @@ impl StackLayouter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this layouter contains any items.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.layouts.is_empty() && self.actions.is_empty()
|
||||
/// Whether the active space of this layouter contains no content.
|
||||
pub fn current_space_is_empty(&self) -> bool {
|
||||
!self.started || self.actions.is_empty()
|
||||
}
|
||||
|
||||
fn overflows(&self, dimensions: Size2D) -> bool {
|
||||
@ -185,7 +186,7 @@ impl StackLayouter {
|
||||
fn start_dimensions(space: LayoutSpace) -> Size2D {
|
||||
match space.alignment {
|
||||
Alignment::Left => Size2D::zero(),
|
||||
Alignment::Right => Size2D::with_x(space.usable().x),
|
||||
Alignment::Right | Alignment::Center => Size2D::with_x(space.usable().x),
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,6 +198,7 @@ fn start_cursor(space: LayoutSpace) -> Size2D {
|
||||
x: match space.alignment {
|
||||
Alignment::Left => space.padding.left,
|
||||
Alignment::Right => space.dimensions.x - space.padding.right,
|
||||
Alignment::Center => space.padding.left + (space.usable().x / 2),
|
||||
},
|
||||
y: space.padding.top,
|
||||
}
|
||||
|
@ -47,8 +47,14 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
|
||||
// Finish the current flex layouting process.
|
||||
Node::Newline => {
|
||||
let space = paragraph_spacing(&self.style);
|
||||
self.layout_flex(space)?;
|
||||
self.layout_flex()?;
|
||||
|
||||
if !self.stack.current_space_is_empty() {
|
||||
let space = paragraph_spacing(&self.style);
|
||||
self.stack.add_space(space)?;
|
||||
}
|
||||
|
||||
self.start_new_flex();
|
||||
}
|
||||
|
||||
// Toggle the text styles.
|
||||
@ -65,11 +71,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
|
||||
/// Finish the layout.
|
||||
fn finish(mut self) -> LayoutResult<MultiLayout> {
|
||||
// If there are remainings, add them to the layout.
|
||||
if !self.flex.is_empty() {
|
||||
self.layout_flex(Size::zero())?;
|
||||
}
|
||||
|
||||
self.layout_flex()?;
|
||||
self.stack.finish()
|
||||
}
|
||||
|
||||
@ -93,22 +95,24 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
}
|
||||
|
||||
/// Finish the current flex layout and add it the stack.
|
||||
fn layout_flex(&mut self, after_space: Size) -> LayoutResult<()> {
|
||||
fn layout_flex(&mut self) -> LayoutResult<()> {
|
||||
if self.flex.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let layouts = self.flex.finish()?;
|
||||
self.stack.add_many(layouts)?;
|
||||
self.stack.add_space(after_space)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a new flex layout.
|
||||
fn start_new_flex(&mut self) {
|
||||
let mut ctx = self.flex.ctx();
|
||||
ctx.space.dimensions = self.stack.remaining();
|
||||
ctx.flex_spacing = flex_spacing(&self.style);
|
||||
|
||||
self.flex = FlexLayouter::new(ctx);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Layout a function.
|
||||
@ -118,12 +122,12 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
|
||||
|
||||
ctx.space.dimensions = self.stack.remaining();
|
||||
ctx.space.padding = SizeBox::zero();
|
||||
ctx.space.shrink_to_fit = false;
|
||||
ctx.space.shrink_to_fit = true;
|
||||
|
||||
if let Some(space) = ctx.extra_space.as_mut() {
|
||||
space.dimensions = space.dimensions.unpadded(space.padding);
|
||||
space.dimensions = space.usable();
|
||||
space.padding = SizeBox::zero();
|
||||
space.shrink_to_fit = false;
|
||||
space.shrink_to_fit = true;
|
||||
}
|
||||
|
||||
let commands = func.body.layout(ctx)?;
|
||||
|
@ -19,6 +19,7 @@ impl Function for AlignFunc {
|
||||
match ident.as_str() {
|
||||
"left" => Alignment::Left,
|
||||
"right" => Alignment::Right,
|
||||
"center" => Alignment::Center,
|
||||
s => return err(format!("invalid alignment specifier: '{}'", s)),
|
||||
}
|
||||
} else {
|
||||
@ -40,6 +41,10 @@ impl Function for AlignFunc {
|
||||
fn layout(&self, mut ctx: LayoutContext) -> LayoutResult<FuncCommands> {
|
||||
if let Some(body) = &self.body {
|
||||
ctx.space.alignment = self.alignment;
|
||||
if let Some(space) = ctx.extra_space.as_mut() {
|
||||
space.alignment = self.alignment;
|
||||
}
|
||||
|
||||
let layouts = layout_tree(body, ctx)?;
|
||||
|
||||
let mut commands = FuncCommands::new();
|
||||
|
9
tests/layouts/align.typ
Normal file
9
tests/layouts/align.typ
Normal file
@ -0,0 +1,9 @@
|
||||
{size:150pt*206pt}
|
||||
|
||||
[align: left][Left: {lorem:20}]
|
||||
|
||||
[align: right][Right: {lorem:20}]
|
||||
|
||||
[align: center][Center: {lorem:80}]
|
||||
|
||||
[align: left][Left: {lorem:20}]
|
@ -1,2 +1,2 @@
|
||||
{size:200pt*200pt}
|
||||
{lorem:400}
|
||||
{lorem:300}
|
||||
|
@ -1,8 +1,3 @@
|
||||
// Basic unboxed
|
||||
{include:shakespeare.tpl}
|
||||
|
||||
// Boxed, but still left-aligned
|
||||
[align: center][{include:shakespeare.tpl}]
|
||||
[align: left][{include:shakespeare.tpl}]
|
||||
|
||||
// Boxed, and right-aligned
|
||||
[align: right][{include:shakespeare.tpl}]
|
||||
|
Loading…
x
Reference in New Issue
Block a user