With the Appearance panel's active row, the row belongs to the first
selected object, which need not paint first. effect.move reported where
the effect landed in the first target in paint order; it now reports
where it landed in the first selected object for the active row, and in
the first target for an explicit fromItem, as effect.apply does.
The reply's index came from the last object the effect was added to, while
its item came from the first selected object (or the first target for an
explicit item). When those objects held different numbers of effects on the
row, the reply named no effect of that object. The index is now read from
that object's effect list.
effect.apply now reports the item of the first selected object, whose row
the active item is. With nothing selected, an explicit aligned item returns
the command's error instead of an internal error.
The Appearance panel's active row is a row of the first selected object's
stack, but fill, stroke, gradient and effect edits matched it against the
object that paints first. When the selection was made top object first and
the objects' stacks differ, an edit changed another row: with the red fill
of [red, white, Stroke] active above a [white, Stroke] object, Fill set the
white row instead.
These edits now match the active row against the first selected object (or
the compound path holding it), as Add New Fill and Reduce to Basic
Appearance already do. Gradient edits also read whether the active row is a
fill or a stroke from that object.
The targets are in paint order, so the first selected object is not always the first target. The active row is now matched against the first selected object wherever it is among them.
Reduce to Basic Appearance kept the topmost visible fill and stroke even
when another fill or stroke row was selected in the Appearance panel.
It now keeps the selected row in place of the topmost one of its kind,
and in each other selected object the row equal to it. With ids, or
with no selected row, it keeps the topmost ones as before.
With a fill row selected in the Appearance panel, Add New Fill now
inserts a copy of that row directly above it and selects the new fill.
Rows already above the selected one stay where they are. Before, the
new fill was always a copy of the topmost fill, added at the top.
The selected row belongs to the first selected object. In a mixed
selection, an object that shares that row gets the copy above its own
equal row; an object that does not share it gets a copy of its top
fill on top, as before. With a stroke row selected, or with `ids` or
`target: "contents"`, Add New Fill works as before.
The panel drops rows that were Cmd- or Shift-clicked together when the
active row changes outside the panel, so after Add New Fill (button,
menu or shortcut) only the new fill is selected.
Review follow-ups:
- items_per_target maps each requested row on its own. Rows deleted or
duplicated together, one shared and one not, fell back to the literal
indices in every object, so the other objects could lose the wrong
items. The first object takes its own rows; each other object takes
its copies of the shared ones only.
- effect.dialog takes `aligned` (the panel passes it on a mixed
selection's shared effect row), and its OK and preview run
effect.setParams with it, so the dialog edits each object's own copy
of the effect instead of the first object's index in every object.
Tests: removing a shared stroke with an unshared fill leaves the other
object's fills; an aligned dialog's command carries `aligned`.
With several objects selected whose appearances differ in anything (two
circles whose fills differ but whose stroke and effect are the same),
the Appearance panel showed only "Mixed Appearances": no stroke, effect
or opacity row, so attributes the objects share couldn't be edited
together. It now lists what they share, as the issue describes:
- `vectorcraft_doc::appearance::SharedStack` (with `align`, a stacking-
order longest-common-subsequence match) gives the fills, strokes and
object-level effects every object has an equal copy of, and where each
is in every object's own stack.
- The panel's object row names the objects' common kind ("Path",
"Ellipse"; "Mixed Objects" only when kinds differ, as the Control bar
does), with an italic "Mixed Appearances" row under it when they
differ, then only the shared rows with their usual controls. The
Opacity row reads "Mixed" when opacity or blend mode differ (an edit
sets them all). The object row shows no fill chip when the fills
differ. Rows aren't dragged in a mixed selection: where a row would go
in each of several different stacks isn't one place.
- The engine maps a shared row to each object's own item: the active
item (a row of the first selected object's stack) always does, so the
Color, Stroke and Transparency panels edit every object's matching
item, and the item and effect commands do with `aligned: true`
(appearance.setItem / removeItem / duplicateItem, effect.remove /
duplicate / setParams / lightingPreset, transparency.set), each as
one undo step. moveItem and effect.move refuse aligned rows of
differing stacks. Without `aligned`, explicit indices keep their
meaning for single objects and scripts.
- "Mixed" is added to every catalog.
Tests: the shared stack of the issue's case, of a stroke at another
index, of nothing alike and of equal stacks, and that matches never
cross; editing the shared stroke edits both strokes (at index 1 and 2)
in one undo step that restores both, also through stroke.set on the
active row; removing a shared effect and adding a fill reach both, and
reordering differing stacks is refused; the panel lists kind, note,
Stroke, Feather and Opacity, not Fill, the full stack for alike objects
and "Mixed Objects" for a path and type. The existing object-row test
now expects the kind for two rectangles and the shared rows when their
opacity differs.