Refactor and Rename to CommandNotch
This commit is contained in:
92
Downterm/CommandNotch/Models/HotkeyBinding.swift
Normal file
92
Downterm/CommandNotch/Models/HotkeyBinding.swift
Normal file
@@ -0,0 +1,92 @@
|
||||
import AppKit
|
||||
import Carbon.HIToolbox
|
||||
|
||||
/// Serializable representation of a keyboard shortcut (modifier flags + key code).
|
||||
/// Stored in UserDefaults as a JSON string.
|
||||
struct HotkeyBinding: Codable, Equatable {
|
||||
var modifiers: UInt // NSEvent.ModifierFlags.rawValue, masked to cmd/shift/ctrl/opt
|
||||
var keyCode: UInt16
|
||||
|
||||
/// Checks whether the given NSEvent matches this binding.
|
||||
func matches(_ event: NSEvent) -> Bool {
|
||||
let mask = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
|
||||
let relevantFlags: NSEvent.ModifierFlags = [.command, .shift, .control, .option]
|
||||
return mask.intersection(relevantFlags).rawValue == modifiers
|
||||
&& event.keyCode == keyCode
|
||||
}
|
||||
|
||||
/// Human-readable label like "⌘⏎" or "⌘⇧T".
|
||||
var displayString: String {
|
||||
var parts: [String] = []
|
||||
let flags = NSEvent.ModifierFlags(rawValue: modifiers)
|
||||
if flags.contains(.control) { parts.append("⌃") }
|
||||
if flags.contains(.option) { parts.append("⌥") }
|
||||
if flags.contains(.shift) { parts.append("⇧") }
|
||||
if flags.contains(.command) { parts.append("⌘") }
|
||||
parts.append(keyName)
|
||||
return parts.joined()
|
||||
}
|
||||
|
||||
private var keyName: String {
|
||||
switch keyCode {
|
||||
case 36: return "⏎"
|
||||
case 48: return "⇥"
|
||||
case 49: return "Space"
|
||||
case 51: return "⌫"
|
||||
case 53: return "⎋"
|
||||
case 123: return "←"
|
||||
case 124: return "→"
|
||||
case 125: return "↓"
|
||||
case 126: return "↑"
|
||||
default:
|
||||
// Try to get the character from the key code
|
||||
let src = TISCopyCurrentKeyboardInputSource().takeRetainedValue()
|
||||
let layoutDataRef = TISGetInputSourceProperty(src, kTISPropertyUnicodeKeyLayoutData)
|
||||
if let layoutDataRef = layoutDataRef {
|
||||
let layoutData = unsafeBitCast(layoutDataRef, to: CFData.self) as Data
|
||||
var deadKeyState: UInt32 = 0
|
||||
var length = 0
|
||||
var chars = [UniChar](repeating: 0, count: 4)
|
||||
layoutData.withUnsafeBytes { ptr in
|
||||
let layoutPtr = ptr.baseAddress!.assumingMemoryBound(to: UCKeyboardLayout.self)
|
||||
UCKeyTranslate(
|
||||
layoutPtr,
|
||||
keyCode,
|
||||
UInt16(kUCKeyActionDisplay),
|
||||
0, // no modifiers for the base character
|
||||
UInt32(LMGetKbdType()),
|
||||
UInt32(kUCKeyTranslateNoDeadKeysBit),
|
||||
&deadKeyState,
|
||||
4,
|
||||
&length,
|
||||
&chars
|
||||
)
|
||||
}
|
||||
if length > 0 {
|
||||
return String(utf16CodeUnits: chars, count: length).uppercased()
|
||||
}
|
||||
}
|
||||
return "Key\(keyCode)"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Serialization
|
||||
|
||||
func toJSON() -> String {
|
||||
(try? String(data: JSONEncoder().encode(self), encoding: .utf8)) ?? "{}"
|
||||
}
|
||||
|
||||
static func fromJSON(_ string: String) -> HotkeyBinding? {
|
||||
guard let data = string.data(using: .utf8) else { return nil }
|
||||
return try? JSONDecoder().decode(HotkeyBinding.self, from: data)
|
||||
}
|
||||
|
||||
// MARK: - Presets
|
||||
|
||||
static let cmdReturn = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 36)
|
||||
static let cmdT = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 17)
|
||||
static let cmdW = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 13)
|
||||
static let cmdShiftRB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 30) // ]
|
||||
static let cmdShiftLB = HotkeyBinding(modifiers: NSEvent.ModifierFlags([.command, .shift]).rawValue, keyCode: 33) // [
|
||||
static let cmdD = HotkeyBinding(modifiers: NSEvent.ModifierFlags.command.rawValue, keyCode: 2)
|
||||
}
|
||||
Reference in New Issue
Block a user