summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/shared
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /crates/typst-library/src/shared
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'crates/typst-library/src/shared')
-rw-r--r--crates/typst-library/src/shared/behave.rs109
-rw-r--r--crates/typst-library/src/shared/ext.rs92
-rw-r--r--crates/typst-library/src/shared/mod.rs7
3 files changed, 208 insertions, 0 deletions
diff --git a/crates/typst-library/src/shared/behave.rs b/crates/typst-library/src/shared/behave.rs
new file mode 100644
index 00000000..6a1aa127
--- /dev/null
+++ b/crates/typst-library/src/shared/behave.rs
@@ -0,0 +1,109 @@
+//! Element interaction.
+
+use typst::model::{Behave, Behaviour, Content, StyleChain, StyleVec, StyleVecBuilder};
+
+/// A wrapper around a [`StyleVecBuilder`] that allows elements to interact.
+#[derive(Debug)]
+pub struct BehavedBuilder<'a> {
+ /// The internal builder.
+ builder: StyleVecBuilder<'a, Content>,
+ /// Staged weak and ignorant elements that we can't yet commit to the
+ /// builder. The option is `Some(_)` for weak elements and `None` for
+ /// ignorant elements.
+ staged: Vec<(Content, Behaviour, StyleChain<'a>)>,
+ /// What the last non-ignorant item was.
+ last: Behaviour,
+}
+
+impl<'a> BehavedBuilder<'a> {
+ /// Create a new style-vec builder.
+ pub fn new() -> Self {
+ Self {
+ builder: StyleVecBuilder::new(),
+ staged: vec![],
+ last: Behaviour::Destructive,
+ }
+ }
+
+ /// Whether the builder is totally empty.
+ pub fn is_empty(&self) -> bool {
+ self.builder.is_empty() && self.staged.is_empty()
+ }
+
+ /// Whether the builder is empty except for some weak elements that will
+ /// probably collapse.
+ pub fn is_basically_empty(&self) -> bool {
+ self.builder.is_empty()
+ && self
+ .staged
+ .iter()
+ .all(|(_, behaviour, _)| matches!(behaviour, Behaviour::Weak(_)))
+ }
+
+ /// Push an item into the sequence.
+ pub fn push(&mut self, elem: Content, styles: StyleChain<'a>) {
+ let interaction = elem
+ .with::<dyn Behave>()
+ .map_or(Behaviour::Supportive, Behave::behaviour);
+
+ match interaction {
+ Behaviour::Weak(level) => {
+ if matches!(self.last, Behaviour::Weak(_)) {
+ let item = elem.with::<dyn Behave>().unwrap();
+ let i = self.staged.iter().position(|prev| {
+ let Behaviour::Weak(prev_level) = prev.1 else { return false };
+ level < prev_level
+ || (level == prev_level && item.larger(&prev.0))
+ });
+ let Some(i) = i else { return };
+ self.staged.remove(i);
+ }
+
+ if self.last != Behaviour::Destructive {
+ self.staged.push((elem, interaction, styles));
+ self.last = interaction;
+ }
+ }
+ Behaviour::Supportive => {
+ self.flush(true);
+ self.builder.push(elem, styles);
+ self.last = interaction;
+ }
+ Behaviour::Destructive => {
+ self.flush(false);
+ self.builder.push(elem, styles);
+ self.last = interaction;
+ }
+ Behaviour::Ignorant => {
+ self.staged.push((elem, interaction, styles));
+ }
+ }
+ }
+
+ /// Iterate over the contained elements.
+ pub fn elems(&self) -> impl DoubleEndedIterator<Item = &Content> {
+ self.builder.elems().chain(self.staged.iter().map(|(item, ..)| item))
+ }
+
+ /// Return the finish style vec and the common prefix chain.
+ pub fn finish(mut self) -> (StyleVec<Content>, StyleChain<'a>) {
+ self.flush(false);
+ self.builder.finish()
+ }
+
+ /// Push the staged elements, filtering out weak elements if `supportive` is
+ /// false.
+ fn flush(&mut self, supportive: bool) {
+ for (item, interaction, styles) in self.staged.drain(..) {
+ if supportive || interaction == Behaviour::Ignorant {
+ self.builder.push(item, styles);
+ }
+ }
+ }
+}
+
+impl<'a> Default for BehavedBuilder<'a> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/crates/typst-library/src/shared/ext.rs b/crates/typst-library/src/shared/ext.rs
new file mode 100644
index 00000000..d7c80a30
--- /dev/null
+++ b/crates/typst-library/src/shared/ext.rs
@@ -0,0 +1,92 @@
+//! Extension traits.
+
+use crate::layout::{AlignElem, MoveElem, PadElem};
+use crate::prelude::*;
+use crate::text::{EmphElem, FontFamily, FontList, StrongElem, TextElem, UnderlineElem};
+
+/// Additional methods on content.
+pub trait ContentExt {
+ /// Make this content strong.
+ fn strong(self) -> Self;
+
+ /// Make this content emphasized.
+ fn emph(self) -> Self;
+
+ /// Underline this content.
+ fn underlined(self) -> Self;
+
+ /// Link the content somewhere.
+ fn linked(self, dest: Destination) -> Self;
+
+ /// Make the content linkable by `.linked(Destination::Location(loc))`.
+ ///
+ /// Should be used in combination with [`Location::variant`].
+ fn backlinked(self, loc: Location) -> Self;
+
+ /// Set alignments for this content.
+ fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self;
+
+ /// Pad this content at the sides.
+ fn padded(self, padding: Sides<Rel<Length>>) -> Self;
+
+ /// Transform this content's contents without affecting layout.
+ fn moved(self, delta: Axes<Rel<Length>>) -> Self;
+}
+
+impl ContentExt for Content {
+ fn strong(self) -> Self {
+ StrongElem::new(self).pack()
+ }
+
+ fn emph(self) -> Self {
+ EmphElem::new(self).pack()
+ }
+
+ fn underlined(self) -> Self {
+ UnderlineElem::new(self).pack()
+ }
+
+ fn linked(self, dest: Destination) -> Self {
+ self.styled(MetaElem::set_data(vec![Meta::Link(dest)]))
+ }
+
+ fn backlinked(self, loc: Location) -> Self {
+ let mut backlink = Content::empty();
+ backlink.set_location(loc);
+ self.styled(MetaElem::set_data(vec![Meta::Elem(backlink)]))
+ }
+
+ fn aligned(self, aligns: Axes<Option<GenAlign>>) -> Self {
+ self.styled(AlignElem::set_alignment(aligns))
+ }
+
+ fn padded(self, padding: Sides<Rel<Length>>) -> Self {
+ PadElem::new(self)
+ .with_left(padding.left)
+ .with_top(padding.top)
+ .with_right(padding.right)
+ .with_bottom(padding.bottom)
+ .pack()
+ }
+
+ fn moved(self, delta: Axes<Rel<Length>>) -> Self {
+ MoveElem::new(self).with_dx(delta.x).with_dy(delta.y).pack()
+ }
+}
+
+/// Additional methods for style lists.
+pub trait StylesExt {
+ /// Set a font family composed of a preferred family and existing families
+ /// from a style chain.
+ fn set_family(&mut self, preferred: FontFamily, existing: StyleChain);
+}
+
+impl StylesExt for Styles {
+ fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) {
+ self.set(TextElem::set_font(FontList(
+ std::iter::once(preferred)
+ .chain(TextElem::font_in(existing))
+ .collect(),
+ )));
+ }
+}
diff --git a/crates/typst-library/src/shared/mod.rs b/crates/typst-library/src/shared/mod.rs
new file mode 100644
index 00000000..f54241cf
--- /dev/null
+++ b/crates/typst-library/src/shared/mod.rs
@@ -0,0 +1,7 @@
+//! Shared definitions for the standard library.
+
+mod behave;
+mod ext;
+
+pub use behave::*;
+pub use ext::*;