Make layout/master placeholder formatting reach slides

Fixes #1487

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilverandClaude Opus 5.5 committed 2026-10-30 23:17:36 +01:00
1 parent c4221b1a7c
commit 446bc528ad
2 files changed
+58

No files matched your search

+32
View File
@@ -31,8 +31,10 @@ pub(crate) fn format_run(s: &mut Session, f: &(dyn Fn(&mut RunProps) + Send + Sy
return Err(bad("format", SELECT_TEXT_OR_SHAPE));
}
let cells = st.selection.cells;
let on_master = editing_master(st);
edit_shapes(s, &json!({"ids": ids}), "format", |sh| {
let id = sh.id;
let inherited = on_master && sh.ph.is_some();
match &mut sh.kind {
ShapeKind::Table(t) => {
// Only the selected cells, when a range of them is selected.
@@ -48,6 +50,13 @@ pub(crate) fn format_run(s: &mut Session, f: &(dyn Fn(&mut RunProps) + Send + Sy
_ => {
let body = sh.text.get_or_insert_with(TextBody::default);
ed::format_all(body, f);
if inherited {
for l in 0..LIST_LEVELS {
let mut lvl = body.list_style.level(l).cloned().unwrap_or_default();
f(&mut lvl.run);
body.list_style.set(l, lvl);
}
}
}
}
Ok(())
@@ -58,6 +67,15 @@ pub(crate) fn format_run(s: &mut Session, f: &(dyn Fn(&mut RunProps) + Send + Sy
ok()
}
/// List-style levels (`lvl1pPr`–`lvl9pPr`) a placeholder passes on to the slides that use it.
const LIST_LEVELS: u8 = 9;
/// Formatting a placeholder on a layout or master (Slide Master view) also sets its list style,
/// which is what the placeholders on slides inherit; its own runs may be few or none.
fn editing_master(st: &DocState) -> bool {
matches!(st.selection.target, crate::Target::Layout { .. } | crate::Target::Master { .. })
}
/// What `format_run` formats while editing: the selection, or for a bare caret the word it's in
/// (without its trailing space). Empty when the caret is at a word boundary. Insert Hyperlink
/// edits this same span, so the dialog reads it too.
@@ -128,8 +146,10 @@ pub(crate) fn format_para(s: &mut Session, f: &(dyn Fn(&mut deckcraft_model::tex
return Err(bad("format", SELECT_TEXT_OR_SHAPE));
}
let cells = st.selection.cells;
let on_master = editing_master(st);
edit_shapes(s, &json!({"ids": ids}), "format", |sh| {
let id = sh.id;
let inherited = on_master && sh.ph.is_some();
match &mut sh.kind {
ShapeKind::Table(t) => {
let range = crate::cmd::table::selected_range(cells, id, t);
@@ -147,6 +167,18 @@ pub(crate) fn format_para(s: &mut Session, f: &(dyn Fn(&mut deckcraft_model::tex
body.paragraphs.push(Default::default());
}
body.paragraphs.iter_mut().for_each(f);
if inherited {
for l in 0..LIST_LEVELS {
let mut lvl = body.list_style.level(l).cloned().unwrap_or_default();
let mut p = deckcraft_model::text::Paragraph { level: l, props: lvl.para, ..Default::default() };
f(&mut p);
// A level change (indent/outdent) means nothing for a list-style level.
if p.level == l {
lvl.para = p.props;
body.list_style.set(l, lvl);
}
}
}
}
}
Ok(())
+26
View File
@@ -5956,3 +5956,29 @@ fn invalid_slide_index_never_falls_back_to_the_selection_in_export_commands() {
}
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn master_layout_placeholder_format_inherits() {
let mut s = Session::new();
sample::open_sample(&mut s).unwrap();
let title_of = |shapes: &[model::Shape]| shapes.iter().find(|sh| sh.ph.as_ref().is_some_and(|p| p.kind.is_title())).map(|sh| sh.id.0).unwrap();
let existing = title_of(&s.doc().unwrap().doc.slides[0].shapes);
s.execute("view.slideMaster", &json!({"master": 0, "layout": 0})).unwrap();
let layout_title = title_of(&s.doc().unwrap().doc.masters[0].layouts[0].shapes);
s.execute("edit.select", &json!({"ids": [layout_title]})).unwrap();
s.execute("format.font", &json!({"family": "Montserrat"})).unwrap();
s.execute("format.align", &json!({"align": "right"})).unwrap();
let st = s.execute("format.state", &json!({})).unwrap();
assert_eq!(st["fontRef"], "Montserrat", "layout placeholder: {st}");
s.execute("view.closeMaster", &json!({})).unwrap();
s.execute("slide.new", &json!({"layout": "title"})).unwrap();
let slide = s.doc().unwrap().selection.slide;
let fresh = title_of(&s.doc().unwrap().doc.slides[slide].shapes);
for (index, id) in [(slide, fresh), (0, existing)] {
s.execute("slide.go", &json!({"index": index})).unwrap();
s.execute("edit.select", &json!({"ids": [id]})).unwrap();
let st = s.execute("format.state", &json!({})).unwrap();
assert_eq!(st["fontRef"], "Montserrat", "slide placeholder {id}: {st}");
assert_eq!(st["align"], "r", "slide placeholder {id}: {st}");
}
}