use typst_library::foundations::StyleChain;
use typst_library::layout::{Abs, Fragment, Frame, FrameItem, HideElem, Point, Sides};
use typst_library::model::{Destination, LinkElem, ParElem};
#[derive(Debug, Clone)]
pub struct FrameModifiers {
dest: Option<Destination>,
hidden: bool,
}
impl FrameModifiers {
pub fn get_in(styles: StyleChain) -> Self {
Self {
dest: styles.get_cloned(LinkElem::current),
hidden: styles.get(HideElem::hidden),
}
}
}
pub trait FrameModify {
fn modify(&mut self, modifiers: &FrameModifiers);
fn modified(mut self, modifiers: &FrameModifiers) -> Self
where
Self: Sized,
{
self.modify(modifiers);
self
}
}
impl FrameModify for Frame {
fn modify(&mut self, modifiers: &FrameModifiers) {
modify_frame(self, modifiers, None);
}
}
impl FrameModify for Fragment {
fn modify(&mut self, modifiers: &FrameModifiers) {
for frame in self.iter_mut() {
frame.modify(modifiers);
}
}
}
impl<T, E> FrameModify for Result<T, E>
where
T: FrameModify,
{
fn modify(&mut self, props: &FrameModifiers) {
if let Ok(inner) = self {
inner.modify(props);
}
}
}
pub trait FrameModifyText {
fn modify_text(&mut self, styles: StyleChain);
}
impl FrameModifyText for Frame {
fn modify_text(&mut self, styles: StyleChain) {
let modifiers = FrameModifiers::get_in(styles);
let expand_y = 0.5 * styles.resolve(ParElem::leading);
let outset = Sides::new(Abs::zero(), expand_y, Abs::zero(), expand_y);
modify_frame(self, &modifiers, Some(outset));
}
}
fn modify_frame(
frame: &mut Frame,
modifiers: &FrameModifiers,
link_box_outset: Option<Sides<Abs>>,
) {
if let Some(dest) = &modifiers.dest {
let mut pos = Point::zero();
let mut size = frame.size();
if let Some(outset) = link_box_outset {
pos.y -= outset.top;
pos.x -= outset.left;
size += outset.sum_by_axis();
}
frame.push(pos, FrameItem::Link(dest.clone(), size));
}
if modifiers.hidden {
frame.hide();
}
}
pub fn layout_and_modify<F, R>(styles: StyleChain, layout: F) -> R
where
F: FnOnce(StyleChain) -> R,
R: FrameModify,
{
let modifiers = FrameModifiers::get_in(styles);
let reset;
let outer = styles;
let mut styles = styles;
if modifiers.dest.is_some() {
reset = LinkElem::current.set(None).wrap();
styles = outer.chain(&reset);
}
layout(styles).modified(&modifiers)
}