mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Outset; fix folding
This commit is contained in:
parent
5f1499d380
commit
84a4961a5d
@ -478,7 +478,7 @@ pub fn rect_paths(
|
||||
];
|
||||
|
||||
for (side, radius) in sides.into_iter().zip(radius.windows(2)) {
|
||||
let stroke_continuity = strokes.get(side) == strokes.get(side.clockwise());
|
||||
let stroke_continuity = strokes.get(side) == strokes.get(side.next_cw());
|
||||
connection = connection.advance(stroke_continuity && side != Side::Left);
|
||||
always_continuous &= stroke_continuity;
|
||||
|
||||
|
@ -128,7 +128,7 @@ impl Side {
|
||||
}
|
||||
|
||||
/// The next side, clockwise.
|
||||
pub fn clockwise(self) -> Self {
|
||||
pub fn next_cw(self) -> Self {
|
||||
match self {
|
||||
Self::Left => Self::Top,
|
||||
Self::Top => Self::Right,
|
||||
@ -138,7 +138,7 @@ impl Side {
|
||||
}
|
||||
|
||||
/// The next side, counter-clockwise.
|
||||
pub fn counter_clockwise(self) -> Self {
|
||||
pub fn next_ccw(self) -> Self {
|
||||
match self {
|
||||
Self::Left => Self::Bottom,
|
||||
Self::Top => Self::Left,
|
||||
|
@ -102,6 +102,7 @@ castable! {
|
||||
}
|
||||
},
|
||||
Value::Length(l) => Sides::splat(Some(l.into())),
|
||||
Value::Ratio(r) => Sides::splat(Some(r.into())),
|
||||
Value::Relative(r) => Sides::splat(Some(r)),
|
||||
}
|
||||
|
||||
@ -117,40 +118,44 @@ impl<const S: ShapeKind> Layout for AngularNode<S> {
|
||||
let inset = styles.get(Self::INSET);
|
||||
|
||||
// Pad the child.
|
||||
let child = child
|
||||
.clone()
|
||||
.padded(inset.map(|side| side.map(|abs| RawLength::from(abs))));
|
||||
let child = child.clone().padded(inset.map(|side| side.map(RawLength::from)));
|
||||
|
||||
let mut pod = Regions::one(regions.first, regions.base, regions.expand);
|
||||
frames = child.layout(ctx, &pod, styles)?;
|
||||
|
||||
// Relayout with full expansion into square region to make sure
|
||||
// the result is really a square or circle.
|
||||
let length = if regions.expand.x || regions.expand.y {
|
||||
let target = regions.expand.select(regions.first, Size::zero());
|
||||
target.x.max(target.y)
|
||||
} else {
|
||||
let size = frames[0].size;
|
||||
let desired = size.x.max(size.y);
|
||||
desired.min(regions.first.x).min(regions.first.y)
|
||||
};
|
||||
if is_quadratic(S) {
|
||||
let length = if regions.expand.x || regions.expand.y {
|
||||
let target = regions.expand.select(regions.first, Size::zero());
|
||||
target.x.max(target.y)
|
||||
} else {
|
||||
let size = frames[0].size;
|
||||
let desired = size.x.max(size.y);
|
||||
desired.min(regions.first.x).min(regions.first.y)
|
||||
};
|
||||
|
||||
pod.first = Size::splat(length);
|
||||
pod.expand = Spec::splat(true);
|
||||
frames = child.layout(ctx, &pod, styles)?;
|
||||
pod.first = Size::splat(length);
|
||||
pod.expand = Spec::splat(true);
|
||||
frames = child.layout(ctx, &pod, styles)?;
|
||||
}
|
||||
} else {
|
||||
// The default size that a shape takes on if it has no child and
|
||||
// enough space.
|
||||
let mut size =
|
||||
Size::new(Length::pt(45.0), Length::pt(30.0)).min(regions.first);
|
||||
|
||||
let length = if regions.expand.x || regions.expand.y {
|
||||
let target = regions.expand.select(regions.first, Size::zero());
|
||||
target.x.max(target.y)
|
||||
if is_quadratic(S) {
|
||||
let length = if regions.expand.x || regions.expand.y {
|
||||
let target = regions.expand.select(regions.first, Size::zero());
|
||||
target.x.max(target.y)
|
||||
} else {
|
||||
size.x.min(size.y)
|
||||
};
|
||||
size = Size::splat(length);
|
||||
} else {
|
||||
size.x.min(size.y)
|
||||
};
|
||||
size = Size::splat(length);
|
||||
size = regions.expand.select(regions.first, size);
|
||||
}
|
||||
|
||||
frames = vec![Arc::new(Frame::new(size))];
|
||||
}
|
||||
@ -162,27 +167,38 @@ impl<const S: ShapeKind> Layout for AngularNode<S> {
|
||||
let stroke = match styles.get(Self::STROKE) {
|
||||
Smart::Auto if fill.is_none() => Sides::splat(Some(Stroke::default())),
|
||||
Smart::Auto => Sides::splat(None),
|
||||
Smart::Custom(strokes) => strokes.map(|s| Some(s.unwrap_or_default())),
|
||||
Smart::Custom(strokes) => strokes.map(|s| s.map(|s| s.unwrap_or_default())),
|
||||
};
|
||||
|
||||
let radius = {
|
||||
let radius = styles.get(Self::RADIUS);
|
||||
|
||||
Sides {
|
||||
left: radius.left.relative_to(frame.size.x / 2.0),
|
||||
top: radius.top.relative_to(frame.size.y / 2.0),
|
||||
right: radius.right.relative_to(frame.size.x / 2.0),
|
||||
bottom: radius.bottom.relative_to(frame.size.y / 2.0),
|
||||
}
|
||||
let outset = styles.get(Self::OUTSET);
|
||||
let outset = Sides {
|
||||
left: outset.left.relative_to(frame.size.x),
|
||||
top: outset.top.relative_to(frame.size.y),
|
||||
right: outset.right.relative_to(frame.size.x),
|
||||
bottom: outset.bottom.relative_to(frame.size.y),
|
||||
};
|
||||
|
||||
let size = Spec::new(
|
||||
frame.size.x + outset.left + outset.right,
|
||||
frame.size.y + outset.top + outset.bottom,
|
||||
);
|
||||
|
||||
let radius = styles.get(Self::RADIUS);
|
||||
let radius = Sides {
|
||||
left: radius.left.relative_to(size.x / 2.0),
|
||||
top: radius.top.relative_to(size.y / 2.0),
|
||||
right: radius.right.relative_to(size.x / 2.0),
|
||||
bottom: radius.bottom.relative_to(size.y / 2.0),
|
||||
};
|
||||
|
||||
|
||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||
let shape = Shape {
|
||||
geometry: Geometry::Rect(frame.size, radius),
|
||||
geometry: Geometry::Rect(size, radius),
|
||||
fill,
|
||||
stroke,
|
||||
};
|
||||
frame.prepend(Point::zero(), Element::Shape(shape));
|
||||
frame.prepend(Point::new(-outset.left, -outset.top), Element::Shape(shape));
|
||||
}
|
||||
|
||||
// Apply link if it exists.
|
||||
@ -208,3 +224,13 @@ const CIRCLE: ShapeKind = 2;
|
||||
|
||||
/// A curve around two focal points.
|
||||
const ELLIPSE: ShapeKind = 3;
|
||||
|
||||
/// Whether a shape kind is curvy.
|
||||
fn is_round(kind: ShapeKind) -> bool {
|
||||
matches!(kind, CIRCLE | ELLIPSE)
|
||||
}
|
||||
|
||||
/// Whether a shape kind has equal side length.
|
||||
fn is_quadratic(kind: ShapeKind) -> bool {
|
||||
matches!(kind, SQUARE | CIRCLE)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use super::{Content, Show, ShowNode};
|
||||
use crate::diag::{At, TypResult};
|
||||
use crate::eval::{Args, Func, Node, Smart, Value};
|
||||
use crate::geom::{Numeric, Relative, Sides, Spec};
|
||||
use crate::geom::{Length, Numeric, Relative, Sides, Spec};
|
||||
use crate::library::layout::PageNode;
|
||||
use crate::library::structure::{EnumNode, ListNode};
|
||||
use crate::library::text::{FontFamily, ParNode, TextNode};
|
||||
@ -459,17 +459,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Fold for Sides<Option<T>>
|
||||
impl<T> Fold for Sides<T>
|
||||
where
|
||||
T: Default,
|
||||
T: Fold,
|
||||
{
|
||||
type Output = Sides<T>;
|
||||
type Output = Sides<T::Output>;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
Sides {
|
||||
left: self.left.fold(outer.left),
|
||||
top: self.top.fold(outer.top),
|
||||
right: self.right.fold(outer.right),
|
||||
bottom: self.bottom.fold(outer.bottom),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for Sides<Option<Relative<Length>>> {
|
||||
type Output = Sides<Relative<Length>>;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
Sides {
|
||||
left: self.left.unwrap_or(outer.left),
|
||||
right: self.right.unwrap_or(outer.right),
|
||||
top: self.top.unwrap_or(outer.top),
|
||||
right: self.right.unwrap_or(outer.right),
|
||||
bottom: self.bottom.unwrap_or(outer.bottom),
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
// Syntax sugar for function definitions.
|
||||
#let fill = conifer
|
||||
#let rect(body) = rect(width: 2cm, fill: fill, padding: 5pt, body)
|
||||
#let rect(body) = rect(width: 2cm, fill: fill, inset: 5pt, body)
|
||||
#rect[Hi!]
|
||||
|
||||
---
|
||||
|
@ -7,6 +7,6 @@
|
||||
#let d = 3
|
||||
#let value = [hi]
|
||||
#let item(a, b) = a + b
|
||||
#let fn = rect.with(fill: conifer, padding: 5pt)
|
||||
#let fn = rect.with(fill: conifer, inset: 5pt)
|
||||
|
||||
Some _includable_ text.
|
||||
|
@ -11,7 +11,7 @@
|
||||
---
|
||||
// Test alignment in automatically sized square and circle.
|
||||
#set text(8pt)
|
||||
#square(padding: 4pt)[
|
||||
#square(inset: 4pt)[
|
||||
Hey there, #align(center + bottom, rotate(180deg, [you!]))
|
||||
]
|
||||
#circle(align(center + horizon, [Hey.]))
|
||||
|
@ -16,13 +16,13 @@ Auto-sized circle. \
|
||||
Center-aligned rect in auto-sized circle.
|
||||
#circle(fill: forest, stroke: conifer,
|
||||
align(center + horizon,
|
||||
rect(fill: conifer, padding: 5pt)[But, soft!]
|
||||
rect(fill: conifer, inset: 5pt)[But, soft!]
|
||||
)
|
||||
)
|
||||
|
||||
Rect in auto-sized circle. \
|
||||
#circle(fill: forest,
|
||||
rect(fill: conifer, stroke: white, padding: 4pt)[
|
||||
rect(fill: conifer, stroke: white, inset: 4pt)[
|
||||
#set text(8pt)
|
||||
But, soft! what light through yonder window breaks?
|
||||
]
|
||||
|
@ -8,7 +8,7 @@
|
||||
#set page(width: 150pt)
|
||||
|
||||
// Fit to text.
|
||||
#rect(fill: conifer, padding: 3pt)[Textbox]
|
||||
#rect(fill: conifer, inset: 3pt)[Textbox]
|
||||
|
||||
// Empty with fixed width and height.
|
||||
#block(rect(
|
||||
@ -18,7 +18,7 @@
|
||||
))
|
||||
|
||||
// Fixed width, text height.
|
||||
#rect(width: 2cm, fill: rgb("9650d6"), padding: 5pt)[Fixed and padded]
|
||||
#rect(width: 2cm, fill: rgb("9650d6"), inset: 5pt)[Fixed and padded]
|
||||
|
||||
// Page width, fixed height.
|
||||
#rect(height: 1cm, width: 100%, fill: rgb("734ced"))[Topleft]
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
---
|
||||
// Test auto-sized square.
|
||||
#square(fill: eastern, padding: 5pt)[
|
||||
#square(fill: eastern, inset: 5pt)[
|
||||
#set text(fill: white, weight: "bold")
|
||||
Typst
|
||||
]
|
||||
|
@ -16,7 +16,7 @@
|
||||
// Test the `columns` function.
|
||||
#set page(width: auto)
|
||||
|
||||
#rect(width: 180pt, height: 100pt, padding: 8pt, columns(2, [
|
||||
#rect(width: 180pt, height: 100pt, inset: 8pt, columns(2, [
|
||||
A special plight has befallen our document.
|
||||
Columns in text boxes reigned down unto the soil
|
||||
to waste a year's crop of rich layouts.
|
||||
@ -40,7 +40,7 @@ a page for a test but it does get the job done.
|
||||
// Test the expansion behavior.
|
||||
#set page(height: 2.5cm, width: 7.05cm)
|
||||
|
||||
#rect(padding: 6pt, columns(2, [
|
||||
#rect(inset: 6pt, columns(2, [
|
||||
ABC \
|
||||
BCD
|
||||
#colbreak()
|
||||
@ -73,7 +73,7 @@ D
|
||||
// Test an empty second column.
|
||||
#set page(width: 7.05cm, columns: 2)
|
||||
|
||||
#rect(width: 100%, padding: 3pt)[So there isn't anything in the second column?]
|
||||
#rect(width: 100%, inset: 3pt)[So there isn't anything in the second column?]
|
||||
|
||||
---
|
||||
// Test columns when one of them is empty.
|
||||
|
@ -16,17 +16,17 @@
|
||||
// but the B should be center-aligned.
|
||||
#set par(align: center)
|
||||
#par(align: right)[
|
||||
A #rect(width: 2cm, fill: conifer, padding: 4pt)[B]
|
||||
A #rect(width: 2cm, fill: conifer, inset: 4pt)[B]
|
||||
]
|
||||
|
||||
---
|
||||
// The inner rectangle should also be yellow here.
|
||||
// (and therefore invisible)
|
||||
[#set rect(fill: yellow);#text(1em, rect(padding: 5pt, rect()))]
|
||||
[#set rect(fill: yellow);#text(1em, rect(inset: 5pt, rect()))]
|
||||
|
||||
---
|
||||
// The inner rectangle should not be yellow here.
|
||||
A #rect(fill: yellow, padding: 5pt, rect()) B
|
||||
A #rect(fill: yellow, inset: 5pt, rect()) B
|
||||
|
||||
---
|
||||
// The inner list should not be indented extra.
|
||||
|
Loading…
x
Reference in New Issue
Block a user