55 lines
1.7 KiB
Swift
55 lines
1.7 KiB
Swift
import AppKit
|
|
import Carbon.HIToolbox
|
|
import SwiftUI
|
|
|
|
/// Invisible NSView overlay that becomes first responder while recording
|
|
/// and reports the next key combo (key code + modifiers) it sees.
|
|
final class KeyCaptureView: NSView {
|
|
var onCapture: ((UInt32, UInt32) -> Void)?
|
|
var onCancel: (() -> Void)?
|
|
|
|
override var acceptsFirstResponder: Bool { true }
|
|
|
|
override func keyDown(with event: NSEvent) {
|
|
if Int(event.keyCode) == kVK_Escape {
|
|
onCancel?()
|
|
return
|
|
}
|
|
|
|
var modifiers: UInt32 = 0
|
|
if event.modifierFlags.contains(.control) { modifiers |= UInt32(controlKey) }
|
|
if event.modifierFlags.contains(.option) { modifiers |= UInt32(optionKey) }
|
|
if event.modifierFlags.contains(.shift) { modifiers |= UInt32(shiftKey) }
|
|
if event.modifierFlags.contains(.command) { modifiers |= UInt32(cmdKey) }
|
|
|
|
// Require at least one modifier so plain letters still work normally elsewhere.
|
|
guard modifiers != 0 else { return }
|
|
onCapture?(UInt32(event.keyCode), modifiers)
|
|
}
|
|
}
|
|
|
|
struct KeyCaptureRepresentable: NSViewRepresentable {
|
|
@Binding var isRecording: Bool
|
|
let onCapture: (UInt32, UInt32) -> Void
|
|
|
|
func makeNSView(context: Context) -> KeyCaptureView {
|
|
let view = KeyCaptureView()
|
|
view.onCapture = { keyCode, modifiers in
|
|
onCapture(keyCode, modifiers)
|
|
isRecording = false
|
|
}
|
|
view.onCancel = {
|
|
isRecording = false
|
|
}
|
|
return view
|
|
}
|
|
|
|
func updateNSView(_ nsView: KeyCaptureView, context: Context) {
|
|
if isRecording {
|
|
DispatchQueue.main.async {
|
|
nsView.window?.makeFirstResponder(nsView)
|
|
}
|
|
}
|
|
}
|
|
}
|