mirror of
https://github.com/typst/typst
synced 2025-05-22 13:05:29 +08:00
Positions placed elements relative to real container size (#1745)
This positions placed elements relative to the real container size instead of relative to the base size of the region. This makes its usage more versatile. Fixes #82 Fixes #685 Fixes #1705
This commit is contained in:
parent
b37c1e2731
commit
3dcd8e6e6b
@ -66,6 +66,8 @@ impl Layout for FlowElem {
|
||||
sticky: true,
|
||||
movable: false,
|
||||
});
|
||||
} else if let Some(placed) = child.to::<PlaceElem>() {
|
||||
layouter.layout_placed(vt, placed, styles)?;
|
||||
} else if child.can::<dyn Layout>() {
|
||||
layouter.layout_multiple(vt, child, styles)?;
|
||||
} else if child.is::<ColbreakElem>() {
|
||||
@ -128,7 +130,13 @@ enum FlowItem {
|
||||
/// (to keep it together with its footnotes).
|
||||
Frame { frame: Frame, aligns: Axes<Align>, sticky: bool, movable: bool },
|
||||
/// An absolutely placed frame.
|
||||
Placed { frame: Frame, y_align: Smart<Option<Align>>, float: bool, clearance: Abs },
|
||||
Placed {
|
||||
frame: Frame,
|
||||
x_align: Align,
|
||||
y_align: Smart<Option<Align>>,
|
||||
float: bool,
|
||||
clearance: Abs,
|
||||
},
|
||||
/// A footnote frame (can also be the separator).
|
||||
Footnote(Frame),
|
||||
}
|
||||
@ -258,6 +266,25 @@ impl<'a> FlowLayouter<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Layout a placed element.
|
||||
fn layout_placed(
|
||||
&mut self,
|
||||
vt: &mut Vt,
|
||||
placed: &PlaceElem,
|
||||
styles: StyleChain,
|
||||
) -> SourceResult<()> {
|
||||
let float = placed.float(styles);
|
||||
let clearance = placed.clearance(styles);
|
||||
let alignment = placed.alignment(styles);
|
||||
let x_align = alignment.map_or(Align::Center, |aligns| {
|
||||
aligns.x.unwrap_or(GenAlign::Start).resolve(styles)
|
||||
});
|
||||
let y_align = alignment.map(|align| align.y.resolve(styles));
|
||||
let frame = placed.layout(vt, styles, self.regions)?.into_frame();
|
||||
let item = FlowItem::Placed { frame, x_align, y_align, float, clearance };
|
||||
self.layout_item(vt, item)
|
||||
}
|
||||
|
||||
/// Layout into multiple regions.
|
||||
fn layout_multiple(
|
||||
&mut self,
|
||||
@ -265,16 +292,6 @@ impl<'a> FlowLayouter<'a> {
|
||||
block: &Content,
|
||||
styles: StyleChain,
|
||||
) -> SourceResult<()> {
|
||||
// Handle placed elements.
|
||||
if let Some(placed) = block.to::<PlaceElem>() {
|
||||
let float = placed.float(styles);
|
||||
let clearance = placed.clearance(styles);
|
||||
let y_align = placed.alignment(styles).map(|align| align.y.resolve(styles));
|
||||
let frame = placed.layout_inner(vt, styles, self.regions)?.into_frame();
|
||||
let item = FlowItem::Placed { frame, y_align, float, clearance };
|
||||
return self.layout_item(vt, item);
|
||||
}
|
||||
|
||||
// Temporarily delegerate rootness to the columns.
|
||||
let is_root = self.root;
|
||||
if is_root && block.is::<ColumnsElem>() {
|
||||
@ -491,7 +508,8 @@ impl<'a> FlowLayouter<'a> {
|
||||
offset += frame.height();
|
||||
output.push_frame(pos, frame);
|
||||
}
|
||||
FlowItem::Placed { frame, y_align, float, .. } => {
|
||||
FlowItem::Placed { frame, x_align, y_align, float, .. } => {
|
||||
let x = x_align.position(size.x - frame.width());
|
||||
let y = if float {
|
||||
match y_align {
|
||||
Smart::Custom(Some(Align::Top)) => {
|
||||
@ -505,7 +523,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
float_bottom_offset += frame.height();
|
||||
y
|
||||
}
|
||||
_ => offset + ruler.position(size.y - used.y),
|
||||
_ => unreachable!("float must be y aligned"),
|
||||
}
|
||||
} else {
|
||||
match y_align {
|
||||
@ -516,7 +534,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
output.push_frame(Point::with_y(y), frame);
|
||||
output.push_frame(Point::new(x, y), frame);
|
||||
}
|
||||
FlowItem::Footnote(frame) => {
|
||||
let y = size.y - footnote_height + footnote_offset;
|
||||
|
@ -266,6 +266,8 @@ fn realize_block<'a>(
|
||||
content: &'a Content,
|
||||
styles: StyleChain<'a>,
|
||||
) -> SourceResult<(Content, StyleChain<'a>)> {
|
||||
// These elements implement `Layout` but still require a flow for
|
||||
// proper layout.
|
||||
if content.can::<dyn Layout>()
|
||||
&& !content.is::<LineElem>()
|
||||
&& !content.is::<RectElem>()
|
||||
@ -275,6 +277,7 @@ fn realize_block<'a>(
|
||||
&& !content.is::<ImageElem>()
|
||||
&& !content.is::<PolygonElem>()
|
||||
&& !content.is::<PathElem>()
|
||||
&& !content.is::<PlaceElem>()
|
||||
&& !applicable(content, styles)
|
||||
{
|
||||
return Ok((content.clone(), styles));
|
||||
|
@ -91,36 +91,13 @@ impl Layout for PlaceElem {
|
||||
vt: &mut Vt,
|
||||
styles: StyleChain,
|
||||
regions: Regions,
|
||||
) -> SourceResult<Fragment> {
|
||||
let mut frame = self.layout_inner(vt, styles, regions)?.into_frame();
|
||||
|
||||
// If expansion is off, zero all sizes so that we don't take up any
|
||||
// space in our parent. Otherwise, respect the expand settings.
|
||||
let target = regions.expand.select(regions.size, Size::zero());
|
||||
frame.resize(target, Align::LEFT_TOP);
|
||||
|
||||
Ok(Fragment::frame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
impl PlaceElem {
|
||||
/// Layout without zeroing the frame size.
|
||||
pub fn layout_inner(
|
||||
&self,
|
||||
vt: &mut Vt,
|
||||
styles: StyleChain,
|
||||
regions: Regions,
|
||||
) -> SourceResult<Fragment> {
|
||||
// The pod is the base area of the region because for absolute
|
||||
// placement we don't really care about the already used area.
|
||||
let base = regions.base();
|
||||
let expand =
|
||||
Axes::new(base.x.is_finite(), base.y.is_finite() && !self.float(styles));
|
||||
|
||||
let pod = Regions::one(base, expand);
|
||||
|
||||
let float = self.float(styles);
|
||||
let alignment = self.alignment(styles);
|
||||
|
||||
if float
|
||||
&& !matches!(
|
||||
alignment,
|
||||
@ -145,7 +122,9 @@ impl PlaceElem {
|
||||
alignment.unwrap_or_else(|| Axes::with_x(Some(Align::Center.into()))),
|
||||
);
|
||||
|
||||
child.layout(vt, styles, pod)
|
||||
let pod = Regions::one(base, Axes::splat(false));
|
||||
let frame = child.layout(vt, styles, pod)?.into_frame();
|
||||
Ok(Fragment::frame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
tests/ref/bugs/place-nested.png
Normal file
BIN
tests/ref/bugs/place-nested.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/ref/layout/place-nested.png
Normal file
BIN
tests/ref/layout/place-nested.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
33
tests/typ/layout/place-nested.typ
Normal file
33
tests/typ/layout/place-nested.typ
Normal file
@ -0,0 +1,33 @@
|
||||
// Test vertical alignment with nested placement.
|
||||
|
||||
---
|
||||
#box(
|
||||
fill: aqua,
|
||||
width: 30pt,
|
||||
height: 30pt,
|
||||
place(bottom,
|
||||
place(line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: red + 3pt))
|
||||
)
|
||||
)
|
||||
|
||||
---
|
||||
#box(
|
||||
fill: aqua,
|
||||
width: 30pt,
|
||||
height: 30pt,
|
||||
{
|
||||
box(fill: yellow, {
|
||||
[Hello]
|
||||
place(horizon, line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: red + 2pt))
|
||||
})
|
||||
place(horizon, line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: green + 3pt))
|
||||
}
|
||||
)
|
||||
|
||||
---
|
||||
#box(fill: aqua)[
|
||||
#place(bottom + right)[Hi]
|
||||
Hello World \
|
||||
How are \
|
||||
you?
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user