5 Commits
Author SHA1 Message Date
9a4d925b34 Brush: remember the last selected brush across restarts (split from #2516) (#3472)
* Brush: remember the last selected brush across restarts (split from #2516)

The persistence half of #2516 on its own, so it can merge and be maintained
apart from the brush-outline half. The tool's brush (settings, tip
references, and the name of the preset it was picked from) is saved once the
pointer is up and restored at launch: options bar, painting cursor and
Brushes panel highlight all start where the session left off.

Review fix from #2516 -- no per-frame work:
- `ToolState::brush_rev` is bumped by every brush/preset setter
  (`tools.setBrush`, preset save/delete/rename/setCurrent/deleteGroup,
  `brush.defineFromSelection`, `brush.presets.importAbr`, the store's tip
  re-linking, and the shell's tool-switch/reset paths); `remember_brush`
  serialises only when it moved since `brush_saved_rev`, so an unchanged
  brush costs a counter compare per frame, not a clone and a JSON dump of
  the whole brush (sampled tip previews included).
- Restoring counts as saved: the launch value is never rewritten.

Bitmaps stay out of the preferences document: remembered tips and patterns
are references (a loaded stored tile drops its full bitmap), and a sampled
bitmap too big to embed is skipped, keeping the previous value so a preview
never enters the prefs file. A malformed remembered brush falls back to the
default.

docs/scorecard.md regenerated with `cargo xtask scorecard` (34 of 154).

Tests: engine 1267 lib (5 new: revision gating, rejected changes, picked
preset, restore round trip, no embedded bitmaps), ui-egui 1982 lib; clippy,
layers, wasm green.

* rustfmt the StoredTile line and regenerate the scorecard after the main merge

* engine tests: destructive Levels keep the signed float overshoot at 32 bits (#2592)

* Scorecard: regenerate after merging upstream/main

* Export As: compare batch paths without the Windows separator

* font fix ?

* fix(ui): only Filter menu dialogs get the in-dialog preview window

#3877 drew the preview window in every schema dialog with a Preview
checkbox, including Image > Rotation > Arbitrary. Photoshop's Rotate
Canvas and adjustment dialogs have no preview window, and the taller
dialog covered the canvas, which broke
arbitrary_rotation_preview::size_changing_rotation_preview_is_visible_and_preserves_the_panned_center
on main. The window and its reserved height now apply to filter.*
commands only.

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

* cargo    fmt --check max_width = 160 fix

---------

Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-13 12:48:41 -04:00
siraxeandJJ 80b8deab0f Brush cursor: shaped tips trace their outline (split from #2516) (#3467)
Add the brush-outline half of #2516 on its own, so review and merge stay
small; the last-brush persistence half follows separately.

- paint: new `outline` module traces the tip shape as closed rings in
  radius units -- a computed ellipse for round tips (rotated/flattened),
  marching squares over the bitmap for sampled/stored tips, at the full
  tip or the 50 % alpha contour. Traced rings stay finite and bounded for
  hostile sizes; tested for the half-coverage contour, blank tips and
  hostile input.
- ui-egui: new `brush_cursor` picks the traced outline when the tip has a
  shape and falls back to the plain circle otherwise; `tool_cursor` gained
  the `Outline` shape (bitmap rasterisation by segment splatting, painter
  fallback on non-Windows).
- The Normal Brush Tip circle now goes through `half_coverage_radius`
  (#2744) so outline and circle agree on the real 50 % contour.

Review fixes from #2516:
- Merged main; docs/scorecard.md is main's version and was verified with
  `cargo xtask scorecard --check` (regeneration changes nothing).
- Dropped the scratch benchmark `crates/paint/examples/tmp_outline_bench.rs`
  and the two `#[ignore = "temp bench"]` tests in tool_cursor/tests.rs.

Co-authored-by: JJ <[email protected]>
2026-10-11 23:23:29 -04:00
4e8aaec643 Addition : Brush popup better UI (#2217)
* Brush name/tip/stroke cards

+ locale

* brush popup scale ui

scale brushes

* align fix + tests

* test fix

* ci fix

* ci fix 2

* don't scale text

* smaller width for tip+name

* scale + spacing + better UX

* Regenerate docs/scorecard.md

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

---------

Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-10 07:04:52 -04:00
35f832f03a Brush panel: update-current-brush button, identity-based preset selection, tip-grid fixes (#2007)
* update brush button

* ui brush duplicated highlight fix

* formatting fix + locale

* i18n: translate "Update the current brush with these settings" in remaining complete catalogs

* Tip Shape picks the tip only; set_brush records the preset once accepted

- A click in the Brush Tip Shape grid copies the tip fields only (main's
  Photoshop behaviour: the dynamics sections stay); the picked preset is
  marked current with brush.presets.setCurrent instead of a whole-preset
  tools.setBrush, so identity-based selection still tracks it.
- tools.setBrush sets current_preset only after the brush merged and
  validated, so a failed call leaves no state behind; a reset still wins
  over a preset in the same call. Tested.
- brush.presets.save / brush.defineFromSelection append at the end again
  (insert-after-the-copied-brush had no Photoshop reference).

* Brush presets: forget the picked preset when the tool's brush swaps; no indexing in brush.presets.update

Each painting tool keeps its own brush (#218), so Update Brush could overwrite the
preset picked with one tool with another tool's brush.

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

---------

Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-09 19:56:41 -04:00
bd7d47bf0f Lasso inside liquify mode , with hotkeys L, alt , ctrl+i, ctrl+h for … (#453)
* Lasso inside liquify mode , with hotkeys L, alt , ctrl+i, ctrl+h for visibility

* Liquify Freeze Lasso: scanline fill, tests, one translated tooltip, theme colours

- apply_lasso fills node rows with an even-odd scanline pass (O(rows x edges) plus the nodes
  written) instead of a point-in-polygon test per node; non-finite points are skipped and huge
  or off-canvas polygons clamp to the field.
- LassoMask joins LiquifyTool::ALL, so the strip draws its button in the loop; its tooltip is
  one translated string (all catalogs); the outline uses theme tokens.
- A closed lasso clears Redo (#435).
- filter.liquify params doc lists lassoMask.
- Tests: concave polygon vs. a reference even-odd test at several cell sizes, thaw, degenerate
  and hostile polygons, replay; the UI lasso records a stroke, Alt thaws, undo/redo.

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

---------

Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-07 07:03:49 -04:00