From 7cb5ec5829c25c466c245224ef198eba2a8ee437 Mon Sep 17 00:00:00 2001 From: moppeeer Date: Thu, 22 Oct 2026 11:58:57 +0200 Subject: [PATCH] IDML object styles: legacy all-corner attributes; parity notes the Object Style Options dialog An object style that gives its corners only with `CornerOption` and `CornerRadius` keeps them; at each level of the Based On chain a per-corner attribute wins over those, as for page items. The parity assessment no longer says there is no Object Style Options dialog. Co-Authored-By: Claude Opus 5.5 --- crates/idml/src/import.rs | 11 +++++++++-- crates/idml/src/tests/object_styles.rs | 13 +++++++++++++ docs/target-app-parity.md | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/idml/src/import.rs b/crates/idml/src/import.rs index a8630010e..74067df60 100644 --- a/crates/idml/src/import.rs +++ b/crates/idml/src/import.rs @@ -481,8 +481,15 @@ fn corners(e: &El, prefix: &str) -> Option<[Corner; 4]> { /// shape and size from the first level that gives it. fn corners_in(levels: &[&El], prefix: &str) -> Option<[Corner; 4]> { let keys = ["TopLeft", "TopRight", "BottomRight", "BottomLeft"]; - let shapes = keys.map(|n| levels.iter().find_map(|e| e.prop(&format!("{prefix}{n}CornerOption")))); - let sizes = keys.map(|n| levels.iter().find_map(|e| e.num(&format!("{prefix}{n}CornerRadius")))); + // An object style (no prefix) may give all four with the legacy `CornerOption` / + // `CornerRadius`; at each level a per-corner attribute wins over it, as for page items. + let legacy = |e: &El, what: &str| if prefix.is_empty() { e.prop(&format!("Corner{what}")) } else { None }; + let shapes = keys.map(|n| levels.iter().find_map(|e| e.prop(&format!("{prefix}{n}CornerOption")).or_else(|| legacy(e, "Option")))); + let sizes = keys.map(|n| { + levels + .iter() + .find_map(|e| e.num(&format!("{prefix}{n}CornerRadius")).or_else(|| legacy(e, "Radius").and_then(|v| v.trim().parse::().ok()))) + }); if shapes.iter().all(Option::is_none) && sizes.iter().all(Option::is_none) { return None; } diff --git a/crates/idml/src/tests/object_styles.rs b/crates/idml/src/tests/object_styles.rs index 41624f855..faaef4f32 100644 --- a/crates/idml/src/tests/object_styles.rs +++ b/crates/idml/src/tests/object_styles.rs @@ -276,3 +276,16 @@ fn a_style_setting_one_corner_inherits_the_others_and_names_them() { style.apply_to(&mut it); assert_eq!(it.corners.corners.map(|c| c.shape), [CornerShape::Rounded, CornerShape::Rounded, CornerShape::Rounded, CornerShape::Bevel]); } + +/// An object style giving its corners only with the legacy all-corners `CornerOption` and +/// `CornerRadius` keeps them, so applying it to another frame rounds it. +#[test] +fn a_style_with_legacy_all_corner_attributes_keeps_its_corners() { + let legacy = r#" + $ID/[None] + "#; + let d = fixture(legacy, &[]); + let style = d.styles.object.iter().find(|s| s.name == "Legacy").unwrap(); + let c = style.corners.expect("the legacy corners").corners; + assert!(c.iter().all(|c| c.shape == CornerShape::Rounded && c.size == 9.0), "{c:?}"); +} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index c7e0678d7..8cf0029c5 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -412,7 +412,7 @@ It is evidence of breadth: each item exists. It is not a claim of InDesign-level - Nested styles (through/up to N sentences, words, characters, letters, digits, tabs, breaks, spaces or given characters) and GREP styles (regex → character style), edited in Paragraph Style Options and composed under local formatting; `ui.dialog.open` for agents. Nested line styles are still to come; nested and GREP styles round-trip through IDML. - Table and cell styles (round-trip through IDML): cell styles (fill, insets, vertical justification, strokes, paragraph style) and table styles (region cell styles, border, alternating rows, spacing); apply from the Table panel, edits re-apply to every user; `style.cell.*`, `style.table.*`. - Style groups: paragraph and character styles shown in group folders, Move to Group (new, existing, none); `style.group` renames every use (styles, stories, table cells, footnotes, object styles). -- Object styles carry effects: `style.object.create` keeps the selection's effects, `style.object.apply` sets them, and `style.object.edit {name, dropShadow?, feather?, …, effects?: null}` changes them on the style and on every item whose effects still match it (items with their own effects keep them; `style.object.clearOverrides` returns them to the style). New frames take their default style's effects. Effect values that aren't finite numbers are refused and the others clamped, in these commands and in the `object.*` effect commands. IDML carries only the drop shadow and feather, for ObjectStyles (read per effects category flag, settings from the BasedOn chain) as for items; the other effects stay in `.designcraft` files. There is no Object Style Options dialog yet. +- Object styles carry effects: `style.object.create` keeps the selection's effects, `style.object.apply` sets them, and `style.object.edit {name, dropShadow?, feather?, …, effects?: null}` changes them on the style and on every item whose effects still match it (items with their own effects keep them; `style.object.clearOverrides` returns them to the style). New frames take their default style's effects. Effect values that aren't finite numbers are refused and the others clamped, in these commands and in the `object.*` effect commands. IDML carries only the drop shadow and feather, for ObjectStyles (read per effects category flag, settings from the BasedOn chain) as for items; the other effects stay in `.designcraft` files. Object Style Options is a dialog (from the Object Styles panel), with General, Fill, Stroke, Stroke & Corner Options, Paragraph Styles (with Apply Next Style) and Text Frame General Options pages. - Scrubby zoom: drag the Zoom tool left or right to zoom continuously around the press point. - Spring-loaded Zoom: hold Cmd+Space (Ctrl+Space on Windows/Linux) for the Zoom cursor and click/scrubby zoom; Alt-click zooms out. Release returns to the previous tool without changing the document or undo history. Text-frame and text-field typing suppress the shortcut; Space remains Hand. - Guides belong to the active layer: hidden with it (or with its Show Guides off) and locked with it.