crates/engine/src/float_cmds.rs: kept main's cut_piece (parallel, shares full tiles) and try_lift_coverage, and the PR's CutParts::outline; the outline is read from the piece's alpha, which holds min(alpha, selection) as the PR's own lift loop computed, instead of replacing cut_piece with that loop.
crates/engine/src/float_cmds/tests.rs: git interleaved the two sides' new tests; took main's file and appended the PR's added tests whole.
docs/ui-parity.md: header line keeps main's text plus the PR's #7538 item.
- Replace whole-region Vec<f32> read_region/write_region in CutParts::new
with tile-parallel cut_piece + try_fill_surface/try_clear_surface,
sharing interior Arc<Tile> allocations in O(1) and activating the
solid_tile hole-fill fast path on Background and mask targets.
- Cache piece_bounds on CutParts so CutParts::place avoids scanning tile
content bounds on every drag frame.
- Parallelize composite_over across top tiles with Rayon and share fully
opaque top tiles by Arc<Tile> clone in O(1), preserving COW tile
identity for GPU canvas uploads.
- Parallelize Surface::translated across target tiles with Rayon and
prune cleared tiles in-place inside edit_tiles.
- Skip zero-offset floating preview generation on pointer-down in
move_ui::floating_doc.
- Exclude SnapKind::Selection when moving selected pixels with the Move
tool so small initial drags do not snap back to (0, 0).
- Keep navigator_plain active during Move/Brush/Patch drags on a GPU
canvas so navigator_gpu is not evicted into a synchronous full-document
CPU thumbnail composite on the first drag frame.
- Parallelize row normalization in compose::render_reduced_in_bands and
add a direct read_rgba_into fast path for unmasked Normal layers over
an empty backdrop.
Loading a layer's transparency as the selection and dragging it with the Move
tool left a ghost of a·(1−a). Photoshop lifts the selection's share of a
pixel's coverage, up to all of it, and leaves the rest, so its own
transparency lifts it whole. Opaque pixels split as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checked against Photoshop 2025 on a 15 px feathered shape moved from a loose
marquee:
- Its selection after the move follows the edge's transparency: the marching
ants run along the 50 % line and Quick Mask shows the edge partly selected
(measured strength 0.10 / 0.29 / 0.42 / 0.64 / 0.81 where alpha is 0.05 /
0.24 / 0.39 / 0.57 / 0.71). We had made those pixels fully selected.
- Moving it again, even after Deselect and Reselect, leaves nothing behind,
though Delete with the same selection leaves a ghost: the Move tool lifts at
most each pixel's alpha, not alpha × selection.
So the move cut lifts min(alpha, selection) of each pixel (feathered moves over
opaque pixels are unchanged: k lifted, 1 − k left), and the moved pixels'
selection is what they took. Moving them again lifts them whole, with no faint
outline, and their ants hug the soft edge's 50 % line.
Also: arrow nudges add up to one floating piece, so up then down was back at
no offset and the drop kept the loose marquee. A piece that moved and came back
now keeps the moved pixels' selection, in one step; a click still changes
nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shrunk selection took the moved pixels' alpha as its coverage, so
anti-aliased edges were only partly selected. Once the move was dropped
(changing layers, say), moving them again lifted a × a of each edge pixel and
left a × (1 − a) behind: up to 25 %, a faint outline where the shape had been.
The selection now keeps its own strength wherever the layer has pixels, so a
later move lifts edge pixels whole. Tested at 8, 16 and 32 bit: nothing is left
behind and the shape's total alpha is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A marquee drawn loosely around an object on a transparent layer kept its
rectangle when the Move tool dragged the pixels. Photoshop shrinks the
marching ants to the pixels being moved as the drag starts: the selection
times the layer's transparency.
- Engine: the floating piece keeps its outline (`CutParts::outline`, the
piece's alpha), and `select.drop` makes it the new selection. A mask,
which has no transparency, and an empty piece keep the selection's shape.
- Canvas: a floating piece's ants follow that outline during the drag.
- Snapping and the move box use the moved pixels' bounds.
Tests at 8, 16 and 32 bit, Alt-drag copies, soft selections over
half-transparent pixels, the Background, a mask target and an empty piece.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Step Backward in the History panel puts a floating selection back like Edit > Undo; Step
Forward and Edit > Redo put it back and step instead of dropping it first (which recorded a
Move and wiped the redo states); with nothing to step to the piece keeps floating.
- Memory figures and the byte budget count the states Clear History keeps for its undo.
- Undoing Clear History keeps a saved document clean.
- New Snapshot... in the panel menu runs history.newSnapshot.
- Tests: Clear on one document leaves the others alone, and each fix above.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Move tool moves the selected pixels when there is a selection (#578)
With a selection, a Move-tool drag (from anywhere) or arrow-key nudge now
floats the active layer's selected pixels (select.float) instead of moving
the whole layer: the hole is transparent, or the background colour on the
Background layer; Alt copies the pixels instead of duplicating the layer;
Shift constrains the drag to 45 degrees; Enter, Deselect or any other
command drops the piece as one "Move" history step. Layers whose pixels
can't float (type, shape, smart object, group, position-locked, hidden, or
several selected layers) and Auto-Select without a selection behave as
before. select.float gains "copy"; a click that moves nothing no longer
leaves a floating piece behind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Pipette cursor test: Windows draws the precise crosshair itself
input_tests::eyedropper_and_alt_sampling_show_a_pipette (#1028) expected
the OS crosshair with Other Cursors = Precise, but on Windows the canvas
draws its own crosshair and hides the OS cursor (visible_crosshair), so
the test failed on every Windows run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHgFEHEsefoQoBParH47JA
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Selection drags: ⌘⌥-drag floats a copy; ⌘-drag with the Polygonal Lasso and Magic Wand
- `select.float {"copy": true}` floats a duplicate of the selected pixels
and leaves the originals in the layer; ⌘⌥-drag inside the selection uses
it (⌘-drag still cuts).
- ⌘-drag inside the selection now also floats it with the Polygonal Lasso
and the Magic Wand (not while a polygon is being drawn); a plain press
there still places a point / samples, so only ⌘ moves the selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ / ⌘⌥ with a selection always cut / copy and drag it, never combine selections
With a selection tool and an active selection, a ⌘ or ⌘⌥ press anywhere (not
only inside the ants) floats the selected pixels (cut, or copy with ⌥) and
drags them; ⌥ no longer turns it into a subtract. The cursor shows no
selection-mode badge while ⌘ is held over a selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ with a selection tool moves the layer outside the selection; ⌘⌥ duplicates it
With a selection tool, ⌘ acts as the Move tool for the drag: inside the
selection it cuts the selected pixels (⌘⌥ copies them), and outside the
selection, or without one, it moves the whole layer; ⌘⌥ duplicates the
layer first and moves the copy. ⌘ never combines selections, so the cursor
shows no add / subtract badge while it is held.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘ outside the selection moves the layer, not the selected pixels
Only a ⌘ press inside the selection floats its pixels; outside it the layer
moves (`command_moves_layer`). Drops the duplicate badge check.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ⌘⌥-drag on a floating piece drops it and drags a copy
A floating piece (from a ⌘ cut-drag) dragged again with ⌘⌥ is dropped where
it is and a copy of it floats with the pointer, as ⌘⌥ does on any selection.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Jason Short <3344856+jason4short@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Engine (scripts, the control channel and agents can drive it):
- `select.float {"dx","dy"}`: cuts the selected pixels of the active layer
into a floating piece the first time, then moves it by whole pixels.
The document is unchanged while it floats (`DocState::floating`);
`float_cmds::displayed` gives the document with the piece at its offset.
- `select.drop`: drops the piece into its layer and moves the selection
with it, one history step ("Move Selected Pixels").
- Any other command drops a floating piece first (a hook at the top of
dispatch, so it lands where it was shown); Undo puts it back instead.
- The layer is split once, only inside the selection, and the piece is
cropped to it: placing it costs the piece, not the layer (6000×4000
layer, 500×400 piece: 18 ms to cut, under 5 ms per move).
Canvas (Marquee / Lasso):
- A drag inside the ants moves the outline (`select.transformSelection`);
a click inside without moving deselects; ⇧ / ⌥ drags still draw.
- ⌘-drag floats the selected pixels and shows them at the pointer live;
plain drags on the piece move it again; a press elsewhere drops it.
- A move cursor over the ants; the ants follow the dragged / floating piece.
Co-authored-by: Jason Short <3344856+jason4short@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>