use parley::AlignmentOptions;
use super::run::{BlockLayout, RichTextRun};
use super::shape::{hal_to_alignment, shape_block_layout, slice_block};
use crate::style_vocab::HAlign;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct EdgeSpacing {
pub(crate) margin_px: f32,
pub(crate) barrier: bool,
pub(crate) padding_px: f32,
}
struct MarginAccumulator {
pending_pos: f32,
pending_neg: f32,
at_document_edge: bool,
}
impl MarginAccumulator {
fn at_document_top() -> Self {
Self {
pending_pos: 0.0,
pending_neg: 0.0,
at_document_edge: true,
}
}
fn fold(&mut self, margin_px: f32) {
if margin_px >= 0.0 {
self.pending_pos = self.pending_pos.max(margin_px);
} else {
self.pending_neg = self.pending_neg.min(margin_px);
}
}
fn flush(&mut self, y: &mut f32) {
if self.at_document_edge {
self.at_document_edge = false;
} else {
*y += self.pending_pos + self.pending_neg;
}
self.pending_pos = 0.0;
self.pending_neg = 0.0;
}
}
fn apply_top_chain(chain: &[EdgeSpacing], y: &mut f32, acc: &mut MarginAccumulator) {
for e in chain {
acc.fold(e.margin_px);
if e.barrier {
acc.flush(y);
*y += e.padding_px;
}
}
}
fn apply_bottom_chain(chain: &[EdgeSpacing], y: &mut f32, acc: &mut MarginAccumulator) {
for e in chain {
if e.barrier {
acc.flush(y);
*y += e.padding_px;
}
acc.fold(e.margin_px);
}
}
pub(crate) fn stack_blocks(blocks: &mut [BlockLayout]) -> f32 {
let mut y: f32 = 0.0;
let mut acc = MarginAccumulator::at_document_top();
for bl in blocks.iter_mut() {
apply_top_chain(&bl.top_chain, &mut y, &mut acc);
acc.flush(&mut y);
bl.y_px = y;
y += bl.height_px;
apply_bottom_chain(&bl.bottom_chain, &mut y, &mut acc);
}
y
}
impl RichTextRun {
pub fn set_max_width(&self, max_width_px: f32, alignment: HAlign) -> f32 {
if *self.last_break.borrow() == Some((max_width_px, alignment)) {
return *self.current_height_px.borrow();
}
let mut blocks = self.blocks.borrow_mut();
for bl in blocks.iter_mut() {
let block_avail = (max_width_px - bl.left_px - bl.right_inset_px).max(1.0);
let effective_align = bl
.alignment_override
.unwrap_or_else(|| hal_to_alignment(alignment, bl.is_rtl));
if bl.first_line_shift_px == bl.continuation_shift_px {
let target = (block_avail - bl.first_line_shift_px).max(1.0);
bl.layout.break_all_lines(Some(target));
bl.layout
.align(effective_align, AlignmentOptions::default());
bl.shape_width_px = target;
bl.height_px = bl.layout.height();
bl.continuation_layout = None;
bl.continuation_baseline_shifts.clear();
bl.continuation_inlines.clear();
bl.first_line_height_px = 0.0;
} else {
let usable_first = (block_avail - bl.first_line_shift_px).max(1.0);
let usable_cont = (block_avail - bl.continuation_shift_px).max(1.0);
let mut first_layout = shape_block_layout(
&bl.source_text,
&bl.source_inlines,
&self.base_style,
self.base_brush,
&self.palette,
self.dpi,
);
first_layout.break_all_lines(Some(usable_first));
first_layout.align(effective_align, AlignmentOptions::default());
let first_line_end = first_layout
.lines()
.next()
.map(|l| l.text_range().end)
.unwrap_or(bl.source_text.len());
let first_line_height = first_layout
.lines()
.next()
.map(|l| l.metrics().line_height)
.unwrap_or(0.0);
if first_line_end >= bl.source_text.len() {
bl.layout = first_layout;
bl.shape_width_px = usable_first;
bl.height_px = bl.layout.height();
bl.continuation_layout = None;
bl.continuation_baseline_shifts.clear();
bl.continuation_inlines.clear();
bl.first_line_height_px = 0.0;
} else {
let (rest_text, rest_inlines, rest_baselines) = slice_block(
&bl.source_text,
&bl.source_inlines,
&bl.source_baselines,
&(first_line_end..bl.source_text.len()),
);
let mut cont_layout = shape_block_layout(
&rest_text,
&rest_inlines,
&self.base_style,
self.base_brush,
&self.palette,
self.dpi,
);
cont_layout.break_all_lines(Some(usable_cont));
cont_layout.align(effective_align, AlignmentOptions::default());
let cont_height = cont_layout.height();
bl.layout = first_layout;
bl.continuation_layout = Some(cont_layout);
bl.continuation_baseline_shifts = rest_baselines;
bl.continuation_inlines = rest_inlines;
bl.first_line_height_px = first_line_height;
bl.shape_width_px = usable_cont;
bl.height_px = first_line_height + cont_height;
}
}
}
let total_height = stack_blocks(&mut blocks);
*self.current_height_px.borrow_mut() = total_height;
*self.last_break.borrow_mut() = Some((max_width_px, alignment));
*self.derived.borrow_mut() = None;
total_height
}
}