For Auto Size area type in columns, every text edit, each keystroke of
the Type tool included, laid the text out in one column and then laid
the columns out at that height and at each step of a bisection for the
least height that holds the text. In the release-build timing test
auto_height_in_columns_typing_timing (stories of 5,000 to 50,000
characters at 9 pt in a frame 500 pt wide), a keystroke laid the
columns out 18 to 22 times, and typing at the start of the story of
50,000 characters in 2 columns took 1,670 ms a keystroke.
The bisection now runs on the lines of the one-column layout.
columns_hold computes where those lines would sit in the columns, with
each column's first line at its first baseline below the column's top
(LineInfo::first_baseline, new in vectorcraft-text). The columns are
then laid out at the upper height the bisection ends on and, when they
hold the text there, at the lower one, unless that is the 1 pt it starts
from. When they hold the text at the upper height and not at the lower
one, Auto Size takes the upper height. When taller columns always hold
the text, the old search ends between the same two heights. When the
text wraps around other objects or does not fit the one-column layout,
or when that check fails, the columns are laid out at every step as
before. The same keystroke now takes 292 ms.
Docs: docs/target-app-parity.md.
On each pointer move of an Alt-drag with the Perspective Selection
tool, Session::preview put the drag's snapshot back and ran
perspective.move with copy again, and persp_edit copied every selected
object again. An Alt-drag of a plane widget with the Perspective Grid
or Perspective Selection tool ran perspective.plane.move with objects
"copy" again, and plane_move copied every object on the plane again.
Both commands now take their copies from preview_copies, as
apply_transform does for the Selection and transform tools: the first
preview copies the objects into a clone of the snapshot, and each later
preview of the same objects on the same snapshot maps only those
copies. The copies keep their ids, and the document, the selection,
the undo step and the journal are the same as before. When the stored
grid still lists attachments of its own, as grids saved before
attachments were kept on the objects do, both commands copy on every
preview as before. With 25,000 rectangles attached to the left plane,
a release build of vectorcraft-cli took a median of 424.8 ms for each
Alt-drag move of the objects after the first preview and takes
328.9 ms with this change. An Alt-drag of the plane's widget took
340.3 ms a move and takes 256.9 ms.
Docs: docs/target-app-parity.md.
The canvas drew artboard 1's edge in black, 1 px wide, whichever
artboard was active, and drew every other artboard's edge in gray,
0.6 px wide.
It now draws the active artboard's edge in black and draws the other
artboards' edges in gray, at the same widths. The active artboard is
the artboard that the status bar's navigator shows and that Align to
Artboard and Paste in Place use. With several artboards selected by
the Artboard tool, it is the one the tool draws with handles and its
name.
Docs: docs/target-app-parity.md.
The Layers panel narrowed its rows by name (Search All) or to layers
alone (Show Layers Only).
The panel now has a Filter button at the right of Search All. Its
popup holds Clear All and a check box for each object kind under By
Object (Paths, Shapes, Text, Images, Effects, Groups, Linked Files,
Guides) and for each row state under By Layer (Locked, Hidden). A row
stays when it is of a ticked kind, is in a ticked state and has the
search text in its name, and a section with nothing ticked keeps every
row. The rows that hold a kept row stay too, as they do for the
search. The button is lit while a filter is on. The filter lasts the
session and does not change the document, its open rows or the
selection. ui.layersFilter {objects?, layers?} sets it. The button
shows a new icon, assets/icons/dc-filter.svg, with its row in
ASSETS.md.
The panel lists its rows again when the filter changes, as it does
when the search changes. On a layer of 5,000 groups of 10 paths, a
release build lists the rows for a filter in 2.9 to 3.6 ms.
Docs: docs/mcp.md (Layers panel), docs/target-app-parity.md.
In Presentation Mode the arrow keys moved the selection, and the
active tool received a click and selected or drew as it does in
Normal Screen Mode. No key or click moved the view to another
artboard.
Left Arrow and Right Arrow now go to the previous and the next
artboard, and a click anywhere goes to the next one, through
view.goToArtboard, as the status bar navigator's Previous and Next
buttons do. On the first artboard Left Arrow leaves the view as it
is, and on the last artboard Right Arrow and a click leave it as it
is. No arrow key moves the selection in the mode, and the tool gets
no press, drag or hover, so a click selects and draws nothing. A drag
with the middle button or with Space held no longer pans the view
there; the mouse wheel still zooms and scrolls it.
Docs: docs/target-app-parity.md.
The status bar's artboard navigator showed only the active artboard's
number between its first, previous, next and last buttons, and it had
no list of the artboards.
The number is now a drop-down field that shows the active artboard's
number and name. Its list shows every artboard by number and name, as
the Artboards panel lists them. Choosing one runs view.goToArtboard,
the command the buttons run, which makes that artboard the active one
(and the Artboard tool's, while that tool is in use) and fits it in
the window. The list lays out only the rows in view and at most one
more, and opens with the active artboard's row in view. In a test
build with 1,000 artboards, the two frames that open the list take
0.12 ms and 0.04 ms, and a frame with the list open takes 0.03 ms. The
timed frames are headless and draw only the status bar.
Docs: docs/target-app-parity.md.
With the Artboard tool, the Properties panel showed the active
artboard's name as a label, and no control in the panel opened
Artboard Options.
The name is now a text field. Typing a new name and pressing Enter or
clicking elsewhere renames the active artboard in one undo step
through artboard.setProps, as the Artboards panel's rename does;
pressing Escape or leaving the field empty keeps the old name. A click
on the Artboard Options icon beside the field runs tool.options with
tool "artboard", which opens Artboard Options for the active artboard,
as a double-click on the Artboard tool's button does.
Docs: docs/target-app-parity.md.
In the View menu, Reset Rotate View was the only rotation item. The
status bar's angle menu set its twelve preset angles by writing the
view's rotation itself, and no command took an angle to turn the view
to.
View › Rotate View now lists the status bar's angles (0°, 15°, 30°,
45°, 60°, 90°, 180°, -15°, -30°, -45°, -60° and -90°) and checks the
one the view is turned to. The new UI command `view.rotate {angle}`
turns the view to any angle about the center of the canvas and
returns the angle it set, normalized to (-180, 180] as a Rotate View
drag normalizes it. The menu's items and the status bar's angle menu
run it from one list of angles; the status bar sets the same angles as
before. Without a document the command returns "no document" and the
items are disabled. Run from the command palette, which passes no
angle, Rotate View opens a dialog that asks for the angle, with the
view's current angle filled in.
The de, it, ja, ko, pl, pt-br, ru, uk, vi and zh-hant catalogs have a
new "Rotate View" row. The strings the catalogs cover leave out the
angle labels, as they leave out Type › Size's point sizes.
Docs: docs/mcp.md, docs/target-app-parity.md.
Export for Screens opened on the artboard choice the document last
exported with (all artboards by default), whichever artboards were
selected with the Artboard tool or in the Artboards panel.
While the Artboard tool is in use, File › Export › Export for Screens…
now opens with the artboards the tool has selected checked, and Export
Artboard writes only those. They are the artboards the Artboards panel
highlights, and they are checked even after a Full Document export.
With another tool in use, the dialog still opens on the saved artboard
choice. The other fields show the saved settings in both cases, and the
params text of file.exportForScreens describes the rule.
Docs: docs/control-protocol.md, docs/target-app-parity.md.
Free Distort's dialog showed the four corners as read-only text, and
so did the Appearance panel's inline editor. Only effect.apply and
effect.setParams could set them.
The dialog now shows a box in the proportions of the first selected
object's bounds with a handle on each corner. Dragging a handle moves
that corner, and the preview follows. A handle focused by a click or
Tab moves 1% of the box with each arrow key, and 10% with Shift. Reset
puts the four corners back on the box. Each handle has a
screen-reader name. On the corners row of the Appearance panel's
inline editor, Edit… opens the dialog.
Docs: docs/effects-parity.md, docs/target-app-parity.md.
Show Options and Hide Options were kept in egui's temporary memory, so
the Color panel showed its options again after every restart. No
command set them or the panel's mode.
- `UiState::color_hide_options` holds Hide Options and is saved with the
preferences. Its reader reads `true` as hidden and any other value as
shown, so a bad value leaves the other preferences as they are.
- `ui.colorPanel {mode?, options?}` picks a mode as the panel menu does
(the active color converts to the mode's model as one undo step) and
shows or hides the options. It answers `{mode, options, shown}`. A mode
the menu does not list, `lab` included, answers an error. The panel
menu's Show Options and Hide Options call the command's function.
- `ui.inspect` reports the setting as `ui.color_hide_options`.
Docs: docs/control-protocol.md, docs/target-app-parity.md.
On main, Glowing Edges… is listed in the Effect menu's vector Stylize
submenu, after Drop Shadow, Inner Glow, Outer Glow and Feather. The
reference app lists it in the Stylize submenu of its Photoshop effects,
below the vector effects.
The Effect menu now has a Stylize submenu under Raster Effects, between
Sharpen and Texture, and Glowing Edges is listed there. The menu sends
the effects at the Stylize path that PIXEL_EFFECTS names to that
submenu, and the vector Stylize submenu keeps its six effects. The
effect's id, its catalog entry, its effect.list entry and Apply Last
Effect do not change.
File › Save as Template always wrote a .vctemplate. The save panel
offered only that type, file.saveAsTemplate ignored its format param,
and a name typed with .ait got .vctemplate added. New from Template
already opened a .ait as an untitled document.
Save as Template now also writes a .ait: the PDF-compatible file Save
As writes for .ai, carrying the document. The desktop save panel lists
VectorCraft Template first and the .ait type beside it, and the web
build's Save as Template dialog gets a Format menu with the same two
types. file.saveAsTemplate writes .ait for format: "ait", or for a path
ending in .ait when no format is given, and takes the .ai options;
file.formatOptions answers for ait. .vctemplate stays the default.
Exports and Export As still do not write .ait.
Docs: docs/mcp.md, docs/file-format-parity.md (.ait row),
docs/target-app-parity.md.
Paste takes a file copied in a file manager by its flavor, which
file_flavour tells from the file's first 4,096 bytes through
fileio::detect. detect names gzip data SVGZ unless it is a compressed
native file, and file_flavour gave no flavor for the format id svgz.
So a copied .svgz file did not paste as its art, although
clipboard.importSvg, the SVG flavor's command, reads SVGZ data.
A copied file whose gzip data unpacks to SVG markup now pastes through
the SVG flavor. file_flavour unpacks the head it is given with the
gzip decoder the SVGZ reader uses, up to where the head ends, and
checks the first 4,096 unpacked bytes with looks_like_svg, as detect
checks an uncompressed file, reading them as UTF-16 after a UTF-16
byte order mark. Other gzip files get no flavor, as before.
Docs: docs/target-app-parity.md.
Paste takes a file copied in a file manager by its flavor, which
file_flavour tells from the file's first 4,096 bytes through
fileio::detect. detect names PDF or PostScript bytes in a file called
.ai or .ait by that extension, and file_flavour gave no flavor for
the format ids ai and ait. So a copied .ai or .ait file did not paste
as its art, while the same bytes in a .pdf or .eps file did.
A copied .ai or .ait file whose bytes are a PDF now pastes through the
PDF flavor (clipboard.importPdf), and one whose bytes are PostScript
pastes through the EPS flavor (clipboard.importEps). Other files
with those extensions paste as before. A .ai saved without its PDF
part also gets the PDF flavor, and Paste answers it with
clipboard.importPdf's error, as it does for the same file named .pdf.
Docs: docs/control-protocol.md, docs/development.md,
docs/target-app-parity.md.
When a .ai file, or an EPS with editing data, opened from its editing
data, the reader converted each embedded 8-bit CMYK image to RGB and
stored it as a PNG, so the document lost the image's ink amounts. When
the same .ai opens from its PDF part, the PDF importer keeps them in a
CMYK TIFF.
The reader now stores those samples with ImageBlob::cmyk_tiff, as the
PDF importer does (crates/pdf/src/import_image.rs). An alpha channel
that hides part of the image becomes the image's opacity mask, a
grayscale image over it, as in PDF import. Grayscale, RGB and 1-bit
images still open as RGBA PNGs.
Docs: docs/file-format-parity.md, docs/target-app-parity.md.