Add Capture Previous Screen Region - #19
Conversation
|
@microsoft-github-policy-service agree |
|
It closes #18 ! I have build the code and tested the code. The previous capture works just fine. |
|
Hi - thanks for the contribution! We'd like to see if the keyboard shortcut for this can be a variant of the existing snip shortcut (CTRL-6)....for example CTRL-SHIFT-OPTION 6. |
There was a problem hiding this comment.
Pull request overview
Adds a new “Capture Previous Region” command to reuse the last successful region snip (without re-opening the drag selection UI), including a configurable hotkey, menu entry, and documentation updates.
Changes:
- Introduces a new app command (
snipPreviousRegion) wired through hotkey handling, mode coordination, and app menu/controller actions. - Tracks the last successful region selection for reuse (idle-mode snips via
SnipController, zoomed-overlay snips viaZoomCanvasView). - Adds settings persistence + UI for configuring the new hotkey, and updates README/help text to document defaults and behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| Sources/ZoomItMacCore/Settings/SettingsWindowController.swift | Adds UI + conflict handling for the new “Capture Previous Region” hotkey and updates help text. |
| Sources/ZoomItMacCore/Settings/SettingsStore.swift | Persists the new hotkey settings and defines defaults. |
| Sources/ZoomItMacCore/Overlay/ZoomCanvasView.swift | Remembers and reuses the previous region within the overlay; refactors region snip execution. |
| Sources/ZoomItMacCore/Overlay/OverlayWindowController.swift | Exposes an overlay-level “capture previous region” entry point. |
| Sources/ZoomItMacCore/Hotkeys/HotkeyService.swift | Registers two hotkeys (copy vs save) for “Capture Previous Region” and maps them to a new command. |
| Sources/ZoomItMacCore/Core/ModeCoordinator.swift | Routes the new command and adds support for reusing the previous region in both idle and overlay modes. |
| Sources/ZoomItMacCore/Core/AppCommand.swift | Adds the new command case. |
| Sources/ZoomItMacCore/Capture/SnipController.swift | Stores the previous selection and implements “capture previous” without UI. |
| Sources/ZoomItMacCore/App/AppDelegate.swift | Adds a menu item for “Capture Previous Region”. |
| Sources/ZoomItMacCore/App/AppController.swift | Handles menu action by dispatching the new command. |
| README.md | Documents the new default hotkeys and describes the feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| guard let display = displayManager.activeDisplay() else { | ||
| finish() | ||
| return true | ||
| } |
| guard rect.width >= 3, rect.height >= 3, let full = captureViewportImage() else { | ||
| return false | ||
| } | ||
|
|
||
| let scale = window?.backingScaleFactor ?? capturedFrame.display.scaleFactor | ||
| let pixelRect = CGRect( | ||
| x: rect.minX * scale, | ||
| y: rect.minY * scale, | ||
| width: rect.width * scale, | ||
| height: rect.height * scale | ||
| ).integral | ||
|
|
||
| guard let cropped = full.cropping(to: pixelRect) else { | ||
| return false | ||
| } | ||
|
|
||
| previousRegionRect = rect |
a38146c to
f18d9a0
Compare
| private func performRegionSnip(rect: CGRect, action: SnipAction) -> Bool { | ||
| guard rect.width >= 3, rect.height >= 3, let full = captureViewportImage() else { | ||
| return false | ||
| } | ||
|
|
||
| let scale = window?.backingScaleFactor ?? capturedFrame.display.scaleFactor | ||
| let pixelRect = CGRect( | ||
| x: rect.minX * scale, | ||
| y: rect.minY * scale, | ||
| width: rect.width * scale, | ||
| height: rect.height * scale | ||
| ).integral | ||
|
|
||
| guard let cropped = full.cropping(to: pixelRect) else { | ||
| return false | ||
| } | ||
|
|
||
| previousRegionRect = rect |
| private func conflictsWithSnipPrevious(code: Int, modifiers: UInt) -> Bool { | ||
| settings.snipPreviousHotKeyCode != 0 && | ||
| code == settings.snipPreviousHotKeyCode && modifiers == settings.snipPreviousHotKeyModifiers | ||
| } |
| guard let display = displayManager.activeDisplay() else { | ||
| finish() | ||
| return true | ||
| } |
| let frame = try await captureService.captureDisplay(display) | ||
| let rect = previousSelection.rectForCurrentDisplay(frame.display) | ||
| _ = self.handleSelection(rect, in: frame) | ||
| self.finish() | ||
| } catch { |
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
| private func conflictsWithSnipPrevious(code: Int, modifiers: UInt) -> Bool { | ||
| settings.snipPreviousHotKeyCode != 0 && | ||
| code == settings.snipPreviousHotKeyCode && modifiers == settings.snipPreviousHotKeyModifiers | ||
| } |
| if reusePreviousRegion { | ||
| isSnipping = true | ||
| let started = snipController.capturePrevious(action: action) { [weak self] in | ||
| self?.isSnipping = false | ||
| } | ||
| if !started { | ||
| isSnipping = false | ||
| NSSound.beep() | ||
| } | ||
| return |
Fix for rectangle Co-authored-by: Copilot Autofix powered by AI <[email protected]>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
Sources/ZoomItMacCore/Settings/SettingsWindowController.swift:685
conflictsWithSnipPreviousonly checks the base modifiers, but HotkeyService registers two global hotkeys for this command (base and base-with-Shift-toggled). This allows other shortcuts to be set to the save-variant combination without being flagged as a conflict, potentially resulting in duplicate registrations and ambiguous behavior.
private func conflictsWithSnipPrevious(code: Int, modifiers: UInt) -> Bool {
settings.snipPreviousHotKeyCode != 0 &&
code == settings.snipPreviousHotKeyCode && modifiers == settings.snipPreviousHotKeyModifiers
}
Sources/ZoomItMacCore/Capture/SnipController.swift:285
capturePreviousalways capturesdisplayManager.activeDisplay()(cursor location) even though the previous selection stores the originaldisplayID. If the user moves the cursor to a different monitor, “Capture Previous Region” can capture from the wrong display. Prefer capturing the previous display when it still exists, and fall back to the active display only if that display is no longer available.
guard let display = displayManager.activeDisplay() else {
NSSound.beep()
finish()
return true
}
Sources/ZoomItMacCore/Capture/SnipController.swift:292
- The result of
handleSelectionis ignored when reusing the previous region. If scaling/intersection/cropping fails, the action will silently do nothing (no copy/save) with no feedback. Consider beeping whenhandleSelectionreturns false so the user knows the capture failed.
let frame = try await captureService.captureDisplay(display)
let rect = previousSelection.rectForCurrentDisplay(frame.display)
_ = self.handleSelection(rect, in: frame)
self.finish()
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (4)
Sources/ZoomItMacCore/Settings/SettingsWindowController.swift:670
- When recording the “Capture Previous Region” hotkey, conflicts are only checked against the base shortcuts for Snip/Record/DemoType/Panorama. Those commands all register a second variant with Shift toggled, so users can currently set “Capture Previous Region” to (for example) the existing “Snip region to file” shortcut without a conflict being detected, leading to duplicate/failed hotkey registrations at runtime. Also, this hotkey’s help text says “Hold Shift … to save”, but recording currently allows Shift in the base modifiers, which inverts/ambiguous behavior because HotkeyService uses XOR to toggle Shift.
case .snipPrevious:
if conflictsWithZoom(code: newCode, modifiers: newModifiers) ||
conflictsWithDraw(code: newCode, modifiers: newModifiers) ||
conflictsWithLive(code: newCode, modifiers: newModifiers) ||
conflictsWithBreak(code: newCode, modifiers: newModifiers) ||
Sources/ZoomItMacCore/Settings/SettingsWindowController.swift:757
conflictsWithSnipPreviousonly checks the base modifiers, but HotkeyService registers two hotkeys for this command (base and base-with-Shift-toggled). This lets another command be recorded to the “save” variant without any conflict being flagged.
private func conflictsWithSnipPrevious(code: Int, modifiers: UInt) -> Bool {
settings.snipPreviousHotKeyCode != 0 &&
code == settings.snipPreviousHotKeyCode && modifiers == settings.snipPreviousHotKeyModifiers
}
Sources/ZoomItMacCore/Capture/SnipController.swift:299
capturePreviousstores the display ID inPreviousSelection, but then always capturesdisplayManager.activeDisplay(). If the cursor is on a different monitor when the hotkey is pressed, the “previous region” is applied to the wrong display, which doesn’t match the expected ‘repeat the last capture area’.
guard let display = displayManager.activeDisplay() else {
NSSound.beep()
finish()
return true
}
Sources/ZoomItMacCore/Capture/SnipController.swift:306
- In
capturePrevious, a failed reuse (e.g. the scaled/clipped rect becomes too small or ends up outside the current display bounds) is currently silent because the return value ofhandleSelectionis ignored. That leaves the user with no feedback when nothing is captured.
let rect = previousSelection.rectForCurrentDisplay(frame.display)
_ = self.handleSelection(rect, in: frame)
self.finish()
| func rectForCurrentDisplay(_ display: DisplayDescriptor) -> CGRect { | ||
| guard displaySize.width > 0, displaySize.height > 0 else { return rect } | ||
|
|
||
| let widthScale = display.frame.width / displaySize.width | ||
| let heightScale = display.frame.height / displaySize.height | ||
| return CGRect( | ||
| x: rect.minX * widthScale, | ||
| y: rect.minY * heightScale, | ||
| width: rect.width * widthScale, | ||
| height: rect.height * heightScale | ||
| ) | ||
| } |
| @objc func capturePreviousRegion() { | ||
| modeCoordinator.handle(.snipPreviousRegion(save: false)) | ||
| } |

Use Case
Many users repeatedly capture the same area of the screen—for example:
Proposed Solution
Add a new command (and optionally a configurable keyboard shortcut) named "Capture Previous Region" that: