Merge pull request #649 from MarekSpodBiedry:issue-479

This commit is contained in:
Brandon Thomas 2026-10-13 00:48:39 -04:00
commit 181915ee57
2 changed files with 70 additions and 10 deletions

View File

@ -1027,6 +1027,21 @@ fn empty_for_overflow(sh: &Sheet, c: CellRef, merges: &[RangeRef]) -> bool {
sh.value_ref(c).is_none_or(|v| v.is_empty()) && !merges.iter().any(|m| m.contains(c))
}
/// Unscaled width of the empty Center Across Selection cells right of `c`, which its text is centred over too.
fn center_across_width(styles: &StyleTable, sh: &Sheet, c: CellRef, merges: &[RangeRef]) -> f32 {
let mut ext = 0.0;
let mut col = c.col + 1;
while col < MAX_COLS && ext < 20_000.0 {
let nc = CellRef::new(c.row, col);
if !empty_for_overflow(sh, nc, merges) || styles.get(sh.style_id(nc)).align.h != HAlign::CenterAcross {
break;
}
ext += sh.col_width(col);
col += 1;
}
ext
}
fn draw_text(pg: &mut Page, b: &Block, c: CellRef, l: &Look, rect: (f32, f32, f32, f32), merged: bool, merges: &[RangeRef], limits: (f32, f32)) {
let (x, y, w, h) = rect;
let sh = b.sh;
@ -1110,8 +1125,11 @@ fn draw_text(pg: &mut Page, b: &Block, c: CellRef, l: &Look, rect: (f32, f32, f3
VAlign::Bottom => y + h - pad - size * 0.22 - (n - 1.0) * line_h,
};
// Overflow into empty neighbours (single-line text, not merged).
let (mut clip_l, mut clip_r) = (x, x + w);
if !wrap && !merged && !is_num && rot.abs() != 90 && tw + 2.0 * pad + indent > w {
// Center Across Selection spans the empty Center Across cells to its right; the text stays within that run.
let spans = st.align.h == HAlign::CenterAcross && !wrap && !merged && rot == 0;
let across = if spans { center_across_width(&wb.styles, sh, c, merges) * b.k } else { 0.0 };
let (mut clip_l, mut clip_r) = (x, x + w + across);
if !wrap && !merged && !is_num && rot.abs() != 90 && across <= 0.0 && tw + 2.0 * pad + indent > w {
let need = tw + 2.0 * pad + indent - w;
let grow = |dir: i64, mut need: f32| -> f32 {
let mut ext = 0.0;
@ -1183,7 +1201,7 @@ fn draw_text(pg: &mut Page, b: &Block, c: CellRef, l: &Look, rect: (f32, f32, f3
let lw = gridcraft_pdf::text_width(font, size, line);
let lx = match h_align {
HAlign::Right => x + w - pad - lw - indent,
HAlign::Center => x + (w - lw) / 2.0,
HAlign::Center => x + (w + across - lw) / 2.0,
_ => x + pad + indent,
};
let ly = first_baseline + i as f32 * line_h;
@ -2088,6 +2106,23 @@ mod tests {
assert!(w.len() > 2);
}
#[test]
fn center_across_spans_following_empty_center_across_cells() {
let mut styles = StyleTable::default();
let across = styles.intern(Style { align: Alignment { h: HAlign::CenterAcross, ..Alignment::default() }, ..Style::default() });
let mut sh = Sheet::new("S");
for col in 0..3 {
sh.set_style(CellRef::new(0, col), across);
}
sh.set_value(CellRef::new(0, 0), Value::Text("Title".into()));
let a1 = CellRef::new(0, 0);
assert_eq!(center_across_width(&styles, &sh, a1, &[]), sh.col_width(1) + sh.col_width(2));
sh.set_value(CellRef::new(0, 2), Value::Text("x".into()));
assert_eq!(center_across_width(&styles, &sh, a1, &[]), sh.col_width(1));
let merged = [RangeRef::new(CellRef::new(0, 1), CellRef::new(1, 1))];
assert_eq!(center_across_width(&styles, &sh, a1, &merged), 0.0);
}
#[test]
fn rotated_wrapped_text_prints_rotated() {
// Upright headers over narrow columns (mark sheets) combine rotation with Wrap Text.

View File

@ -373,6 +373,15 @@ fn numfmt_color(c: gridcraft_engine::numfmt::FormatColor) -> Color32 {
}
}
/// Last column of a Center Across Selection run starting at `start`: it extends over following columns (up to `limit`) while `continues` holds.
fn center_across_end(start: u32, limit: u32, continues: impl Fn(u32) -> bool) -> u32 {
let mut end = start;
while end < limit && end + 1 < MAX_COLS && continues(end + 1) {
end += 1;
}
end
}
fn look(wb: &Workbook, si: usize, sh: &Sheet, c: CellRef, cf: &mut CfCache) -> (Look, Option<CfLook>) {
let mut style = wb.styles.get(sh.style_id(c)).clone();
if let Some(t) = sh.table_at(c) {
@ -1047,17 +1056,25 @@ pub(crate) fn paint_quadrant(
galley = p.layout_no_wrap(s, font.clone(), color);
w = galley.size().x;
}
// Center Across Selection: the text is centred over this cell and the empty cells to its right that also use it.
let across_end = if halign == HAlign::CenterAcross && !rotated && in_merge(c).is_none() {
center_across_end(c.col, c1 + 20, |col| {
let n = CellRef::new(c.row, col);
empty_ok(n) && wb.styles.get(sh.style_id(n)).align.h == HAlign::CenterAcross
})
} else {
c.col
};
// Overflow into empty neighbours for text (not upright text, whose width is its line height).
let mut clip = *rect;
if filter_space == 0.0
&& !numeric
&& !matches!(v, Value::Number(_))
&& st.align.rotation.abs() != 90
&& w > rect.width() - 2.0 * pad
&& in_merge(c).is_none()
let overflows = !numeric && !matches!(v, Value::Number(_)) && st.align.rotation.abs() != 90 && w > rect.width() - 2.0 * pad;
if filter_space == 0.0 && (overflows || across_end > c.col) && in_merge(c).is_none()
{
let mut right = rect.right();
let mut left = rect.left();
if across_end > c.col {
right = geo.x(sh, across_end + 1);
}
if matches!(halign, HAlign::Left | HAlign::Center | HAlign::Justify) {
let mut cc = c.col + 1;
while right - rect.left() < w + 2.0 * pad && cc < MAX_COLS && empty_ok(CellRef::new(c.row, cc)) && cc <= c1 + 20 {
@ -1087,7 +1104,8 @@ pub(crate) fn paint_quadrant(
}
}
let x = match halign {
HAlign::Center | HAlign::CenterAcross | HAlign::Distributed => rect.center().x - w / 2.0,
HAlign::CenterAcross => clip.center().x - w / 2.0,
HAlign::Center | HAlign::Distributed => rect.center().x - w / 2.0,
HAlign::Right => rect.right() - pad - w - indent,
_ => text_left + pad + indent,
};
@ -3129,6 +3147,13 @@ mod selection_fill_tests {
mod tests {
use super::*;
#[test]
fn center_across_run_stops_at_first_non_continuing_column() {
assert_eq!(center_across_end(0, 20, |c| c <= 2), 2);
assert_eq!(center_across_end(0, 20, |_| false), 0);
assert_eq!(center_across_end(0, 1, |_| true), 1);
}
#[test]
fn filter_menu_uses_the_table_whose_header_was_clicked() {
use gridcraft_engine::model::Table;