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;