Refactor folding (#3294)
This commit is contained in:
parent
a3e1c70e9e
commit
f14288cacf
@ -106,7 +106,9 @@ impl Elem {
|
||||
/// This includes:
|
||||
/// - Fields that are not synthesized.
|
||||
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 };
|
||||
}
|
||||
|
||||
if field.fold {
|
||||
let output = &field.output;
|
||||
field.output = parse_quote! { <#output as #foundations::Fold>::Output };
|
||||
}
|
||||
|
||||
validate_attrs(&attrs)?;
|
||||
|
||||
Ok(field)
|
||||
@ -555,32 +552,42 @@ fn create_style_chain_access(
|
||||
let elem = &element.ident;
|
||||
|
||||
let Field { ty, default, enum_ident, .. } = field;
|
||||
let getter = match (field.fold, field.resolve, field.borrowed) {
|
||||
(false, false, false) => quote! { get },
|
||||
(false, false, true) => quote! { get_borrowed },
|
||||
(false, true, _) => quote! { get_resolve },
|
||||
(true, false, _) => quote! { get_fold },
|
||||
(true, true, _) => quote! { get_resolve_fold },
|
||||
|
||||
let getter = match (field.fold, field.borrowed) {
|
||||
(false, false) => quote! { get },
|
||||
(false, true) => quote! { get_ref },
|
||||
(true, _) => quote! { get_folded },
|
||||
};
|
||||
|
||||
let default = default
|
||||
.clone()
|
||||
.unwrap_or_else(|| parse_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 },
|
||||
));
|
||||
let mut default = match default {
|
||||
Some(default) => quote! { #default },
|
||||
None => quote! { ::std::default::Default::default() },
|
||||
};
|
||||
|
||||
quote! {
|
||||
#init
|
||||
let mut init = None;
|
||||
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>(
|
||||
<Self as #foundations::NativeElement>::elem(),
|
||||
<#elem as #foundations::Fields>::Enum::#enum_ident as u8,
|
||||
#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,
|
||||
required,
|
||||
default,
|
||||
fold,
|
||||
ty,
|
||||
output,
|
||||
..
|
||||
} = field;
|
||||
let named = !positional;
|
||||
let settable = field.settable();
|
||||
let default_ty = if *fold { &output } else { &ty };
|
||||
let default = quote_option(&settable.then(|| {
|
||||
let default = default
|
||||
.clone()
|
||||
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
|
||||
quote! {
|
||||
|| {
|
||||
let typed: #default_ty = #default;
|
||||
let typed: #ty = #default;
|
||||
#foundations::IntoValue::into_value(typed)
|
||||
}
|
||||
}
|
||||
|
@ -239,14 +239,14 @@ impl<T: Resolve> Resolve for Smart<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Fold for Smart<T>
|
||||
where
|
||||
T: Fold,
|
||||
T::Output: Default,
|
||||
{
|
||||
type Output = Smart<T::Output>;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
self.map(|inner| inner.fold(outer.unwrap_or_default()))
|
||||
impl<T: Fold> Fold for Smart<T> {
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
use Smart::Custom;
|
||||
match (self, outer) {
|
||||
(Custom(inner), Custom(outer)) => Custom(inner.fold(outer)),
|
||||
// An explicit `auto` should be respected, thus we don't do
|
||||
// `inner.or(outer)`.
|
||||
(inner, _) => inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,6 @@ impl Dict {
|
||||
};
|
||||
|
||||
let mut msg = String::from(match unexpected.len() {
|
||||
0 => unreachable!(),
|
||||
1 => "unexpected key ",
|
||||
_ => "unexpected keys ",
|
||||
});
|
||||
|
@ -6,7 +6,6 @@ use std::{iter, mem, ptr};
|
||||
|
||||
use comemo::Prehashed;
|
||||
use ecow::{eco_vec, EcoString, EcoVec};
|
||||
use once_cell::sync::Lazy;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::diag::{SourceResult, Trace, Tracepoint};
|
||||
@ -457,54 +456,47 @@ impl<'a> StyleChain<'a> {
|
||||
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,
|
||||
/// returning a borrowed value if possible.
|
||||
pub fn get_borrowed<T: Clone>(
|
||||
/// returning a borrowed value.
|
||||
pub fn get_ref<T: 'static>(
|
||||
self,
|
||||
func: Element,
|
||||
id: u8,
|
||||
inherent: Option<&'a T>,
|
||||
default: &'static Lazy<T>,
|
||||
default: impl Fn() -> &'a T,
|
||||
) -> &'a T {
|
||||
self.properties::<T>(func, id, inherent)
|
||||
.next()
|
||||
.unwrap_or_else(|| default)
|
||||
.unwrap_or_else(default)
|
||||
}
|
||||
|
||||
/// Cast the first value for the given property in the chain.
|
||||
pub fn get<T: Clone>(
|
||||
/// Cast the first value for the given property in the chain, taking
|
||||
/// `Fold` implementations into account.
|
||||
pub fn get_folded<T: Fold + Clone + 'static>(
|
||||
self,
|
||||
func: Element,
|
||||
id: u8,
|
||||
inherent: Option<&T>,
|
||||
default: &'static Lazy<T>,
|
||||
default: impl Fn() -> 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>(
|
||||
mut values: impl Iterator<Item = T>,
|
||||
default: &impl Fn() -> T::Output,
|
||||
) -> T::Output {
|
||||
default: &impl Fn() -> T,
|
||||
) -> T {
|
||||
values
|
||||
.next()
|
||||
.map(|value| value.fold(next(values, default)))
|
||||
@ -513,36 +505,6 @@ impl<'a> StyleChain<'a> {
|
||||
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.
|
||||
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
|
||||
self.entries().filter_map(Style::recipe)
|
||||
@ -925,38 +887,36 @@ impl<T: Resolve> Resolve for Option<T> {
|
||||
/// #rect()
|
||||
/// ```
|
||||
pub trait Fold {
|
||||
/// The type of the folded output.
|
||||
type Output;
|
||||
|
||||
/// 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>
|
||||
where
|
||||
T: Fold,
|
||||
T::Output: Default,
|
||||
{
|
||||
type Output = Option<T::Output>;
|
||||
impl Fold for bool {
|
||||
fn fold(self, _: Self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
self.map(|inner| inner.fold(outer.unwrap_or_default()))
|
||||
impl<T: Fold> Fold for Option<T> {
|
||||
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> {
|
||||
type Output = Vec<T>;
|
||||
|
||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
||||
fn fold(mut self, outer: Self) -> Self {
|
||||
self.extend(outer);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Fold for SmallVec<[T; N]> {
|
||||
type Output = SmallVec<[T; N]>;
|
||||
|
||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
||||
fn fold(mut self, outer: Self) -> Self {
|
||||
self.extend(outer);
|
||||
self
|
||||
}
|
||||
|
@ -215,9 +215,7 @@ impl Repr for Alignment {
|
||||
}
|
||||
|
||||
impl Fold for Alignment {
|
||||
type Output = Self;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
match (self, outer) {
|
||||
(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),
|
||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
|
||||
|
||||
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::util::Get;
|
||||
|
||||
@ -84,14 +84,6 @@ impl<T> Axes<T> {
|
||||
{
|
||||
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> {
|
||||
@ -198,16 +190,6 @@ cast! {
|
||||
"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>> {
|
||||
/// Unwrap the individual fields.
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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()) {
|
||||
body = body.padded(inset.map(|side| side.map(Length::from)));
|
||||
}
|
||||
@ -154,20 +154,24 @@ impl Packed<BoxElem> {
|
||||
|
||||
// Prepare fill and stroke.
|
||||
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
|
||||
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 radius = self.radius(styles);
|
||||
let radius = self.radius(styles).unwrap_or_default();
|
||||
frame.clip(clip_rect(size, radius, &stroke));
|
||||
}
|
||||
|
||||
// Add fill and/or stroke.
|
||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||
let outset = self.outset(styles);
|
||||
let radius = self.radius(styles);
|
||||
let outset = self.outset(styles).unwrap_or_default();
|
||||
let radius = self.radius(styles).unwrap_or_default();
|
||||
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
|
||||
}
|
||||
|
||||
@ -350,7 +354,7 @@ impl LayoutMultiple for Packed<BlockElem> {
|
||||
) -> SourceResult<Fragment> {
|
||||
// Apply inset.
|
||||
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()) {
|
||||
body = body.clone().padded(inset.map(|side| side.map(Length::from)));
|
||||
}
|
||||
@ -418,14 +422,18 @@ impl LayoutMultiple for Packed<BlockElem> {
|
||||
|
||||
// Prepare fill and stroke.
|
||||
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
|
||||
if self.clip(styles) {
|
||||
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 radius = self.radius(styles);
|
||||
let radius = self.radius(styles).unwrap_or_default();
|
||||
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());
|
||||
}
|
||||
|
||||
let outset = self.outset(styles);
|
||||
let radius = self.radius(styles);
|
||||
let outset = self.outset(styles).unwrap_or_default();
|
||||
let radius = self.radius(styles).unwrap_or_default();
|
||||
for frame in frames.iter_mut().skip(skip as usize) {
|
||||
frame.fill_and_stroke(
|
||||
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> {
|
||||
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
|
||||
T: PartialEq + IntoValue,
|
||||
{
|
||||
fn into_value(self) -> Value {
|
||||
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 handle = |key: &str, component: T| {
|
||||
let value = component.into_value();
|
||||
if value != Value::None {
|
||||
dict.insert(key.into(), value);
|
||||
let mut handle = |key: &str, component: Option<T>| {
|
||||
if let Some(c) = component {
|
||||
dict.insert(key.into(), c.into_value());
|
||||
}
|
||||
};
|
||||
|
||||
@ -228,12 +239,13 @@ impl<T: Resolve> Resolve for Corners<T> {
|
||||
}
|
||||
|
||||
impl<T: Fold> Fold for Corners<Option<T>> {
|
||||
type Output = Corners<T::Output>;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
self.zip(outer).map(|(inner, outer)| match inner {
|
||||
Some(value) => value.fold(outer),
|
||||
None => outer,
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
self.zip(outer).map(|(inner, outer)| match (inner, outer) {
|
||||
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
|
||||
// Usually, folding an inner `None` with an `outer` preferres the
|
||||
// explicit `None`. However, here `None` means unspecified and thus
|
||||
// we want `outer`.
|
||||
(inner, outer) => inner.or(outer),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ pub trait ResolvableCell {
|
||||
y: usize,
|
||||
fill: &Option<Paint>,
|
||||
align: Smart<Alignment>,
|
||||
inset: Sides<Rel<Length>>,
|
||||
inset: Sides<Option<Rel<Length>>>,
|
||||
styles: StyleChain,
|
||||
) -> Cell;
|
||||
|
||||
@ -204,7 +204,7 @@ impl CellGrid {
|
||||
cells: &[T],
|
||||
fill: &Celled<Option<Paint>>,
|
||||
align: &Celled<Smart<Alignment>>,
|
||||
inset: Sides<Rel<Length>>,
|
||||
inset: Sides<Option<Rel<Length>>>,
|
||||
engine: &mut Engine,
|
||||
styles: StyleChain,
|
||||
span: Span,
|
||||
|
@ -13,8 +13,8 @@ use crate::foundations::{
|
||||
cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value,
|
||||
};
|
||||
use crate::layout::{
|
||||
Abs, AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel,
|
||||
Sides, Sizing,
|
||||
AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel, Sides,
|
||||
Sizing,
|
||||
};
|
||||
use crate::syntax::Span;
|
||||
use crate::util::NonZeroExt;
|
||||
@ -264,7 +264,6 @@ pub struct GridElem {
|
||||
/// )
|
||||
/// ```
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(0.0).into()))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// The contents of the grid cells.
|
||||
@ -462,7 +461,7 @@ impl ResolvableCell for Packed<GridCell> {
|
||||
y: usize,
|
||||
fill: &Option<Paint>,
|
||||
align: Smart<Alignment>,
|
||||
inset: Sides<Rel<Length>>,
|
||||
inset: Sides<Option<Rel<Length>>>,
|
||||
styles: StyleChain,
|
||||
) -> Cell {
|
||||
let cell = &mut *self;
|
||||
@ -481,9 +480,8 @@ impl ResolvableCell for Packed<GridCell> {
|
||||
Smart::Auto => cell.align(styles),
|
||||
});
|
||||
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 }
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use std::ops::{Add, Div, Mul, Neg};
|
||||
use ecow::{eco_format, EcoString};
|
||||
|
||||
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::syntax::Span;
|
||||
use crate::util::Numeric;
|
||||
@ -274,3 +274,9 @@ impl Resolve for Length {
|
||||
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 {
|
||||
type Output = Margin;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
let sides =
|
||||
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 }
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Margin {
|
||||
sides: self.sides.fold(outer.sides),
|
||||
two_sided: self.two_sided.fold(outer.two_sided),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,19 +259,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for Rel<Abs> {
|
||||
type Output = Self;
|
||||
|
||||
fn fold(self, _: Self::Output) -> Self::Output {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold for Rel<Length> {
|
||||
type Output = Self;
|
||||
|
||||
fn fold(self, _: Self::Output) -> Self::Output {
|
||||
self
|
||||
impl<T> Fold for Rel<T>
|
||||
where
|
||||
T: Numeric + Fold,
|
||||
{
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self { rel: self.rel, abs: self.abs.fold(outer.abs) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>> {
|
||||
/// Evaluate the sides relative to the given `size`.
|
||||
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
|
||||
T: PartialEq + IntoValue,
|
||||
{
|
||||
fn into_value(self) -> Value {
|
||||
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 handle = |key: &str, component: T| {
|
||||
let value = component.into_value();
|
||||
if value != Value::None {
|
||||
dict.insert(key.into(), value);
|
||||
let mut handle = |key: &str, component: Option<T>| {
|
||||
if let Some(c) = component {
|
||||
dict.insert(key.into(), c.into_value());
|
||||
}
|
||||
};
|
||||
|
||||
@ -233,12 +244,13 @@ impl<T: Resolve> Resolve for Sides<T> {
|
||||
}
|
||||
|
||||
impl<T: Fold> Fold for Sides<Option<T>> {
|
||||
type Output = Sides<T::Output>;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
self.zip(outer).map(|(inner, outer)| match inner {
|
||||
Some(value) => value.fold(outer),
|
||||
None => outer,
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
self.zip(outer).map(|(inner, outer)| match (inner, outer) {
|
||||
(Some(inner), Some(outer)) => Some(inner.fold(outer)),
|
||||
// Usually, folding an inner `None` with an `outer` preferres the
|
||||
// explicit `None`. However, here `None` means unspecified and thus
|
||||
// we want `outer`.
|
||||
(inner, outer) => inner.or(outer),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ pub struct CancelElem {
|
||||
#[fold]
|
||||
#[default(Stroke {
|
||||
// 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()
|
||||
})]
|
||||
pub stroke: Stroke,
|
||||
|
@ -425,11 +425,10 @@ fn layout_mat_body(
|
||||
};
|
||||
|
||||
let (hline, vline, stroke) = match augment {
|
||||
Some(v) => {
|
||||
// need to get stroke here for ownership
|
||||
let stroke = v.stroke_or(default_stroke);
|
||||
|
||||
(v.hline, v.vline, stroke)
|
||||
Some(augment) => {
|
||||
// We need to get stroke here for ownership.
|
||||
let stroke = augment.stroke.unwrap_or_default().unwrap_or(default_stroke);
|
||||
(augment.hline, augment.vline, stroke)
|
||||
}
|
||||
_ => (AugmentOffsets::default(), AugmentOffsets::default(), default_stroke),
|
||||
};
|
||||
@ -593,11 +592,19 @@ pub struct Augment<T: Numeric = Length> {
|
||||
pub stroke: Smart<Stroke<T>>,
|
||||
}
|
||||
|
||||
impl Augment<Abs> {
|
||||
fn stroke_or(&self, fallback: FixedStroke) -> FixedStroke {
|
||||
match &self.stroke {
|
||||
Smart::Custom(v) => v.clone().unwrap_or(fallback),
|
||||
Smart::Auto => fallback,
|
||||
impl<T: Numeric + Fold> Fold for Augment<T> {
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self {
|
||||
stroke: match (self.stroke, outer.stroke) {
|
||||
(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! {
|
||||
Augment,
|
||||
self => {
|
||||
@ -637,13 +629,11 @@ cast! {
|
||||
return self.vline.0[0].into_value();
|
||||
}
|
||||
|
||||
let d = dict! {
|
||||
"hline" => self.hline.into_value(),
|
||||
"vline" => self.vline.into_value(),
|
||||
"stroke" => self.stroke.into_value()
|
||||
};
|
||||
|
||||
d.into_value()
|
||||
dict! {
|
||||
"hline" => self.hline,
|
||||
"vline" => self.vline,
|
||||
"stroke" => self.stroke,
|
||||
}.into_value()
|
||||
},
|
||||
v: isize => Augment {
|
||||
hline: AugmentOffsets::default(),
|
||||
@ -651,15 +641,15 @@ cast! {
|
||||
stroke: Smart::Auto,
|
||||
},
|
||||
mut dict: Dict => {
|
||||
// need the transpose for the defaults to work
|
||||
let hline = dict.take("hline").ok().map(AugmentOffsets::from_value)
|
||||
.transpose().unwrap_or_default().unwrap_or_default();
|
||||
let vline = dict.take("vline").ok().map(AugmentOffsets::from_value)
|
||||
.transpose().unwrap_or_default().unwrap_or_default();
|
||||
|
||||
let stroke = dict.take("stroke").ok().map(Stroke::from_value)
|
||||
.transpose()?.map(Smart::Custom).unwrap_or(Smart::Auto);
|
||||
|
||||
let mut take = |key| dict.take(key).ok().map(AugmentOffsets::from_value).transpose();
|
||||
let hline = take("hline")?.unwrap_or_default();
|
||||
let vline = take("vline")?.unwrap_or_default();
|
||||
let stroke = dict.take("stroke")
|
||||
.ok()
|
||||
.map(Stroke::from_value)
|
||||
.transpose()?
|
||||
.map(Smart::Custom)
|
||||
.unwrap_or(Smart::Auto);
|
||||
Augment { hline, vline, stroke }
|
||||
},
|
||||
}
|
||||
|
@ -323,7 +323,6 @@ impl LocalName for Packed<BibliographyElem> {
|
||||
}
|
||||
|
||||
/// A loaded bibliography.
|
||||
#[ty]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Bibliography {
|
||||
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.
|
||||
fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> EcoString {
|
||||
let Some(error) = errors.first() else {
|
||||
|
@ -36,6 +36,6 @@ pub struct EmphElem {
|
||||
impl Show for Packed<EmphElem> {
|
||||
#[typst_macros::time(name = "emph", span = self.span())]
|
||||
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 smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
cast, elem, scope, Array, Content, Fold, Packed, Smart, StyleChain,
|
||||
};
|
||||
use crate::foundations::{cast, elem, scope, Array, Content, Packed, Smart, StyleChain};
|
||||
use crate::layout::{
|
||||
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
|
||||
LayoutMultiple, Length, Regions, Sizing, Spacing, VAlignment,
|
||||
@ -199,7 +199,7 @@ pub struct EnumElem {
|
||||
/// The numbers of parent items.
|
||||
#[internal]
|
||||
#[fold]
|
||||
parents: Parent,
|
||||
parents: SmallVec<[usize; 4]>,
|
||||
}
|
||||
|
||||
#[scope]
|
||||
@ -229,6 +229,8 @@ impl LayoutMultiple for Packed<EnumElem> {
|
||||
let mut cells = vec![];
|
||||
let mut number = self.start(styles);
|
||||
let mut parents = self.parents(styles);
|
||||
parents.reverse();
|
||||
|
||||
let full = self.full(styles);
|
||||
|
||||
// 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(Content::empty()));
|
||||
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);
|
||||
}
|
||||
@ -309,21 +311,3 @@ cast! {
|
||||
},
|
||||
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())
|
||||
};
|
||||
|
||||
let depth = self.depth(styles);
|
||||
let Depth(depth) = self.depth(styles);
|
||||
let marker = self
|
||||
.marker(styles)
|
||||
.resolve(engine, depth)?
|
||||
@ -162,8 +162,9 @@ impl LayoutMultiple for Packed<ListElem> {
|
||||
cells.push(Cell::from(Content::empty()));
|
||||
cells.push(Cell::from(marker.clone()));
|
||||
cells.push(Cell::from(Content::empty()));
|
||||
cells
|
||||
.push(Cell::from(item.body().clone().styled(ListElem::set_depth(Depth))));
|
||||
cells.push(Cell::from(
|
||||
item.body().clone().styled(ListElem::set_depth(Depth(1))),
|
||||
));
|
||||
}
|
||||
|
||||
let stroke = None;
|
||||
@ -235,19 +236,11 @@ cast! {
|
||||
v: Func => Self::Func(v),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
|
||||
struct Depth;
|
||||
|
||||
cast! {
|
||||
Depth,
|
||||
self => Value::None,
|
||||
_: Value => Self,
|
||||
}
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
|
||||
struct Depth(usize);
|
||||
|
||||
impl Fold for Depth {
|
||||
type Output = usize;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
outer + 1
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self(outer.0 + self.0)
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ pub struct TableElem {
|
||||
/// )
|
||||
/// ```
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
||||
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// The contents of the table cells.
|
||||
@ -378,7 +378,7 @@ impl ResolvableCell for Packed<TableCell> {
|
||||
y: usize,
|
||||
fill: &Option<Paint>,
|
||||
align: Smart<Alignment>,
|
||||
inset: Sides<Rel<Length>>,
|
||||
inset: Sides<Option<Rel<Length>>>,
|
||||
styles: StyleChain,
|
||||
) -> Cell {
|
||||
let cell = &mut *self;
|
||||
@ -397,9 +397,8 @@ impl ResolvableCell for Packed<TableCell> {
|
||||
Smart::Auto => cell.align(styles),
|
||||
});
|
||||
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 }
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,10 @@
|
||||
use kurbo::{BezPath, Line, ParamCurve};
|
||||
use smallvec::smallvec;
|
||||
use ttf_parser::{GlyphId, OutlineBuilder};
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
|
||||
use crate::diag::SourceResult;
|
||||
use crate::engine::Engine;
|
||||
use crate::foundations::{
|
||||
elem, ty, Content, Fold, Packed, Repr, Show, Smart, StyleChain,
|
||||
};
|
||||
use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain};
|
||||
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
|
||||
use crate::syntax::Span;
|
||||
use crate::text::{
|
||||
@ -89,7 +86,7 @@ pub struct UnderlineElem {
|
||||
impl Show for Packed<UnderlineElem> {
|
||||
#[typst_macros::time(name = "underline", span = self.span())]
|
||||
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 {
|
||||
stroke: self.stroke(styles).unwrap_or_default(),
|
||||
offset: self.offset(styles),
|
||||
@ -97,7 +94,7 @@ impl Show for Packed<UnderlineElem> {
|
||||
background: self.background(styles),
|
||||
},
|
||||
extent: self.extent(styles),
|
||||
})))
|
||||
}])))
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +178,7 @@ pub struct OverlineElem {
|
||||
impl Show for Packed<OverlineElem> {
|
||||
#[typst_macros::time(name = "overline", span = self.span())]
|
||||
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 {
|
||||
stroke: self.stroke(styles).unwrap_or_default(),
|
||||
offset: self.offset(styles),
|
||||
@ -189,7 +186,7 @@ impl Show for Packed<OverlineElem> {
|
||||
background: self.background(styles),
|
||||
},
|
||||
extent: self.extent(styles),
|
||||
})))
|
||||
}])))
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,7 +255,7 @@ pub struct StrikeElem {
|
||||
impl Show for Packed<StrikeElem> {
|
||||
#[typst_macros::time(name = "strike", span = self.span())]
|
||||
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.
|
||||
line: DecoLine::Strikethrough {
|
||||
stroke: self.stroke(styles).unwrap_or_default(),
|
||||
@ -266,7 +263,7 @@ impl Show for Packed<StrikeElem> {
|
||||
background: self.background(styles),
|
||||
},
|
||||
extent: self.extent(styles),
|
||||
})))
|
||||
}])))
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,14 +325,14 @@ pub struct HighlightElem {
|
||||
impl Show for Packed<HighlightElem> {
|
||||
#[typst_macros::time(name = "highlight", span = self.span())]
|
||||
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 {
|
||||
fill: self.fill(styles),
|
||||
top_edge: self.top_edge(styles),
|
||||
bottom_edge: self.bottom_edge(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
|
||||
/// a background.
|
||||
#[ty]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Decoration {
|
||||
line: DecoLine,
|
||||
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.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
enum DecoLine {
|
||||
|
@ -32,6 +32,7 @@ use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use rustybuzz::{Feature, Tag};
|
||||
use smallvec::SmallVec;
|
||||
use ttf_parser::Rect;
|
||||
|
||||
use crate::diag::{bail, SourceResult, StrResult};
|
||||
@ -39,8 +40,9 @@ use crate::engine::Engine;
|
||||
use crate::foundations::Packed;
|
||||
use crate::foundations::{
|
||||
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::model::ParElem;
|
||||
use crate::syntax::Spanned;
|
||||
@ -214,7 +216,8 @@ pub struct TextElem {
|
||||
/// ```
|
||||
#[parse(args.named_or_find("size")?)]
|
||||
#[fold]
|
||||
#[default(Abs::pt(11.0))]
|
||||
#[default(TextSize(Abs::pt(11.0).into()))]
|
||||
#[resolve]
|
||||
#[ghost]
|
||||
pub size: TextSize,
|
||||
|
||||
@ -628,7 +631,7 @@ pub struct TextElem {
|
||||
/// Whether the font style should be inverted.
|
||||
#[internal]
|
||||
#[fold]
|
||||
#[default(false)]
|
||||
#[default(ItalicToggle(false))]
|
||||
#[ghost]
|
||||
pub emph: ItalicToggle,
|
||||
|
||||
@ -636,7 +639,7 @@ pub struct TextElem {
|
||||
#[internal]
|
||||
#[fold]
|
||||
#[ghost]
|
||||
pub deco: Decoration,
|
||||
pub deco: SmallVec<[Decoration; 1]>,
|
||||
|
||||
/// A case transformation that should be applied to the text.
|
||||
#[internal]
|
||||
@ -763,12 +766,12 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
|
||||
TextElem::stretch_in(styles),
|
||||
);
|
||||
|
||||
let delta = TextElem::delta_in(styles);
|
||||
let WeightDelta(delta) = TextElem::delta_in(styles);
|
||||
variant.weight = variant
|
||||
.weight
|
||||
.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 {
|
||||
FontStyle::Normal => FontStyle::Italic,
|
||||
FontStyle::Italic => FontStyle::Normal,
|
||||
@ -784,10 +787,20 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
|
||||
pub struct TextSize(pub Length);
|
||||
|
||||
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;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
self.0.em.at(outer) + self.0.abs
|
||||
fn resolve(self, styles: StyleChain) -> Self::Output {
|
||||
self.0.resolve(styles)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,11 +1069,8 @@ cast! {
|
||||
}
|
||||
|
||||
impl Fold for FontFeatures {
|
||||
type Output = Self;
|
||||
|
||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
||||
self.0.extend(outer.0);
|
||||
self
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self(self.0.fold(outer.0))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1133,36 +1143,20 @@ pub(crate) fn features(styles: StyleChain) -> Vec<Feature> {
|
||||
|
||||
/// A toggle that turns on and off alternatingly if folded.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct ItalicToggle;
|
||||
|
||||
cast! {
|
||||
ItalicToggle,
|
||||
self => Value::None,
|
||||
_: Value => Self,
|
||||
}
|
||||
pub struct ItalicToggle(pub bool);
|
||||
|
||||
impl Fold for ItalicToggle {
|
||||
type Output = bool;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
!outer
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self(self.0 ^ outer.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
cast! {
|
||||
WeightDelta,
|
||||
self => self.0.into_value(),
|
||||
v: i64 => Self(v),
|
||||
}
|
||||
|
||||
impl Fold for WeightDelta {
|
||||
type Output = i64;
|
||||
|
||||
fn fold(self, outer: Self::Output) -> Self::Output {
|
||||
outer + self.0
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self(outer.0 + self.0)
|
||||
}
|
||||
}
|
||||
|
@ -658,11 +658,8 @@ cast! {
|
||||
}
|
||||
|
||||
impl Fold for SyntaxPaths {
|
||||
type Output = Self;
|
||||
|
||||
fn fold(mut self, outer: Self::Output) -> Self::Output {
|
||||
self.0.extend(outer.0);
|
||||
self
|
||||
fn fold(self, outer: Self) -> Self {
|
||||
Self(self.0.fold(outer.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ pub struct RectElem {
|
||||
/// See the [box's documentation]($box.outset) for more details.
|
||||
#[resolve]
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
||||
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// 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.
|
||||
#[resolve]
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
||||
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// 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.
|
||||
#[resolve]
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
||||
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// 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),
|
||||
Axes::new(self.width(styles), self.height(styles)),
|
||||
self.fill(styles),
|
||||
self.stroke(styles).map(Sides::splat),
|
||||
self.stroke(styles).map(|s| Sides::splat(Some(s))),
|
||||
self.inset(styles),
|
||||
self.outset(styles),
|
||||
Corners::splat(Rel::zero()),
|
||||
Corners::splat(None),
|
||||
self.span(),
|
||||
)
|
||||
}
|
||||
@ -403,7 +403,7 @@ pub struct CircleElem {
|
||||
/// [box's documentation]($box.inset) for more details.
|
||||
#[resolve]
|
||||
#[fold]
|
||||
#[default(Sides::splat(Abs::pt(5.0).into()))]
|
||||
#[default(Sides::splat(Some(Abs::pt(5.0).into())))]
|
||||
pub inset: Sides<Option<Rel<Length>>>,
|
||||
|
||||
/// 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),
|
||||
Axes::new(self.width(styles), self.height(styles)),
|
||||
self.fill(styles),
|
||||
self.stroke(styles).map(Sides::splat),
|
||||
self.stroke(styles).map(|s| Sides::splat(Some(s))),
|
||||
self.inset(styles),
|
||||
self.outset(styles),
|
||||
Corners::splat(Rel::zero()),
|
||||
Corners::splat(None),
|
||||
self.span(),
|
||||
)
|
||||
}
|
||||
@ -453,18 +453,21 @@ fn layout(
|
||||
body: &Option<Content>,
|
||||
sizing: Axes<Smart<Rel<Length>>>,
|
||||
fill: Option<Paint>,
|
||||
stroke: Smart<Sides<Option<Stroke<Abs>>>>,
|
||||
mut inset: Sides<Rel<Abs>>,
|
||||
outset: Sides<Rel<Abs>>,
|
||||
radius: Corners<Rel<Abs>>,
|
||||
stroke: Smart<Sides<Option<Option<Stroke<Abs>>>>>,
|
||||
inset: Sides<Option<Rel<Abs>>>,
|
||||
outset: Sides<Option<Rel<Abs>>>,
|
||||
radius: Corners<Option<Rel<Abs>>>,
|
||||
span: Span,
|
||||
) -> SourceResult<Frame> {
|
||||
let resolved = sizing
|
||||
.zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)));
|
||||
|
||||
let mut frame;
|
||||
let mut inset = inset.unwrap_or_default();
|
||||
|
||||
if let Some(child) = body {
|
||||
let region = resolved.unwrap_or(regions.base());
|
||||
|
||||
if kind.is_round() {
|
||||
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
|
||||
}
|
||||
@ -507,19 +510,27 @@ fn layout(
|
||||
let stroke = match stroke {
|
||||
Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
|
||||
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.
|
||||
if fill.is_some() || stroke.iter().any(Option::is_some) {
|
||||
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 pos = Point::new(-outset.left, -outset.top);
|
||||
let shape = ellipse(size, fill, stroke.left);
|
||||
frame.prepend(pos, FrameItem::Shape(shape, span));
|
||||
} 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 {
|
||||
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! {
|
||||
type Stroke,
|
||||
thickness: Length => Self {
|
||||
|
Loading…
x
Reference in New Issue
Block a user