Effect › Distort & Transform › Zig Zag… lays out its options the way
Illustrator's dialog does: Size and Ridges per segment each have a
slider beside their field, Relative / Absolute are radio buttons under
Size, and Points offers Smooth and Corner as radio buttons instead of a
dropdown. The label reads "Ridges per segment".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ••• button at the bottom of the toolbar no longer opens All Tools as a
centred dialog. It opens the All Tools drawer beside the toolbar, next to the
button. Escape or a click outside closes it; a click in the toolbar doesn't.
Tools that are already in the toolbar show dimmed in the drawer.
While the drawer is open the toolbar can be edited, as the public
documentation describes:
- drag a tool from the drawer onto a toolbar button's top or bottom edge
(left or right edge in two columns) to put it in its own slot there;
- drag it onto a button's middle to add it to that button's group;
- drag a toolbar button into the drawer to take its tool out;
- drag a toolbar button to another place in the toolbar to move its tool.
A line or outline shows where the tool will go, and the dragged tool follows
the pointer. While the drawer is closed the toolbar can't be edited.
The Basic and Advanced toolbars each keep their own edits in
UiState::toolbar_edits, which is saved with the preferences. It is serde
default, so older preferences still load, and a hand-edited file is cleaned
up when it loads. An edit that brings back the default toolbar clears the
saved edits. New UI commands toolbar.addTool {tool, slot?, place?},
toolbar.removeTool {tool, group?}, toolbar.reset and
window.editToolbar {open?} let MCP and the control channel make the same
edits. The drawer also has a Reset button.
The drawer moved from dialogs/all_tools.rs into the new edit_toolbar.rs. It
is tested with headless egui frame tests driven by pointer events (placement,
Escape, click outside, dimming, drag and drop in each direction, the locked
toolbar), along with command, persistence and drop-zone tests. The three new
command labels are translated in every complete catalog, and the error
messages reuse existing translated templates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR kept a field's integer kind only while its value had been an
integer: typing 2.5 in Columns and then 3 sent 3.0, and the mosaic fell
back to 10 × 10 again. A whole number typed in any command dialog is now
an integer (`typed_number`) whatever the field held before; commands
that take any number read integers as well, and a fraction stays one.
Test: Object › Create Object Mosaic… opened from its menu item, 3 typed
over Columns and Enter makes a 3 × 10 mosaic (it made 10 × 10 before,
#1000).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Six raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::texture), under a new Effect › Texture
submenu between Sharpen and Video in the Raster Effects section:
- Craquelure (crackSpacing pt 2..100, default 15; crackDepth 0..10, 6;
crackBrightness 0..10, 9): the object on relief plaster that cracks into
plates about Crack Spacing apart (a Voronoi network over jittered points)
and along the quarter, half and three-quarter contours of its softened
tones. Depth sets how dark and wide the cracks are and how high the
plates' bevels and the tones stand (lit from the top left); Brightness
how brightly the surface is lit.
- Grain (intensity 0..100, 40; contrast 0..100, 50 = unchanged; grainType
Regular, Soft, Sprinkles, Clumped, Contrasty, Enlarged, Stippled,
Horizontal, Vertical, Speckle; default Regular): coloured 1-point grains,
smooth ones, clumps, 2-point grains, streaks along rows or columns, dark
specks; Sprinkles and Stippled use the background colour, white.
- Mosaic Tiles (tileSize pt 2..100, 12; groutWidth 1..15, 3; lightenGrout
0..10, 9): irregular tiles bevelled at their edges, with sunken grout half
a point wide per step, lightened as Lighten Grout says.
- Patchwork (squareSize pt 0..10, 4; relief 0..25, 8): squares filled with
the colour around their centres, raised to heights that follow the
highlights and shadows, a little more or less at random.
- Stained Glass (cellSize pt 2..50, 10; borderThickness 1..20, 4;
lightIntensity 0..10, 3): single-coloured panes leaded in the foreground
colour (black, half a point per step), lit from behind at the object's
centre and fading out towards its corners (PixelSpace gains `extent`).
- Texturizer (texture Brick, Burlap, Canvas, Sandstone, default Canvas;
scaling % 50..200, 100; relief 0..50, 4; lightDirection Bottom, Bottom
Left, Left, Top Left, Top, Top Right, Right, Bottom Right, default Top;
invert): the object on a surface in relief. The four surfaces are height
fields generated in code (bricks in stretcher bond, plain weaves coarse
and fine, octaves of smooth noise); no image files. Load Texture is left
out.
Every pattern lies in document space around the object's centre, so the
result is the same at any zoom or raster resolution, moves with the object
and is identical on every redraw (hash noise of cell indices); slopes are
taken in document units. Work per pixel is bounded (a 5 x 5 cell search,
four or five height samples, box blurs); parameters are clamped to their
ranges and finite-checked. The Pixelate helpers (Place, the cell search,
the softened colours) are shared, Mezzotint uses the shared smooth noise,
and the effect dialogs' dropdowns translate their labels in an "effect"
context where the plain word means something else (Soft, Horizontal and
Vertical are type settings in Japanese and Chinese).
Catalogue rows with parameter docs, outsets and reaches, menu entries,
dropdowns (Grain Type, Texture, Light Direction), translations, tests
(parameters and clamping, cracks and contours, ten distinct grains, tiles
and grout, squares in relief, leaded panes and the centre light, the
textures lit and inverted, the same at twice the resolution and when
moved, canvas, multithreaded, PDF and SVG, the command sweep with junk
parameters, the dialogs and the menu) and ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four Photoshop-style raster effects on the pixel-filter pipeline
(vectorcraft_effects::pixel::pixelate), under a new Effect › Pixelate
submenu between Blur and Sharpen in the Raster Effects section:
- Color Halftone (maxRadius 4..127, default 8; channel1..channel4 screen
angles -360..360, defaults 108/162/90/45): each colour channel is
screened at its angle in square cells sized so a dot of Max. Radius
fills one; each cell's dot covers the share of the cell that is the
channel's coverage-weighted mean there (white stays white, black
black). RGB documents screen R, G, B (channels 1 to 3); CMYK documents
screen C, M, Y, K inks (channels 1 to 4), also when they are drawn ink
plane by ink plane (the K plane uses channel 4).
- Crystallize (cellSize 3..300, default 10): every pixel takes the
colour at the nearest of one jittered point per cell (exact Voronoi
over a 5 x 5 neighbourhood, pruned by distance).
- Mezzotint (type: Fine/Medium/Grainy/Coarse Dots, Short/Medium/Long
Lines, Short/Medium/Long Strokes; default Fine Dots; a dropdown in the
dialog): each channel is turned fully on or off against its own random
threshold pattern, so the object becomes pure colours.
- Pointillize (cellSize 3..300, default 5): one dot of random size per
cell at a jittered point, coloured by the softened object there and as
opaque as the object at its centre, stacked in random order over a
white canvas where the object is.
Lengths are in points (Scale Strokes & Effects scales them) and every
pattern lies in document space around the object's centre, so the
result is the same at any zoom or raster resolution, moves with the
object, and is identical on every redraw (hash noise of the pattern's
cell indices, no state). Work per pixel is bounded whatever the
parameters, which are clamped to their ranges and finite-checked; the
halftone's cell grid is capped by the raster's size and a screen finer
than 3 pixels leaves the image as it is (its average). PixelSpace gains
`channels` so the renderer tells the filters what the channels hold.
Catalogue rows with parameter docs, outsets and reaches for cropping,
menu entries, dialog labels (Max. Radius, Channel 1-4, Cell Size), the
Mezzotint Type dropdown, translations, and tests: parameters and
clamping, tone and coverage, CMYK inks, polygon crystals, pure-colour
mezzotints with ten distinct patterns, dots on a white canvas, the same
result at twice the resolution and when moved, the canvas, PDF and SVG
through the engine, the command sweep with junk parameters, the dialogs
and the menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effect › Distort & Transform › Transform… was a plain list of fields.
It now has Transform Each's layout: Scale and Move with sliders, Rotate
with an angle dial, then Copies, Reflect X and Y, the reference point and
Random, kept with the effect so editing it from the Appearance panel
shows them again.
- The effect scales, rotates and reflects about the chosen point of the
9-point grid (`reference`, the centre by default).
- Random takes a share of each value, drawn from the object's id: each
object varies its own way and keeps its result on every redraw; a
group's members move together.
- Copies stop before one reaches past 1e9 pt, and the scale and move are
capped, so large values can't overflow.
- Transform Each and the effect share the sections and options code; the
angle dial is a new widget.
- A plain number field now takes the focus when it is a dialog's first
field, so both dialogs open with Scale › Horizontal ready for typing.
Transform Objects, Transform Patterns and Scale Strokes & Effects are not
in the effect yet: the geometry effects reshape paths only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicking a brush in the Brushes panel (or the panel menu's Brush
Options…) opens its options. Calligraphic Brush Options: Angle,
Roundness and Size, each Fixed, Random or Pressure with its Variation,
over live previews of the nib and of a stroke. Bristle Brush Options:
Shape and the bristle sliders. OK runs brush.options; strokes painted
with the brush update.
The Paintbrush records the pen pressure along a stroke (path.freehand
takes [x, y, pressure] points), and a Calligraphic brush with Pressure
variation changes its nib along the stroke, from the value less the
variation at the lightest pressure to the value plus it at the heaviest.
Brushes saved before keep their Random variation. Pen pressure doesn't
reach the app on macOS yet (winit 0.30 reports none there).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Artboard Options listed its fields by key (Height, Name, Width); it now
lists Name, then Width and Height.
- One-key tool and command shortcuts (V, P, X, D…) matched the character
typed, so with a layout without Latin letters (Persian, Arabic, Russian,
Greek…) they did nothing. When the text typed holds no ASCII character,
the key's position stands in: the V key selects the Selection tool
whatever it types. Latin layouts (AZERTY…) keep the letter typed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Effect › Blur › Radial Blur (spin/zoom, draft/good/best) and Smart Blur
(Normal mode), Effect › Sharpen › Unsharp Mask: Photoshop-style filters in
vectorcraft_effects::pixel, run by the renderer on the object's pixels in
stack order with Gaussian Blur and Feather, cached while panning; lengths in
points, bounded work per pixel. PDF, Expand Appearance and SVG export write
the effected objects as images. Dialogs with preview (method/quality
dropdowns, fields in documented order), menu entries, translations, tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The modal dialogs were anchored to the window's centre, so none of them
could be moved. They now share one modal frame (dialogs/modal.rs): the
band from the window's top edge down to its heading moves it, as a title
bar would, with a grab cursor; it stays on screen and keeps where it was
moved (per dialog, for the session). The shared dialog frame, New
Document (and More Settings), Preferences, Keyboard Shortcuts,
Workspaces and Find Font all use it; About moves by its title bar.
The form dialogs drew every string field as a text box, so Offset
Path's Joins had to be typed. Their choice fields are now dropdowns
(Joins: Miter, Round, Bevel; Average's axis; Shear's and Reflect's
axis), the fields follow the dialog's order (Offset, Joins, Miter Limit;
Rows, Columns, Gutter) instead of alphabetical, and Offset Path has a
Preview that Cancel rolls back.
The axis labels are translated in a new "axis" catalog context: the
plain Horizontal and Vertical rows mean writing direction in Japanese.
ja, es, zh-hans and zh-hant get the axis rows (Horizontal, Vertical,
Both) and Joins, Miter, Bevel, Offset and Axis.
Headless tests drag the heading of four dialog windows (moves, a drag
below the heading doesn't, kept on screen, reopens where it was left),
open the Joins dropdown and choose Round (same result as the command
with round joins), and check the axis dropdowns and the preview.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rotate, Scale, Move and the other form dialogs opened with no field
focused, so a typed value went nowhere and Enter applied the default (a
Rotate by 0 degrees). The frame a dialog opens on (after the window's
measuring frame), its first text or number field takes the keyboard
focus with its text selected, so typing replaces it and Enter applies
it, as in the reference app.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the in-window menu bar (Windows and Linux) each top-level menu needed
its own click: with File open, moving the pointer onto Edit or Object did
nothing. Now, as on a native menu bar, while one menu is open a moving
pointer that reaches another title opens that menu instead (ported from
PhotoCraft). A pointer resting on a title doesn't switch, and a tall menu
that covers the bar doesn't switch to the title underneath it. The native
macOS menu bar is unchanged.
Also adds a regression test for #292: the Simplify, Offset Path, Move,
Rotate, Split Into Grid and Average dialogs are 366 pt wide in a 1600 pt
window (already fixed by #239).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lists and headings that mix our labels with names no longer send the
names through the catalog, so a preset, printer or plug-in called
"Regular" or "Black" keeps its name in every language.
- dialogs::shown_names / mixed_dropdown: translate only a list's built-in
entries and show the rest with widgets::dropdown_names (same indices).
- Artboard lists (Save for Office, Save for Web, save options) and
plug-in choices: dropdown_names.
- Mixed lists translate only their built-in entries: Recolor's Limit to
Library, Save for Web, Print, Save PDF and Export for Screens presets,
printers (Default Printer, PDF File), colour profiles and flattener
presets (profile_pick, flatten::preset_dropdown), perspective presets,
New Document's preset cards and Size, and the shortcut set.
- Plug-in dialogs and plug-in effects are headed by the plug-in's name as
it is; plug-in parameter labels are not translated.
- The confirm dialog shows its message and detail as given, and the
shared frame no longer translates headings again: each heading is
translated by its spec. File > Revert translates its template before
filling in the document's name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The OK/Cancel row was laid out right to left at the full available
width, and a dialog without a maximum width may use the whole window:
the row, and the window with it, spanned the window (Clean Up, Blend
Options, every generic parameter dialog). The row now spans the fields
above it, at least the dialog's minimum width, so a dialog is sized to
its content.
Fixes#238.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recolor Artwork keys colours by identity (model and exact values), so CMYK
colours stay CMYK and lookalikes stay apart. recolor.reduce groups the
selection's colours into rows (k-means in Lab, weighted by use; tints of a
global swatch share its row) with Preserve White/Black/Grays and Limit to
Library; recolor.apply maps rows by Exact, Preserve Tints, Scale Tints, Tints
and Shades or Hue Shift; recolor.randomize shuffles or varies new colours.
The dialog has Assign (rows with merge, separate, exclude, randomize, method,
library) and Edit (hue/saturation wheel with linked or unlinked handles,
brightness, harmony rules) tabs, and Edit Colors gains Recolor with Preset
(1/2/3 Color Job, Color Library). Colour parameters accept colour keys.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fourteen dialog openers (swatch, colour, style, flattener, expand, effect and
parameter dialogs) are drawn on a 1000 pt and a 2000 pt tall screen; each
dialog must be the same height on both, so nothing inside one fills the
available height again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared dialog frame's button row took all the height left in the window
(a right-to-left layout fills the available height), and every dialog shared
one window id, so a dialog never shrank and inherited the size of the one
before it: the save prompt showed a tall empty band around its buttons. The
row is now as tall as the buttons and each dialog kind has its own window.
Menu items forced themselves to the popup's full available width, so every
menu opened at egui's maximum menu width. The menu layout already justifies
items, so popups are now as wide as their widest label and shortcut (at least
230 pt).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
effect.apply, effect.remove, effect.setParams and the new effect.duplicate
take item (omitted: the Appearance panel's active item, else the object;
null: the object's own effects), so the fx button, the Effect menu, Apply
Last Effect and the effect dialogs (effect.dialog {item}) put the effect on
the selected fill or stroke. effect.list reports each object's item effects
and the active item. appearance.addEffect is an alias of effect.apply.
In the Appearance panel, item effect rows sit under their fill or stroke
with working eyes and inline editors, and can be selected for Duplicate
and Delete; a selected effect row is remembered per object.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No behaviour change: every command keeps its id, label, menu, shortcut and
params doc.
- Engine: swatch.* -> cmd/swatch.rs, graphicStyle.* -> cmd/style.rs,
appearance.* (incl. copyFrom) -> cmd/appearance.rs, stroke.set/setAdvanced
-> cmd/stroke.rs, paint.editGradient/setGradientGeom -> cmd/gradient.rs.
Shared helpers in cmd/mod.rs: leaf_targets/paint_targets (replacing three
copies) and unique_name (swatch and graphic style naming).
- UI: dialogs.rs -> dialogs/ with one file per dialog and a DialogSpec
registry (one DialogKind row per dialog); shared field widgets in
dialogs/form.rs. Headless frame tests draw and confirm every dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>