Commit Graph
8427 Commits
Author SHA1 Message Date
Ammar Almakzumi 822e4669ce fix(ui): avoid duplicate cursor without Wayland theme 2026-11-05 10:54:45 +07:00
BFlat dfc0f28004 Merge pull request #5733 from mockydevs/feat/set-corners-odd-even-5546
Set Corners: round all, odd-numbered or even-numbered anchors (#5546)
2026-11-05 10:54:27 +07:00
bflatastic 95d5c738dc i18n: Set Corners dialog strings in every catalog 2026-11-05 10:42:37 +07:00
Don Omondi 7aa759d4de Set Corners: round all, odd-numbered or even-numbered anchors (#5546)
addon.pathtools.cornerSet takes `every` ("all", "odd" or "even"; default
"all"), which keeps, of the anchors already chosen (named, direct-selected
or all), only the odd- or even-numbered ones, counted along each path from
its first anchor as number 1. Anchors that take no corner still count, so
the pattern follows the path's anchor order on open and closed paths; when
nothing is left the command says so. Unknown values are refused.

Object › Plug-ins › Path Precision Tools › Set Corners… (also a button in
the Anchor Inspector's Corners section) opens a dialog with Radius, Corner
kind and Anchors (All / Odd-Numbered / Even-Numbered) that previews on the
canvas and commits as one undo step.
2026-11-05 10:42:37 +07:00
BFlat 76ef53045e Merge pull request #5732 from dotCooCoo/fix/render-stroke-outline-reused-on-move
Render: a moved path reuses its stroke outline
2026-11-05 10:40:33 +07:00
BFlat fd7606bacc Merge pull request #5731 from mockydevs/fix/artboards-multi-apply-5698
Artboards: Shift- and Ctrl/Cmd-click in the Artboards panel select several artboards with any tool, and Fill applies to all of them (#5698)
2026-11-05 10:40:17 +07:00
BFlat f30d7a6696 Merge pull request #5730 from dotCooCoo/fix/layers-thumbs-weigh-heavy-objects
Layers: heavy type, symbols, meshes and images get their first thumbnail off the UI thread on the desktop
2026-11-05 10:40:00 +07:00
BFlat cd01c2ce95 Merge pull request #5728 from mockydevs/fix/expand-transform-groups-5612
Effects: Expand Appearance makes each copy of a path's Transform effect an object of its own (#5612)
2026-11-05 10:39:44 +07:00
dotCooCoo 2bb6db5e8f Tests: movedstroke imports wrapped to the rustfmt width 2026-11-05 10:26:08 +07:00
dotCooCoo 1c0fcd707b Docs: stroke outline reuse timings measured against current main 2026-11-05 10:26:08 +07:00
dotCooCoo 413fe034f0 Render: a moved path's stroke is matched by its anchors, without a copy of its line
The outline cache kept a copy of each stroke's line to recognize a moved
path, and made that copy on every miss, so an anchor edit of a long path
paid for copying its whole line. The entry now records whether its outline
was made along a line of the object's own path. On a miss, the anchors of
the earlier object are compared with the new one's, stopping at the first
that differs, and the earlier line is made again from the object and its
stroke only when they match.

Strokes with arrowheads keep the reuse: the check uses the untrimmed path.
2026-11-05 10:26:08 +07:00
dotCooCoo e71daf48cc Tests: a sheared path or another stroke style outlines the stroke again
A shear, a wider stroke, another cap, another dash pattern and another
width profile each make one new outline and draw the pixels a new
renderer draws, for open and closed, short and long, plain, dashed,
profiled and gradient-filled paths.
2026-11-05 10:26:08 +07:00
dotCooCoo a4e74ed0f0 Render: stroke outline reuse leaves the shared imports and the dashed-outline branch as they were
The moved-outline key is imported where it is used, and the outline is
stored through one helper, so the change touches fewer lines that other
work on the renderer also changes.
2026-11-05 10:26:08 +07:00
dotCooCoo e6331e559a Render: a moved path reuses its stroke outline
A path object that moves without changing shape drew its stroke by
outlining the whole line again, because the stroke outline cache is
keyed by the node. The renderer now keeps, per object, stroke and
outline width, the latest outline with the line it was made from. When
the new line is that line moved, it draws the kept outline moved by the
same offset, and moves the region a long path was outlined in with it.

Scaling, rotating or editing the path, changing the stroke's width,
join, cap, dashes or profile, a zoom that changes the outline's
tolerance, and a move that takes the view past the outlined region
still outline the stroke again. Only the latest outline of each stroke
keeps its line.
2026-11-05 10:26:08 +07:00
Don Omondi 26c27b8b85 Artboards: Shift- and Ctrl/Cmd-click in the Artboards panel select several artboards with any tool, and Fill applies to all of them (#5698) 2026-11-05 10:26:07 +07:00
dotCooCoo 5baf83f1de Layers: counting a row's art for its first thumbnail stops after 20,000 steps
A row drawn before was counted with no limit on its objects, so symbols
that each hold many instances of the next made the count grow with the
product of the instances, and the Layers panel could stall on such a file.

- Counting stops after 20,000 steps: each object, subpath, text run and
  symbol looked at is one step, and every 64 bytes of a symbol or placed
  file name looked up is one more. A row that needs more gets its
  thumbnail on the thumbnail thread, and the painted thumbnail draws
  nothing for it.
- A row drawn before counts at most 20,000 objects.
2026-11-05 10:26:07 +07:00
dotCooCoo 0f9839f7f6 Layers: heavy type, symbols, meshes and images get their first thumbnail off the UI thread
The Layers panel decided whether to make a row's first thumbnail on the
UI thread by counting path anchors and type frames only, so point type,
symbol instances, gradient meshes, images and placed documents counted
as one object with no anchors, and a heavy one was rendered on the UI
thread.

- small_node takes the row's document. Each glyph of type counts as 4
  anchors, each mesh point as 1 and every 100 image pixels as 1, against
  the same 2,000-anchor limit; glyphs are counted only up to the limit.
- A symbol instance and an inline graphic count their symbol's art, and
  a placed document counts its file's art once the file has been read;
  one not read yet goes to the thumbnail thread.
- Symbols and placed documents nested more than 8 deep go to the
  thumbnail thread, so a symbol holding an instance of itself is counted
  to an end.
- The painted fallback thumbnail counts as before.
2026-11-05 10:26:07 +07:00
bflatastic d62c90f41e Tests: Expand Appearance of a Transform's copies makes a group of them 2026-11-05 10:25:44 +07:00
Don Omondi 231e43a490 Effects: Expand Appearance makes each copy of a path's Transform effect an object of its own (#5612)
A path or compound path whose Transform effect made copies expanded to
one compound path of every copy. It now expands to a group of one object
per copy, each painted as the path, as gradient copies already did; a
second Transform effect's copies nest in a group per copy of the first.
Drawing is unchanged.

# Conflicts:
#	docs/effects-parity.md
2026-11-05 10:25:44 +07:00
FredApps ed78763866 Merge pull request #4784 from datatimp/fix/3260-halftone-radius-pixels
Color Halftone: Max Radius in pixels of the raster effects resolution
2026-11-05 04:09:48 +01:00
BFlat 40bfb22304 Merge pull request #5727 from mockydevs/fix/numeric-math-decimal-comma-5684
Numeric fields: a decimal comma works in arithmetic next to a number with a point (#5684)
2026-11-05 09:54:35 +07:00
Don Omondi e82fd3d5ca Numeric fields: a decimal comma works in arithmetic next to a number with a point (#5684)
A numeric field read a comma as a decimal point only when the text had no
point at all. A field shows its value with a point (100.5 pt), so typing
-20,5 after it gave 100.5 pt-20,5, which kept its comma and was refused.

Each number now reads its own separator: commas become points unless the
text reads as thousands (1,000, as before). A number written with both
separators (1.234,5, 1,000.5) ends up with two points and is still refused
rather than guessed at.
2026-11-05 09:46:13 +07:00
BFlat 7ef4829e5e Merge pull request #5726 from mockydevs/fix/compound-shape-insertion-5309
Isolation mode: a locked or hidden layer keeps the isolated art from clicks and marquees (#5309)
2026-11-05 09:45:56 +07:00
Don Omondi 23bcef97c7 Isolation mode: a locked or hidden layer keeps the isolated art from clicks and marquees (#5309)
Locking or hiding the layer (or a group) around the isolated object left
its art selectable with the Selection tool: the isolation branch of the
hit test only checked for a locked artboard, and the marquee's checked
nothing. Both now pass over isolated art that isn't editable.
2026-11-05 09:32:28 +07:00
BFlat ffcc10e165 Merge pull request #5724 from mockydevs/fix/pdf-devicen-spot-3235
PDF import: a DeviceN gradient of a spot ink over process plates keeps the ink and the plates, and PDF export writes it as DeviceN again (#3235)
2026-11-05 09:23:06 +07:00
Don Omondi ce4f16369e PDF import: a DeviceN gradient of a spot ink over process plates keeps the ink and the plates, and PDF export writes it as DeviceN again (#3235) 2026-11-05 09:09:53 +07:00
BFlat 49b5246a3a Merge pull request #5723 from mockydevs/fix/font-menu-test-race-5647
Tests: the Type › Font menu test waits for the installed fonts' scan (#5647)
2026-11-05 09:06:30 +07:00
Don Omondi 4818b462b7 Tests: the Type › Font menu test waits for the installed fonts' scan (#5647)
type_font_menu_lists_every_available_family read Type › Font while the
scan the app starts could still be running. The scan bumps the font
generation a moment before it counts as done, so a read in that gap lists
only the loaded fonts under the new generation, and the test's
"generation held still" check let the comparison with the full list fail.
The test now waits for the scan after making the app, as the native menu
tests do since #5653.
2026-11-05 08:59:03 +07:00
BFlat 3856218553 Merge pull request #5722 from dotCooCoo/fix/type-paste-textless-clipboard
Type: a refused copy is dropped when a textless clipboard can't track it
2026-11-05 08:58:49 +07:00
dotCooCoo 4e00d84404 Type: a refused copy is dropped when a textless clipboard can't track it
On a platform without a clipboard change count (macOS, Linux), a Type
copy that the system clipboard refused is kept only when the clipboard
held text when the write failed. A clipboard with no text, one that
couldn't be read, or one holding a picture can't show that another app
copied something later, so Paste no longer inserts the earlier copy
there.
2026-11-05 08:50:33 +07:00
BFlat 2b9c7995a5 Merge pull request #5721 from dotCooCoo/fix/glyph-fallback-cluster
Glyphs: a fallback face's consonant glyph is reported, not its vowel sign's
2026-11-05 08:49:10 +07:00
dotCooCoo f0335d2f17 Glyphs: a fallback face's consonant glyph is reported, not its vowel sign's
When a fallback face draws Ka with the vowel sign I, the vowel sign's
glyph comes first in Ka's cluster, and text.glyphAlternates reported it
for Ka, laid out or overflowing. The query now finds the face that draws
the character and takes that face's glyph for it by the same rule as
for the requested face.
2026-11-05 08:37:18 +07:00
BFlat 511fbd47ad Merge pull request #5720 from dotCooCoo/fix/open-export-avoidable-work
SVG export: type with Mojikumi and no Japanese characters is laid out once
2026-11-05 08:37:03 +07:00
dotCooCoo befc4b959d SVG export: type with Mojikumi and no Japanese characters is laid out once
To find the characters that justification or Mojikumi moves, SVG export
without Fewer <tspan> Elements laid out every live type object a second
time, as point type with
Mojikumi None and default Justification options, whenever its paragraph
had Mojikumi on. New type gets Line-end Punctuation Half Width, so every
new type object was laid out twice. Mojikumi only spaces Japanese
punctuation and Japanese letters, so type without them lays out the same
either way.

- TextLayout::mojikumi_applies says whether the text or a list marker
  holds a character Mojikumi spaces.
- reader_advances keeps such a paragraph's Mojikumi, and reads the glyphs
  from the layout it was given when the object it would lay out is the
  same; otherwise it lays the object out again as before.

The SVG is unchanged. Exporting 5,000 point type objects takes a median
of 109.4 ms instead of 171.5 ms on Windows 11.
2026-11-05 08:24:33 +07:00
BFlat 4cce3363da Merge pull request #5719 from dotCooCoo/fix/type-paste-copy-during-refused-write
Type: a copy elsewhere during a refused write wins the paste
2026-11-05 08:17:37 +07:00
dotCooCoo 8a81c95685 Type: a copy elsewhere during a refused write wins the paste
When another app copies while VectorCraft's write is trying to open the
clipboard and the write then fails, Paste takes the other app's copy. A
change during the failed write counts as the write's own only when the
clipboard then holds this copy's text, or no text and either nothing to
paste or one of this copy's formats.
2026-11-05 08:17:22 +07:00
BFlat 644acb89cc Merge pull request #5718 from dotCooCoo/fix/glyph-query-overflow-fallback
Glyphs: the Glyphs query reports the fallback glyph of an overflowing character
2026-11-05 08:08:19 +07:00
dotCooCoo 8118b59d19 Glyphs: the Glyphs query reports the fallback glyph of an overflowing character
When the requested font has no glyph for a character, the text draws it
from a fallback face, and text.glyphAlternates reports that glyph. For
a character that overflows its area, the query shaped the character
alone but kept only a glyph of the requested face, so it reported the
requested face's default glyph (0). It now takes the first glyph drawn
for the character when the requested face draws none, as it does for
laid-out text.
2026-11-05 07:56:26 +07:00
BFlat 1fc630e697 Merge pull request #5717 from dotCooCoo/fix/zero-opacity-stain-keeps-swatches
Symbols: an instance fill with no opacity keeps the art's swatch links
2026-11-05 07:56:13 +07:00
dotCooCoo ee9cce43dc Symbols: an instance fill with no opacity keeps the art's swatch links
A symbol instance whose fill has an opacity of 0 no longer counts as
stained. Expand Appearance and the other places that build an
instance's art used to rebuild every solid paint as an unlinked color
even though the tint changed nothing, so the art lost its global and
spot swatch links.
2026-11-05 07:44:08 +07:00
BFlat 373e013670 Merge pull request #5712 from dotCooCoo/fix/glyph-cluster-order
Glyphs: a character's glyph is the first of its glyphs in its cluster
2026-11-05 07:43:53 +07:00
BFlat f9bb83c312 Merge pull request #5711 from dotCooCoo/fix/layers-pending-thumb-doc
Docs: describe what a Layers row shows while its first thumbnail renders
2026-11-05 07:43:37 +07:00
dotCooCoo e33be0eea9 Glyphs: a character's glyph is the first of its glyphs in its cluster
Ka, the halant, Ka again and the vowel sign I are shaped as one cluster:
the vowel sign, the first Ka's half form, then the second Ka's default
glyph. The glyph lookup looked for the character's default glyph across
the whole cluster before its alternates, so for the first Ka it found
the second Ka's default glyph instead of the half form. It again goes
through the cluster in order, and takes the first glyph that is the
character's default glyph or one of its alternates and is not the
default glyph of another character of the grapheme, so a vowel sign's
glyph that the font also lists as an alternate of the consonant is not
reported for the consonant.
2026-11-05 07:30:43 +07:00
dotCooCoo f619fcf64b Docs: describe what a Layers row shows while its first thumbnail renders
A row sent to the thumbnail thread because its opacity masks pass the
2,000-anchor limit still paints the boxes of its own paths until the
thumbnail is ready. The progress line said such a row shows no picture.

Follows #5705.
2026-11-05 07:30:40 +07:00
BFlat eb71a02723 Merge pull request #5716 from storytold/fix/control-graceful-close
Control server: close connections gracefully, so a reply is not lost to a reset
2026-11-05 07:30:23 +07:00
BFlat 49ec86b104 Merge pull request #5709 from dotCooCoo/fix/type-paste-refused-copy
Type: Paste keeps a copy the system clipboard refused
2026-11-05 07:30:07 +07:00
bflatastic 7b608b2dab Control server: close connections gracefully, so a reply is not lost to a reset
A connection ended (a junk line, an idle or stalled peer, the app going away) by dropping its socket. On Windows, a reset can follow and discard replies the peer has not read yet, which made tcp_connection_is_closed_after_junk fail at random in CI with ConnectionReset. serve_socket now shuts the write side down and reads what the peer still sends until it closes, for at most a second and 1 MiB, before dropping the socket. The test goes through serve_socket, the path the app uses.
2026-11-05 07:13:06 +07:00
dotCooCoo 59a3cad1dc Type: Paste keeps a copy the system clipboard refused
When a Copy in the Type tool cannot be written to the system clipboard
(another app holds it open, or a format fails after the clipboard was
emptied), Paste into type or a text field now inserts that copy, with
its style in type, instead of the text the clipboard kept. This covers
Edit > Paste, the paste key, and a paste that arrives while the copy is
still being written. When another app copies something, before or after
the failed write, Paste takes that.

The new SystemClipboard::change_count reports the clipboard's change
count (Windows' sequence number). Where a platform has none, a change
of the clipboard's text marks a new copy.
2026-11-05 07:13:03 +07:00
BFlat 8f1be2376f Merge pull request #5708 from dotCooCoo/fix/cheap-enabled-checks
Menus: Delete Empty Artboards and Save Gradient Preset decide their enabled state with less measuring
2026-11-05 07:11:04 +07:00
BFlat aaa59d4b98 Merge pull request #5707 from dotCooCoo/fix/glyph-alternates-overflow-probe
Glyphs: the Glyphs query reports the glyph of an overflowing character
2026-11-05 07:10:53 +07:00