Unify brush effect ux - #9
Conversation
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>
|
Firebase Hosting preview is ready. Preview URL: �[36m�[1mi hosting:channel:�[22m�[39m https://vteam-paint--pr-9-deca0q7g.web.app |
There was a problem hiding this comment.
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.
| // 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; |
| /// 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). |
Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
Type of Change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Checklist:
Screenshots (if appropriate):
Additional Notes: