Last active
September 1, 2021 03:14
-
-
Save orgmir/f48a138339d3f1d175ba3161b2501439 to your computer and use it in GitHub Desktop.
SwiftUI DDHotKeyTextField wrapper, so you can set hotkeys using https://github.com/davedelong/DDHotKey
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Combine | |
import SwiftUI | |
// Don't forget to add https://github.com/davedelong/DDHotKey to your project | |
struct HotKeyTextField: View { | |
@Binding var keyCode: Int | |
@Binding var modifierFlags: Int | |
var body: some View { | |
DDHotKeyTextFieldWrapper(keyCode: $keyCode, modifierFlags: $modifierFlags) | |
.padding(4) | |
.background(Capsule().fill(Color(NSColor.textBackgroundColor))) | |
} | |
private struct DDHotKeyTextFieldWrapper: View, NSViewRepresentable { | |
typealias NSViewType = DDHotKeyTextField | |
@Binding var keyCode: Int | |
@Binding var modifierFlags: Int | |
func makeNSView(context: Context) -> DDHotKeyTextField { | |
let textField = DDHotKeyTextField() | |
textField.hotKey = DDHotKey(keyCode: UInt16(keyCode), modifierFlags: UInt(modifierFlags), task: { _ in }) | |
textField.alignment = NSTextAlignment.center | |
textField.isBordered = false | |
textField.bezelStyle = .roundedBezel | |
textField.sizeToFit() | |
textField.delegate = context.coordinator | |
// Use KVO to detect when the user sets a new hotkey | |
textField.addObserver(context.coordinator, forKeyPath: "hotKey", options: .new, context: nil) | |
return textField | |
} | |
func updateNSView(_ textField: DDHotKeyTextField, context: Context) { | |
textField.hotKey = DDHotKey(keyCode: UInt16(keyCode), modifierFlags: UInt(modifierFlags), task: { _ in }) | |
context.coordinator.window = textField.window | |
} | |
static func dismantleNSView(textField: DDHotKeyTextField, coordinator: Coordinator) { | |
textField.removeObserver(coordinator, forKeyPath: "hotKey") | |
} | |
func makeCoordinator() -> Coordinator { | |
Coordinator(self) | |
} | |
class Coordinator: NSObject, NSTextDelegate, NSTextFieldDelegate { | |
var parent: DDHotKeyTextFieldWrapper | |
var window: NSWindow? | |
private var sink = Set<AnyCancellable>() | |
init(_ parent: DDHotKeyTextFieldWrapper) { | |
self.parent = parent | |
super.init() | |
setupNotification() | |
} | |
// For some reason SwiftUI doesn't resignFirstResponder on the text field, | |
// so we have to do it ourselves. Otherwise, the DDHotKey global listener will | |
// still register events even if we are in a different same app window | |
private func setupNotification() { | |
NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification) | |
.sink { notif in | |
guard let window = self.window, let notifWindow = notif.object as? NSWindow else { return } | |
if window == notifWindow { | |
// this will disable the DDHotKeyTextField global key listener | |
window.makeFirstResponder(nil) | |
} | |
} | |
.store(in: &sink) | |
} | |
@objc override func observeValue(forKeyPath _: String?, | |
of object: Any?, | |
change _: [NSKeyValueChangeKey: Any]?, | |
context _: UnsafeMutableRawPointer?) | |
{ | |
guard let textField = object as? DDHotKeyTextField else { return } | |
let keyCode = Int(textField.hotKey.keyCode) | |
let modifierFlags = Int(textField.hotKey.modifierFlags) | |
if keyCode != parent.keyCode || modifierFlags != parent.modifierFlags { | |
parent.keyCode = keyCode | |
parent.modifierFlags = modifierFlags | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment