#4985 added a canvas Shift+right-click popup that worked only for the
Brush and listed only its painting modes. In the reference application
Shift+right-click opens a menu for every painting tool: Edit Brush…
(the Brush Preset picker a plain right-click opens), a separator, then
the tool's own Mode list from its options bar:
- Brush, Pencil: the painting modes with Behind and Clear
- Clone Stamp, Pattern Stamp, History Brush: the blend modes
- Mixer Brush, Color Replacement, Background Eraser: Edit Brush… only
- Eraser: Brush / Pencil / Block (no Edit Brush…)
- Blur, Sharpen, Smudge: Edit Brush… (the reference lists modes too)
- Dodge, Burn: Shadows / Midtones / Highlights; Sponge: Desaturate /
Saturate
- Healing Brush: its blend modes; Gradient: the blend modes; Paint
Bucket: the blend modes with Clear (no Edit Brush…)
- Spot Healing, Magic Eraser and non-painting tools: no menu, the
gesture keeps its old route
brush_mode_menu.rs is now a per-tool item list (Item enum + items(tool))
instead of a Brush-only list. Each row sets exactly what the tool's
options-bar control sets: a brush-based tool's mode through
tools.setBrush (Brush/Pencil also Behind and their own Clear flag), the
Eraser mode, tone range, sponge mode, gradient and bucket modes as tool
options. Blend modes are grouped with separators; the current choice is
checked; Behind and Clear are greyed on the Background layer. The menu
remembers the tool that opened it and closes on a tool change.
Only settings the options bar already has are offered. Not offered yet
(no setting here): Behind for the stamps, History Brush, Gradient and
Paint Bucket; the Blur/Sharpen/Smudge modes; the Spot Healing modes;
the Healing Brush's Sampled/Pattern source and Replace; the History
Brush source.
Control: ui.inspect's brushModeMenu reports tool, mode, choices (the
active tool's labels) and items (label/enabled/checked, separators);
ui.context.choose {id: <label>} picks a row, an unknown or greyed row
is an error that changes nothing.
Tests (kittest): each representative tool's rows in order, choosing a
row sets the options-bar state and journal, Edit Brush… opens the
picker at the click, Behind/Clear greyed on the Background, Magic
Eraser and Spot Healing open nothing; control graceful failure for bad
choices. "Edit Brush…" added to every catalog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Add bounded deterministic suggestions via current error/session/control modules while preserving authorization/modal checks. Register hints and all original tests; fix scrollbar choice import and updated PSD image fixture field found by 100-head check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Port guarded filter context actions and selection updates into current Layers/Smart UI modules. Register all new modules and regressions; preserve current filter blend button and drag-to-reorder routing, document/modal/job ownership.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Port arbitrary-stop editor into current responsive dialog. Preserve source interpolation and exact model colors; unchanged dense imported snapshot lists roundtrip while edited lists retain 256-stop cap. Retain both validation guards and all original regression tests. Correct saved-path callers, floating-window and crop fixtures found by frozen 88-head all-target check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrated independent exact shape paints and atomic pattern clipboard snapshots into current modules, preserving current soft-mask coverage, shape cache keys and gradient stop caps. Shared bounded COW pattern sampling now retains minification filtering; shapes/clipboards resolve lazy catalog entries to full pixels. Preserved CPU/GPU/depth/model regression matrices; also corrected missing glow-test enum imports found by the 80-head check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported Behind paint across engine/render and shared options-bar/canvas mode menus, preserving Clear and current popup ownership. Added localized responsive picker title/width and File Info multiline fields without dropping ordered fields, Browse or camera metadata; ported Pen anchor repositioning, copy cursor, grayscale-target sampling and authorized select-under-cursor dispatch into current modules with original regressions.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Register starter actions and bounded background-blur command in current modules; preserve persistent migration marker and portable JSON defaults compatibility.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Resolve recorded visibility targets before both engine and shell authorization, preserving bounded playback, paused dialogs and existing journal coalescing; retain ActionState take generation.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Keep action-definition undo separate from document history and preserve upstream panel routing and layout.
Main moved the engine commands into crates/engine/src/cmd/ and the
dialogs and panels into submodules. The PR's placement calls follow
them, and main's new insert sites (new_adjustment in adjust_params.rs,
Place in file_cmds/open.rs, shapes in vector_cmds/shape.rs) use
layer_place::insert_new like the PR's originals. The New Group dialog
moves to dialogs/new_group_ui.rs; the Layers panel flyout's New Group…
opens it.
Main's Session::precheck now also checks layer.new.group's and
layer.new.groupFromLayers' new params (name, color, mode, opacity,
position), so precheck_agrees_with_execute holds. Main had since added
"New Group…" to every catalog: the PR's duplicate rows are dropped.
Main's newer tests that made a layer with an open group active now
open the group last, as the PR did for the older ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reason other than the memory budget (a flipped view, Advanced Blending…)
now shows "CPU Rendering" even while the measured layer fit is over the
budget. perf.gpu_fallback is set in one place from the active document's
reason, also each frame after a switch of document, so a refresh of an
arranged cell or another window can't make it disagree with
perf.renderPath's current. The control protocol says a history entry's doc
is the changed document's id, or 0 for every document.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A clipped or Blend If adjustment preview runs on the CPU proxy. Its reason
lives in a slot of its own, separate from the canvas's per-document reasons
and the app-wide reason, so closing or retargeting the dialog ends only the
preview's entry and can never report the canvas back on the GPU.
Conflicts: the batch's wheel reads (#5547 Animated Zoom) keep their extra
argument with main's ids (`Id::unique`, `ui.scope_id()`); crop presets keep
#6620's `apply_choice` with main's accessible name; Bevel & Emboss keeps
#9370's per-mode id with main's name argument; the Stroke field keeps
#7203's gradients with main's name; tone.rs keeps #9139's move of the
Histogram panel to histogram_panel.rs (main only renamed ids there);
translation catalogs as unions.
Not flagged by git, the batch's new code follows egui 0.37 too: dropdowns
take an accessible name (Histogram Channel/Source #9139, tool Mode #5504,
Art History Style #1610, New Snapshot From #3567), MouseWheel events carry
`source`, `WidgetType` is `Role` (ColorButton is ColorWell, #7203),
`Id::new`/`Ui::id` are `Id::unique`/`Ui::scope_id`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* egui037: name each control for what it sets, and order the eager fonts for the UI
CodeRabbit review follow-ups on the migration: the Artboard X/Y fields, the CAF
dropdowns, the channel Solidity field, the GPU backend/adapter dropdowns, the
corner radius fields and the canvas extension colour all had an accessible name
that named another control (or was shared by four). The eager craft-fonts
registration now puts the UI Japanese face first, as the lazy loader does, so
Japanese UI is not drawn in the manifest's Mincho face and shared Han characters
do not take Japanese forms.
Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
* egui037: rustfmt the eager craft-font order
---------
Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
- Cache Levels 1 in Preferences turned off the 250 ms recount throttle,
because "forced" was derived from max_level == 1: only an Uncached
Refresh skips it now.
- The proxy factor became 2^(level-1) for every count, so the Levels,
Curves and Threshold dialogs counted a 6000x3000 image on an 8x proxy
instead of main's 7x (a 36 MP one 16x instead of 9x). Uncapped counts
use main's factor again; only a Cache Levels cap halves by powers of two.
- The panel opens in Compact View, as in Photoshop, and Compact View
counts the entire image whatever Source Expanded View was left at.
- The clipping corner marks are gone: Photoshop's Histogram panel has none.
- ui.inspect: "preview" counts only the active document's preview, and
"current" compares the source and cache level too, not only the
revision (a Source change left it true until the recount landed).
- ui-parity row lists two more differences (selection, Selected Layer
with a dialog preview); cargo fmt.
Co-authored-by: mathildalabs <347373177+mathildalabs@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged with our fix-up pr-3297-fixup (1de1e9eb58) on top of the author's head 053e1e0d03.
Conflicts resolved: channel_cmds.rs routed() keeps the PR's PAINT_IDS list plus the batch's
paint.artHistoryBrush; paint_cmds.rs Paint Bucket params keep the batch's "mode" and the PR's
"filterMask" target; docs/scorecard.md takes the batch's numbers with FILE-215-3 done.
Redone on the PR head 053e1e0d0 (the author's main merge); the earlier
fix-up f7af09d0f carried the first six points.
With a smart object's filter mask targeted, every routed command got
"target":"filterMask" and the filter-mask precondition instead of its own:
- Cmd+T and Edit > Transform > Flip/Rotate failed ("can't transform a
filter mask on its own"); now the smart object transforms, as in
Photoshop (lone_target -> Ok(None), and transforms get no target).
- Move (layer.translate) re-rendered the whole filter stack on every
nudge, and failed for contents that can't render.
- Convert for Smart Filters became enabled and nested the smart object;
Apply Image, Last Filter, Liquify, the lens filters, Vanishing Point,
the render filters and Desaturate ignore the target.
`follows_filter_mask` now leaves these out, in the shell, in
inject_target, in the precondition and in the re-render hook; Paste
keeps its own precondition.
- With the filter mask shown (Alt-click), clicking the layer, layer-mask
or vector-mask thumbnail (or ui.set maskTarget/vectorMaskTarget/
filterMaskTarget:false) left the view on (and ui.set maskTarget:false
left the filter mask targeted), so strokes kept landing in
the filter mask; those now end the view (CodeRabbit, panels.rs).
- ui.set filterMaskTarget:true on a layer without smart filters, or with
a single channel targeted, cleared the mask target and answered ok; it
is now refused before anything changes (CodeRabbit, control.rs).
- Curves on a targeted filter mask showed colour channels and the smart
object's histogram; it is now a gray plane with the mask's histogram
(GraySurface::FilterMask).
New on this head:
- Filters with a parameter schema (Gaussian Blur, High Pass, ...)
refused "target":"filterMask" ("`target` must be pixels, mask,
quickMask or {channel}"), so blurring the filter mask, the usual way to
feather it, failed from the menu, the dialog and agents. The filter
validation now accepts it (blurring_the_filter_mask_feathers_it).
- Delete with a selection and the filter mask targeted ran Edit > Clear
on the smart object ("not a pixel layer"); it now fills the filter mask
with the background colour there, as Photoshop does. With no selection
Photoshop deletes the layer, which Delete already did
(delete_with_the_filter_mask_targeted_edits_the_filter_mask).
- docs/control-protocol.md: the ui.set line lost its closing parenthesis
in the main merge.
Co-authored-by: Valentin Klein <271991477+vangel76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
panels.rs: the PR's snapshot rows next to main's History Brush source
and scroll-to-current; ui-parity.md: main's status line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Open wrote PhotoCraft's crs: subset over any existing sidecar, on by
default: a Lightroom or Bridge IMG_0001.xmp lost its crop, masks, rating
and keywords, even when the raw was only opened with Open in Camera Raw
off (FredApps' review). Now a sidecar is only written when the settings
changed, never over one whose x:xmptk isn't PhotoCraft's (a notice says
it was kept, camera_raw_xmp::is_photocraft_xmp), and never for a control
or MCP request, which has no authority to write beside the user's files.
A write error goes to a notice instead of a status line the develop
overwrote at once.
The Camera Raw Defaults' colour space, 8-bit and smart-object steps ran
before the Camera Raw step, so with the default Adobe RGB every raw opened
with the dialog's settings applied to other pixels than it previewed. They
now follow the step (also when a background re-develop lands), as
Camera Raw converts to the workflow space last. Main already sets the raw
resolution at import (#7811), so the PR's Image Size step is dropped.
Two raws opened back to back with raw-stage edits: the second one's
background re-develop replaced the first one's pending entry, so the first
never got its Camera Raw step, conversions or unmodified mark. Pending
re-develops are now a list keyed by job. Sidecars are read only from
regular files of at most MAX_XMP_BYTES (a pipe or a huge file named
IMG.xmp no longer blocks or fills memory), and a conversion that fails
after the Camera Raw step is reported instead of leaving the dialog open
for a second Open that would apply the step twice.
Tests: a_foreign_sidecar_is_never_overwritten,
a_rating_only_sidecar_is_left_alone,
opening_without_the_dialog_does_not_rewrite_the_sidecar,
a_control_request_never_writes_the_sidecar,
a_sidecar_write_error_stays_reported,
the_camera_raw_step_runs_before_the_workflow_conversion (inline and
background), two_raws_opened_back_to_back_both_finish_their_open,
sidecars_are_read_from_small_regular_files_only,
is_photocraft_xmp_reads_the_toolkit_marker; the existing
sidecar round trip now uses a PhotoCraft-written fixture.
Co-authored-by: Fabio Lissi <3223836+FabioLissi@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prefs.rs keeps main's demosaic default and the PR's write_xmp_sidecars, and
unhides the Camera Raw Defaults the PR makes live; the raw-open RawOpen built
without the dialog gains main's developed_with/demosaic; i18n and docs as
unions; docs/scorecard.md is regenerated in the fix-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The desktop build enables craft-ui's native-windows feature; the web build
keeps floating panels in the page. Every floating window gets its own OS
window (Ctrl/Cmd-drag, Float Panel, a drop that floats, restored layouts),
except the Studio themes' Properties card, which stays over the canvas.
Wayland keeps floating windows in the main window by craft-ui's rule.
- window.panel.window {panel, native?} runs as craft-ui's command; the web
refuses it, and so does the Studio card.
- Keys a panel window forwards run PhotoCraft's shortcuts in the same
frame, clipboard events included; keys held there count as held.
- While a dialog or other modal is open, the panel windows take no input.
- Files dropped on a panel window open as documents.
- Ctrl+click is a secondary click in panel windows on macOS too.
- The canvas paints no tool cursor while another of the app's windows has
the pointer.
A saved docking value that is not a craft-ui workspace, such as the bare
layout with dockingHidden written before workspaces, no longer migrates:
the window opens with the default layout built from the saved panels,
dockTabs and dock, and its collapsed custom groups are dropped. Workspaces
load as saved. The width a user dragged the native dock to, which egui
remembers under the native dock's panel id, sizes the default column once.
Repin every craft-libs crate to 2869b8cc. The customized layout is a
craft_ui::docking::Workspace; window.panel.*, window.workspace.* and
ui.dock parse as DockCommand and apply through it. Workspaces saved
before the version field load with their hidden panels, dock-back
places and collapsed groups.
Reconcile main 486afd82cc while preserving reviewed lifecycle, capture, IME and settings migrations. Include canonical dependency pins and the generated lockfile.
All nine CI stages passed: 11,580 passing test executions, zero failures and 91 ignored across 93 summaries. Native/browser acceptance and paired app performance remain open.