M3: add Selection colors for mixed selections (#403)

This commit is contained in:
Ivan Shishkanov
2026-10-15 19:05:24 +03:00
parent 102404e8c3
commit 7a5885cde9
14 changed files with 879 additions and 2 deletions
+21
View File
@@ -1342,6 +1342,27 @@ through the outlines of its layers.
clears it; no `preset` reads it). The desktop app reads pressure from pen input on Windows; in the
browser from stylus touch events that report a force; macOS and Linux tablets report none.
### Selection colors
`selection.colors {ids?}` returns `{colors: [{paint, style, variable, targets}]}` across the
selection and its descendants, deduplicating overlapping roots. Only visible solid and gradient
paints are listed; masks and boolean operands are omitted. Styles and variables distinguish
otherwise equal paints. Each target is `{id, fills, index, range}`; `range` is null for a node
paint or `[start, end]` in character offsets for an effective text-range fill.
Pass the returned target list to:
- `selection.setPaint {targets, paint}` to replace only those slots (same paint syntax as `fill.set`).
- `selection.selectColor {targets}` to select their distinct owning layers, without a document undo step.
- `selection.applyColorStyle {targets, style}` to apply a document paint style. As with `style.apply`,
this replaces the matching node's entire fill/stroke list and links the style. Text ranges copy
resolved paints and keep their typography, without a paint-style link.
Keep the targets fixed throughout a live interaction. A color becoming equal to another color
must not add slots to the gesture. Invalid or missing targets fail atomically. Direct paint edits
detach bindings on changed paint values (opacity alone keeps the color binding); a text-range edit preserves the node bindings other
characters still inherit. Each mutating command is one undo step (an interaction can group them).
### Paint, effect and layout grid lists
A layer's fills, strokes, effects and layout grids are lists kept **bottom first**: index 0 paints
+21 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** minor (the Small caps sample in Type settings drawn from capitals; recent files as cards; vector edit mode: hovering, hatching, selecting and dragging filled regions; home screen: Templates tab, first-run templates and Getting started tips; number fields: scrub rates, arrow steps, arithmetic with brackets, relative edits of mixed values; outline mode's hidden layers and object bounds; Hide when publishing; cursors per tool and modifier; panel widths; dark pages for files made in the dark theme; drag threshold and double-clicks into text in groups; rotation origin on Alt+R and placing with Enter: no documented shortcut missing; keyboard shortcut parity suite and bindings; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and the vector edit tail; canvas navigation, guides and grids behaviour suite and fixes; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; layers panel keyboard: F2 rename, Shift+F10 context menu, F8 box selection; selection and transform behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; boolean hit testing on the result, group drags, frame outlines and slices; outlines for the selection, mask outlines; arc fields, live boolean editing, shape builder on strokes, one-sided widths) · **Target:** Figma Design UI3 (desktop app 126.10.7)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-15 · **Change:** minor (Selection colors for mixed selections (#403); the Small caps sample in Type settings drawn from capitals; recent files as cards; vector edit mode: hovering, hatching, selecting and dragging filled regions; home screen: Templates tab, first-run templates and Getting started tips; number fields: scrub rates, arrow steps, arithmetic with brackets, relative edits of mixed values; outline mode's hidden layers and object bounds; Hide when publishing; cursors per tool and modifier; panel widths; dark pages for files made in the dark theme; drag threshold and double-clicks into text in groups; rotation origin on Alt+R and placing with Enter: no documented shortcut missing; keyboard shortcut parity suite and bindings; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and the vector edit tail; canvas navigation, guides and grids behaviour suite and fixes; shapes and vector behaviour suite and fixes; paint and effects behaviour suite and fixes; frames, groups, sections and masks behaviour suite and fixes; layers panel keyboard: F2 rename, Shift+F10 context menu, F8 box selection; selection and transform behaviour suite and fixes; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; boolean hit testing on the result, group drags, frame outlines and slices; outlines for the selection, mask outlines; arc fields, live boolean editing, shape builder on strokes, one-sided widths) · **Target:** Figma Design UI3 (desktop app 126.10.7)
Fine canvas control is what Figma users feel first: handles, snapping, nudging, modifier keys,
drag behaviour, numeric precision. This checklist covers the tools, gestures and panels. Status:
@@ -107,6 +107,25 @@ interaction feel not yet compared.
| Cursors per tool and modifier | done | the engine names a cursor for every tool, modifier and hover target (`tool.overlay.cursor`, tabled in `cursor_tests`) and the UI shows the system's or draws its own at the pointer (`cursors.rs`, `cursors_tests`; original glyphs from lines and the code-drawn icons). Arrow for Move and Scale; two-headed arrows on the handles that follow the handle and the box's rotation; outside a corner a curved rotate arrow turned to that corner, turning with the pointer while rotating; crosshair for the frame, section, slice, shape, pencil and Text tools; the I-beam over a text with the Text tool and over the text being edited; the pen as a cross with what a click does beside it (nothing: a new point; a ring: closes, connects to or goes on from a point; a plus: adds a point on a segment, also Alt in vector edit mode; a curve: bends, the Bend tool or Cmd/Ctrl); open and closed hand for the Hand tool, Space and the middle button; zoom in and out (Alt) while Z is held; eyedropper; comment pin; the copy cursor with Alt over a selected layer and while Alt-dragging; not-allowed while a drag resizes, rotates or moves a layer inside an instance (it is undone on release) and over a layers row that takes no drop. Ruler guides and gradient, crop and padding handles keep their own cursors (the tool's used to replace them). `ui.inspect` reports `cursor`. Left: no cursor for removing a point with the pen (the pen doesn't remove points); the drawn cursors follow the pointer a frame behind the system's; not compared with Figma's side by side |
| Multiplayer cursors, cursor chat, spotlight | partial | in live sessions: named cursors, chat bubbles that fade, `/` to chat, spotlight from your avatar's menu with a banner; no audio or reactions |
### Selection colors
The Design panel shows Selection colors below Effects when the selection contains multiple
paint groups. It includes solid fills and gradients, strokes, descendants and effective text
range fills; images, patterns, hidden paints and masks are omitted. A boolean group's own paints
are included, without its operands. Equal paints from different styles or variables stay separate.
Each row edits the matching slots through the ordinary color picker or hex/opacity fields and
can select their owning layers. A picker drag is one undo step and retains its original targets
even when two colors become equal. Paint discovery is cached per document snapshot and selection;
the rows are virtualized.
Color styles already in the document (including imported styles) can be applied from a row.
As with the existing Fill/Stroke style controls, applying a style replaces the matching layer's
whole fill or stroke list and links it. A text range takes the style's resolved paints while
keeping its typography; the document model has no paint-style link on character ranges.
Browsing enabled libraries, binding/detaching variables from this section and a side-by-side
comparison against the running incumbent remain unchecked or unfinished; this is **partial** UI
parity. Behavior reference: [Figma's public selection-colors guide](https://help.figma.com/hc/en-us/articles/360042553434-View-and-adjust-colors-in-a-mixed-selection).
## Keyboard shortcuts
One keymap (`figcraft_engine::keymap`) drives the menus, the canvas, the shortcuts panel, the
@@ -164,6 +183,7 @@ in the table. `crates/ui-egui/src/shortcut_keys_tests.rs` drives the keys a look
| Date | Change | Summary |
|---|---|---|
| 2026-10-15 | minor | Selection colors (#403): mixed-selection paint editing, color styles and select-by-color; engine and egui regression tests. Bounds and remaining parity are documented above; headline estimates unchanged. |
| 2026-10-14 | minor | Type settings: the Small caps button draws its sample as a capital and a smaller capital, no longer as a box where the UI font lacks the small-capital letter (egui's default fonts) |
| 2026-10-14 | minor | Home screen recent files as cards with thumbnails on their page colour, a sortable table view, selection, keyboard and a context menu |
| 2026-10-13 | minor | Vector edit mode regions: with the Move tool the filled area under the pointer is hatched with "/" lines in the selection colour (about 1.4 px), clipped by the area's own fill rule so holes stay clear, and the hatch goes when the pointer is 3.5 px from a point or 2 px from a segment (screen pixels at any zoom); the spacing follows octave folding, fitted to measurements at several zoom levels (8 px at every power-of-two zoom, the gap growing with the zoom and stepping back at √2 · 2ⁿ), anchored to the vector so it doesn't slide when panning, one continuous pattern across selected areas; a click selects an area with its points (they move with it but keep the unselected look), Shift adds an area or takes a selected one out, a press on a selected area drags everything selected, drags show no hover hatch, a click on an empty spot (such as a hole) clears the areas and stays in edit mode, the last area listed wins where areas overlap, only filled vectors have areas; `vector.edit.select {regions}` and `vector.edit.state` report them (`regions_tests`, `vector_regions_tests`) |