The first thumbnail of a row goes to the thumbnail thread when its paths hold
more than 2,000 anchors in all. The count now also takes in the frame of area
type and the path of type on a path, so a long frame or path no longer renders
on the UI thread, and the painted fallback thumbnail leaves such a row out.
The Layers panel chose between the UI thread and the thumbnail thread
for a row's first thumbnail by counting objects only, so one path of
200,000 anchors was rendered on the UI thread. A row whose paths hold
more than 2,000 anchors in all now goes to the thumbnail thread, as a
row of more than 200 objects already did.
While such a row waits, the painted fallback thumbnail draws nothing
for it instead of measuring the path's bounds on every frame.
Opening the panel on one path of 200,000 anchors: the first frame takes
2.4 ms instead of 393 ms (release build, median of three runs).
A document font drawn in a substitute style stayed listed and could be chosen as the
replacement: the family is installed, so the old check passed, and Change sent the
unavailable style to text.replaceFont, which stored it and warned that the text is drawn
in a stand-in. The exact face now decides both the automatic choice and whether Change
is available; the row stays listed.
Listing System asked the font database for each family's style-less face, and for a
family not loaded yet that reads all its cataloged files, so opening the dialog could
load a whole font library. FontDb::closest_style answers from the catalog's weight and
slant instead, and the rows put that style first as before.
Tests: the dialog's 26 (the closest style is the face a style-less request resolves to,
a stand-in style is listed but not chosen) and the text crate's 474.
The rows repeated each family: a nameless "closest style" row came first and then
the same style again with its name. A family now lists each of its styles once,
the closest style first under its own name, so every row reads "Family Style" as
the reference app's list does.
The search field used a label no translation catalog has; it now uses the font
menu's "Search", so the two fields read the same in every language.
Switching Replace With Font From kept a family and style the new list might not
show, so Change could replace with a closest style of its own choosing instead of
a row on screen; the family and the style now both come from a row the list
shows, and the chosen font stays only when that exact pair is one of them.
Confirming the dialog with Enter closed it without clearing the search the Done
and Escape paths clear; all three now share the same cleanup.
Tests: a new source picks a row its list shows (family and style), and confirming
the dialog clears the replacement search.
Choosing a replacement meant picking the family from one drop-down and its style
from another, and only then seeing what the style would be. Replace With Font now
lists rows of a family and one of its styles - the family's closest style first,
then each style it has - and one click sets both. A search above the list filters
the rows by family and style, as the Character panel's font menu searches.
Each list scrolls on its own: the document's fonts and the replacement rows keep
separate scroll states and scrollbars, and the replacement list is ten rows tall
at most, no taller than the rows it lists.
Review: the rows are rebuilt after a replacement, the search clears when the
dialog closes, and a double-click respects the same guard as the Change buttons;
the type-parity checklist has its status line and revision row.
Closes#4972.
Data Recovery lists the folders in its recovery folder before it starts, so a
folder that can be written but not listed (a Unix folder with mode 0333) left
it unable to start. Choose... now lists the picked folder as well as making a
folder in it, and refuses it with a status message when either fails. The
progress line says what is checked.
The Choose... check now creates and removes an empty folder in the picked
folder, as the recovery store makes its own folder there, and reports a
folder it cannot remove again. The folder picker's answer fills the open
Preferences dialog even when the active document closed while the picker
was open, since the recovery folder is an app preference.
File Handling > Data Recovery > Folder has a Choose... button on the
desktop that opens the folder picker and puts the chosen folder in the
field. Cancel leaves the field as it was, and the field stays editable.
A chosen folder that no file can be created in is refused with a status
message. The web build has no folder picker, so the button is not shown
there.
A percentage typed alone in a size field (W, H and the other fields
that take a share of their value) read its decimal comma before the
`%` was dropped. The thousands check then saw `1,000%`, and the comma
was read as a decimal point, so `1,000%` set the size to 1% of its
value. `share_of` now reads the decimal comma after dropping the `%`:
`1,000%` is refused, and `12,5%` still reads as 12.5%.
The link-watch worker now signals a condition variable when it ends,
whether it stored its stamps or unwound, and Session::wait_link_scan
blocks until then. The place tests' idle helper waits on it once per look
in place of a 5 ms sleep under a 20 s wall-clock bound, and the engine's
link-watch and link-path tests wait on it in place of spinning under a
10 s bound. The link-watch tests date a rewritten file 2 s after the one
it replaces instead of sleeping 30 ms before writing it.
A package written in the background that finished while another dialog
was open showed no Package created dialog, but its status line still cut
the list of fonts left out at 500 bytes, so the fonts past the cut were
named nowhere. The status line now cuts the list only when the dialog is
shown.
field_number read a decimal comma before it dropped Kerning's pair of
parentheses, the suffix, % and degrees, so the thousands check saw
(1,000) and 1,000% and read the comma as a decimal point: (1,000)
committed 1. The comma is now read after those are dropped, so
(1,000), 1,000% and 1,000 degrees are refused as 1,000 already is,
while (1,5) and 12,5% still read as 1.5 and 12.5.
Comparing the fitted widths missed the case it was meant for. Name is the
only growing column, so a longer name takes its extra width from the
spare room and the fit comes out the same: a dragged Name width stayed,
clipping the longer name.
It could also drop dragged widths by accident. The fit comes from the
available width inside the scroll area, which scrolling can nudge, so
scrolling a widened table could reset it.
The table now compares the natural widths and the clip rect's width,
which scrolling does not move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A compound path a filter made a guide was still given to later filters, which
could change or delete it; compound paths of guide paths are now left out as
guide paths are. Hex colors and the color button's choice are display sRGB and
are now converted into the working RGB space, so they show as given whatever
the working profile; a color default is kept as written and read when used.
Follow-up to #5630.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Check for Updates and its window, the panel-layout reset notice and the
Image Trace progress lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plain click chose only the clicked row and dropped artboards chosen
but hidden by the search. It now replaces only the rows shown, as None
and Select All already do, so hidden rows keep their state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows #5442.
- text.setGlyphAlternate keeps the default glyph as a choice when a
feature of the character's style (salt, a stylistic set) draws another
glyph for it, so the default glyph is drawn. A character that is not
laid out (overflowing text) is shaped alone in its style to decide.
gid: null reports the glyph the style draws.
- The Glyphs panel's alternates grid shows the default glyph first when
the font does not list it, so a chosen alternate can be put back.
- A test for a chosen character that starts a longer grapheme (f with a
combining acute), which is not split from its mark.
- The alternates tests in the engine hold the shared font lock while
they add fonts to the global font database.
The bar decided to show the inline fields against a fixed 440-point
width, smaller than the room the fields take, so just above that width
the H field ran past the bar's right margin and was clipped. The bar
now adds up the room from the values that draw the fields: the label
widths in the current language, the four 80-point fields, the W/H link
and the item spacing ahead of each. With less room it shows the
Transform link alone, as before.
VectorcraftApp::new starts the installed fonts' scan in the background,
and Type > Font lists only the loaded fonts until it ends (#5483). When
the scan ended between two reads of the menus, the second read had the
installed families too, and every_in_window_item_is_in_the_mac_menu_bar_once
and documents_and_selections_change_state_not_structure failed. The
tests' app() helper now waits for the scan after making the app.
Space pressed after a tool's press has begun (Pencil, a brush, a shape, a
selection drag) switched the cursor to the hand but panned nothing: the pan
starts only with Space down at the press. It is ignored now, so the cursor keeps
the tool's own.
Test: space_during_a_pencil_drag_keeps_the_pencil_cursor (fails without the fix:
Grab instead of the pencil's crosshair).
The last column was egui's remainder column, which has no drag handle, so
it could only be resized from the edge before it. It is now an ordinary
column with its own handle.
A table keeps its column widths, so switching to a longer name left the
Name column at its old width and clipped the text. The table now goes
back to the opening fit whenever that fit changes: a longer widest cell
or a resized window. Dragged widths are dropped at that point.
fit_widths could overflow the room when one column hit the minimum,
because the other columns did not take up its share. Columns now give
way in proportion to their width above the minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
None now clears only the rows the search shows, as Select All adds only
those, so artboards hidden by the search stay chosen. The count reads
"Chosen: {n} of {total}". The dialog's new strings have rows in all 26
catalogs, written from their meaning with each catalog's own terms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A double-click makes the row's artboard active and fits it in the
window (view.goToArtboard, as the Artboards panel does), keeping the
dialog open and the artboards chosen before it. Parity docs record the
Selection Tools add-on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Object › Plug-ins › Selection Tools › Select by Artboards… lists each
artboard's number, name and size, filters by name, chooses rows with
click, Shift-click and Cmd/Ctrl-click, and runs addon.select.byArtboards
on OK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OK in Tate-chu-yoko Settings read an Up/Down or Left/Right that is not a
length as 0 and applied it. It now returns "Up/Down must be a number" or
"Left/Right must be a number" and keeps the dialog open, and a blank
field leaves its value alone.
A `color` parameter shows a color button that opens the color picker in plug-in
dialogs and the Appearance panel's effect editor; its default and values may be
a color object or a hex string, and the plug-in gets a color object. A filter's
returned objects may set `guide` and `locked`; objects it locked or made guides
are left out of the selection after the run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a_file_placed_while_idle_follows_its_changes asserts that an idle
document without links asks for no frame. Its app started the real render
worker, and once a frame took over 8 ms the canvas sent the next render to
it. The worker asks for a repaint when it finishes, at a time the test
does not control, so under heavy CPU load the asserted frame could report
a repaint delay of 0 instead of none.
The test now sets canvas.worker_started, as the cursor idle test does, so
every frame renders on the test thread. It also dates the rewritten file
2 s after the original with File::set_times, as the opening tests do,
instead of sleeping 30 ms before the write. Shipped code is unchanged.
At a caret the panel sends text.setRangeStyle with no range, which
styles all of the text, but the check that skips a choice that changes
nothing read only the insertion style. When the characters at the caret
already had the chosen feature and others did not, the choice did
nothing. The check now covers every character of the text at a caret,
so the choice applies, and a choice that changes no character still
adds no undo step.
A plural status message such as "Saved {path} with {n} note: {first}"
matched a path that holds " with " by taking the first split, so for
"/out/art with notes.pdf" the count became "notes.pdf with 1". The
count did not parse, the translation took the plural form and split
the path.
The {n} of a @msgplural template now matches only a number, and the
matcher tries the next split when it does not. {n} in a @msg template
still matches any text, since those name brushes, symbols and presets.
File > Export > Export As PNG... opened PNG Options with no artboard
chosen, and a single-image export without one writes the first
artboard, whichever was in use. With no artboard choice given, it now
takes the active artboard, or each artboard the Artboard tool has
selected (one file each). A choice the caller gives is kept.
type_font_menu_lists_every_available_family compared two reads of Type › Font while a test running alongside could add a font in between, so the menu was rebuilt with one more family. It now judges a run in which the font generation held still.
idle_expiry_closes_without_writing_a_line took a connection reset as a failure; on Windows the server's close of an idle connection can reach the client as one.
Choosing units in Preset Details restyled every preset card in the Recent
and Saved tabs, so switching between inches and millimetres rewrote all the
user's templates at once. The pane's options apply to the selected preset
(helpx, "Create and save custom document presets"), so only the chosen card
follows the chosen units now; the others keep the units their document or
preset was made in, and clicking one brings its units back. The built-in
category tabs still show the chosen units (#2078).
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
A dialog slider whose field shows another unit, such as Transform Each's
Move in millimeters, converted the number back to points and held it to
the rail's range, so a set value past the rail that the field holds came
back as the rail's end.
Dialog sliders and the Blob Brush tip preview offered screen readers only
the increment and decrement actions. They now also offer the set-value
action: the last request in a frame sets the number within the slider's
range, rounded to the places its field shows, and a value that is not a
finite number leaves it as it is.
Kerning read one pair of parentheses around its text only while it
showed an automatic value as (12), so typing (10+5) over a manual value
committed nothing. spin_kerning now reads that pair in both states;
the field still shows parentheses only for an automatic value, and
(10+5)*2 is still refused.
The opening tests waited 400 or 500 ms of wall-clock time for files read off
the UI thread, and up to 30 s in loops that polled until the queue emptied.
Under load a read could take longer, and
a_file_that_opens_and_asks_holds_back_the_next_one_ready failed.
wait_read now blocks on each pending file's own result channel and keeps the
result for poll, and open_ready waits and then polls once. The background save
in long_operation_busy_while_a_file_opens_or_a_background_save_runs waits with
background::wait_all, and its job waits on the release channel without a
timeout. Shipped code is unchanged.
On macOS the menu bar is native and is read again only when its state
hash changes. Type > Font now lists the loaded fonts until the installed
ones are scanned, so the hash takes the font generation and whether the
scan has ended. The tests that wait for process-wide generations to
hold still wait for the fonts too.
The menu bar builds Type > Font in every frame, the first included, and
its family list waited for the scan of every installed font. When that
scan took longer than the 20 s first-frame watchdog (thousands of fonts
on a slow disk, or each file checked by antivirus), the app took the
graphics adapter for hung and started again without it, until none was
left and no window ever showed.
The menu now lists the fonts already loaded until the scan ends, then
all of them (FontDb::menu_family_list_now, installed_fonts_ready).
text.tabs.set and text.tabs.clear return without an edit when every
targeted paragraph already holds the stops. A Ctrl-drag that the 0 pt
bound keeps from moving, or a drag released where it started, no longer
adds an undo step that changes nothing. When one targeted paragraph
holds other stops, the command still sets them all.
OK in Shape Builder Tool Options sent highlightColor to tool.setOption,
which keeps only #rrggbb, so #ff000080 was applied as #ff0000 with no
error. OK now gives the same error as the Live Paint tool options for a
hex color with an alpha part (#rrggbbaa or #rgba), the dialog stays open
and the tool's options do not change. The tool.options params text,
docs/mcp.md and the progress doc say so.