diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-07 10:50:39 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-07 11:07:00 +0200 |
| commit | 3d52387eea321e94c13b61666f7a758052b20c5d (patch) | |
| tree | 5c55c51ca7e4b53dee61d280c39b7f664b8b9d6b /src/library/text | |
| parent | 20b4d590b3efbd9b7a44fd6d3a658e7b84d21b99 (diff) | |
Rework style chains
Diffstat (limited to 'src/library/text')
| -rw-r--r-- | src/library/text/deco.rs | 10 | ||||
| -rw-r--r-- | src/library/text/link.rs | 13 | ||||
| -rw-r--r-- | src/library/text/mod.rs | 162 | ||||
| -rw-r--r-- | src/library/text/par.rs | 9 | ||||
| -rw-r--r-- | src/library/text/raw.rs | 20 | ||||
| -rw-r--r-- | src/library/text/shaping.rs | 16 |
6 files changed, 140 insertions, 90 deletions
diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs index b98eb0b2..a6375e4e 100644 --- a/src/library/text/deco.rs +++ b/src/library/text/deco.rs @@ -21,11 +21,11 @@ pub type OverlineNode = DecoNode<OVERLINE>; #[node(showable)] impl<const L: DecoLine> DecoNode<L> { /// Stroke color of the line, defaults to the text color if `None`. - #[shorthand] + #[property(shorthand)] pub const STROKE: Option<Paint> = None; /// Thickness of the line's strokes (dependent on scaled font size), read /// from the font tables if `None`. - #[shorthand] + #[property(shorthand)] pub const THICKNESS: Option<Linear> = None; /// Position of the line relative to the baseline (dependent on scaled font /// size), read from the font tables if `None`. @@ -45,16 +45,16 @@ impl<const L: DecoLine> DecoNode<L> { impl<const L: DecoLine> Show for DecoNode<L> { fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { Ok(styles - .show(self, ctx, [Value::Content(self.0.clone())])? + .show::<Self, _>(ctx, [Value::Content(self.0.clone())])? .unwrap_or_else(|| { - self.0.clone().styled(TextNode::LINES, vec![Decoration { + self.0.clone().styled(TextNode::DECO, Decoration { line: L, stroke: styles.get(Self::STROKE), thickness: styles.get(Self::THICKNESS), offset: styles.get(Self::OFFSET), extent: styles.get(Self::EXTENT), evade: styles.get(Self::EVADE), - }]) + }) })) } } diff --git a/src/library/text/link.rs b/src/library/text/link.rs index 4c2b5e7b..3ef7011d 100644 --- a/src/library/text/link.rs +++ b/src/library/text/link.rs @@ -29,14 +29,13 @@ impl LinkNode { impl Show for LinkNode { fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { + let args = [Value::Str(self.url.clone()), match &self.body { + Some(body) => Value::Content(body.clone()), + None => Value::None, + }]; + let mut body = styles - .show(self, ctx, [ - Value::Str(self.url.clone()), - match &self.body { - Some(body) => Value::Content(body.clone()), - None => Value::None, - }, - ])? + .show::<Self, _>(ctx, args)? .or_else(|| self.body.clone()) .unwrap_or_else(|| { let url = &self.url; diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index 2c163a59..64994136 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -13,7 +13,6 @@ pub use raw::*; pub use shaping::*; use std::borrow::Cow; -use std::ops::BitXor; use ttf_parser::Tag; @@ -28,7 +27,7 @@ pub struct TextNode; #[node] impl TextNode { /// A prioritized sequence of font families. - #[variadic] + #[property(referenced, variadic)] pub const FAMILY: Vec<FontFamily> = vec![FontFamily::new("IBM Plex Sans")]; /// Whether to allow font fallback when the primary font list contains no /// match. @@ -40,14 +39,13 @@ impl TextNode { pub const WEIGHT: FontWeight = FontWeight::REGULAR; /// The width of the glyphs. pub const STRETCH: FontStretch = FontStretch::NORMAL; + /// The size of the glyphs. + #[property(shorthand, fold)] + pub const SIZE: FontSize = Length::pt(11.0); /// The glyph fill color. - #[shorthand] + #[property(shorthand)] pub const FILL: Paint = Color::BLACK.into(); - /// The size of the glyphs. - #[shorthand] - #[fold(Linear::compose)] - pub const SIZE: Linear = Length::pt(11.0).into(); /// The amount of space that should be added between characters. pub const TRACKING: Em = Em::zero(); /// The ratio by which spaces should be stretched. @@ -84,26 +82,24 @@ impl TextNode { /// Whether to convert fractions. ("frac") pub const FRACTIONS: bool = false; /// Raw OpenType features to apply. + #[property(fold)] pub const FEATURES: Vec<(Tag, u32)> = vec![]; /// Whether the font weight should be increased by 300. - #[skip] - #[fold(bool::bitxor)] - pub const STRONG: bool = false; + #[property(hidden, fold)] + pub const STRONG: Toggle = false; /// Whether the the font style should be inverted. - #[skip] - #[fold(bool::bitxor)] - pub const EMPH: bool = false; - /// The case transformation that should be applied to the next. - #[skip] + #[property(hidden, fold)] + pub const EMPH: Toggle = false; + /// A case transformation that should be applied to the text. + #[property(hidden)] pub const CASE: Option<Case> = None; - /// Decorative lines. - #[skip] - #[fold(|a, b| a.into_iter().chain(b).collect())] - pub const LINES: Vec<Decoration> = vec![]; /// An URL the text should link to. - #[skip] + #[property(hidden, referenced)] pub const LINK: Option<EcoString> = None; + /// Decorative lines. + #[property(hidden, fold)] + pub const DECO: Decoration = vec![]; fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { // The text constructor is special: It doesn't create a text node. @@ -113,44 +109,6 @@ impl TextNode { } } -/// Strong text, rendered in boldface. -#[derive(Debug, Hash)] -pub struct StrongNode(pub Content); - -#[node(showable)] -impl StrongNode { - fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) - } -} - -impl Show for StrongNode { - fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { - Ok(styles - .show(self, ctx, [Value::Content(self.0.clone())])? - .unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, true))) - } -} - -/// Emphasized text, rendered with an italic face. -#[derive(Debug, Hash)] -pub struct EmphNode(pub Content); - -#[node(showable)] -impl EmphNode { - fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { - Ok(Content::show(Self(args.expect("body")?))) - } -} - -impl Show for EmphNode { - fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { - Ok(styles - .show(self, ctx, [Value::Content(self.0.clone())])? - .unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, true))) - } -} - /// A font family like "Arial". #[derive(Clone, Eq, PartialEq, Hash)] pub struct FontFamily(EcoString); @@ -228,6 +186,26 @@ castable! { Value::Relative(v) => Self::from_ratio(v.get() as f32), } +/// The size of text. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct FontSize(pub Linear); + +impl Fold for FontSize { + type Output = Length; + + fn fold(self, outer: Self::Output) -> Self::Output { + self.0.rel.resolve(outer) + self.0.abs + } +} + +castable! { + FontSize, + Expected: "linear", + Value::Length(v) => Self(v.into()), + Value::Relative(v) => Self(v.into()), + Value::Linear(v) => Self(v), +} + castable! { Em, Expected: "float", @@ -353,6 +331,15 @@ castable! { .collect(), } +impl Fold for Vec<(Tag, u32)> { + type Output = Self; + + fn fold(mut self, outer: Self::Output) -> Self::Output { + self.extend(outer); + self + } +} + /// A case transformation on text. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum Case { @@ -371,3 +358,62 @@ impl Case { } } } + +/// A toggle that turns on and off alternatingly if folded. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Toggle; + +impl Fold for Toggle { + type Output = bool; + + fn fold(self, outer: Self::Output) -> Self::Output { + !outer + } +} + +impl Fold for Decoration { + type Output = Vec<Self>; + + fn fold(self, mut outer: Self::Output) -> Self::Output { + outer.insert(0, self); + outer + } +} + +/// Strong text, rendered in boldface. +#[derive(Debug, Hash)] +pub struct StrongNode(pub Content); + +#[node(showable)] +impl StrongNode { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { + Ok(Content::show(Self(args.expect("body")?))) + } +} + +impl Show for StrongNode { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { + Ok(styles + .show::<Self, _>(ctx, [Value::Content(self.0.clone())])? + .unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, Toggle))) + } +} + +/// Emphasized text, rendered with an italic face. +#[derive(Debug, Hash)] +pub struct EmphNode(pub Content); + +#[node(showable)] +impl EmphNode { + fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { + Ok(Content::show(Self(args.expect("body")?))) + } +} + +impl Show for EmphNode { + fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { + Ok(styles + .show::<Self, _>(ctx, [Value::Content(self.0.clone())])? + .unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, Toggle))) + } +} diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 97e5a3f5..67357135 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -28,6 +28,7 @@ pub enum ParChild { #[node] impl ParNode { /// An ISO 639-1 language code. + #[property(referenced)] pub const LANG: Option<EcoString> = None; /// The direction for text and inline objects. pub const DIR: Dir = Dir::LTR; @@ -611,7 +612,7 @@ fn breakpoints<'a>( let mut lang = None; if styles.get(ParNode::HYPHENATE).unwrap_or(styles.get(ParNode::JUSTIFY)) { lang = styles - .get_ref(ParNode::LANG) + .get(ParNode::LANG) .as_ref() .and_then(|iso| iso.as_bytes().try_into().ok()) .and_then(hypher::Lang::from_iso); @@ -768,7 +769,7 @@ fn stack( regions: &Regions, styles: StyleChain, ) -> Vec<Arc<Frame>> { - let em = styles.get(TextNode::SIZE).abs; + let em = styles.get(TextNode::SIZE); let leading = styles.get(ParNode::LEADING).resolve(em); let align = styles.get(ParNode::ALIGN); let justify = styles.get(ParNode::JUSTIFY); @@ -832,7 +833,7 @@ fn commit( if let Some(glyph) = text.glyphs.first() { if text.styles.get(TextNode::OVERHANG) { let start = text.dir.is_positive(); - let em = text.styles.get(TextNode::SIZE).abs; + let em = text.styles.get(TextNode::SIZE); let amount = overhang(glyph.c, start) * glyph.x_advance.resolve(em); offset -= amount; remaining += amount; @@ -847,7 +848,7 @@ fn commit( && (reordered.len() > 1 || text.glyphs.len() > 1) { let start = !text.dir.is_positive(); - let em = text.styles.get(TextNode::SIZE).abs; + let em = text.styles.get(TextNode::SIZE); let amount = overhang(glyph.c, start) * glyph.x_advance.resolve(em); remaining += amount; } diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs index 5c2133c2..f09bc1d0 100644 --- a/src/library/text/raw.rs +++ b/src/library/text/raw.rs @@ -3,7 +3,7 @@ use syntect::easy::HighlightLines; use syntect::highlighting::{FontStyle, Highlighter, Style, Theme, ThemeSet}; use syntect::parsing::SyntaxSet; -use super::{FontFamily, TextNode}; +use super::{FontFamily, TextNode, Toggle}; use crate::library::prelude::*; use crate::source::SourceId; use crate::syntax::{self, RedNode}; @@ -27,8 +27,10 @@ pub struct RawNode { #[node(showable)] impl RawNode { /// The raw text's font family. Just the normal text family if `none`. + #[property(referenced)] pub const FAMILY: Smart<FontFamily> = Smart::Custom(FontFamily::new("IBM Plex Mono")); /// The language to syntax-highlight in. + #[property(referenced)] pub const LANG: Option<EcoString> = None; fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { @@ -41,7 +43,7 @@ impl RawNode { impl Show for RawNode { fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> { - let lang = styles.get_ref(Self::LANG).as_ref(); + let lang = styles.get(Self::LANG).as_ref(); let foreground = THEME .settings .foreground @@ -49,14 +51,16 @@ impl Show for RawNode { .unwrap_or(Color::BLACK) .into(); - let mut content = if let Some(content) = styles.show(self, ctx, [ + let args = [ Value::Str(self.text.clone()), match lang { Some(lang) => Value::Str(lang.clone()), None => Value::None, }, Value::Bool(self.block), - ])? { + ]; + + let mut content = if let Some(content) = styles.show::<Self, _>(ctx, args)? { content } else if matches!( lang.map(|s| s.to_lowercase()).as_deref(), @@ -93,8 +97,8 @@ impl Show for RawNode { }; let mut map = StyleMap::new(); - if let Smart::Custom(family) = styles.get_cloned(Self::FAMILY) { - map.set_family(family, styles); + if let Smart::Custom(family) = styles.get(Self::FAMILY) { + map.set_family(family.clone(), styles); } content = content.styled_with_map(map); @@ -118,11 +122,11 @@ fn styled(piece: &str, foreground: Paint, style: Style) -> Content { } if style.font_style.contains(FontStyle::BOLD) { - styles.set(TextNode::STRONG, true); + styles.set(TextNode::STRONG, Toggle); } if style.font_style.contains(FontStyle::ITALIC) { - styles.set(TextNode::EMPH, true); + styles.set(TextNode::EMPH, Toggle); } if style.font_style.contains(FontStyle::UNDERLINE) { diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs index 6087032f..66936792 100644 --- a/src/library/text/shaping.rs +++ b/src/library/text/shaping.rs @@ -77,7 +77,7 @@ impl<'a> ShapedText<'a> { for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) { let pos = Point::new(offset, self.baseline); - let size = self.styles.get(TextNode::SIZE).abs; + let size = self.styles.get(TextNode::SIZE); let fill = self.styles.get(TextNode::FILL); let glyphs = group .iter() @@ -99,7 +99,7 @@ impl<'a> ShapedText<'a> { let width = text.width(); // Apply line decorations. - for deco in self.styles.get_cloned(TextNode::LINES) { + for deco in self.styles.get(TextNode::DECO) { decorate(&mut frame, &deco, fonts, &text, pos, width); } @@ -108,7 +108,7 @@ impl<'a> ShapedText<'a> { } // Apply link if it exists. - if let Some(url) = self.styles.get_ref(TextNode::LINK) { + if let Some(url) = self.styles.get(TextNode::LINK) { frame.link(url); } @@ -127,7 +127,7 @@ impl<'a> ShapedText<'a> { .filter(|g| g.is_space()) .map(|g| g.x_advance) .sum::<Em>() - .resolve(self.styles.get(TextNode::SIZE).abs) + .resolve(self.styles.get(TextNode::SIZE)) } /// Reshape a range of the shaped text, reusing information from this @@ -154,7 +154,7 @@ impl<'a> ShapedText<'a> { /// Push a hyphen to end of the text. pub fn push_hyphen(&mut self, fonts: &mut FontStore) { - let size = self.styles.get(TextNode::SIZE).abs; + let size = self.styles.get(TextNode::SIZE); let variant = variant(self.styles); families(self.styles).find_map(|family| { let face_id = fonts.select(family, variant)?; @@ -467,7 +467,7 @@ fn measure( let mut top = Length::zero(); let mut bottom = Length::zero(); - let size = styles.get(TextNode::SIZE).abs; + let size = styles.get(TextNode::SIZE); let top_edge = styles.get(TextNode::TOP_EDGE); let bottom_edge = styles.get(TextNode::BOTTOM_EDGE); @@ -537,7 +537,7 @@ fn families(styles: StyleChain) -> impl Iterator<Item = &str> + Clone { let tail = if styles.get(TextNode::FALLBACK) { FALLBACKS } else { &[] }; styles - .get_ref(TextNode::FAMILY) + .get(TextNode::FAMILY) .iter() .map(|family| family.as_str()) .chain(tail.iter().copied()) @@ -609,7 +609,7 @@ fn tags(styles: StyleChain) -> Vec<Feature> { feat(b"frac", 1); } - for &(tag, value) in styles.get_ref(TextNode::FEATURES).iter() { + for (tag, value) in styles.get(TextNode::FEATURES) { tags.push(Feature::new(tag, value, ..)) } |
