From 658a6e42dc4bbdcf76ec7c9c1b585d67a68f877a Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 11 Nov 2026 21:33:24 -0800 Subject: [PATCH] Object styles: edits reach text frames anchored in stories, cells and notes style.object.edit updates the attributes and paragraph styles of text frames anchored in stories, table cells and notes, as IDML import places them, as it does for frames on spreads. --- crates/engine/src/cmd/style/object.rs | 18 +++++------------- crates/engine/src/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/crates/engine/src/cmd/style/object.rs b/crates/engine/src/cmd/style/object.rs index dc5f81643..8a156d654 100644 --- a/crates/engine/src/cmd/style/object.rs +++ b/crates/engine/src/cmd/style/object.rs @@ -422,7 +422,7 @@ pub(super) fn edit_object(s: &mut Session, p: &Value) -> Result { || corners.as_ref().is_some_and(|(o, _)| it.corners == o.named_to_path(&it.path)) || frame.as_ref().is_some_and(|(o, n)| it.text_frame().is_some_and(|t| t.options.clone().follow_general(o, n))) }; - let updated = d.update_items( + let updated = d.update_placed_items( |it| it.object_style == new.name && follows(it), |it| { if let Some((o, n)) = &effects @@ -458,18 +458,10 @@ pub(super) fn edit_object(s: &mut Session, p: &Value) -> Result { // is in the old one's; when the style set none before, all of it does. let mut restyled = 0; if new.paragraph_style.is_some() && (new.paragraph_style != old.paragraph_style || new.apply_next_style != old.apply_next_style) { - let mut stories = Vec::new(); - for sp in d.spreads.iter().chain(d.parents.iter()) { - for it in &sp.items { - it.walk(&mut |i| { - if i.object_style == new.name - && let Some(tf) = i.text_frame() - { - stories.push(tf.story); - } - }); - } - } + // Frames on spreads and parents, in groups, and anchored in stories, table cells and + // notes (as IDML import places them). + let mut stories: Vec<_> = + d.placed_items().into_iter().filter(|i| i.object_style == new.name).filter_map(|i| i.text_frame().map(|tf| tf.story)).collect(); stories.sort_unstable(); stories.dedup(); let styles = d.styles.clone(); diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index a871b70de..5824be21c 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -1002,6 +1002,31 @@ fn a_stroke_added_to_an_object_style_reaches_default_strokes() { assert_eq!(weight(&s, stroked_text), 1.0, "a text frame's own 1 pt stroke stays"); } +/// A text frame anchored in a story (as IDML import places one) takes a new paragraph style of +/// its object style like a frame on the page. +#[test] +fn an_object_style_edit_restyles_an_anchored_text_frame() { + use designcraft_doc::ItemId; + let mut s = session(); + s.execute("style.paragraph.create", &json!({"name": "Body"})).unwrap(); + s.execute("style.paragraph.create", &json!({"name": "Lead"})).unwrap(); + s.execute("style.object.create", &json!({"name": "Sidebar", "fromSelection": false, "paragraphStyle": "Body"})).unwrap(); + let r = s.execute("frame.create", &json!({"rect": [36, 36, 136, 86], "content": "text", "text": "Inner", "caret": false})).unwrap(); + let (inner, inner_story) = (ItemId(r["id"].as_u64().unwrap()), designcraft_doc::StoryId(r["story"].as_u64().unwrap())); + s.execute("style.object.apply", &json!({"name": "Sidebar", "ids": [inner.0]})).unwrap(); + let r = s.execute("frame.create", &json!({"rect": [36, 200, 400, 400], "content": "text", "text": "Before after.", "caret": false})).unwrap(); + let host = designcraft_doc::StoryId(r["story"].as_u64().unwrap()); + { + let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc); + let it = d.item(inner).unwrap().clone(); + std::sync::Arc::make_mut(&mut d.spreads[0]).items.retain(|i| i.id != inner); + d.story_mut(host).unwrap().insert_object(7, designcraft_doc::AnchoredObject::new(it, Default::default())); + } + let r = s.execute("style.object.edit", &json!({"name": "Sidebar", "paragraphStyle": "Lead"})).unwrap(); + assert_eq!(r["restyled"], 1, "{r}"); + assert_eq!(s.doc().unwrap().doc.story(inner_story).unwrap().paras[0].style, "Lead"); +} + #[test] fn object_style_effects_reach_grouped_and_anchored_items() { use designcraft_doc::ItemId;