Initial commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// HotkeyRecorder.swift
|
||||
// GreenshotMac
|
||||
//
|
||||
// A small AppKit control that records the next key combination, wrapped for SwiftUI.
|
||||
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import Carbon.HIToolbox
|
||||
|
||||
final class HotkeyRecorderControl: NSButton {
|
||||
|
||||
var onRecord: ((HotkeySpec) -> Void)?
|
||||
var spec: HotkeySpec = .none {
|
||||
didSet { refreshTitle() }
|
||||
}
|
||||
|
||||
private var isRecording = false {
|
||||
didSet { refreshTitle() }
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
bezelStyle = .rounded
|
||||
setButtonType(.momentaryPushIn)
|
||||
target = self
|
||||
action = #selector(startRecording)
|
||||
refreshTitle()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) is not used") }
|
||||
|
||||
private func refreshTitle() {
|
||||
title = isRecording ? "Press a key…" : HotkeyManager.description(of: spec)
|
||||
}
|
||||
|
||||
@objc private func startRecording() {
|
||||
isRecording = true
|
||||
window?.makeFirstResponder(self)
|
||||
}
|
||||
|
||||
override var acceptsFirstResponder: Bool { true }
|
||||
|
||||
override func keyDown(with event: NSEvent) {
|
||||
guard isRecording else { return super.keyDown(with: event) }
|
||||
|
||||
if event.keyCode == UInt16(kVK_Escape) {
|
||||
isRecording = false
|
||||
return
|
||||
}
|
||||
if event.keyCode == UInt16(kVK_Delete) {
|
||||
isRecording = false
|
||||
spec = .none
|
||||
onRecord?(.none)
|
||||
return
|
||||
}
|
||||
|
||||
let modifiers = HotkeyManager.carbonModifiers(from: event.modifierFlags)
|
||||
guard modifiers != 0 else {
|
||||
NSSound.beep() // a global hotkey without modifiers would swallow normal typing
|
||||
return
|
||||
}
|
||||
let recorded = HotkeySpec(keyCode: UInt32(event.keyCode), modifiers: modifiers)
|
||||
isRecording = false
|
||||
spec = recorded
|
||||
onRecord?(recorded)
|
||||
}
|
||||
|
||||
override func resignFirstResponder() -> Bool {
|
||||
isRecording = false
|
||||
return super.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
struct HotkeyRecorder: NSViewRepresentable {
|
||||
let action: HotkeyAction
|
||||
|
||||
func makeNSView(context: Context) -> HotkeyRecorderControl {
|
||||
let control = HotkeyRecorderControl()
|
||||
control.spec = HotkeyBindings.shared.spec(for: action)
|
||||
control.onRecord = { spec in
|
||||
HotkeyBindings.shared.set(spec, for: action)
|
||||
}
|
||||
return control
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: HotkeyRecorderControl, context: Context) {
|
||||
nsView.spec = HotkeyBindings.shared.spec(for: action)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user