summaryrefslogtreecommitdiff
path: root/src/library/text
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/text')
-rw-r--r--src/library/text/deco.rs10
-rw-r--r--src/library/text/link.rs13
-rw-r--r--src/library/text/mod.rs162
-rw-r--r--src/library/text/par.rs9
-rw-r--r--src/library/text/raw.rs20
-rw-r--r--src/library/text/shaping.rs16
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, ..))
}