Skip to content

Unify brush effect ux - #9

Merged
jpdup merged 11 commits into
mainfrom
unify-brush-effect-ux
Jul 6, 2026
Merged

Unify brush effect ux#9
jpdup merged 11 commits into
mainfrom
unify-brush-effect-ux

Conversation

@jpdup

@jpdup jpdup commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other (please describe):

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.

  • Unit tests
  • Integration tests
  • Manual testing

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Screenshots (if appropriate):

Additional Notes:

jpdup and others added 11 commits July 3, 2026 17:50
Reframe brushes and effects into one coherent model: anything applied by
gesture is a brush, region filters are adjustments applied to a layer or
selection, and selection is an orthogonal modifier.

Phase 1 - ungate effects from selection: startEffectPreview targets the
whole active layer when no selection is visible, without leaving a phantom
select-all region behind.

Phase 2 - one tool rail: new ToolFamily + ToolDescriptor unify ActionType
and SelectionEffect behind one type; ToolFamilyRail renders Draw / Retouch
/ Adjust groups (smudge & blur now sit beside the paint tools, effects
under Adjust). getListOfTools, the dead _EffectsSection, and
_buildPanelSection are removed from tools_panel.

Phase 3 - selection as a persistent modifier: add a "Clipped to selection"
badge with one-tap clear, shown when a selection is active on a gesture
tool, plus a guard test locking in persist-across-tool-switch clipping.

fcheck 100%, coverage 85.9%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the three-family model down to the two families that match the tools'
actual nature:
- Smudge moves into Draw (it's a gesture brush with no region/filter form,
  so it belongs beside the freehand brushes).
- Blur leaves the rail and lives only under Adjust (apply or paint) - this
  removes the blur-in-two-places redundancy that paintable filters exposed.
- ToolFamily is now {draw, adjust}. The blur pixel-brush engine is kept
  (still used by rendering/export) but is no longer a selectable UI tool.

Also give Adjust paint mode its own brush-size slider; it previously
borrowed the ambient brush size.

fcheck 100%, coverage 85.8%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Firebase Hosting preview is ready.

Preview URL: �[36m�[1mi hosting:channel:�[22m�[39m https://vteam-paint--pr-9-deca0q7g.web.app
Channel: pr-9
Expires: 3d

@jpdup
jpdup merged commit b871b69 into main Jul 6, 2026
4 checks passed
@jpdup
jpdup deleted the unify-brush-effect-ux branch July 6, 2026 08:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR unifies the “brushes & effects” UX so all pixel-changing tools live in a single Brush rail and effects can be armed and painted like brushes, while selection behaves as a persistent clipping modifier. It also updates the Edge Detection (wand) interaction with drag-to-adjust tolerance feedback and adds persistence for restoring the last selected layer when reopening files.

Changes:

  • Introduces a unified tool rail (gesture tools + effects) and adds paint-mode effect brushing, including bipolar “Sharpness” replacing separate sharpen/soften behaviors.
  • Reworks Edge Detection wand UX (prewarm cache, drag-to-adjust tolerance HUD + haptic ticks) and simplifies selection math toggles.
  • Persists/restores selected layer across formats (ORA/TIFF embed; flat formats stored in preferences), with broad test + localization + documentation updates and version bump to 1.9.0.

Reviewed changes

Copilot reviewed 66 out of 79 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/widgets/shortcuts_help_test.dart Updates shortcut help text for renamed wand action.
test/widgets/pixel_brush_marquee_test.dart Adds coverage ensuring armed effect brush strokes take precedence over starting a new selection.
test/providers/app_provider_tools_test.dart Adds tests for wand drag-to-adjust tolerance, HUD, and cache prewarm behavior.
test/providers/app_provider_selection_test.dart Adds tests for whole-layer effect targeting and effect-brush arming/commit behavior.
test/providers/app_preferences_test.dart Adds tests for last-selected-layer persistence in preferences.
test/panels/tools_panel_test.dart Updates tools panel tests for effect arming/disarming behavior in the unified Brush section.
test/painting_scenario_layers/scenario_coverage_exercises.dart Ensures the new top-colors section toggle is exercised in scenario coverage.
test/painting_scenario_layers/paint_layer_sun.dart Updates effect usage to Sharpness with signed strength.
test/painting_scenario_layers/paint_layer_lake.dart Updates soften behavior to negative Sharpness with clarified comments.
test/output/cc.txt Updates coverage snapshot output.
test/models/user_action_drawing_test.dart Updates smudge icon expectation.
test/models/tool_descriptor_test.dart Adds tests for tool rail descriptors and labels.
test/models/selection_effect_test.dart Updates effect list expectations; adds bipolar behavior tests (brightness/sharpness).
test/models/effect_brush_model_test.dart Adds tests for EffectBrushModel arm/disarm behavior.
test/l10n/app_localizations_test.dart Updates localization string exercise for renamed/removed keys.
test/helpers/transform_helper_test.dart Adds coverage for wand tolerance haptic trigger helper.
test/helpers/painting_test_helpers.dart Updates selector math “replace” helper to match new toggle behavior.
test/files/save_test.dart Adds tests verifying flat-format saves remember last selected layer.
test/files/file_tiff_test.dart Adds tests verifying TIFF round-trips selected layer metadata.
test/files/file_ora_test.dart Adds tests verifying ORA round-trips selected layer metadata.
RULES.md Documents “Brushes & Effects” tool model invariants for contributors.
README.md Highlights the unified tool model in the feature list.
pubspec.yaml Bumps version to 1.9.0+1 and pins dependency versions.
lib/widgets/shell_selection_sub_toolbar.dart Adds wand cache prewarm; replaces “Replace” button with add/remove toggle behavior.
lib/widgets/main_view.dart Adds wand tolerance HUD overlay; adjusts brush size preview visibility logic.
lib/widgets/effect_intensity_controls.dart Enables signed strength slider range for bipolar effects.
lib/widgets/canvas_gesture_handler.dart Adds wand-specific cursor logic and reduces rebuild scope for cursor wrapper.
lib/widgets/canvas_gesture_handler_state_methods.dart Implements effect-brush stroke path and wand drag-to-adjust tolerance flow.
lib/widgets/canvas_gesture_handler_pixel_brush.dart Resets effect-brush stroke state alongside pixel-brush state.
lib/providers/app_provider.dart Adds effect brush model, wand tolerance HUD state, and isWandSelectionActive helper.
lib/providers/app_provider_selection.dart Adds wand cache prewarm + drag tolerance resample helpers and makes selection library include effects/crop parts.
lib/providers/app_provider_selection_effects.dart Adds effect target capture for whole-layer fallback and implements effect-brush stroke commit.
lib/providers/app_preferences.dart Adds last-selected-layer persistence keyed by file path for flat formats.
lib/panels/tools/tools_panel.dart Replaces legacy brushes/effects sections with the unified ToolFamilyRail; adds collapsible top-colors UI.
lib/panels/tools/tool_family_rail.dart New unified Brush rail UI including effect arming and effect brush controls.
lib/models/version.dart Updates generated version constants to 1.9.0.
lib/models/user_action_drawing.dart Updates smudge icon mapping.
lib/models/tool_family.dart Adds centralized localized labels for gesture tools in the rail.
lib/models/tool_descriptor.dart Defines ordered rail descriptors for gesture tools + effects.
lib/models/selection_effect.dart Replaces sharpen/soften with bipolar Sharpness; introduces bipolar flag and signed strength mapping.
lib/models/effect_labels.dart Updates effect label mapping for Sharpness.
lib/models/effect_brush_model.dart New model for effect-brush armed state and parameters.
lib/models/app_icon_enum.dart Adds new smudge icon entry.
lib/l10n/app_localizations.dart Renames/removes effect/tool strings (Sharpness, Brush section, Edge Detection) and reorganizes declarations.
lib/l10n/app_localizations_fr.dart Updates FR translations for renamed/removed strings.
lib/l10n/app_localizations_es.dart Updates ES translations for renamed/removed strings.
lib/l10n/app_localizations_en.dart Updates EN strings for renamed/removed strings.
lib/l10n/app_fr.arb Updates FR arb keys for Sharpness/Brush section/Edge Detection.
lib/l10n/app_es.arb Updates ES arb keys for Sharpness/Brush section/Edge Detection.
lib/l10n/app_en.arb Updates EN arb keys for Sharpness/Brush section/Edge Detection.
lib/helpers/transform_helper.dart Adds haptic tick logic for wand tolerance snapping.
lib/helpers/shortcuts_constants.dart Renames wand sample-all-layers shortcut label to Edge Detection.
lib/helpers/image_effects.dart Adjusts strength guard to allow negative (bipolar) strengths.
lib/files/tiff_constants.dart Adds TIFF metadata key for selected-layer marker.
lib/files/save.dart Records last-selected-layer index in preferences for non-layered formats.
lib/files/import_files.dart Restores last-selected-layer for flat formats on open; updates open API to accept preferences.
lib/files/file_tiff.dart Embeds/restores selected-layer flag in TIFF metadata and restores selection after replaceAll.
lib/files/file_ora.dart Embeds/restores selected-layer flag in ORA stack.xml and restores selection after replaceAll.
lib/constants/keys.dart Adds keys for top-colors toggle and effect-paint sliders.
lib/constants/app_math.dart Adds wand tolerance snap interval constant.
lib/constants/app_interaction.dart Adds wand tolerance drag pixels-per-unit constant.
lib/constants/app_defaults.dart Updates default wand tolerance.
FEATURES.MD Documents the unified Brush rail model and updated effect list/behavior.
fcheck_packages.svg Updates package label version text.
CHANGELOG.md Adds 1.9.0 release notes reflecting the unified tool model and related UX changes.
BRUSHES_AND_EFFECTS.md Adds canonical spec for the unified tool model and invariants.
assets/icons/smudge.svg Adds new smudge icon asset.
assets/icons/selector_wand.svg Updates wand icon artwork (adds center square detail).
AGENTS.md Documents tool-model invariants and points contributors to BRUSHES_AND_EFFECTS.md.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +68 to +71
// The brush-size ring is a paint-tool affordance; the selector
// (incl. Edge Detection wand) never paints, so never show it there.
final bool showBrushSizePreview =
appProvider.isBrushSizePreviewVisible && appProvider.selectedAction != ActionType.selector;
Comment on lines +276 to +281
/// Restores the remembered selection for a flat (non-layered) file.
///
/// Flat formats cannot embed the selected layer, so it is looked up from
/// [preferences] by path. The index is clamped to the current layer range,
/// which in practice resolves to the base layer since flat files reload as a
/// single layer. No-op when the load failed or no preference is available.
/// The effect armed as a brush, or null when nothing is armed.
SelectionEffect? effect;

/// Strength applied by each painted stroke (0..1).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants