mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refactor folding (#3294)
This commit is contained in:
parent
a3e1c70e9e
commit
f14288cacf
@ -106,7 +106,9 @@ impl Elem {
|
|||||||
/// This includes:
|
/// This includes:
|
||||||
/// - Fields that are not synthesized.
|
/// - Fields that are not synthesized.
|
||||||
fn construct_fields(&self) -> impl Iterator<Item = &Field> + Clone {
|
fn construct_fields(&self) -> impl Iterator<Item = &Field> + Clone {
|
||||||
self.real_fields().filter(|field| !field.synthesized)
|
self.real_fields().filter(|field| {
|
||||||
|
!field.synthesized && (!field.internal || field.parse.is_some())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,11 +265,6 @@ fn parse_field(field: &syn::Field) -> Result<Field> {
|
|||||||
field.output = parse_quote! { <#output as #foundations::Resolve>::Output };
|
field.output = parse_quote! { <#output as #foundations::Resolve>::Output };
|
||||||
}
|
}
|
||||||
|
|
||||||
if field.fold {
|
|
||||||
let output = &field.output;
|
|
||||||
field.output = parse_quote! { <#output as #foundations::Fold>::Output };
|
|
||||||
}
|
|
||||||
|
|
||||||
validate_attrs(&attrs)?;
|
validate_attrs(&attrs)?;
|
||||||
|
|
||||||
Ok(field)
|
Ok(field)
|
||||||
@ -555,32 +552,42 @@ fn create_style_chain_access(
|
|||||||
let elem = &element.ident;
|
let elem = &element.ident;
|
||||||
|
|
||||||
let Field { ty, default, enum_ident, .. } = field;
|
let Field { ty, default, enum_ident, .. } = field;
|
||||||
let getter = match (field.fold, field.resolve, field.borrowed) {
|
|
||||||
(false, false, false) => quote! { get },
|
let getter = match (field.fold, field.borrowed) {
|
||||||
(false, false, true) => quote! { get_borrowed },
|
(false, false) => quote! { get },
|
||||||
(false, true, _) => quote! { get_resolve },
|
(false, true) => quote! { get_ref },
|
||||||
(true, false, _) => quote! { get_fold },
|
(true, _) => quote! { get_folded },
|
||||||
(true, true, _) => quote! { get_resolve_fold },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let default = default
|
let mut default = match default {
|
||||||
.clone()
|
Some(default) => quote! { #default },
|
||||||
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
None => quote! { ::std::default::Default::default() },
|
||||||
let (init, default) = field.fold.then(|| (None, quote! { || #default })).unwrap_or_else(|| (
|
};
|
||||||
Some(quote! {
|
|
||||||
static DEFAULT: ::once_cell::sync::Lazy<#ty> = ::once_cell::sync::Lazy::new(|| #default);
|
|
||||||
}),
|
|
||||||
quote! { &DEFAULT },
|
|
||||||
));
|
|
||||||
|
|
||||||
quote! {
|
let mut init = None;
|
||||||
#init
|
if field.borrowed {
|
||||||
|
init = Some(quote! {
|
||||||
|
static DEFAULT: ::once_cell::sync::Lazy<#ty> = ::once_cell::sync::Lazy::new(|| #default);
|
||||||
|
});
|
||||||
|
default = quote! { &DEFAULT };
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut value = quote! {
|
||||||
styles.#getter::<#ty>(
|
styles.#getter::<#ty>(
|
||||||
<Self as #foundations::NativeElement>::elem(),
|
<Self as #foundations::NativeElement>::elem(),
|
||||||
<#elem as #foundations::Fields>::Enum::#enum_ident as u8,
|
<#elem as #foundations::Fields>::Enum::#enum_ident as u8,
|
||||||
#inherent,
|
#inherent,
|
||||||
#default,
|
|| #default,
|
||||||
)
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if field.resolve {
|
||||||
|
value = quote! { #foundations::Resolve::resolve(#value, styles) };
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#init
|
||||||
|
#value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -995,21 +1002,18 @@ fn create_param_info(field: &Field) -> TokenStream {
|
|||||||
variadic,
|
variadic,
|
||||||
required,
|
required,
|
||||||
default,
|
default,
|
||||||
fold,
|
|
||||||
ty,
|
ty,
|
||||||
output,
|
|
||||||
..
|
..
|
||||||
} = field;
|
} = field;
|
||||||
let named = !positional;
|
let named = !positional;
|
||||||
let settable = field.settable();
|
let settable = field.settable();
|
||||||
let default_ty = if *fold { &output } else { &ty };
|
|
||||||
let default = quote_option(&settable.then(|| {
|
let default = quote_option(&settable.then(|| {
|
||||||
let default = default
|
let default = default
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
||||||
quote! {
|
quote! {
|
||||||
|| {
|
|| {
|
||||||
let typed: #default_ty = #default;
|
let typed: #ty = #default;
|
||||||
#foundations::IntoValue::into_value(typed)
|
#foundations::IntoValue::into_value(typed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,14 +239,14 @@ impl<T: Resolve> Resolve for Smart<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Fold for Smart<T>
|
impl<T: Fold> Fold for Smart<T> {
|
||||||
where
|
fn fold(self, outer: Self) -> Self {
|
||||||
T: Fold,
|
use Smart::Custom;
|
||||||
T::Output: Default,
|
match (self, outer) {
|
||||||
{
|
(Custom(inner), Custom(outer)) => Custom(inner.fold(outer)),
|
||||||
type Output = Smart<T::Output>;
|
// An explicit `auto` should be respected, thus we don't do
|
||||||
|
// `inner.or(outer)`.
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
(inner, _) => inner,
|
||||||
self.map(|inner| inner.fold(outer.unwrap_or_default()))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,6 @@ impl Dict {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut msg = String::from(match unexpected.len() {
|
let mut msg = String::from(match unexpected.len() {
|
||||||
0 => unreachable!(),
|
|
||||||
1 => "unexpected key ",
|
1 => "unexpected key ",
|
||||||
_ => "unexpected keys ",
|
_ => "unexpected keys ",
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,6 @@ use std::{iter, mem, ptr};
|
|||||||
|
|
||||||
use comemo::Prehashed;
|
use comemo::Prehashed;
|
||||||
use ecow::{eco_vec, EcoString, EcoVec};
|
use ecow::{eco_vec, EcoString, EcoVec};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::diag::{SourceResult, Trace, Tracepoint};
|
use crate::diag::{SourceResult, Trace, Tracepoint};
|
||||||
@ -457,54 +456,47 @@ impl<'a> StyleChain<'a> {
|
|||||||
Chainable::chain(local, self)
|
Chainable::chain(local, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cast the first value for the given property in the chain.
|
||||||
|
pub fn get<T: Clone + 'static>(
|
||||||
|
self,
|
||||||
|
func: Element,
|
||||||
|
id: u8,
|
||||||
|
inherent: Option<&T>,
|
||||||
|
default: impl Fn() -> T,
|
||||||
|
) -> T {
|
||||||
|
self.properties::<T>(func, id, inherent)
|
||||||
|
.next()
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(default)
|
||||||
|
}
|
||||||
|
|
||||||
/// Cast the first value for the given property in the chain,
|
/// Cast the first value for the given property in the chain,
|
||||||
/// returning a borrowed value if possible.
|
/// returning a borrowed value.
|
||||||
pub fn get_borrowed<T: Clone>(
|
pub fn get_ref<T: 'static>(
|
||||||
self,
|
self,
|
||||||
func: Element,
|
func: Element,
|
||||||
id: u8,
|
id: u8,
|
||||||
inherent: Option<&'a T>,
|
inherent: Option<&'a T>,
|
||||||
default: &'static Lazy<T>,
|
default: impl Fn() -> &'a T,
|
||||||
) -> &'a T {
|
) -> &'a T {
|
||||||
self.properties::<T>(func, id, inherent)
|
self.properties::<T>(func, id, inherent)
|
||||||
.next()
|
.next()
|
||||||
.unwrap_or_else(|| default)
|
.unwrap_or_else(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the first value for the given property in the chain.
|
/// Cast the first value for the given property in the chain, taking
|
||||||
pub fn get<T: Clone>(
|
/// `Fold` implementations into account.
|
||||||
|
pub fn get_folded<T: Fold + Clone + 'static>(
|
||||||
self,
|
self,
|
||||||
func: Element,
|
func: Element,
|
||||||
id: u8,
|
id: u8,
|
||||||
inherent: Option<&T>,
|
inherent: Option<&T>,
|
||||||
default: &'static Lazy<T>,
|
default: impl Fn() -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
self.get_borrowed(func, id, inherent, default).clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast the first value for the given property in the chain.
|
|
||||||
pub fn get_resolve<T: Clone + Resolve>(
|
|
||||||
self,
|
|
||||||
func: Element,
|
|
||||||
id: u8,
|
|
||||||
inherent: Option<&T>,
|
|
||||||
default: &'static Lazy<T>,
|
|
||||||
) -> T::Output {
|
|
||||||
self.get(func, id, inherent, default).resolve(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast the first value for the given property in the chain.
|
|
||||||
pub fn get_fold<T: Clone + Fold + 'static>(
|
|
||||||
self,
|
|
||||||
func: Element,
|
|
||||||
id: u8,
|
|
||||||
inherent: Option<&T>,
|
|
||||||
default: impl Fn() -> T::Output,
|
|
||||||
) -> T::Output {
|
|
||||||
fn next<T: Fold>(
|
fn next<T: Fold>(
|
||||||
mut values: impl Iterator<Item = T>,
|
mut values: impl Iterator<Item = T>,
|
||||||
default: &impl Fn() -> T::Output,
|
default: &impl Fn() -> T,
|
||||||
) -> T::Output {
|
) -> T {
|
||||||
values
|
values
|
||||||
.next()
|
.next()
|
||||||
.map(|value| value.fold(next(values, default)))
|
.map(|value| value.fold(next(values, default)))
|
||||||
@ -513,36 +505,6 @@ impl<'a> StyleChain<'a> {
|
|||||||
next(self.properties::<T>(func, id, inherent).cloned(), &default)
|
next(self.properties::<T>(func, id, inherent).cloned(), &default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast the first value for the given property in the chain.
|
|
||||||
pub fn get_resolve_fold<T>(
|
|
||||||
self,
|
|
||||||
func: Element,
|
|
||||||
id: u8,
|
|
||||||
inherent: Option<&T>,
|
|
||||||
default: impl Fn() -> <T::Output as Fold>::Output,
|
|
||||||
) -> <T::Output as Fold>::Output
|
|
||||||
where
|
|
||||||
T: Resolve + Clone + 'static,
|
|
||||||
T::Output: Fold,
|
|
||||||
{
|
|
||||||
fn next<T>(
|
|
||||||
mut values: impl Iterator<Item = T>,
|
|
||||||
styles: StyleChain,
|
|
||||||
default: &impl Fn() -> <T::Output as Fold>::Output,
|
|
||||||
) -> <T::Output as Fold>::Output
|
|
||||||
where
|
|
||||||
T: Resolve + 'static,
|
|
||||||
T::Output: Fold,
|
|
||||||
{
|
|
||||||
values
|
|
||||||
.next()
|
|
||||||
.map(|value| value.resolve(styles).fold(next(values, styles, default)))
|
|
||||||
.unwrap_or_else(default)
|
|
||||||
}
|
|
||||||
|
|
||||||
next(self.properties::<T>(func, id, inherent).cloned(), self, &default)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterate over all style recipes in the chain.
|
/// Iterate over all style recipes in the chain.
|
||||||
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
|
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
|
||||||
self.entries().filter_map(Style::recipe)
|
self.entries().filter_map(Style::recipe)
|
||||||
@ -925,38 +887,36 @@ impl<T: Resolve> Resolve for Option<T> {
|
|||||||
/// #rect()
|
/// #rect()
|
||||||
/// ```
|
/// ```
|
||||||
pub trait Fold {
|
pub trait Fold {
|
||||||
/// The type of the folded output.
|
|
||||||
type Output;
|
|
||||||
|
|
||||||
/// Fold this inner value with an outer folded value.
|
/// Fold this inner value with an outer folded value.
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output;
|
fn fold(self, outer: Self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Fold for Option<T>
|
impl Fold for bool {
|
||||||
where
|
fn fold(self, _: Self) -> Self {
|
||||||
T: Fold,
|
self
|
||||||
T::Output: Default,
|
}
|
||||||
{
|
}
|
||||||
type Output = Option<T::Output>;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
impl<T: Fold> Fold for Option<T> {
|
||||||
self.map(|inner| inner.fold(outer.unwrap_or_default()))
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
match (self, outer) {
|
||||||
|
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
|
||||||
|
// An explicit `None` should be respected, thus we don't do
|
||||||
|
// `inner.or(outer)`.
|
||||||
|
(inner, _) => inner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Fold for Vec<T> {
|
impl<T> Fold for Vec<T> {
|
||||||
type Output = Vec<T>;
|
fn fold(mut self, outer: Self) -> Self {
|
||||||
|
|
||||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.extend(outer);
|
self.extend(outer);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Fold for SmallVec<[T; N]> {
|
impl<T, const N: usize> Fold for SmallVec<[T; N]> {
|
||||||
type Output = SmallVec<[T; N]>;
|
fn fold(mut self, outer: Self) -> Self {
|
||||||
|
|
||||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.extend(outer);
|
self.extend(outer);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -215,9 +215,7 @@ impl Repr for Alignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Alignment {
|
impl Fold for Alignment {
|
||||||
type Output = Self;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
match (self, outer) {
|
match (self, outer) {
|
||||||
(Self::H(x), Self::V(y) | Self::Both(_, y)) => Self::Both(x, y),
|
(Self::H(x), Self::V(y) | Self::Both(_, y)) => Self::Both(x, y),
|
||||||
(Self::V(y), Self::H(x) | Self::Both(x, _)) => Self::Both(x, y),
|
(Self::V(y), Self::H(x) | Self::Both(x, _)) => Self::Both(x, y),
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
|
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
|
||||||
|
|
||||||
use crate::diag::bail;
|
use crate::diag::bail;
|
||||||
use crate::foundations::{array, cast, Array, Fold, Resolve, Smart, StyleChain};
|
use crate::foundations::{array, cast, Array, Resolve, Smart, StyleChain};
|
||||||
use crate::layout::{Abs, Dir, Length, Ratio, Rel};
|
use crate::layout::{Abs, Dir, Length, Ratio, Rel};
|
||||||
use crate::util::Get;
|
use crate::util::Get;
|
||||||
|
|
||||||
@ -84,14 +84,6 @@ impl<T> Axes<T> {
|
|||||||
{
|
{
|
||||||
f(&self.x) && f(&self.y)
|
f(&self.x) && f(&self.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter the individual fields with a mask.
|
|
||||||
pub fn filter(self, mask: Axes<bool>) -> Axes<Option<T>> {
|
|
||||||
Axes {
|
|
||||||
x: if mask.x { Some(self.x) } else { None },
|
|
||||||
y: if mask.y { Some(self.y) } else { None },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default> Axes<T> {
|
impl<T: Default> Axes<T> {
|
||||||
@ -198,16 +190,6 @@ cast! {
|
|||||||
"vertical" => Self::Y,
|
"vertical" => Self::Y,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Axes<Option<T>> {
|
|
||||||
/// Unwrap the individual fields.
|
|
||||||
pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
|
|
||||||
Axes {
|
|
||||||
x: self.x.unwrap_or(other.x),
|
|
||||||
y: self.y.unwrap_or(other.y),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Axes<Smart<T>> {
|
impl<T> Axes<Smart<T>> {
|
||||||
/// Unwrap the individual fields.
|
/// Unwrap the individual fields.
|
||||||
pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
|
pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
|
||||||
@ -325,14 +307,3 @@ impl<T: Resolve> Resolve for Axes<T> {
|
|||||||
self.map(|v| v.resolve(styles))
|
self.map(|v| v.resolve(styles))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Fold> Fold for Axes<Option<T>> {
|
|
||||||
type Output = Axes<T::Output>;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.zip_map(outer, |inner, outer| match inner {
|
|
||||||
Some(value) => value.fold(outer),
|
|
||||||
None => outer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -133,7 +133,7 @@ impl Packed<BoxElem> {
|
|||||||
|
|
||||||
// Apply inset.
|
// Apply inset.
|
||||||
let mut body = self.body(styles).unwrap_or_default();
|
let mut body = self.body(styles).unwrap_or_default();
|
||||||
let inset = self.inset(styles);
|
let inset = self.inset(styles).unwrap_or_default();
|
||||||
if inset.iter().any(|v| !v.is_zero()) {
|
if inset.iter().any(|v| !v.is_zero()) {
|
||||||
body = body.padded(inset.map(|side| side.map(Length::from)));
|
body = body.padded(inset.map(|side| side.map(Length::from)));
|
||||||
}
|
}
|
||||||
@ -154,20 +154,24 @@ impl Packed<BoxElem> {
|
|||||||
|
|
||||||
// Prepare fill and stroke.
|
// Prepare fill and stroke.
|
||||||
let fill = self.fill(styles);
|
let fill = self.fill(styles);
|
||||||
let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default));
|
let stroke = self
|
||||||
|
.stroke(styles)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|s| s.map(Stroke::unwrap_or_default));
|
||||||
|
|
||||||
// Clip the contents
|
// Clip the contents
|
||||||
if self.clip(styles) {
|
if self.clip(styles) {
|
||||||
let outset = self.outset(styles).relative_to(frame.size());
|
let outset =
|
||||||
|
self.outset(styles).unwrap_or_default().relative_to(frame.size());
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles).unwrap_or_default();
|
||||||
frame.clip(clip_rect(size, radius, &stroke));
|
frame.clip(clip_rect(size, radius, &stroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add fill and/or stroke.
|
// Add fill and/or stroke.
|
||||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||||
let outset = self.outset(styles);
|
let outset = self.outset(styles).unwrap_or_default();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles).unwrap_or_default();
|
||||||
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
|
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +354,7 @@ impl LayoutMultiple for Packed<BlockElem> {
|
|||||||
) -> SourceResult<Fragment> {
|
) -> SourceResult<Fragment> {
|
||||||
// Apply inset.
|
// Apply inset.
|
||||||
let mut body = self.body(styles).unwrap_or_default();
|
let mut body = self.body(styles).unwrap_or_default();
|
||||||
let inset = self.inset(styles);
|
let inset = self.inset(styles).unwrap_or_default();
|
||||||
if inset.iter().any(|v| !v.is_zero()) {
|
if inset.iter().any(|v| !v.is_zero()) {
|
||||||
body = body.clone().padded(inset.map(|side| side.map(Length::from)));
|
body = body.clone().padded(inset.map(|side| side.map(Length::from)));
|
||||||
}
|
}
|
||||||
@ -418,14 +422,18 @@ impl LayoutMultiple for Packed<BlockElem> {
|
|||||||
|
|
||||||
// Prepare fill and stroke.
|
// Prepare fill and stroke.
|
||||||
let fill = self.fill(styles);
|
let fill = self.fill(styles);
|
||||||
let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default));
|
let stroke = self
|
||||||
|
.stroke(styles)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.map(|s| s.map(Stroke::unwrap_or_default));
|
||||||
|
|
||||||
// Clip the contents
|
// Clip the contents
|
||||||
if self.clip(styles) {
|
if self.clip(styles) {
|
||||||
for frame in frames.iter_mut() {
|
for frame in frames.iter_mut() {
|
||||||
let outset = self.outset(styles).relative_to(frame.size());
|
let outset =
|
||||||
|
self.outset(styles).unwrap_or_default().relative_to(frame.size());
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles).unwrap_or_default();
|
||||||
frame.clip(clip_rect(size, radius, &stroke));
|
frame.clip(clip_rect(size, radius, &stroke));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,8 +445,8 @@ impl LayoutMultiple for Packed<BlockElem> {
|
|||||||
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
|
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
let outset = self.outset(styles);
|
let outset = self.outset(styles).unwrap_or_default();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles).unwrap_or_default();
|
||||||
for frame in frames.iter_mut().skip(skip as usize) {
|
for frame in frames.iter_mut().skip(skip as usize) {
|
||||||
frame.fill_and_stroke(
|
frame.fill_and_stroke(
|
||||||
fill.clone(),
|
fill.clone(),
|
||||||
|
@ -80,6 +80,16 @@ impl<T> Corners<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Corners<Option<T>> {
|
||||||
|
/// Unwrap-or-default the individual corners.
|
||||||
|
pub fn unwrap_or_default(self) -> Corners<T>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.map(Option::unwrap_or_default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Get<Corner> for Corners<T> {
|
impl<T> Get<Corner> for Corners<T> {
|
||||||
type Component = T;
|
type Component = T;
|
||||||
|
|
||||||
@ -133,20 +143,21 @@ impl<T: Reflect> Reflect for Corners<Option<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntoValue for Corners<T>
|
impl<T> IntoValue for Corners<Option<T>>
|
||||||
where
|
where
|
||||||
T: PartialEq + IntoValue,
|
T: PartialEq + IntoValue,
|
||||||
{
|
{
|
||||||
fn into_value(self) -> Value {
|
fn into_value(self) -> Value {
|
||||||
if self.is_uniform() {
|
if self.is_uniform() {
|
||||||
return self.top_left.into_value();
|
if let Some(top_left) = self.top_left {
|
||||||
|
return top_left.into_value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dict = Dict::new();
|
let mut dict = Dict::new();
|
||||||
let mut handle = |key: &str, component: T| {
|
let mut handle = |key: &str, component: Option<T>| {
|
||||||
let value = component.into_value();
|
if let Some(c) = component {
|
||||||
if value != Value::None {
|
dict.insert(key.into(), c.into_value());
|
||||||
dict.insert(key.into(), value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -228,12 +239,13 @@ impl<T: Resolve> Resolve for Corners<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Fold> Fold for Corners<Option<T>> {
|
impl<T: Fold> Fold for Corners<Option<T>> {
|
||||||
type Output = Corners<T::Output>;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
self.zip(outer).map(|(inner, outer)| match (inner, outer) {
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
|
||||||
self.zip(outer).map(|(inner, outer)| match inner {
|
// Usually, folding an inner `None` with an `outer` preferres the
|
||||||
Some(value) => value.fold(outer),
|
// explicit `None`. However, here `None` means unspecified and thus
|
||||||
None => outer,
|
// we want `outer`.
|
||||||
|
(inner, outer) => inner.or(outer),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ pub trait ResolvableCell {
|
|||||||
y: usize,
|
y: usize,
|
||||||
fill: &Option<Paint>,
|
fill: &Option<Paint>,
|
||||||
align: Smart<Alignment>,
|
align: Smart<Alignment>,
|
||||||
inset: Sides<Rel<Length>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell;
|
) -> Cell;
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ impl CellGrid {
|
|||||||
cells: &[T],
|
cells: &[T],
|
||||||
fill: &Celled<Option<Paint>>,
|
fill: &Celled<Option<Paint>>,
|
||||||
align: &Celled<Smart<Alignment>>,
|
align: &Celled<Smart<Alignment>>,
|
||||||
inset: Sides<Rel<Length>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
engine: &mut Engine,
|
engine: &mut Engine,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -13,8 +13,8 @@ use crate::foundations::{
|
|||||||
cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value,
|
cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value,
|
||||||
};
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Abs, AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel,
|
AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel, Sides,
|
||||||
Sides, Sizing,
|
Sizing,
|
||||||
};
|
};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::NonZeroExt;
|
use crate::util::NonZeroExt;
|
||||||
@ -264,7 +264,6 @@ pub struct GridElem {
|
|||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(0.0).into()))]
|
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// The contents of the grid cells.
|
/// The contents of the grid cells.
|
||||||
@ -462,7 +461,7 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
y: usize,
|
y: usize,
|
||||||
fill: &Option<Paint>,
|
fill: &Option<Paint>,
|
||||||
align: Smart<Alignment>,
|
align: Smart<Alignment>,
|
||||||
inset: Sides<Rel<Length>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell {
|
) -> Cell {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
@ -481,9 +480,8 @@ impl ResolvableCell for Packed<GridCell> {
|
|||||||
Smart::Auto => cell.align(styles),
|
Smart::Auto => cell.align(styles),
|
||||||
});
|
});
|
||||||
cell.push_inset(Smart::Custom(
|
cell.push_inset(Smart::Custom(
|
||||||
cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some),
|
cell.inset(styles).map_or(inset, |inner| inner.fold(inset)),
|
||||||
));
|
));
|
||||||
|
|
||||||
Cell { body: self.pack(), fill, colspan }
|
Cell { body: self.pack(), fill, colspan }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use std::ops::{Add, Div, Mul, Neg};
|
|||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
|
|
||||||
use crate::diag::{At, Hint, SourceResult};
|
use crate::diag::{At, Hint, SourceResult};
|
||||||
use crate::foundations::{func, scope, ty, Repr, Resolve, StyleChain, Styles};
|
use crate::foundations::{func, scope, ty, Fold, Repr, Resolve, StyleChain, Styles};
|
||||||
use crate::layout::{Abs, Em};
|
use crate::layout::{Abs, Em};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::util::Numeric;
|
use crate::util::Numeric;
|
||||||
@ -274,3 +274,9 @@ impl Resolve for Length {
|
|||||||
self.abs + self.em.resolve(styles)
|
self.abs + self.em.resolve(styles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Fold for Length {
|
||||||
|
fn fold(self, _: Self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -549,18 +549,11 @@ impl Margin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Margin {
|
impl Fold for Margin {
|
||||||
type Output = Margin;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Margin {
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
sides: self.sides.fold(outer.sides),
|
||||||
let sides =
|
two_sided: self.two_sided.fold(outer.two_sided),
|
||||||
self.sides
|
}
|
||||||
.zip(outer.sides)
|
|
||||||
.map(|(inner, outer)| match (inner, outer) {
|
|
||||||
(Some(value), Some(outer)) => Some(value.fold(outer)),
|
|
||||||
_ => inner.or(outer),
|
|
||||||
});
|
|
||||||
let two_sided = self.two_sided.or(outer.two_sided);
|
|
||||||
Margin { sides, two_sided }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,19 +259,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Rel<Abs> {
|
impl<T> Fold for Rel<T>
|
||||||
type Output = Self;
|
where
|
||||||
|
T: Numeric + Fold,
|
||||||
fn fold(self, _: Self::Output) -> Self::Output {
|
{
|
||||||
self
|
fn fold(self, outer: Self) -> Self {
|
||||||
}
|
Self { rel: self.rel, abs: self.abs.fold(outer.abs) }
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for Rel<Length> {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn fold(self, _: Self::Output) -> Self::Output {
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +94,16 @@ impl<T: Add> Sides<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Sides<Option<T>> {
|
||||||
|
/// Unwrap-or-default the individual sides.
|
||||||
|
pub fn unwrap_or_default(self) -> Sides<T>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.map(Option::unwrap_or_default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Sides<Rel<Abs>> {
|
impl Sides<Rel<Abs>> {
|
||||||
/// Evaluate the sides relative to the given `size`.
|
/// Evaluate the sides relative to the given `size`.
|
||||||
pub fn relative_to(self, size: Size) -> Sides<Abs> {
|
pub fn relative_to(self, size: Size) -> Sides<Abs> {
|
||||||
@ -159,20 +169,21 @@ impl<T: Reflect> Reflect for Sides<Option<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntoValue for Sides<T>
|
impl<T> IntoValue for Sides<Option<T>>
|
||||||
where
|
where
|
||||||
T: PartialEq + IntoValue,
|
T: PartialEq + IntoValue,
|
||||||
{
|
{
|
||||||
fn into_value(self) -> Value {
|
fn into_value(self) -> Value {
|
||||||
if self.is_uniform() {
|
if self.is_uniform() {
|
||||||
return self.left.into_value();
|
if let Some(left) = self.left {
|
||||||
|
return left.into_value();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dict = Dict::new();
|
let mut dict = Dict::new();
|
||||||
let mut handle = |key: &str, component: T| {
|
let mut handle = |key: &str, component: Option<T>| {
|
||||||
let value = component.into_value();
|
if let Some(c) = component {
|
||||||
if value != Value::None {
|
dict.insert(key.into(), c.into_value());
|
||||||
dict.insert(key.into(), value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -233,12 +244,13 @@ impl<T: Resolve> Resolve for Sides<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Fold> Fold for Sides<Option<T>> {
|
impl<T: Fold> Fold for Sides<Option<T>> {
|
||||||
type Output = Sides<T::Output>;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
self.zip(outer).map(|(inner, outer)| match (inner, outer) {
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
|
||||||
self.zip(outer).map(|(inner, outer)| match inner {
|
// Usually, folding an inner `None` with an `outer` preferres the
|
||||||
Some(value) => value.fold(outer),
|
// explicit `None`. However, here `None` means unspecified and thus
|
||||||
None => outer,
|
// we want `outer`.
|
||||||
|
(inner, outer) => inner.or(outer),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ pub struct CancelElem {
|
|||||||
#[fold]
|
#[fold]
|
||||||
#[default(Stroke {
|
#[default(Stroke {
|
||||||
// Default stroke has 0.5pt for better visuals.
|
// Default stroke has 0.5pt for better visuals.
|
||||||
thickness: Smart::Custom(Abs::pt(0.5)),
|
thickness: Smart::Custom(Abs::pt(0.5).into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})]
|
})]
|
||||||
pub stroke: Stroke,
|
pub stroke: Stroke,
|
||||||
|
@ -425,11 +425,10 @@ fn layout_mat_body(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (hline, vline, stroke) = match augment {
|
let (hline, vline, stroke) = match augment {
|
||||||
Some(v) => {
|
Some(augment) => {
|
||||||
// need to get stroke here for ownership
|
// We need to get stroke here for ownership.
|
||||||
let stroke = v.stroke_or(default_stroke);
|
let stroke = augment.stroke.unwrap_or_default().unwrap_or(default_stroke);
|
||||||
|
(augment.hline, augment.vline, stroke)
|
||||||
(v.hline, v.vline, stroke)
|
|
||||||
}
|
}
|
||||||
_ => (AugmentOffsets::default(), AugmentOffsets::default(), default_stroke),
|
_ => (AugmentOffsets::default(), AugmentOffsets::default(), default_stroke),
|
||||||
};
|
};
|
||||||
@ -593,11 +592,19 @@ pub struct Augment<T: Numeric = Length> {
|
|||||||
pub stroke: Smart<Stroke<T>>,
|
pub stroke: Smart<Stroke<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Augment<Abs> {
|
impl<T: Numeric + Fold> Fold for Augment<T> {
|
||||||
fn stroke_or(&self, fallback: FixedStroke) -> FixedStroke {
|
fn fold(self, outer: Self) -> Self {
|
||||||
match &self.stroke {
|
Self {
|
||||||
Smart::Custom(v) => v.clone().unwrap_or(fallback),
|
stroke: match (self.stroke, outer.stroke) {
|
||||||
Smart::Auto => fallback,
|
(Smart::Custom(inner), Smart::Custom(outer)) => {
|
||||||
|
Smart::Custom(inner.fold(outer))
|
||||||
|
}
|
||||||
|
// Usually, folding an inner `auto` with an `outer` preferres
|
||||||
|
// the explicit `auto`. However, here `auto` means unspecified
|
||||||
|
// and thus we want `outer`.
|
||||||
|
(inner, outer) => inner.or(outer),
|
||||||
|
},
|
||||||
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,21 +621,6 @@ impl Resolve for Augment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Augment<Abs> {
|
|
||||||
type Output = Augment<Abs>;
|
|
||||||
|
|
||||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
|
||||||
// Special case for handling `auto` strokes in subsequent `Augment`.
|
|
||||||
if self.stroke.is_auto() && outer.stroke.is_custom() {
|
|
||||||
self.stroke = outer.stroke;
|
|
||||||
} else {
|
|
||||||
self.stroke = self.stroke.fold(outer.stroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cast! {
|
cast! {
|
||||||
Augment,
|
Augment,
|
||||||
self => {
|
self => {
|
||||||
@ -637,13 +629,11 @@ cast! {
|
|||||||
return self.vline.0[0].into_value();
|
return self.vline.0[0].into_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
let d = dict! {
|
dict! {
|
||||||
"hline" => self.hline.into_value(),
|
"hline" => self.hline,
|
||||||
"vline" => self.vline.into_value(),
|
"vline" => self.vline,
|
||||||
"stroke" => self.stroke.into_value()
|
"stroke" => self.stroke,
|
||||||
};
|
}.into_value()
|
||||||
|
|
||||||
d.into_value()
|
|
||||||
},
|
},
|
||||||
v: isize => Augment {
|
v: isize => Augment {
|
||||||
hline: AugmentOffsets::default(),
|
hline: AugmentOffsets::default(),
|
||||||
@ -651,15 +641,15 @@ cast! {
|
|||||||
stroke: Smart::Auto,
|
stroke: Smart::Auto,
|
||||||
},
|
},
|
||||||
mut dict: Dict => {
|
mut dict: Dict => {
|
||||||
// need the transpose for the defaults to work
|
let mut take = |key| dict.take(key).ok().map(AugmentOffsets::from_value).transpose();
|
||||||
let hline = dict.take("hline").ok().map(AugmentOffsets::from_value)
|
let hline = take("hline")?.unwrap_or_default();
|
||||||
.transpose().unwrap_or_default().unwrap_or_default();
|
let vline = take("vline")?.unwrap_or_default();
|
||||||
let vline = dict.take("vline").ok().map(AugmentOffsets::from_value)
|
let stroke = dict.take("stroke")
|
||||||
.transpose().unwrap_or_default().unwrap_or_default();
|
.ok()
|
||||||
|
.map(Stroke::from_value)
|
||||||
let stroke = dict.take("stroke").ok().map(Stroke::from_value)
|
.transpose()?
|
||||||
.transpose()?.map(Smart::Custom).unwrap_or(Smart::Auto);
|
.map(Smart::Custom)
|
||||||
|
.unwrap_or(Smart::Auto);
|
||||||
Augment { hline, vline, stroke }
|
Augment { hline, vline, stroke }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -323,7 +323,6 @@ impl LocalName for Packed<BibliographyElem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A loaded bibliography.
|
/// A loaded bibliography.
|
||||||
#[ty]
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct Bibliography {
|
pub struct Bibliography {
|
||||||
map: Arc<IndexMap<PicoStr, hayagriva::Entry>>,
|
map: Arc<IndexMap<PicoStr, hayagriva::Entry>>,
|
||||||
@ -422,12 +421,6 @@ impl Hash for Bibliography {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repr for Bibliography {
|
|
||||||
fn repr(&self) -> EcoString {
|
|
||||||
"..".into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Format a BibLaTeX loading error.
|
/// Format a BibLaTeX loading error.
|
||||||
fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> EcoString {
|
fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> EcoString {
|
||||||
let Some(error) = errors.first() else {
|
let Some(error) = errors.first() else {
|
||||||
|
@ -36,6 +36,6 @@ pub struct EmphElem {
|
|||||||
impl Show for Packed<EmphElem> {
|
impl Show for Packed<EmphElem> {
|
||||||
#[typst_macros::time(name = "emph", span = self.span())]
|
#[typst_macros::time(name = "emph", span = self.span())]
|
||||||
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body().clone().styled(TextElem::set_emph(ItalicToggle)))
|
Ok(self.body().clone().styled(TextElem::set_emph(ItalicToggle(true))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult};
|
use crate::diag::{bail, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{cast, elem, scope, Array, Content, Packed, Smart, StyleChain};
|
||||||
cast, elem, scope, Array, Content, Fold, Packed, Smart, StyleChain,
|
|
||||||
};
|
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
||||||
LayoutMultiple, Length, Regions, Sizing, Spacing, VAlignment,
|
LayoutMultiple, Length, Regions, Sizing, Spacing, VAlignment,
|
||||||
@ -199,7 +199,7 @@ pub struct EnumElem {
|
|||||||
/// The numbers of parent items.
|
/// The numbers of parent items.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[fold]
|
#[fold]
|
||||||
parents: Parent,
|
parents: SmallVec<[usize; 4]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[scope]
|
#[scope]
|
||||||
@ -229,6 +229,8 @@ impl LayoutMultiple for Packed<EnumElem> {
|
|||||||
let mut cells = vec![];
|
let mut cells = vec![];
|
||||||
let mut number = self.start(styles);
|
let mut number = self.start(styles);
|
||||||
let mut parents = self.parents(styles);
|
let mut parents = self.parents(styles);
|
||||||
|
parents.reverse();
|
||||||
|
|
||||||
let full = self.full(styles);
|
let full = self.full(styles);
|
||||||
|
|
||||||
// Horizontally align based on the given respective parameter.
|
// Horizontally align based on the given respective parameter.
|
||||||
@ -263,7 +265,7 @@ impl LayoutMultiple for Packed<EnumElem> {
|
|||||||
cells.push(Cell::from(resolved));
|
cells.push(Cell::from(resolved));
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::from(Content::empty()));
|
||||||
cells.push(Cell::from(
|
cells.push(Cell::from(
|
||||||
item.body().clone().styled(EnumElem::set_parents(Parent(number))),
|
item.body().clone().styled(EnumElem::set_parents(smallvec![number])),
|
||||||
));
|
));
|
||||||
number = number.saturating_add(1);
|
number = number.saturating_add(1);
|
||||||
}
|
}
|
||||||
@ -309,21 +311,3 @@ cast! {
|
|||||||
},
|
},
|
||||||
v: Content => v.unpack::<Self>().unwrap_or_else(Self::new),
|
v: Content => v.unpack::<Self>().unwrap_or_else(Self::new),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
|
|
||||||
struct Parent(usize);
|
|
||||||
|
|
||||||
cast! {
|
|
||||||
Parent,
|
|
||||||
self => self.0.into_value(),
|
|
||||||
v: usize => Self(v),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for Parent {
|
|
||||||
type Output = Vec<usize>;
|
|
||||||
|
|
||||||
fn fold(self, mut outer: Self::Output) -> Self::Output {
|
|
||||||
outer.push(self.0);
|
|
||||||
outer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -150,7 +150,7 @@ impl LayoutMultiple for Packed<ListElem> {
|
|||||||
.unwrap_or_else(|| *BlockElem::below_in(styles).amount())
|
.unwrap_or_else(|| *BlockElem::below_in(styles).amount())
|
||||||
};
|
};
|
||||||
|
|
||||||
let depth = self.depth(styles);
|
let Depth(depth) = self.depth(styles);
|
||||||
let marker = self
|
let marker = self
|
||||||
.marker(styles)
|
.marker(styles)
|
||||||
.resolve(engine, depth)?
|
.resolve(engine, depth)?
|
||||||
@ -162,8 +162,9 @@ impl LayoutMultiple for Packed<ListElem> {
|
|||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::from(Content::empty()));
|
||||||
cells.push(Cell::from(marker.clone()));
|
cells.push(Cell::from(marker.clone()));
|
||||||
cells.push(Cell::from(Content::empty()));
|
cells.push(Cell::from(Content::empty()));
|
||||||
cells
|
cells.push(Cell::from(
|
||||||
.push(Cell::from(item.body().clone().styled(ListElem::set_depth(Depth))));
|
item.body().clone().styled(ListElem::set_depth(Depth(1))),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stroke = None;
|
let stroke = None;
|
||||||
@ -235,19 +236,11 @@ cast! {
|
|||||||
v: Func => Self::Func(v),
|
v: Func => Self::Func(v),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
|
||||||
struct Depth;
|
struct Depth(usize);
|
||||||
|
|
||||||
cast! {
|
|
||||||
Depth,
|
|
||||||
self => Value::None,
|
|
||||||
_: Value => Self,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for Depth {
|
impl Fold for Depth {
|
||||||
type Output = usize;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self(outer.0 + self.0)
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
outer + 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ pub struct TableElem {
|
|||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// The contents of the table cells.
|
/// The contents of the table cells.
|
||||||
@ -378,7 +378,7 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
y: usize,
|
y: usize,
|
||||||
fill: &Option<Paint>,
|
fill: &Option<Paint>,
|
||||||
align: Smart<Alignment>,
|
align: Smart<Alignment>,
|
||||||
inset: Sides<Rel<Length>>,
|
inset: Sides<Option<Rel<Length>>>,
|
||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
) -> Cell {
|
) -> Cell {
|
||||||
let cell = &mut *self;
|
let cell = &mut *self;
|
||||||
@ -397,9 +397,8 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
Smart::Auto => cell.align(styles),
|
Smart::Auto => cell.align(styles),
|
||||||
});
|
});
|
||||||
cell.push_inset(Smart::Custom(
|
cell.push_inset(Smart::Custom(
|
||||||
cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some),
|
cell.inset(styles).map_or(inset, |inner| inner.fold(inset)),
|
||||||
));
|
));
|
||||||
|
|
||||||
Cell { body: self.pack(), fill, colspan }
|
Cell { body: self.pack(), fill, colspan }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
use kurbo::{BezPath, Line, ParamCurve};
|
use kurbo::{BezPath, Line, ParamCurve};
|
||||||
|
use smallvec::smallvec;
|
||||||
use ttf_parser::{GlyphId, OutlineBuilder};
|
use ttf_parser::{GlyphId, OutlineBuilder};
|
||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain};
|
||||||
elem, ty, Content, Fold, Packed, Repr, Show, Smart, StyleChain,
|
|
||||||
};
|
|
||||||
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
|
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
|
||||||
use crate::syntax::Span;
|
use crate::syntax::Span;
|
||||||
use crate::text::{
|
use crate::text::{
|
||||||
@ -89,7 +86,7 @@ pub struct UnderlineElem {
|
|||||||
impl Show for Packed<UnderlineElem> {
|
impl Show for Packed<UnderlineElem> {
|
||||||
#[typst_macros::time(name = "underline", span = self.span())]
|
#[typst_macros::time(name = "underline", span = self.span())]
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
|
Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
|
||||||
line: DecoLine::Underline {
|
line: DecoLine::Underline {
|
||||||
stroke: self.stroke(styles).unwrap_or_default(),
|
stroke: self.stroke(styles).unwrap_or_default(),
|
||||||
offset: self.offset(styles),
|
offset: self.offset(styles),
|
||||||
@ -97,7 +94,7 @@ impl Show for Packed<UnderlineElem> {
|
|||||||
background: self.background(styles),
|
background: self.background(styles),
|
||||||
},
|
},
|
||||||
extent: self.extent(styles),
|
extent: self.extent(styles),
|
||||||
})))
|
}])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +178,7 @@ pub struct OverlineElem {
|
|||||||
impl Show for Packed<OverlineElem> {
|
impl Show for Packed<OverlineElem> {
|
||||||
#[typst_macros::time(name = "overline", span = self.span())]
|
#[typst_macros::time(name = "overline", span = self.span())]
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
|
Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
|
||||||
line: DecoLine::Overline {
|
line: DecoLine::Overline {
|
||||||
stroke: self.stroke(styles).unwrap_or_default(),
|
stroke: self.stroke(styles).unwrap_or_default(),
|
||||||
offset: self.offset(styles),
|
offset: self.offset(styles),
|
||||||
@ -189,7 +186,7 @@ impl Show for Packed<OverlineElem> {
|
|||||||
background: self.background(styles),
|
background: self.background(styles),
|
||||||
},
|
},
|
||||||
extent: self.extent(styles),
|
extent: self.extent(styles),
|
||||||
})))
|
}])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +255,7 @@ pub struct StrikeElem {
|
|||||||
impl Show for Packed<StrikeElem> {
|
impl Show for Packed<StrikeElem> {
|
||||||
#[typst_macros::time(name = "strike", span = self.span())]
|
#[typst_macros::time(name = "strike", span = self.span())]
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
|
Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
|
||||||
// Note that we do not support evade option for strikethrough.
|
// Note that we do not support evade option for strikethrough.
|
||||||
line: DecoLine::Strikethrough {
|
line: DecoLine::Strikethrough {
|
||||||
stroke: self.stroke(styles).unwrap_or_default(),
|
stroke: self.stroke(styles).unwrap_or_default(),
|
||||||
@ -266,7 +263,7 @@ impl Show for Packed<StrikeElem> {
|
|||||||
background: self.background(styles),
|
background: self.background(styles),
|
||||||
},
|
},
|
||||||
extent: self.extent(styles),
|
extent: self.extent(styles),
|
||||||
})))
|
}])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,14 +325,14 @@ pub struct HighlightElem {
|
|||||||
impl Show for Packed<HighlightElem> {
|
impl Show for Packed<HighlightElem> {
|
||||||
#[typst_macros::time(name = "highlight", span = self.span())]
|
#[typst_macros::time(name = "highlight", span = self.span())]
|
||||||
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||||
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
|
Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
|
||||||
line: DecoLine::Highlight {
|
line: DecoLine::Highlight {
|
||||||
fill: self.fill(styles),
|
fill: self.fill(styles),
|
||||||
top_edge: self.top_edge(styles),
|
top_edge: self.top_edge(styles),
|
||||||
bottom_edge: self.bottom_edge(styles),
|
bottom_edge: self.bottom_edge(styles),
|
||||||
},
|
},
|
||||||
extent: self.extent(styles),
|
extent: self.extent(styles),
|
||||||
})))
|
}])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,28 +340,12 @@ impl Show for Packed<HighlightElem> {
|
|||||||
///
|
///
|
||||||
/// Can be positioned over, under, or on top of text, or highlight the text with
|
/// Can be positioned over, under, or on top of text, or highlight the text with
|
||||||
/// a background.
|
/// a background.
|
||||||
#[ty]
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Decoration {
|
pub struct Decoration {
|
||||||
line: DecoLine,
|
line: DecoLine,
|
||||||
extent: Abs,
|
extent: Abs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Decoration {
|
|
||||||
type Output = Vec<Self>;
|
|
||||||
|
|
||||||
fn fold(self, mut outer: Self::Output) -> Self::Output {
|
|
||||||
outer.insert(0, self);
|
|
||||||
outer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Repr for Decoration {
|
|
||||||
fn repr(&self) -> EcoString {
|
|
||||||
eco_format!("{self:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A kind of decorative line.
|
/// A kind of decorative line.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
enum DecoLine {
|
enum DecoLine {
|
||||||
|
@ -32,6 +32,7 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
|
|
||||||
use ecow::{eco_format, EcoString};
|
use ecow::{eco_format, EcoString};
|
||||||
use rustybuzz::{Feature, Tag};
|
use rustybuzz::{Feature, Tag};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use ttf_parser::Rect;
|
use ttf_parser::Rect;
|
||||||
|
|
||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
@ -39,8 +40,9 @@ use crate::engine::Engine;
|
|||||||
use crate::foundations::Packed;
|
use crate::foundations::Packed;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
|
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
|
||||||
NativeElement, Never, PlainText, Repr, Resolve, Scope, Set, Smart, StyleChain, Value,
|
NativeElement, Never, PlainText, Repr, Resolve, Scope, Set, Smart, StyleChain,
|
||||||
};
|
};
|
||||||
|
use crate::layout::Em;
|
||||||
use crate::layout::{Abs, Axis, Dir, Length, Rel};
|
use crate::layout::{Abs, Axis, Dir, Length, Rel};
|
||||||
use crate::model::ParElem;
|
use crate::model::ParElem;
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
@ -214,7 +216,8 @@ pub struct TextElem {
|
|||||||
/// ```
|
/// ```
|
||||||
#[parse(args.named_or_find("size")?)]
|
#[parse(args.named_or_find("size")?)]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Abs::pt(11.0))]
|
#[default(TextSize(Abs::pt(11.0).into()))]
|
||||||
|
#[resolve]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub size: TextSize,
|
pub size: TextSize,
|
||||||
|
|
||||||
@ -628,7 +631,7 @@ pub struct TextElem {
|
|||||||
/// Whether the font style should be inverted.
|
/// Whether the font style should be inverted.
|
||||||
#[internal]
|
#[internal]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(false)]
|
#[default(ItalicToggle(false))]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub emph: ItalicToggle,
|
pub emph: ItalicToggle,
|
||||||
|
|
||||||
@ -636,7 +639,7 @@ pub struct TextElem {
|
|||||||
#[internal]
|
#[internal]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[ghost]
|
#[ghost]
|
||||||
pub deco: Decoration,
|
pub deco: SmallVec<[Decoration; 1]>,
|
||||||
|
|
||||||
/// A case transformation that should be applied to the text.
|
/// A case transformation that should be applied to the text.
|
||||||
#[internal]
|
#[internal]
|
||||||
@ -763,12 +766,12 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
|
|||||||
TextElem::stretch_in(styles),
|
TextElem::stretch_in(styles),
|
||||||
);
|
);
|
||||||
|
|
||||||
let delta = TextElem::delta_in(styles);
|
let WeightDelta(delta) = TextElem::delta_in(styles);
|
||||||
variant.weight = variant
|
variant.weight = variant
|
||||||
.weight
|
.weight
|
||||||
.thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16);
|
.thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16);
|
||||||
|
|
||||||
if TextElem::emph_in(styles) {
|
if TextElem::emph_in(styles).0 {
|
||||||
variant.style = match variant.style {
|
variant.style = match variant.style {
|
||||||
FontStyle::Normal => FontStyle::Italic,
|
FontStyle::Normal => FontStyle::Italic,
|
||||||
FontStyle::Italic => FontStyle::Normal,
|
FontStyle::Italic => FontStyle::Normal,
|
||||||
@ -784,10 +787,20 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
|
|||||||
pub struct TextSize(pub Length);
|
pub struct TextSize(pub Length);
|
||||||
|
|
||||||
impl Fold for TextSize {
|
impl Fold for TextSize {
|
||||||
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
// Multiply the two linear functions.
|
||||||
|
Self(Length {
|
||||||
|
em: Em::new(self.0.em.get() * outer.0.em.get()),
|
||||||
|
abs: self.0.em.get() * outer.0.abs + self.0.abs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolve for TextSize {
|
||||||
type Output = Abs;
|
type Output = Abs;
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||||
self.0.em.at(outer) + self.0.abs
|
self.0.resolve(styles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,11 +1069,8 @@ cast! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for FontFeatures {
|
impl Fold for FontFeatures {
|
||||||
type Output = Self;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self(self.0.fold(outer.0))
|
||||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.0.extend(outer.0);
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,36 +1143,20 @@ pub(crate) fn features(styles: StyleChain) -> Vec<Feature> {
|
|||||||
|
|
||||||
/// A toggle that turns on and off alternatingly if folded.
|
/// A toggle that turns on and off alternatingly if folded.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct ItalicToggle;
|
pub struct ItalicToggle(pub bool);
|
||||||
|
|
||||||
cast! {
|
|
||||||
ItalicToggle,
|
|
||||||
self => Value::None,
|
|
||||||
_: Value => Self,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for ItalicToggle {
|
impl Fold for ItalicToggle {
|
||||||
type Output = bool;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self(self.0 ^ outer.0)
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
!outer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A delta that is summed up when folded.
|
/// A delta that is summed up when folded.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct WeightDelta(pub i64);
|
pub struct WeightDelta(pub i64);
|
||||||
|
|
||||||
cast! {
|
|
||||||
WeightDelta,
|
|
||||||
self => self.0.into_value(),
|
|
||||||
v: i64 => Self(v),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fold for WeightDelta {
|
impl Fold for WeightDelta {
|
||||||
type Output = i64;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self(outer.0 + self.0)
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
outer + self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -658,11 +658,8 @@ cast! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for SyntaxPaths {
|
impl Fold for SyntaxPaths {
|
||||||
type Output = Self;
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self(self.0.fold(outer.0))
|
||||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
|
||||||
self.0.extend(outer.0);
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ pub struct RectElem {
|
|||||||
/// See the [box's documentation]($box.outset) for more details.
|
/// See the [box's documentation]($box.outset) for more details.
|
||||||
#[resolve]
|
#[resolve]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// How much to expand the rectangle's size without affecting the layout.
|
/// How much to expand the rectangle's size without affecting the layout.
|
||||||
@ -219,7 +219,7 @@ pub struct SquareElem {
|
|||||||
/// [box's documentation]($box.inset) for more details.
|
/// [box's documentation]($box.inset) for more details.
|
||||||
#[resolve]
|
#[resolve]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// How much to expand the square's size without affecting the layout. See
|
/// How much to expand the square's size without affecting the layout. See
|
||||||
@ -298,7 +298,7 @@ pub struct EllipseElem {
|
|||||||
/// [box's documentation]($box.inset) for more details.
|
/// [box's documentation]($box.inset) for more details.
|
||||||
#[resolve]
|
#[resolve]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// How much to expand the ellipse's size without affecting the layout. See
|
/// How much to expand the ellipse's size without affecting the layout. See
|
||||||
@ -331,10 +331,10 @@ impl LayoutSingle for Packed<EllipseElem> {
|
|||||||
&self.body(styles),
|
&self.body(styles),
|
||||||
Axes::new(self.width(styles), self.height(styles)),
|
Axes::new(self.width(styles), self.height(styles)),
|
||||||
self.fill(styles),
|
self.fill(styles),
|
||||||
self.stroke(styles).map(Sides::splat),
|
self.stroke(styles).map(|s| Sides::splat(Some(s))),
|
||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
Corners::splat(Rel::zero()),
|
Corners::splat(None),
|
||||||
self.span(),
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -403,7 +403,7 @@ pub struct CircleElem {
|
|||||||
/// [box's documentation]($box.inset) for more details.
|
/// [box's documentation]($box.inset) for more details.
|
||||||
#[resolve]
|
#[resolve]
|
||||||
#[fold]
|
#[fold]
|
||||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||||
pub inset: Sides<Option<Rel<Length>>>,
|
pub inset: Sides<Option<Rel<Length>>>,
|
||||||
|
|
||||||
/// How much to expand the circle's size without affecting the layout. See
|
/// How much to expand the circle's size without affecting the layout. See
|
||||||
@ -434,10 +434,10 @@ impl LayoutSingle for Packed<CircleElem> {
|
|||||||
&self.body(styles),
|
&self.body(styles),
|
||||||
Axes::new(self.width(styles), self.height(styles)),
|
Axes::new(self.width(styles), self.height(styles)),
|
||||||
self.fill(styles),
|
self.fill(styles),
|
||||||
self.stroke(styles).map(Sides::splat),
|
self.stroke(styles).map(|s| Sides::splat(Some(s))),
|
||||||
self.inset(styles),
|
self.inset(styles),
|
||||||
self.outset(styles),
|
self.outset(styles),
|
||||||
Corners::splat(Rel::zero()),
|
Corners::splat(None),
|
||||||
self.span(),
|
self.span(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -453,18 +453,21 @@ fn layout(
|
|||||||
body: &Option<Content>,
|
body: &Option<Content>,
|
||||||
sizing: Axes<Smart<Rel<Length>>>,
|
sizing: Axes<Smart<Rel<Length>>>,
|
||||||
fill: Option<Paint>,
|
fill: Option<Paint>,
|
||||||
stroke: Smart<Sides<Option<Stroke<Abs>>>>,
|
stroke: Smart<Sides<Option<Option<Stroke<Abs>>>>>,
|
||||||
mut inset: Sides<Rel<Abs>>,
|
inset: Sides<Option<Rel<Abs>>>,
|
||||||
outset: Sides<Rel<Abs>>,
|
outset: Sides<Option<Rel<Abs>>>,
|
||||||
radius: Corners<Rel<Abs>>,
|
radius: Corners<Option<Rel<Abs>>>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> SourceResult<Frame> {
|
) -> SourceResult<Frame> {
|
||||||
let resolved = sizing
|
let resolved = sizing
|
||||||
.zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)));
|
.zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)));
|
||||||
|
|
||||||
let mut frame;
|
let mut frame;
|
||||||
|
let mut inset = inset.unwrap_or_default();
|
||||||
|
|
||||||
if let Some(child) = body {
|
if let Some(child) = body {
|
||||||
let region = resolved.unwrap_or(regions.base());
|
let region = resolved.unwrap_or(regions.base());
|
||||||
|
|
||||||
if kind.is_round() {
|
if kind.is_round() {
|
||||||
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
|
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
|
||||||
}
|
}
|
||||||
@ -507,19 +510,27 @@ fn layout(
|
|||||||
let stroke = match stroke {
|
let stroke = match stroke {
|
||||||
Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
|
Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
|
||||||
Smart::Auto => Sides::splat(None),
|
Smart::Auto => Sides::splat(None),
|
||||||
Smart::Custom(strokes) => strokes.map(|s| s.map(Stroke::unwrap_or_default)),
|
Smart::Custom(strokes) => {
|
||||||
|
strokes.unwrap_or_default().map(|s| s.map(Stroke::unwrap_or_default))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add fill and/or stroke.
|
// Add fill and/or stroke.
|
||||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||||
if kind.is_round() {
|
if kind.is_round() {
|
||||||
let outset = outset.relative_to(frame.size());
|
let outset = outset.unwrap_or_default().relative_to(frame.size());
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let pos = Point::new(-outset.left, -outset.top);
|
let pos = Point::new(-outset.left, -outset.top);
|
||||||
let shape = ellipse(size, fill, stroke.left);
|
let shape = ellipse(size, fill, stroke.left);
|
||||||
frame.prepend(pos, FrameItem::Shape(shape, span));
|
frame.prepend(pos, FrameItem::Shape(shape, span));
|
||||||
} else {
|
} else {
|
||||||
frame.fill_and_stroke(fill, stroke, outset, radius, span);
|
frame.fill_and_stroke(
|
||||||
|
fill,
|
||||||
|
stroke,
|
||||||
|
outset.unwrap_or_default(),
|
||||||
|
radius.unwrap_or_default(),
|
||||||
|
span,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,6 +330,19 @@ impl<T: Numeric + Repr> Repr for Stroke<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Numeric + Fold> Fold for Stroke<T> {
|
||||||
|
fn fold(self, outer: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
paint: self.paint.or(outer.paint),
|
||||||
|
thickness: self.thickness.or(outer.thickness),
|
||||||
|
cap: self.cap.or(outer.cap),
|
||||||
|
join: self.join.or(outer.join),
|
||||||
|
dash: self.dash.or(outer.dash),
|
||||||
|
miter_limit: self.miter_limit.or(outer.miter_limit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Resolve for Stroke {
|
impl Resolve for Stroke {
|
||||||
type Output = Stroke<Abs>;
|
type Output = Stroke<Abs>;
|
||||||
|
|
||||||
@ -345,21 +358,6 @@ impl Resolve for Stroke {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fold for Stroke<Abs> {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
paint: self.paint.or(outer.paint),
|
|
||||||
thickness: self.thickness.or(outer.thickness),
|
|
||||||
cap: self.cap.or(outer.cap),
|
|
||||||
join: self.join.or(outer.join),
|
|
||||||
dash: self.dash.or(outer.dash),
|
|
||||||
miter_limit: self.miter_limit.or(outer.miter_limit),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cast! {
|
cast! {
|
||||||
type Stroke,
|
type Stroke,
|
||||||
thickness: Length => Self {
|
thickness: Length => Self {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user