fix(regionSelector): drop freehand mask in Crop mode - true-shape is Instant-only

The crop editor is a rectangle tool; a polygon mask its handles cannot
reshape only confuses. A freehand loop in Crop still seeds the editor
with its bounding box, but confirm captures the adjusted rect.
This commit is contained in:
DJ committed 2026-07-04 23:52:05 -04:00
1 parent 510446e499
commit 0ac5c4fca8
3 files changed
+11 -10

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi
- **Crop Mode** - Press `C` (or the new Instant/Crop toolbar tabs) to switch the region selector into Crop mode: releasing a selection opens an adjustable editor (8 resize handles + interior move, min-size and screen clamping, live size label) instead of capturing immediately; **Enter**/**Space** or the confirm button captures, **Escape** returns to drawing. Works with both selection modes (a circle loop seeds the editor with its bounding box). Works for screenshots and recording alike; configurable via `regionSelector.crop.minSize`/`handleSize`
- **`R` Hotkey** - Toggles Rect/Freehand selection mode inside the overlay (the "Circle" tab is now labeled **Freehand**; config keys keep their `circle` names)
- **Freehand True-Shape Capture** - Freehand selections now capture only the pixels inside the drawn loop (transparent outside, via an ImageMagick polygon mask). OCR flattens onto white; recording remains rectangular. In Crop mode the mask survives the editor - confirm captures the loop intersected with the adjusted rect
- **Freehand True-Shape Capture** - Freehand selections now capture only the pixels inside the drawn loop (transparent outside, via an ImageMagick polygon mask). OCR flattens onto white; recording and the crop editor remain rectangular (true-shape applies to Instant-mode freehand only)
- **`regionSelector.dragThreshold` Config Key** - Pixel dead-zone (default `6`) for the region selector's click/drag discriminator; `0` disables it, restoring the previous per-pixel sensitivity (circle mode always uses `0` — every circle gesture is the drawn shape)
### Changed
+1 -1
View File
@@ -27,7 +27,7 @@ Click and drag to select a rectangular region. Features:
### Freehand Selection
Freehand captures the true drawn shape: the output is cropped to the loop's bounding box and everything outside the loop is transparent. OCR flattens the mask onto white (tesseract dislikes alpha); recording always captures the bounding rectangle (wf-recorder is rectangle-only). In Crop mode the mask survives the editor: confirm captures the drawn loop intersected with the adjusted rect.
Freehand captures the true drawn shape: the output is cropped to the loop's bounding box and everything outside the loop is transparent. OCR flattens the mask onto white (tesseract dislikes alpha); recording always captures the bounding rectangle (wf-recorder is rectangle-only). In Crop mode the mask is discarded — the crop editor is a rectangle tool, so a freehand seed adjusted there captures the adjusted rect.
Used for image search by default. Draw a freeform circle around content:
@@ -430,23 +430,24 @@ PanelWindow {
// Circle dragging?
else if (root.selectionMode === RegionSelection.SelectionMode.Circle) {
const padding = Config.options.regionSelector.circle.padding + Config.options.regionSelector.circle.strokeWidth / 2;
// True-shape capture: stage the path for snip()'s polygon mask.
// Degenerate paths (< 3 vertices) fall back to the bounding box.
// Staged in Crop too — the mask is screen-anchored (snip() transforms
// it against the final rect), so confirm captures loop ∩ adjusted rect.
if (dragState.points.length >= 3) {
root.pendingSnipPoints = dragState.points.slice();
}
if (root.isCropMode) {
// Crop: seed the editor from the loop's bounding box WITHOUT
// setRegionFromCirclePoints — that writes region* directly and
// would break the bindings Esc-back-to-draw depends on
// would break the bindings Esc-back-to-draw depends on.
// No mask staging: the crop editor is a rectangle tool, and a
// mask its handles can't reshape only confuses — true-shape
// capture is Instant-mode freehand only.
const box = dragState.circleBoundingBox(padding, mouseArea.mouseX, mouseArea.mouseY);
dragState.endDrag();
root.openCropEditor(box.x, box.y, box.width, box.height);
return;
}
dragState.setRegionFromCirclePoints(padding, mouseArea.mouseX, mouseArea.mouseY);
// True-shape capture (Instant only): stage the path for snip()'s
// polygon mask. Degenerate paths (< 3 vertices) keep the bbox.
if (dragState.points.length >= 3) {
root.pendingSnipPoints = dragState.points.slice();
}
}
dragState.endDrag();
if (root.isCropMode) {