27 Commits
Author SHA1 Message Date
Vikram Bhaskaran 7c916d9ecd Validate selection modification limits before allocating the canvas mask. (#4591) 2026-10-14 01:58:51 -04:00
Vikram Bhaskaran 79e2107f71 Report channel copy allocation failures without losing the clipboard. (#4587) 2026-10-14 01:58:46 -04:00
33b360d5ee Reject clipboard origins that cannot form a new document (#4586)
* Reject clipboard origins that cannot be moved into a new document.

* Fill Clip.resolution in the unrepresentable-origin clipboard test.

main added resolution to Clip; the synthetic overflow fixture omitted it and failed engine lib tests.

* Format the clipboard origin test (cargo fmt)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:58:42 -04:00
Vikram Bhaskaran b52115fc0a Preserve document and history when Fade cannot allocate its buffers. (#4593) 2026-10-13 12:46:10 -04:00
Vikram Bhaskaran 213770008a Return allocation errors when clearing a color channel. (#4589) 2026-10-13 12:45:49 -04:00
Vikram Bhaskaran 759780e40d Clip Fill source reads to the canvas before allocating pixels. (#4590) 2026-10-13 12:45:42 -04:00
Vikram BhaskaranandBrandon Thomas 753b610105 Keep gradient stops closer than one LUT step on the CPU. (#4712)
The GPU ramp is 4096 entries, so stops at 0.4999 and 0.5 interpolated to gray while the CPU kept the step. Stops that share an entry now use the CPU composite. Fixes #3053.

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 02:47:31 -04:00
Vikram BhaskaranandBrandon Thomas cb2cf3ccbd Show a Cancel mnemonic on the unsaved-changes prompt. (#4622)
Windows and Linux already marked Yes and No. Cancel had no letter, so the prompt now shows (C)ancel and C cancels. macOS still hides the letters and keeps the shortcut. (#4582)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:14:12 -04:00
Vikram BhaskaranandBrandon Thomas d825840bce Make the space between popup menu rows part of the rows. (#4624)
Context menus inherited a 6 pt item gap that belonged to neither button, so a drag across Merge Down or an adjustment list fell into dead space. Popup menus now stack their rows, and Pro rows use a square hover. (#4602)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:14:01 -04:00
Vikram BhaskaranandBrandon Thomas 53db0f5c1f Indent layer-effect rows under their layer. (#4627)
Effect eyes sat in the same column as layer eyes, so a long list read as extra layers. The eye, its hit target and the label now sit to the right of that column. (#4572)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:13:50 -04:00
Vikram BhaskaranandBrandon Thomas 24930b3188 Draw adjustment thumbnails as a half-filled circle. (#4628)
The contrast glyph put a smaller semicircle inside the ring, which read as concentric rings on a layer thumbnail. It is now one circle with a filled half. (#4561)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:13:39 -04:00
Vikram BhaskaranandBrandon Thomas 2fc225c782 Translate Turkish Amount as Miktar. (#4629)
Tutar is a sum of money. Filter and adjustment Amount strings now use Miktar, the word for a quantity. (#4350)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:13:28 -04:00
Vikram BhaskaranandBrandon Thomas 438490040b Keep the scrollbar thumb visible while the pointer is on it. (#4630)
The thin scrollbar faded its handle to the same opacity as the track, so scrolling a menu showed a bar with no position. The handle stays opaque and the resting bar is slightly wider. (#4611)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:13:16 -04:00
Vikram BhaskaranandBrandon Thomas 339397aabb Reject negative PSD style-sheet indexes and a non-finite dpi. (#4631)
A negative Parent, font or normal-sheet index was cast to a huge usize, which skipped the name fallback and could hide the text. Those indexes now have to fit in a usize, and an infinite dpi uses 72. (#4429)

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-13 00:13:05 -04:00
a7919b24e1 Edit › Transform › Rotate and Skew no longer scale (#3941) (#3986)
* Make Edit Transform Rotate and Skew use their own handle drags.

Rotate and Scale both started Free Transform, so a corner drag scaled the box. Skew only remapped edge handles, so corners still scaled. Give Rotate its own mode that turns the box from handles, and skew a corner along its adjacent edge (#3941).

* Rotate mode: fix the Skew doc comment and rustfmt

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 23:55:31 -04:00
Vikram BhaskaranandBrandon Thomas 13a16a695b Resize the Liquify brush with Alt+right-drag instead of painting. (#4707)
The preview treated every drag, including Alt plus the right button, as a stroke. That gesture now sets the diameter and density, and a plain left drag still paints. Fixes #4702.

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 23:35:35 -04:00
Vikram BhaskaranandBrandon Thomas 964f9fb9d0 Sample every visible layer in Content-Aware Fill by default. (#4133)
* Sample every visible layer in Content-Aware Fill by default.

An empty layer above the photo used to report a successful fill and stay empty, because only the active layer was sampled. The result is still written to the active layer. sampleAllLayers can be set to false to keep the old behaviour.

Fixes #4026

* Sample other layers in Content-Aware Fill only when asked or the layer is empty.

Reuse the Remove tool's composite, which writes the active layer's format, and leave Photoshop's Sample All Layers checkbox off unless the window has no pixels on this layer. An explicit false still reads only this layer.

---------

Co-authored-by: Brandon Thomas <[email protected]>
2026-10-12 23:35:21 -04:00
ef937adf98 Refresh Liquify's zoomed preview when a stroke is undone. (#4135)
* Refresh Liquify's zoomed preview when a stroke is undone.

Above 100% zoom the dialog shows a source-resolution crop. Undo rebuilt the proxy and left that crop untouched, so the stroke stayed on screen until the next zoom.

Refs #4100

* Format

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 20:06:24 -04:00
Vikram Bhaskaran bfa63f77b4 Snap 1 px pencil clicks to the pixel the aliased tip paints. (#4140)
A shift-line between two clicks inside the same pixels changed with the sub-pixel position. Pencil and pencil-mode Eraser clicks now use the same grid centre as the dab.

Fixes #4071
2026-10-12 19:58:03 -04:00
Vikram Bhaskaran 4709af046b Draw the Free Transform rotate arrow instead of hiding the pointer. (#4139)
The rotate zone requested CursorIcon::None, which removes the system cursor. Crop already draws a turn arrow for that case. Free Transform now uses the same arrow.

Fixes #4050
2026-10-12 19:57:49 -04:00
Vikram Bhaskaran 9fd20de408 Keep Liquify undo and redo working while a numeric field is focused. (#4134)
Ctrl+Z was ignored whenever a slider's text edit had focus, so a stroke could not be undone from the keyboard. Other Liquify keys still stay out of that field. The bracket keys also step the brush by at least one pixel, so a 2 px brush can reach 1 px.

Fixes #4027
2026-10-12 19:57:34 -04:00
ae72084a9c Resolve every pattern name before a batch delete removes any of them. (#4132)
* Resolve every pattern name before a batch delete removes any of them.

A missing name used to delete the earlier presets and then return an error. The pattern library is not on the document undo stack, so that partial delete could not be undone.

Fixes #4103

* Pattern batch delete: reject non-string entries and resolve against the library only

A document-only pattern resolved through pattern_cmds::resolve, so the batch
deleted earlier entries before pattern.delete refused it. Non-string array
members were silently dropped (ported from #4127).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 19:19:23 -04:00
67c90e307f Quantize the colour field when Only Web Colors is on. (#4138)
* Quantize the colour field when Only Web Colors is on.

The picked colour already snapped to the web-safe ramp, but the field and slider stayed continuous, so the restriction was invisible.

Fixes #4107

* Only Web Colors: draw the field and slider in flat web-safe bands

The coarse interpolated mesh blended between snapped corners, so it showed a
smooth low-resolution gradient rather than bands. Draw flat cells sampled at
their centres instead, and test that every cell is a single web-safe colour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 19:12:59 -04:00
4ff2dc33ca Fix multi-layer Delete and Move drags on a locked Background (#4109)
* Fix multi-layer Delete and Move drags on a locked Background.

Delete was removing only the active layer's targeted mask when several layers were selected. A Move drag that starts on the locked Background now draws the layer marquee instead of showing the lock dialog.

* Drop the multi-layer Delete hunk: #4078 fixes #4025

This PR keeps only the Move-on-locked-Background marquee fix (#4058).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* Move: a click on a locked layer still selects it before the marquee

Keeps hidden_layer_tests::auto_select_skips_hidden_layers green: Auto-Select
picks the visible Background under the pointer, and a drag's marquee replaces
that selection on release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-12 18:56:58 -04:00
Vikram Bhaskaran ad3417dc05 Dismiss Home when the document count changes. (#3984)
Chrome stored the document count when Home opened so File New could leave the overlay. Closing a tab that restored that count re-opened Home and hid remaining tabs (#3981). Clear the stored count as soon as it no longer matches, then keep Home dismissed until the user opens it again.
2026-10-12 06:18:58 -04:00
Vikram Bhaskaran 2af2cec6ea Edit > Copy takes a targeted layer mask, so masks travel between layers and documents (#2928) (#3267) 2026-10-11 20:50:49 -04:00
39c97c9be0 Keep the selection when smart selection finds nothing (#2235)
* Keep the selection when smart selection finds nothing

Select Subject and Object Selection run heuristics that often miss on busy
photos. In Replace mode an empty result cleared the selection, so the Select
Subject button looked dead and even destroyed existing work. apply() now
preserves the selection and reports changed:false in every mode.

The UI also answers visibly instead of staying silent: empty smart-selection
results set an info status suggesting what to try next, and a click with
Object Selection falls back to a Quick Selection dab at the brush size
instead of doing nothing.

* Translate the smart-selection status messages; drop needless borrows (clippy)

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 01:42:28 -04:00