From 3bbfdbb6bfe19c6a88b0c09d0f29199b060523fe Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 4 Jul 2026 23:56:46 -0400 Subject: [PATCH] feat(regionSelector): Crop implies Rect - auto-swap Freehand on entry Entering Crop while Freehand is active swaps selection to Rect; the R key and the Rect/Freehand tabs are inert while Crop is on. Leaving Crop keeps Rect (no restore); already-Rect swaps are no-ops. --- CHANGELOG.md | 2 +- MANUAL.md | 4 ++-- .../modules/regionSelector/OptionsToolbar.qml | 7 +++---- .../modules/regionSelector/RegionSelection.qml | 17 ++++++++++++----- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 022b94a..16e571b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Conventional Commits](https://www.conventionalcommi ### Added -- **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` +- **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. Crop implies rectangles: entering it from Freehand auto-swaps to Rect (Freehand is unselectable while Crop is active). 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 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) diff --git a/MANUAL.md b/MANUAL.md index 6fd7d53..eee1eea 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -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 is discarded — the crop editor is a rectangle tool, so a freehand seed adjusted there captures 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). Freehand is Instant-mode only — entering Crop swaps to rectangle selection. Used for image search by default. Draw a freeform circle around content: @@ -55,7 +55,7 @@ By default captures happen instantly when you release the mouse. Press `C` (or u - `Enter` or `Space` (or the confirm button) captures the adjusted region; `Escape` goes back to drawing - Works for all actions, including recording; right-click selections still open the editor and route to Edit on confirm -Crop mode is per monitor and always starts off (the overlay opens in Instant mode). Freehand selection works in Crop too — the drawn loop's bounding box seeds the editor. Toggle Rect/Freehand with `R`. Minimum size and handle size are configurable via `regionSelector.crop` in the config. +Crop mode is per monitor and always starts off (the overlay opens in Instant mode). Crop implies rectangle selection: entering Crop while Freehand is active swaps to Rect, and Freehand cannot be selected while Crop is on (toggle Rect/Freehand with `R` in Instant mode). Minimum size and handle size are configurable via `regionSelector.crop` in the config. ## Screenshot Actions diff --git a/quickshell/modules/regionSelector/OptionsToolbar.qml b/quickshell/modules/regionSelector/OptionsToolbar.qml index c087748..cdcc475 100644 --- a/quickshell/modules/regionSelector/OptionsToolbar.qml +++ b/quickshell/modules/regionSelector/OptionsToolbar.qml @@ -75,12 +75,11 @@ Toolbar { } } - // Selection mode tabs (Rect/Circle) - usable in Crop too (a circle loop seeds the - // editor via its bounding box); inert only while the crop editor is open, same as - // the capture tabs + // Selection mode tabs (Rect/Freehand) - inert while Crop is active (Crop implies + // rectangles; entering Crop from Freehand auto-swaps to Rect) ToolbarTabBar { id: tabBar - enabled: !root.cropEditing + enabled: !root.cropActive opacity: enabled ? 1 : 0.4 tabButtonList: [ {"icon": "activity_zone", "name": Translation.tr("Rect")}, diff --git a/quickshell/modules/regionSelector/RegionSelection.qml b/quickshell/modules/regionSelector/RegionSelection.qml index a125983..1fa4643 100644 --- a/quickshell/modules/regionSelector/RegionSelection.qml +++ b/quickshell/modules/regionSelector/RegionSelection.qml @@ -46,13 +46,17 @@ PanelWindow { property bool isCropMode: (root.captureMode === RegionSelection.CaptureMode.Crop) // Swapping capture modes resets the editor and any in-progress drag — never the - // frame or the selection mode. Circle works in Crop too: the drawn loop's bounding - // box seeds the editor. Lives here so both the C key and the toolbar tabs get the - // same behavior. + // frame. Crop implies rectangles: entering Crop from Freehand auto-swaps to Rect + // (and Freehand stays unselectable while Crop is active — R key and tab are inert). + // Leaving Crop does NOT restore Freehand; already-Rect swaps are no-ops. Lives here + // so both the C key and the toolbar tabs get the same behavior. onCaptureModeChanged: { root.cropEditing = false; root.pendingSnipPoints = []; dragState.reset(); + if (root.isCropMode && root.selectionMode === RegionSelection.SelectionMode.Circle) { + root.selectionMode = RegionSelection.SelectionMode.RectCorners; + } } // Restore session defaults when a capture shortcut re-triggers while the overlay is @@ -374,9 +378,10 @@ PanelWindow { root.captureMode = root.isCropMode ? RegionSelection.CaptureMode.Instant : RegionSelection.CaptureMode.Crop; - } else if (event.key === Qt.Key_R && event.modifiers === Qt.NoModifier && !dragState.dragging && !root.cropEditing) { + } else if (event.key === Qt.Key_R && event.modifiers === Qt.NoModifier && !dragState.dragging && !root.isCropMode) { // Rect/Freehand toggle — same guards as C (no mid-drag swaps), plus - // inert while the editor is open, matching the toolbar tab gating + // inert while Crop is active (Crop implies rectangles), matching the + // toolbar tab gating root.selectionMode = root.selectionMode === RegionSelection.SelectionMode.RectCorners ? RegionSelection.SelectionMode.Circle : RegionSelection.SelectionMode.RectCorners; @@ -600,6 +605,8 @@ PanelWindow { selectionMode: root.selectionMode captureMode: root.captureMode onSelectionModeSelected: (mode) => { + // Crop implies rectangles (tab is disabled then too — belt and braces) + if (root.isCropMode && mode === RegionSelection.SelectionMode.Circle) return; if (root.selectionMode !== mode) root.selectionMode = mode; } onCaptureModeSelected: (mode) => {