Discord feedback: type in a frame dragged with the Type tool could not be resized or switched
between area and point type the way the reference app allows.
- The Selection tool shows the type widget beside a single selected point or area type object:
a small circle outside the middle of its bounding box's right side, hollow on point type and
filled on area type (vectorcraft_tools::typewidget, drawn by the canvas from the same geometry).
Hovering it shows the arrow with a type badge; its clicks neither drag nor deselect, and a
double-click runs type.convertToAreaType / type.convertToPointType (the Type menu's commands),
keeping the text and its styles in place, one undo step, reachable over MCP.
- Resizing area type made by dragging already works on main (#252): the bounding box resizes the
type area and the text reflows. A regression test now covers the reported flow.
- docs/mcp.md and ROADMAP.md describe the widget.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Discord feedback.
- Properties panel: the Appearance section's Fill and Stroke rows now use the
Control bar's widgets: the chips with their chevron and swatch/mixer
popover, the underlined Stroke link that opens the Stroke panel as a
popover (stroke::link, shared with the Control bar) and the weight spinner
with its presets (stroke::weight_field, which also follows the Type tool's
selected characters). With nothing selected the Document view shows them
too, as the Control bar does: they set up the next object drawn.
- Window menu panel keys as in Illustrator: Brushes F5, Layers F7, Align
Shift+F7, Transform Shift+F8, Info Cmd+F8, Pathfinder Cmd+Shift+F9,
Symbols Cmd+Shift+F11, Character Cmd+T, Paragraph Cmd+Alt+T, Tabs
Cmd+Shift+T, OpenType Cmd+Alt+Shift+T (beside the existing Color, Color
Guide, Appearance, Graphic Styles, Stroke, Gradient, Transparency and
Attributes keys). The dock's tabs (Properties, Layers, Libraries) join the
panel shortcut table and the Keyboard Shortcuts editor through
state::all_panels, which window.panel's name lookup and the catalog's
label list use too.
- Alt-wheel (Option on the Mac) zooms about the pointer whatever General >
Zoom with Mouse Wheel says: egui spreads a notch over a few frames, and the
rest of an Alt turn scrolled once Alt was up (also over the control
channel's ui.wheel); the turn's own modifiers now decide. Pinch zoom is
covered by the test too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A screenshot on the clipboard didn't paste everywhere:
- macOS: the Paste menu item (which takes Cmd+V there) was enabled for text
only, so with nothing copied in the app Cmd+V did nothing; and arboard
reads the pasteboard's TIFF only. The pasteboard's types now enable Paste,
and its PNG, else TIFF, pastes as it is.
- Windows: bitmaps came through CF_BITMAP and GetDIBits, which drops alpha.
Paste now reads PNG, else CF_DIBV5, else CF_DIB, decoded with their colour
masks and tables (a BMP file header in front, which image's headerless
decoder gets wrong for masks after a BITMAPINFOHEADER), opaque when the
alpha is zero throughout as screenshots leave it, capped at 32768 px a side.
- Web: only SVG text pasted. The page's paste event now hands the first
picture or file that is art to the app (VectorcraftApp::paste_from_host),
pasted with the chord held, like the desktop's paste keys.
- Linux reads image/png already; its Paste menu item still waits for text
(no cheap way to ask arboard for a bitmap), the paste keys take one.
Bitmaps paste as embedded images at the view centre, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Properties panel's Font Size and Leading were plain typed fields and the
Control bar had no type controls. All three places now use the shared
editable combo (`widgets::spin_field`: typing, arrow and spinner stepping,
label scrubbing, a preset dropdown in the type unit):
- Character panel: the size and leading grid is shared with the Properties
panel (`character::metrics_grid`), which now shows the font style, Font
Size, Leading, Kerning and Tracking as combos and applies them to the
Type tool's selected characters as the Character panel does.
- Control bar: with type selected or edited, a Character link (the Character
panel in a popover), the font family and style menus and the Font Size combo.
- Presets as Illustrator lists them: sizes 6-72 pt; Leading has Auto first
(`widgets::spin_field_auto`, `text.setStyle {leading: "auto"}`) and then
the size ladder instead of 120% values; Kerning and Tracking -100...200.
- Font Size, Leading, Kerning and Tracking show blank where the selected
text's values differ (`character::shared`); the spinner does nothing on a
blank field instead of jumping to the step size.
Each pick is one `text.setStyle` / `text.setRangeStyle` step, so MCP and the
control channel are unchanged. Translations for the two new strings.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- `Targets::align_rects` tells `snap_point_with` an axis lined up even when
nothing moved or Alignment Guides hide the line, so the "align" label stays.
- Free Transform's move offset is a measurement label (as the Selection
tool's), its scale/rotate readouts Transform Tools; slice move/resize
readouts follow Measurement Labels.
- Snap to Point (Smart Guides off) keeps the default look for moves too, as
for drawn points and guides.
- Test: guides dragged out of a ruler (#451) follow Snapping Tolerance,
Anchor/Path Labels and Measurement Labels; docs/mcp.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Color, Alignment Guides, Anchor/Path Labels, Measurement Labels,
Transform Tools, Object Highlighting and Snapping Tolerance are read:
- tools: `guides::GuideStyle` on `Targets` (`styled(cx)`) colours the
lines and labels and leaves them out when their option is off, the
snap itself unchanged; `ToolContext` gains the six fields and
`snap_tol()`, replacing the 5 px literal for every smart guide snap
(drawn points, moves, bounding-box handles, ruler guides, artboards,
reference points). The tools gate their `Overlay::Measure` readouts on
Measurement Labels (drawing, moving, ruler guides, artboards) or
Transform Tools (scaling, rotating, Free Transform, transform tools).
- engine: `with_tool_cx` fills them from `Prefs` (the colour parsed, magenta
as fallback); the colour's default is `#ff3dfc`, what the tools drew.
- ui-egui: the hover outline needs Object Highlighting and Smart Guides.
- Tests in tools (guides, select), engine (tests_prefs) and the canvas;
docs/mcp.md and ROADMAP.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The toolbar went to two columns whenever the window was shorter than the
single column (about 990 pt for Basic, more for Advanced), so on most
screens it was always double and the double arrow and the Window menu item
flipped a flag nothing read. The column count is now the user's choice
alone; a window too short for the tools scrolls them (#84).
- window.toolbarColumns takes {double?: bool} (set or toggle, returns the
new state) and reports `checked`; the double arrow runs it.
- The Window › Toolbars item reads "Double Column" with a check mark;
catalogs updated (cs, es, ja, pt-br, zh-hans, zh-hant) and an es @msg row
for the new error.
- Headless tests: the arrow and the command toggle the column count in a
700 pt window (both fail on main).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Guides dragged out of a ruler did not snap at all (only Shift to the ruler's
ticks): they now go through the engine (`Session::ruler_guide`, a
`NewGuide` previewing `guide.add`, one undo step) and snap as a moved guide
does: with Smart Guides to the edges, centres (so the sides' midpoints) and
anchors of the art and of the artboards, labelled; with Smart Guides off,
View > Snap to Point pulls new and moved guides onto anchors. Moving art
with the Selection tool already snapped flush to artboard edges and centres
with Smart Guides on; a test now covers it.
Artboard guides: `Guide.artboard` holds an artboard id (written only when
set, so canvas guides save as before and older files open unchanged).
`guide.add {artboard}` makes one, `guide.list` reports it; a guide dragged
out of a ruler with the Artboard tool belongs to the active artboard. It is
drawn, picked and snapped to across its artboard only, moves with it
(`artboard.move`, a pure move in `artboard.setProps`, `artboard.rearrange`),
is copied with it (`artboard.duplicate`, Alt-drag copy) and deleted with it
(`artboard.delete`, the guide selection kept in step).
Guides as Layers rows with their own lock and hide (part d) stay open: ruler
guides are not layer nodes; Lock Guides locks them all, and Make Guides
paths are Layers rows already. Listed in the ROADMAP parity row.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With the Artboard tool chosen, Copy and Cut take its artboard together with
the art fully inside it (when Move/Copy Artwork with Artboard is on), and
Paste brings both back in one undo step: Paste right of the last artboard,
Paste in Place / in Front / in Back where it was, into this or another
document, the art going back into the layers it came from (Paste on All
Artboards pastes the art alone). The clipboard carries the artboard
(Clipboard::artboard); a pasted artboard keeps its name when it is free.
Duplicate Artboards (the Artboards panel menu, and now a row dragged onto
New Artboard) copies the art too: artboard.duplicate {index?, art?} defaults
to the Artboard tool's artboard and moveArt option and reports the copies.
It shares copy_artboard with the Alt-drag copy (artboard.move {copy}).
New commands reachable over MCP: artboard.copy and artboard.cut
{index?, art?}; edit.copy / edit.cut run them while the Artboard tool is
chosen (and are enabled then with nothing selected). Docs (mcp.md), the
tool and panel docs, translations of the new labels and the ROADMAP follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Over the canvas the window hid the system cursor and painted the tool's
cursor glyph into its own frame. That glyph reached the screen only after
the frame was rendered, queued (two frames by default) and composited, so
on Windows it visibly trailed the real pointer, on any GPU: the "sluggish
mouse" of the report. Frame times were not the cause (0.4 ms empty,
1.5 ms median on a 20 000-path document, and no repaints while idle).
- cursors: the glyphs are collected as shapes, tessellated by egui and
rasterized into straight-alpha RGBA bitmaps (anti-aliased, hotspot on a
pixel corner), cached per cursor and display scale so the same Arc is
handed over every frame and egui-winit keeps its OS cursor.
- canvas: on Windows and Linux the tool cursor goes to the OS through
ctx.set_cursor_image; the system moves it itself. macOS (bitmaps sized
in points) and the web (no bitmap cursors in eframe) keep painting it.
- app: the wgpu surface queues one frame instead of two (LOW_LATENCY):
the GPU only composites the CPU-rendered canvas, so everything drawn in
the window follows the pointer a frame sooner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Live rectangles drew elliptical corners after an uneven scale in v0.4.0;
inconsistent on a rectangle that isn't square:
- doc: LiveShape::fold_scale moves an uneven scale left in `xf` (files
saved before #291) into the size and radii; `transform` uses it, and
`folded()`/`corner_style` give Live Corners document units.
- engine: object.setLiveShape folds that scale first, so setting a radius
or kind (fields, dialog, widgets, MCP) gives circular corners on such
shapes instead of keeping the ellipses.
- geom: max_corner_radius/fitted_corner_radius are the one limit every
corner shares (half the shorter side).
- tools: corner widgets sit at the centres of the corners as drawn (a
radius past the limit put them off the arc, further in along the long
side), drag in document units on stretched shapes, and outline the
edited corners in red at the limit, as the reference app does.
- svg: CSS export writes the drawn radii (CSS would draw a radius past the
limit when its neighbours leave room).
- docs: mcp.md Live Corners, ROADMAP.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The scan read only the platform's font folders, once per session (or on
Character panel menu > Refresh Font List). Fonts Windows registers outside
its font folders (installed as shortcuts, or by programs into their own
folders) never listed, nor did fonts installed while the app was open.
- Windows: the font files registered under the local machine's and the
user's Fonts registry keys outside the scanned folders are scanned too
(windows-registry, the windows-rs project's safe registry API).
- Linux/BSD: the XDG data folders' fonts (XDG_DATA_HOME, XDG_DATA_DIRS:
Flatpak, Snap, NixOS, Guix) and a Flatpak sandbox's host fonts
(/run/host/fonts, local-fonts, user-fonts); macOS: /Network/Library/Fonts.
- The global database looks the platform's paths up again at each scan,
and a scan's roots may be font files as well as folders.
- FontDb::installed_fonts_changed compares the folders the last scan read
(their modification times, and the paths it started from); the app
checks it when its window comes back to the front and rescans
(text.rescanFonts) only when fonts were installed or removed meanwhile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Alt already deleted regions and pieces of open lines, but erase mode had
no edges of closed shapes, no distinct pointer, highlighted regions in the
merge-mode grey, only noticed Alt on a mouse move, and an erase drag could
flip mode at release. Like the reference app's erase mode:
- pathops::shape_builder(shapes, edges) returns a BuilderArrangement; with
`edges` it also cuts the outlines of closed (and filled open) paths
wherever other paths meet them. pathops::cut_out removes such a piece
from a path: a closed subpath opens there, an open one splits or
shortens, a piece all the way round takes its subpath, and a filled open
path's invisible closing edge never comes back as a visible segment.
Specks where paths only touch cut nothing; a zero-length input is no
longer the source of edges running through it. encloses_area is shared
with the tool's closing of filled open paths.
- BuilderMap (with_edges) hit-tests edges like line pieces (nearest wins);
an erase gesture that crosses an edge into a region deletes the region,
not the edge (erased_edges).
- shapeBuilder.merge {erase}: deletes the edges touched within tolerance
(from every shape along them), after the regions; returns `edges`.
shapeBuilder.regions lists `edges` (bounds, source). One undo step.
- Shape Builder tool: Alt held switches to erase mode and back on release:
red mesh/edge highlights of what will be deleted, a minus pointer
(Cursor::ShapeBuilder / ShapeBuilderErase, a plus in merge mode); a drag
keeps the mode it started in. The canvas re-hovers the tool when a
modifier changes, so the highlight follows Alt without a mouse move.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- A click on a flyout's tear-off bar, or the release of the long press
that opened it over the bar, tears the flyout off too, as in
Illustrator; dragging it still does.
- window.floatTools {tool, floating?} floats or puts back the group
holding a tool, for the control channel and MCP's invoke_menu.
- Floating strips are part of user workspaces; the built-in ones float
none, so Reset puts them back.
- Loaded preferences keep one strip per group, of known tools.
- No per-frame clone of the strips or copy of the active tool's id.
- docs/control-protocol.md, ROADMAP, Float Tool Group translations.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A press on a single tool's button no longer schedules the repaint that
would open a flyout it doesn't have. The ROADMAP's UI list and Last
updated line name the right and corner press.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A grab bar runs down the right side of a tool group's flyout. Dragging it
tears the flyout off: the group floats as a strip of its tools' buttons
(the active one lit, each tool's name and shortcut in its tool tip), with
the bar on its right. The same bar moves the strip, and the × at the top
of the bar puts the group back in the toolbar. While a group floats, the
presses that would open its flyout (right, long or corner press) raise
the strip and flash its border instead, so a group is never both a
flyout and a floating strip. Floating strips are kept with the UI
preferences and kept on screen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A right press opens the flyout on the press instead of on the release,
including on the active tool's button, and the release leaves it open. A
primary press on the button's corner triangle opens it at once without
picking the tool; a plain click in the middle still picks it. The long
press keeps scheduling the frame where it becomes long.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Illustrator documents two drop targets in the Layers panel's bottom bar:
dragging items to Create New Layer duplicates them, to the Delete icon
deletes them. Dropping rows on New Sublayer or Make/Release Clipping
Mask no longer creates a sublayer or clips the row's layer; those
buttons only act on a click, and the new test checks a drop on New
Sublayer changes nothing. The trash and New Layer share one drop loop,
and the drag ghost paints its colour bar and thumbnail with the row's
own helpers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dragging a row shows a dimmed copy of it (colour bar, thumbnail, name)
under the pointer. A row dropped on Delete, New Layer, New Sublayer or
Make/Release Clipping Mask runs that button on the row's layer, as a
click would: Delete removes the row itself and New Layer duplicates it,
as in Illustrator.
Rows dragged onto the trash weren't deleted. The trash took the drop as
a target-circle payload first, and egui's take discards a payload of
another type, so the rows reached the second check as nothing. It now
peeks at the type before taking. The trash is also outlined while rows
or a target circle are over it, like the other drop buttons.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Each preset in the Color dropdown has a chip of its colour before its
name; Custom has none, its name aligned with the others.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Type on a path could only be repositioned by Type on a Path Options, whose menu item sent
`{start: 0}`, so OK threw the text to the start of its path; nothing showed or dragged its
brackets. Now, as in Illustrator:
- Type on a path flows between a start and an end bracket (`TextKind::OnPath { end }`, None:
the end of the path or once round a closed one; `TextKind::path_span`). Flipping
(`TextObject::flip_on_path`) reverses the path keeping a closed path's first anchor and swaps
the brackets, so the type keeps its stretch of the path.
- Type on a Path Options gains Align to Path (ascender, descender, center, baseline) and
Spacing (glyphs spaced as if set that far above the path: closed up round the outside of
curves, opened up round the inside), laid out by the text engine.
- `type.pathOptions {start?, end?, flip?, effect?, alignToPath?, spacing?, ids?}` sets them in
one undo step; with none of them it queries. The menu item opens the dialog with the selected
type's Effect, Flip, Align to Path and Spacing (dropdowns for the choices) and leaves the
brackets alone, so OK as opened changes nothing.
- The Selection and Direct Selection tools show selected type on a path's path and its start,
centre and end brackets (`tools::pathtype`, with a bracket cursor): dragging the start or end
bracket sets where the type runs, dragging the centre bracket slides it along the path and,
across the path, flips it (Cmd/Ctrl held: it only slides). Each drag is one undo step.
- Arc-length measuring moved to `vectorcraft_geom::ArcPath` (layout, the tools and
`text.createInPath` share it). SVG export outlines path type whose end bracket, alignment or
spacing `<textPath>` can't express.
- docs/mcp.md documents the command and the drags; ROADMAP updated; dialog strings translated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The popover kept its open flag while its anchor was hidden (panel closed, selection changed), so
it came back open the next time the row was drawn. It now remembers the last frame it was open in
and counts as closed after a frame without it, as egui's remembered popups do. ROADMAP: the
Appearance panel's Opacity popup.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The pt ladder stays 0.25 … 10, 20, 30 … 100 pt (the dropdown's documented 20/30/40 pt steps).
- Feet, yards and meters returned the pt ladder converted as if it were in that unit (a 100 m
stroke); they now get the pt ladder in points.
- weight_presets returns a fixed array (no allocation per frame); the pica ladder is stored in
points; 0.325 in is 0.375 in (the eighths ladder).
- Fields keep three decimals in points, pixels and picas (595.276 pt) and show four in mm, cm,
inches and larger units (8.2677 in, 0.0078 in).
- ROADMAP: the Stroke panel's per-unit presets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
egui remembers one open popup at a time, so opening the blend-mode
dropdown or the opacity slider inside the Opacity popup closed the popup,
and the dropdown or slider with it. A new widgets::popover keeps its own
open state and closes only on Escape, its anchor, or a click outside it
and the popups it opened. The Opacity popup and the Stroke popover (which
holds dropdowns too) use it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The Stroke panel weight dropdown converted one pt-based preset list into
whatever Units > Stroke was set to, which produced ugly rounded values like
0.088 mm, 0.265 mm, 10.583 mm from the pt ladder. Each unit now has its own
native ladder (observed from Illustrator's running dropdowns and typed in),
converted to pt once at use. Points, Millimeters, Centimeters, Inches,
Pixels and Picas each get Illustrator's actual values; the exotic Unit
variants (Feet & Inches, Meters, Yards, Feet) fall back to the pt ladder.
Picas round-trip to exact pt weights (1 pt, 12 pt, 60 pt …) but display as
decimal picas until Unit::number learns 0p1 / 1p3 notation; that is a
system-wide formatter change left for a separate PR.
Also bumps Unit::number precision from 3 to 4 decimals so Illustrator's
sub-eighth-inch strokes (0.0078, 0.0156, 0.0313, 0.0625 in) show their
authored values instead of rounding to 0.008 / 0.016 / 0.031 / 0.063.
Trailing zeros are still trimmed, so 1 pt stays "1".
Co-Authored-By: Claude Opus 4.7 <[email protected]>