use super::ir::{Bbox, PlacedNode};
use super::{anchors, primitives, text};
use crate::error::Error;
use crate::resolve::{NodeKind, ResolvedInst, ResolvedValue};
use crate::span::Span;
pub(super) fn apply_max_width(
inst: &ResolvedInst,
children: &mut [PlacedNode],
own: f64,
span: Span,
) -> Result<(), Error> {
let Some(max_w) = inst.attrs.number("max-width") else {
return Ok(());
};
if let Some(w) = inst.attrs.number("width")
&& w > max_w
{
return Err(Error::at(
span,
format!("'width: {w}' exceeds 'max-width: {max_w}'"),
));
}
let pad = primitives::padding(&inst.attrs, span)?;
let stroke = inst.attrs.number("stroke-width").unwrap_or(0.0);
let avail = (max_w * own) - pad.left - pad.right - stroke;
let nowrap = matches!(
inst.attrs.get("text-wrap"),
Some(ResolvedValue::Ident(s)) if s == "nowrap"
);
for child in children.iter_mut() {
if anchors::is_pinned(&child.attrs) {
continue;
}
if child.bbox.w() <= avail + 1e-9 {
continue;
}
if child.kind != NodeKind::Text {
return Err(Error::at(
child.span,
format!("a child is wider than 'max-width: {max_w}' — only text wraps"),
));
}
if nowrap {
return Err(Error::at(
child.span,
format!(
"text cannot fit 'max-width: {max_w}' without wrapping — widen it or drop 'text-wrap: nowrap'"
),
));
}
let Some(label) = child.label.clone() else {
continue;
};
let font = crate::font::Font::of(&child.attrs);
let size = child.attrs.number("font-size").unwrap_or(15.0);
let ls = child.attrs.number("letter-spacing").unwrap_or(0.0);
let lsp = child.attrs.number("line-spacing").unwrap_or(0.0);
let wrapped = text::wrap(&label, font, size, ls, avail).join("\n");
child.bbox = Bbox::centered(
text::approx_width(&wrapped, font, size, ls),
text::approx_height(&wrapped, size, lsp),
);
child.label = Some(wrapped);
}
Ok(())
}