Main added Brush Tip Shape Angle/Roundness tests and doc rows at the same places as
this PR's Scatter header tests and rows; both sides are kept, and the doc status
lines name both changes.
Co-Authored-By: augrclk <augrclk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main moved brush_sections.rs to panels/, its tests to brush_sections/tests.rs and
split widgets.rs into modules. The PR's tests now live in brush_sections/tests.rs and
scrub_label_within is crate-visible from widgets/scrub.rs; both doc status lines and
history rows keep both sides.
main moved brush_sections.rs to panels/ with its tests in
panels/brush_sections/tests.rs, and gave the Angle and Roundness fields
accessible names. Keep the PR's left column and right-hand widget with
main's accessible names, move the PR's three layout tests and
section_harness_width into the new tests file, and find the captions with
main's caption_rect (get_by_label now also matches the named fields).
Docs: both sides' status entries kept.
Co-Authored-By: augrclk <217665348+augrclk@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pointer hooks read PEN_FLAG_INVERTED from GetPointerPenInfo and
report the pen end through Services::pen_end, so flipping the pen
switches to the Eraser and the tip restores the previous tool, as on
macOS. A WH_CALLWNDPROC hook beside WH_GETMESSAGE catches
WM_POINTERLEAVE, which Windows documents as sent, not posted.
PEN_FLAG_ERASER alone is an eraser button on the barrel and does not
switch tools.
* Art History Brush: Opacity on the options bar (#10263)
The Art History Brush's own brush opacity is shown in its options bar
(0-100%) and sent to paint.artHistoryBrush. It is kept per tool, saved
across launches, set by the 1-0 keys and restored to 100% by Reset Tool,
as the Clone Stamp's is. Tests cover the UI wiring, per-tool persistence,
Reset Tool and stroke output at 8/16/32-bit.
* Update crates/ui-egui/src/panels/options_bar.rs
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
* Art History Brush starts at 100% opacity on first use
A tool picked for the first time takes the outgoing brush; the Art
History Brush now takes it at full opacity, as the issue's default asks,
so a 30% Brush no longer makes the first Art History stroke 30%. Other
tools keep inheriting opacity.
* Art History Brush: document Opacity in the command contract and carry it in tool presets
---------
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
#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>
brush-parity.md, gaps.md and ui-parity.md had 2026-10-25 revision rows
under a 2026-10-23/24 "Last updated" header; the headers now carry the
latest row. gaps.md (24 of 171), ui-parity.md and target-app-parity.md
(46 of 149) quoted stale counts of preferences that do nothing; they now
quote the scorecard's 12 of 178. target-app-parity.md still listed the
Camera Raw Calibration panel as missing, which landed on 2026-10-13
(#5128); the row now names per-camera colour for non-DNG cameras, as
gaps.md G14 does.
Found by CodeRabbit on #9386.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Tolerance defaulted to 50 and averaged alpha into the difference, so an
ordinary edit (e.g. #c03030 -> #d04848) was never painted back, and a stroke
that wrote nothing still left an empty History step. Photoshop's default is
0; the difference is now the larger of the mean colour change and the
alpha change (the Eraser keeps colour and lowers alpha), and a stroke that
writes nothing is an error instead of an empty step.
- Each tile copied a source window of tile +- halo per dab: a leftward stroke
or a curl across x = 64 read empty pixels there (seam), and a size-1000 click
copied ~5e9 floats (11.9 s). The rotated read stays inside the dab, so it now
samples the source directly; bilinear reads at pixel centres, so an
unrotated dab copies the history pixel instead of a 2x2 blend.
- The brush used its own history lookup (state 0 by default): it now takes
the History Brush's source (history.setSource, snapshots, refusal after a
canvas size change).
- TOOL-213-20 is partial (8 of 10 styles, Area scales spacing); docs follow.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- algo/paint.rs: main locks transparency with composite_atop; keep that
for every blend mode, and Clear on a locked layer leaves the pixel.
- paint_mouse.rs: main's Some arm (loads stored tips), the PR's None arm
(a first-seen tool starts in Normal).
- retouch_ui.rs: main moved the Pattern Stamp params into
pattern_stamp_params, shared with its live preview; the PR's `mode`
goes there, so preview and commit agree.
- tool_reset.rs: main's Type keys plus the PR's Paint Bucket mode keys.
- distort_ui.rs (from #5478): main's Delete/Backspace pin branch, then
tool_keys.
- docs: both sides' history rows and status lines.
Co-authored-by: paulou-jpg <79102098+paulou-jpg@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve both progress histories, all independent regression tests and the incoming Mixer Brush and tilt-axis behavior. Integrate the existing PR without rewriting public history.
Co-authored-by: GPT-6 <noreply@openai.com>
Preserve both progress histories, all independent regression tests and the incoming Mixer Brush and tilt-axis behavior. Integrate the existing PR without rewriting public history.
Co-authored-by: GPT-6 <noreply@openai.com>
Preserve both progress histories, all independent regression tests and the incoming Mixer Brush and tilt-axis behavior. Integrate the existing PR without rewriting public history.
Co-authored-by: GPT-6 <noreply@openai.com>
Keep the brush Mode menu's early return in canvas_buttons ahead of main's
physical right-button handling, keep both sets of control tests, and keep
both notes in docs/brush-parity.md and docs/control-protocol.md.
docs/brush-parity.md: kept main's header and added the PR's change item; the PR's change-log row added.
docs/gaps.md: kept main's header and added the PR's change item; the PR's G4 progress note and change-log row added.
crates/paint/src/brush_tests.rs: both sides added tests at the same spot; kept main's four smoothing tests and added the PR's three pose_axes tests after them.
docs/brush-parity.md: kept main's header and added the PR's change item.
docs/gaps.md: kept main's header, added the PR's change item; G4 progress notes and the change-log row kept from both sides.
docs/control-protocol.md: kept main's eyedropper line (with the document.sampleColor wording) and added the PR's Brush Pose line before it.
Photoshop gives these painting tools a Mode in the options bar; here they
always painted in Normal.
- Clone Stamp, Pattern Stamp and History Brush offer the layer modes, and
Healing its own list (Normal, Multiply, Screen, Darken, Lighten, Color,
Luminosity). The mode is kept in each tool's own brush, like its size,
and reaches the live stroke and the commit.
- Paint Bucket: paint.bucket takes "mode" (any blend mode, or "clear" to
erase the filled area). algo's bucket fill gains a FillMode for it. The
options bar's Mode has Clear after Dissolve, as the Brush's does.
- A tool's first brush now starts in Normal instead of inheriting the
previous tool's mode, so a Multiply Brush no longer makes the first
Clone Stamp stroke multiply.
- The Shift+Alt letter and Shift+Plus/Minus mode keys reach these tools,
within each tool's menu. Tools with no blend mode (the Erasers, Mixer
Brush, Dodge, Burn, Sponge) now pass the keys to the selected layers.
Not yet: Healing's Replace mode, and a Mode for Spot Healing, Color
Replacement, Blur, Sharpen and Smudge (they ignore the keys meanwhile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Mixer Brush could not be emptied. MixerState::clean dropped only
the pickup well, paint.mixerBrush refilled the reservoir from the
foreground before every stroke unless loadAfterStroke was false, and
the app always sent the defaults. A stroke on a new layer over an image
always carried the foreground color, and the options bar had no Load
Brush or Clean Brush control.
Cleaning now empties both wells, so a clean brush lays down only the
color it picks up from the canvas. The new commands brush.mixer.load
and brush.mixer.clean load and empty the brush. paint.mixerBrush reads
loadAfterStroke and cleanAfterStroke from the brush and applies them
after the stroke. Both are on by default, and a stroke with both on
paints as before. A stroke whose color differs from the one recorded
by the last stroke, load or clean loads that color first. A load or
clean records the foreground, so choosing a new foreground loads the
reservoir.
The options bar has the Current Brush Load swatch, whose menu holds
Load Brush and Clean Brush, and two toggles for the automatic Load and
Clean. Brush Settings shows the same two options. The new strings have
rows in every catalog.
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Retain both progress histories and integrate the existing Brush Settings changes with upstream. Preserve the response-curve editor, options-label scrubbing and independent preset regression tests where they overlap.
Co-authored-by: GPT-6 <noreply@openai.com>
Resolve the docs/brush-parity.md conflict by keeping both sides: the
upstream notes (Hue Jitter amount track, Color Replacement) and the
Brush Shift+right-click Mode picker note, in the header and the change table.
Keep X/Y flags through ABR import, presets, commands and painting; retain legacy combined inputs and expose both controls with regression coverage.
Co-authored-by: GPT-6 <noreply@openai.com>