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 <noreply@anthropic.com>
This commit is contained in:
moppeeerandClaude Opus 5.5 committed 2026-10-22 11:58:57 +02:00
1 parent 38443c0b86
commit 7cb5ec5829
3 files changed
+23 -3

No files matched your search

+9 -2
View File
@@ -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::<f64>().ok())))
});
if shapes.iter().all(Option::is_none) && sizes.iter().all(Option::is_none) {
return None;
}
+13
View File
@@ -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#"<ObjectStyle Self="ObjectStyle/Legacy" Name="Legacy" CornerOption="RoundedCorner" CornerRadius="9">
<Properties><BasedOn type="string">$ID/[None]</BasedOn></Properties>
</ObjectStyle>"#;
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:?}");
}
+1 -1
View File
@@ -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.