mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-30 21:47:00 -06:00
[II5] A paragraph with its own numbering start begins a new run at that number
Layout added the paragraph's start to the running count, so start k set paragraph by paragraph read 1, 3, 5. A change of start_at between neighbouring numbered paragraphs now restarts the count, as PowerPoint's Start at does; the same start on every paragraph still counts on. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
f893f6af7d
commit
afe4c324a7
@@ -179,7 +179,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Numbering",
|
||||
["Home", "Paragraph"],
|
||||
None,
|
||||
"{on?: bool, scheme?: arabicPeriod|arabicParenR|romanUcPeriod|romanLcPeriod|alphaUcPeriod|alphaLcParenR|alphaLcPeriod, start?: n, color?, size?: % of text}",
|
||||
"{on?: bool, scheme?: arabicPeriod|arabicParenR|romanUcPeriod|romanLcPeriod|alphaUcPeriod|alphaLcParenR|alphaLcPeriod, start?: n (a numbered paragraph whose start differs from that of the last numbered paragraph at its level begins a new run of numbers at n, as PowerPoint's Start at does), color?, size?: % of text}",
|
||||
has_text_or_shapes,
|
||||
numbering
|
||||
),
|
||||
|
||||
+42
-20
@@ -132,38 +132,60 @@ impl BoxGeom {
|
||||
}
|
||||
}
|
||||
|
||||
/// Number shown for a paragraph at list level `lvl`, advancing the per-level counters.
|
||||
fn next_number(numbering: &mut [u32], lvl: usize, auto_num: bool, empty: bool) -> Option<u32> {
|
||||
if auto_num && !empty {
|
||||
if let Some(n) = numbering.get_mut(lvl) {
|
||||
*n += 1;
|
||||
}
|
||||
for deeper in numbering.iter_mut().skip(lvl + 1) {
|
||||
*deeper = 0;
|
||||
}
|
||||
numbering.get(lvl).copied()
|
||||
} else if auto_num {
|
||||
// Preview the next number without consuming it: empty paragraphs do not advance
|
||||
// numbering in the saved document or affect later populated paragraphs.
|
||||
numbering.get(lvl).map(|n| n.saturating_add(1))
|
||||
} else {
|
||||
if !empty {
|
||||
for n in numbering.iter_mut().skip(lvl) {
|
||||
/// Number shown for a paragraph at list level `lvl`, advancing the per-level counters. `start_at`
|
||||
/// is the paragraph's numbering start (`None`: not numbered); `run_start` holds the start each
|
||||
/// level's current run of numbers began with, and a paragraph that changes it starts a new run
|
||||
/// (PowerPoint's "Start at").
|
||||
fn next_number(numbering: &mut [u32], run_start: &mut [u32], lvl: usize, start_at: Option<u32>, empty: bool) -> Option<u32> {
|
||||
match start_at {
|
||||
Some(start) if !empty => {
|
||||
if let (Some(rs), Some(n)) = (run_start.get_mut(lvl), numbering.get_mut(lvl))
|
||||
&& *rs != start
|
||||
{
|
||||
*rs = start;
|
||||
*n = 0;
|
||||
}
|
||||
if let Some(n) = numbering.get_mut(lvl) {
|
||||
*n += 1;
|
||||
}
|
||||
for deeper in numbering.iter_mut().skip(lvl + 1) {
|
||||
*deeper = 0;
|
||||
}
|
||||
numbering.get(lvl).copied()
|
||||
}
|
||||
// Preview the next number without consuming it: empty paragraphs do not advance
|
||||
// numbering in the saved document or affect later populated paragraphs. A paragraph
|
||||
// with its own start would begin a new run, so it previews that run's first number.
|
||||
Some(start) => {
|
||||
if run_start.get(lvl).is_some_and(|rs| *rs != start) {
|
||||
Some(1)
|
||||
} else {
|
||||
numbering.get(lvl).map(|n| n.saturating_add(1))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !empty {
|
||||
for n in numbering.iter_mut().skip(lvl) {
|
||||
*n = 0;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn shape_paragraphs(ctx: &Ctx, shape: &Shape, body: &TextBody, opts: &Opts, scale: f64, upright: Upright) -> Vec<ParaShaped> {
|
||||
let mut numbering: Vec<u32> = vec![0; 9];
|
||||
let mut run_start: Vec<u32> = vec![0; 9];
|
||||
let mut shaped_paras: Vec<ParaShaped> = Vec::with_capacity(body.paragraphs.len());
|
||||
for (pi, para) in body.paragraphs.iter().enumerate() {
|
||||
let lvl = para.level.min(8) as usize;
|
||||
let pp = resolve::para(ctx, shape, para);
|
||||
let auto_num = matches!(pp.bullet, Some(Bullet::AutoNum { .. }));
|
||||
let number = next_number(&mut numbering, lvl, auto_num, para.is_empty());
|
||||
let start_at = match &pp.bullet {
|
||||
Some(Bullet::AutoNum { start_at, .. }) => Some(*start_at),
|
||||
_ => None,
|
||||
};
|
||||
let number = next_number(&mut numbering, &mut run_start, lvl, start_at, para.is_empty());
|
||||
shaped_paras.push(shape_para(ctx, shape, body, pi, scale, opts, number, upright));
|
||||
}
|
||||
shaped_paras
|
||||
|
||||
@@ -1845,3 +1845,28 @@ fn an_empty_paragraph_keeps_its_paragraph_mark_height() {
|
||||
let h = l.lines[1].bottom - l.lines[1].top;
|
||||
assert!((h - h10).abs() < 0.5, "a 10pt empty paragraph should give {h10}, got {h}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_paragraph_with_its_own_start_at_restarts_the_count_there() {
|
||||
// PowerPoint's "Start at" on a paragraph begins a new run of numbers at that value, so setting
|
||||
// start k on paragraph k reads 1, 2, 3, not 1, 3, 5.
|
||||
// The bullets' glyph ids next to those of the expected labels, as pairs (drawn, expected).
|
||||
let check = |starts: &[u32], expect: &[u32]| {
|
||||
let (p, mut sh) = setup("a\nb\nc\nd", 600.0, 300.0);
|
||||
for (para, st) in sh.text.as_mut().unwrap().paragraphs.iter_mut().zip(starts) {
|
||||
para.props.bullet = Some(Bullet::AutoNum { scheme: "arabicPeriod".into(), start_at: *st });
|
||||
}
|
||||
let l = lay(&p, &sh, 600.0, 300.0);
|
||||
let bullets: Vec<_> = l.runs.iter().filter(|r| r.cells.is_empty()).collect();
|
||||
assert_eq!(bullets.len(), expect.len());
|
||||
for (b, n) in bullets.iter().zip(expect) {
|
||||
let want = measure_str(&b.face, b.size, &format!("{n}.")).1.iter().map(|g| g.0).collect::<Vec<_>>();
|
||||
assert_eq!(b.glyphs.iter().map(|g| g.0).collect::<Vec<_>>(), want, "expected {n}. for starts {starts:?}");
|
||||
}
|
||||
};
|
||||
check(&[1, 2, 3, 4], &[1, 2, 3, 4]);
|
||||
// The same start on every paragraph still counts on from it (a whole box numbered at once).
|
||||
check(&[3, 3, 3, 3], &[3, 4, 5, 6]);
|
||||
// A later paragraph with a new start begins a fresh run.
|
||||
check(&[1, 1, 7, 7], &[1, 2, 7, 8]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user