Commit Graph
8972 Commits
Author SHA1 Message Date
dotCooCoo 142366d9ee Effects: placed gradients move with Transform and Free Distort effects
A gradient placed with the Gradient tool stayed where it was when the
object's Transform or Free Distort effect moved, turned or scaled the
outline, so the shape slid across its own gradient.

The Transform effect now maps placed gradients by its own transform,
and each of its copies takes them as many times as it takes the
transform. Under Free Distort, each path's placed gradients move and
scale from the path's bounds before the effect to its bounds after it.
This change does not alter gradients fitted to the object.

The canvas, Expand Appearance and export, Pathfinder and compound
shapes, groups, a fill's or stroke's own effects, and Object › Expand
of a gradient fill apply the same rule. In a group, the own fills and
strokes of the group, of the groups inside it and of a symbol's art
follow it too, and images and symbol instances that a group's
Transform effect moves take the placed gradients of their own fills
and strokes along. A group whose path keeps its shape because the
effect would pass the geometry budget keeps its own gradients in
place.

This changes how existing documents draw: a gradient placed on a path,
or on an image's or symbol instance's own fill, inside a group with a
Transform effect now moves with it. Before, only the gradients of type
moved (#2214).

How gradients follow each effect is VectorCraft's own rule. The
Distort & Transform help page describes what the effects do to the
object:
https://helpx.adobe.com/illustrator/desktop/manage-objects/reshape-transform-objects/distort-and-transform-effects-in-illustrator.html
2026-11-07 21:11:07 -08:00
BFlat e89c036079 Merge pull request #6046 from dotCooCoo/fix/menu-test-font-race
Tests: the Mac menu bar tests do not depend on fonts other tests install
2026-11-08 12:36:55 +08:00
BFlat 6f1b6aa91c Merge pull request #6045 from dotCooCoo/fix/tr-params-row
i18n: the Turkish params and effect errors name their own keys
2026-11-08 12:36:40 +08:00
BFlat d60ec7a808 Merge pull request #6044 from dotCooCoo/fix/paste-many
Paste: skips relinking copies with no renames, global swatches or styles, and finds type in one walk
2026-11-08 12:36:24 +08:00
BFlat 36680d1e02 Merge pull request #6038 from dotCooCoo/fix/issue-5582
Panels: floating panels reopen on the monitor where they were left
2026-11-08 12:36:10 +08:00
BFlat 44b0136554 Merge pull request #6037 from dotCooCoo/fix/delete-empty-check-once
Menus: Delete Empty Artboards' enabled check does not walk the document every frame
2026-11-08 12:35:55 +08:00
BFlat 60303fbe93 Merge pull request #6036 from dotCooCoo/fix/issue-5347
Fix #5347: a floating panel's menu scrolls inside its window
2026-11-08 12:35:40 +08:00
BFlat 2347f968d3 Merge pull request #6034 from dotCooCoo/fix/ai-legacy-break-direction
.ai import: legacy overflow breaks keep the direction and paragraph attributes they were read with
2026-11-08 12:35:25 +08:00
BFlat d9d7c00e41 Merge pull request #6033 from dotCooCoo/fix/dimension-expand-stays-plain
Dimension tool: Expand keeps a dimension as plain art after it moves
2026-11-08 12:35:10 +08:00
BFlat ac11373527 Merge pull request #6032 from dotCooCoo/fix/ai-clip-path-keeps-fill
.ai import: a filled clipping path keeps its fill
2026-11-08 12:34:55 +08:00
BFlat 5ca010d7aa Merge pull request #6030 from dotCooCoo/fix/hex-field-paste
Color fields: a pasted hex code replaces the field's digits
2026-11-08 12:34:39 +08:00
BFlat a0fbeb0c06 Merge pull request #6028 from dotCooCoo/feat/actions-insert-stop
Actions: Insert Stop pauses playback and shows a message
2026-11-08 12:34:24 +08:00
BFlat 9a474f5138 Merge pull request #6024 from dotCooCoo/feat/ai-editing-data-variables
.ai save: the editing copy keeps variables, data sets and bindings
2026-11-08 12:34:08 +08:00
BFlat b63c07c1f9 Merge pull request #6006 from dotCooCoo/fix/ai-brush-art-colors
.ai import: spot colors in a kept brush's art become swatches
2026-11-08 12:33:52 +08:00
BFlat 46057d0453 Merge pull request #5996 from dotCooCoo/fix/ai-type-on-path-options
.ai import: type on a path keeps its effect, alignment and flip
2026-11-08 12:33:38 +08:00
BFlat 06dea5922e Merge pull request #5995 from dotCooCoo/fix/simplify-long-paths
Simplify: fitted curves stay within the tolerance of the path, and serpentines simplify faster
2026-11-08 12:33:23 +08:00
BFlat b019f881d9 Merge pull request #5994 from dotCooCoo/fix/artboard-locked-art-journal
Artboards: duplicate, copy, cut, move and rearrange journal whether locked and hidden art goes
2026-11-08 12:33:12 +08:00
BFlat 427a8a44c5 Merge pull request #5975 from dotCooCoo/fix/ai-reopen-keeps-active-artboard
.ai save: a reopened file opens on the artboard active when it was saved
2026-11-08 12:32:56 +08:00
BFlat 1a46511c5d Merge pull request #5890 from dotCooCoo/feat/execute-checks-params
Engine: execute refuses keys a command doesn't name, in every build
2026-11-08 12:32:40 +08:00
dotCooCoo eb99aaabfd Tests: the Mac menu bar tests do not depend on fonts other tests install
Type › Font and the plug-in rows come from process-wide state, and a test
running alongside can add to them. Two tests read the menus more than once
and compared the reads, so a font added in between failed them:
fonts_that_load_read_the_menus_again adds Vertitest Sans, for one.

every_in_window_item_is_in_the_mac_menu_bar_once now lays out the Mac bar
from the in-window bar it compares it with, so both come from one read.
documents_and_selections_change_state_not_structure changes the document
between its reads, so it runs through undisturbed, which runs it again when
the shortcut, plug-in or font generations change.
2026-11-08 11:22:42 +07:00
dotCooCoo 2af4c12dd3 i18n: the Turkish params and effect errors name their own keys
``params` must be an object` read as "gradyan" and `missing `effect`` read as "isim", the keys of two other rows. Both rows now keep the key in backticks, as the rows next to them do.
2026-11-08 11:22:41 +07:00
dotCooCoo a18fbee3c9 Docs: the paste timing line gives CPU time per paste and undo 2026-11-08 11:22:41 +07:00
dotCooCoo b17a744a9c Panels: floating panels reopen on the monitor where they were left
Squashed from #6038 onto main.
2026-11-08 11:22:40 +07:00
dotCooCoo 2f09d9998e Menus: Delete Empty Artboards' enabled check does not walk the document every frame
The Artboards panel menu asks whether Delete Empty Artboards can run on
every frame while it is open, and each ask measured the document. With
an open path of 200,000 anchors across two artboards, that took 70.4 ms
a frame in a release build.

The answer is now kept per open document with the version of the
document it was measured on, and is measured again when the document
changes. A later frame takes under 0.001 ms. The first frame after an
edit measures once. A change of the selection or the view keeps the
answer.
2026-11-08 11:22:40 +07:00
dotCooCoo 7d8f2a315a Paste: skips relinking when nothing changes and finds pasted type in one walk
A paste relinked every copy to the document's resources even when no
resource had another name or color and the objects linked no graphic
style. It now leaves those copies as they are.

Finding the pasted type looked each id up and compared every text object
with the ones already found. Text commands and the bounds refresh after
a paste now look all ids up in one walk and keep the found ids in a set.
Giving a pasted group new ids builds its map of old to new ids only for
an Objects on Path group, which is the only kind that reads it.

A pasted solid paint with no swatch link and a tint other than 1 now
keeps that tint when no relinking runs. Saved art carries a tint of 1 on
such a paint.
2026-11-08 11:22:40 +07:00
dotCooCoo 2432807197 Tests: the short-window submenu helper names its rows with expect 2026-11-08 11:22:39 +07:00
dotCooCoo 37245779aa .ai import: legacy overflow breaks keep the direction and paragraph attributes they were read with
The reader holds an overflow paragraph break (`(\r) TX`) until more type
follows it. When that type came, the break took the state in force then,
not the state where the break was read:

- A story that started with the break took the `TV` direction of the
  characters after it, so `1 TV (\r) TX 0 TV (Body) Tx` opened horizontal
  and gave no warning.
- A break read in the other direction inside a story did not count as a
  direction change.
- The empty paragraph that the break ended took the attributes of the
  paragraph after it, while `(\r) Tx` keeps its own.

The pending break now records `TV` and, when it ends an empty paragraph,
that paragraph's attributes.
2026-11-08 11:22:39 +07:00
dotCooCoo c952b8d298 Fix #5347: a floating panel's menu scrolls inside its window
The main window's menu floor was stored once for the whole egui context,
so a menu in a panel's OS window stopped at the main window's bottom bars
instead of its own edge. The floor is now kept per window, and a window
with no recorded floor uses its bottom edge. A menu list is also never
taller than the window, so the last items of a menu stay reachable in a
100 pt window, below the 120 pt minimum.

The Digits, Stylistic Sets, Save Selection and Move to Group submenus,
the libraries menu, the Color Guide's Limit to Library menu and the Layers
panel's Filter popup now scroll the same way.
2026-11-08 11:22:39 +07:00
dotCooCoo 09aa816394 Docs: dimensions state when the menus dim Expand and when a dimension stays live 2026-11-08 11:22:38 +07:00
dotCooCoo 9e0e8993b9 Dimension tool: state the Expand rule after object.expand's parameter list
The sentence inside the braces made every_description_parses fail. The object.expand and effect.expandAppearance texts now also limit the replaced-art case to a dimension selected with that art.
2026-11-08 11:22:38 +07:00
dotCooCoo bc9ea516f5 Dimension tool: Expand keeps a dimension as plain art after it moves
Object > Expand left a dimension's recipe on its group, so the next move
redrew the live dimension and discarded the expanded art. Expand now ends
the live link of a dimension whose art it changes, in the same undo step.
Moving or scaling the group leaves its art as it is, the art no longer
follows what it measured, and undo gives the live dimension back.

Expand Appearance, Create Outlines and Outline Stroke had the same fault
and now do the same, also when they reach only a part of a dimension. A
dimension whose art a command leaves as it was stays live. A dimension
selected with the art it measures becomes a plain group when Expand,
Expand Appearance or Outline Stroke replaces only that art.
2026-11-08 11:22:38 +07:00
dotCooCoo bbf4834f12 .ai import: a hidden clipping path's fill leaves with it
The fill read apart from a clipping path that was read on %_ lines, and
the copies of its subpaths, are recorded as read on those lines too, so
the copy of a look's art that is removed takes them along. The file
format notes list a compound clipping path whose subpaths paint
differently among the exceptions to a clipping path keeping its fill.
2026-11-08 11:22:37 +07:00
dotCooCoo 589fd863c4 .ai import: a compound path's plain subpath keeps its fill
In a compound clipping path whose subpaths paint differently, the fill
of a subpath that does not clip was read apart with the clipping
subpaths' fills. Only the clipping subpaths' fills are read apart now,
and a subpath that does not clip keeps its fill, as before.
2026-11-08 11:22:37 +07:00
dotCooCoo b33cdfd14f .ai import: a clipping path's fill read apart keeps its own ids
A fill read apart from a clipping path took a copy of the clipping
path's opacity mask whose art kept the original's ids, so the document
held two objects with one id. The copy of the mask's art, and the
subpaths of a fill read apart from a compound clipping path, now get
ids of their own and stay linked to the same swatches.
2026-11-08 11:22:37 +07:00
dotCooCoo 72959e6e80 .ai import: a multi-layer mask's fill paints once
Each layer a multi-layer mask covers gets a copy of the mask's path, so
a fill kept on that path painted in every layer, over the art the mask
covers in the layers before. The mask's fill is read as a path of its
own again, where the mask's path is, and the copies have no fill. A
fill read apart from a compound clipping path is a compound path whose
subpaths do not clip and have ids of their own. A fill read apart, its
subpaths and their points count toward the importer's object limit.

Docs: docs/file-format-parity.md.
2026-11-08 11:22:36 +07:00
dotCooCoo 6ab82ca71e .ai import: a filled clipping path keeps its fill
The reader took the fill off a clipping path in a file's editing data and
added a copy of the path, holding only that fill, under the art it clips.
A clipping path paints its own fill under the clipped art and its stroke
over it, so the reader now keeps the fill on the clipping path. A filled
clipping path opens as one path instead of two, in a clipping group, a
clipping layer and each level of nested clipping paths. Its note, tags and
gradient stroke (`1 BB`) are on that path as before.

A compound clipping path whose subpaths all paint alike keeps their fill.
When its subpaths paint differently, the fills of the filled subpaths are
still a compound path of their own under the clipped art, so a subpath
the file does not fill stays unfilled. Clipping paths in a compound path
that holds its drawn look still have their fills read as paths of their
own under the art, each with its clipping path's opacity mask.

Docs: docs/file-format-parity.md.
2026-11-08 11:22:36 +07:00
dotCooCoo 97df62547b Tests: the hex paste dialog tests release Enter after each press
A dialog's OK ignores a repeat of Enter, and an egui context keeps a key
down until it is released. The Color Picker, Swatch Options and Define
Grid tests pressed Enter in one context without releasing it, so every
press after the first was a repeat and the dialog stayed open. Each test
now releases Enter after pressing it.
2026-11-08 11:22:35 +07:00
dotCooCoo 0b6a6d44c6 Color fields: a hex paste never goes before a leading #, and a replacing paste drops earlier inserts
With the caret before a # that stays, bare digits are not inserted ahead of it; a full code replaces the text instead. A paste that replaces the field's digits also removes bare-digit pastes kept earlier in the same frame.
2026-11-08 11:22:35 +07:00
dotCooCoo a386b3e955 Color fields: a pasted hex code replaces the field's digits
A click put the caret among the six digits and the text box holds seven
characters, so only the first character of a pasted code went in. The
field then held no new color, and Enter closed the dialog with the old
color. A press on a field drawn before the hex field also dropped what
was typed, while Tab and Enter applied it.

The hex fields of the Color Picker, Swatch Options, Recolor Artwork, the
Color panel, Document Setup, Duotone, Color Themes and Perspective Grid
now share one set of rules:

- A click or keyboard focus selects the digits.
- A code of six digits, with or without #, replaces the digits wherever
  the caret is.
- A code of three digits also replaces them, unless the paste holds only
  those digits and exactly three digits stay. Those digits then edit the
  text in place.
- A paste of only one, two, four or five digits edits the text in place
  when it does not cover all of the text and the result has at most six
  digits.
- Any other paste leaves the field as it was.
- A press elsewhere commits the text in the frame of the press.
- A commit returns the digits without # and writes a three-digit code out
  as six, so Duotone reads a typed # and Perspective Grid, whose parser
  reads six digits only, takes a three-digit code.

Part of #5013
2026-11-08 11:22:35 +07:00
dotCooCoo 7d1f2062e3 Actions: Insert Stop pauses playback and shows a message
Squashed from #6028 onto main.
2026-11-08 11:22:34 +07:00
dotCooCoo fe0f3d919c .ai save: the editing copy keeps variables, data sets and bindings
Squashed from #6024 onto main.
2026-11-08 11:22:34 +07:00
dotCooCoo 57dbbbb41b Docs: .ai brush art spot color limits
The file-format entry states the 20,000-name limit across brush art and per section, the first-definition rule, and that colors past the limit still count toward the color mode.
2026-11-08 11:22:33 +07:00
dotCooCoo 8a34c89d7c .ai import: remove the duplicated editing-copy paragraph 2026-11-08 11:22:33 +07:00
dotCooCoo 77b72da70d .ai import: Stair Step spacing is fitted from its glyphs' origins
Stair Step's glyphs stay upright, so their matrices never show the path
turning and the spacing fit found no turn at all. The fit now takes the
path through each glyph's origin, which lies on the path, and measures
how far it turns over a step from the circle through three origins in a
row.
2026-11-08 11:22:32 +07:00
dotCooCoo d180ad456e .ai import: brush art brings at most 20,000 color names, each once
Each brush art section keeps only the first definition of each color name, and at most 20,000 names. The art of the brushes that come in brings at most 20,000 different color names in all, every section's color operators before any section's gradient stops, so art that no brush uses or that a later section replaces takes no places. Colors left out still count toward the color mode.

A test also checks that a global process color which only a brush's art defines with its color operator stays a process color when a gradient stop names it.
2026-11-08 11:22:32 +07:00
dotCooCoo cbfca36c85 .ai import: spot colors in a kept brush's art become swatches
The reader kept a brush's art but dropped the colors it read from it, so
a spot color that only a Scatter or Art brush's art used made no swatch,
and the art's process colors did not count toward the document's color
mode.

The reader now keeps each brush art section's colors with its art. When
the document is finished, the colors of the art that a kept brush uses
are added after the rest of the file's, in the order the art was read:
its spot colors make swatches and its colors count toward the color
mode. Art that no kept brush uses (a pattern brush's tiles, a section
that a later one of the same name replaced, a section inside a symbol's
definition) brings none of its colors, and a spot color that the page's
art, a pattern or a symbol also defines keeps the value defined there.

Part of #637.
2026-11-08 11:22:32 +07:00
dotCooCoo 2fbd60a6dd .ai import: 3D Ribbon spacing holds where the path turns back
3D Ribbon's level baseline faces the way the path runs, so where the path
runs left its baseline and foot are turned round. The spacing fit read
that as a half turn and left the pair out, so a short run on either side
of a vertical tangent came in with no spacing. The fit now turns those
glyphs back first. With an alignment, the two glyphs either side of that
point lie on opposite sides of the path, and that pair is left out.
2026-11-08 11:22:31 +07:00
dotCooCoo 2faec2fa11 .ai import: Skew, Stair Step and Gravity measure the path's turn square to it
The spacing fitted from a type-on-a-path layout measured each turn against
the glyph's own foot. Skew's feet stay upright, so on a tilted or leftward
path the turn picked up a false angle and the fitted spacing was wrong (a
straight path at 30 degrees gave -9.7 instead of 0). Effects other than
Rainbow and 3D Ribbon now measure the turn against the baseline turned a
right angle.

The file-format doc states that a frame whose options set key 6 to true
keeps the default effect, alignment and Flip.
2026-11-08 11:22:31 +07:00
dotCooCoo bc5eb7aa47 Docs: state the limits of .ai type on a path import
Align to Path takes its distance from the path from VectorCraft's font metrics, and the fit under Gravity does not reproduce the file's layout.
2026-11-08 11:22:31 +07:00
dotCooCoo bc19dd64db Simplify: Convert to Straight Lines keeps the tip of a path that turns back past its end
`rdp` measured each sample to the infinite line through the ends of the
line that would replace it. A sample beyond either end could be far from
that line segment and still be dropped: a line to (100, 0), a 0.2 pt
U-turn and a line back to (50, 0.2) became one line at a tolerance of
1 pt, leaving the tip 50 pt away. `rdp` now measures to the segment, so a
sample beyond an end is measured to that end, and a zero-length chord
measures to its point as before.

The test `a_path_that_turns_back_past_its_end_converted_to_straight_lines_keeps_its_tip`
covers that path.
2026-11-08 11:22:30 +07:00