Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Example/Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
keyCombo: keyCombo,
target: self,
action: #selector(AppDelegate.tappedHotKey),
autoReRegisterOnKeyboardKeyCodesChange: true)
autoReRegisterOnKeyboardKeyCodesChange: true,
pausesWhenMenuIsTracking: true)
hotKey.register()

// Shift + Control + A
Expand Down Expand Up @@ -68,6 +69,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {

@objc func tappedHotKey() {
print("hotKey!!!!")
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Menu", action: nil, keyEquivalent: ""))
menu.addItem(.separator())
menu.popUp(positioning: nil, at: NSEvent.mouseLocation, in: nil)
}

@objc func tappedHotKey2() {
Expand Down
17 changes: 16 additions & 1 deletion Lib/Magnet/HotKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ open class HotKey: NSObject {
/// change later, the registered hot key may no longer point to the intended
/// physical key unless it is registered again.
public let autoReRegisterOnKeyboardKeyCodesChange: Bool
/// When enabled, Magnet unregisters this hot key while an NSMenu is tracking
/// events and restores it after menu tracking ends.
///
/// This prevents Carbon hot key events from being delivered after the menu
/// closes when they were pressed while AppKit's menu tracking run loop was
/// active.
///
/// Hot keys whose KeyCombo uses doubled modifiers always behave as if this
/// option is enabled.
public let pausesWhenMenuIsTracking: Bool

var hotKeyId: UInt32?
var hotKeyRef: EventHotKeyRef?
Expand Down Expand Up @@ -59,7 +69,8 @@ open class HotKey: NSObject {
target: AnyObject,
action: Selector,
actionQueue: ActionQueue = .main,
autoReRegisterOnKeyboardKeyCodesChange: Bool = false
autoReRegisterOnKeyboardKeyCodesChange: Bool = false,
pausesWhenMenuIsTracking: Bool = false
) {
self.identifier = identifier
self.keyCombo = keyCombo
Expand All @@ -68,6 +79,7 @@ open class HotKey: NSObject {
self.action = action
self.actionQueue = actionQueue
self.autoReRegisterOnKeyboardKeyCodesChange = autoReRegisterOnKeyboardKeyCodesChange
self.pausesWhenMenuIsTracking = pausesWhenMenuIsTracking
super.init()
}

Expand All @@ -76,6 +88,7 @@ open class HotKey: NSObject {
keyCombo: KeyCombo,
actionQueue: ActionQueue = .main,
autoReRegisterOnKeyboardKeyCodesChange: Bool = false,
pausesWhenMenuIsTracking: Bool = false,
handler: @escaping ((HotKey) -> Void)
) {
self.identifier = identifier
Expand All @@ -85,6 +98,7 @@ open class HotKey: NSObject {
self.action = nil
self.actionQueue = actionQueue
self.autoReRegisterOnKeyboardKeyCodesChange = autoReRegisterOnKeyboardKeyCodesChange
self.pausesWhenMenuIsTracking = pausesWhenMenuIsTracking
super.init()
}
}
Expand Down Expand Up @@ -128,6 +142,7 @@ extension HotKey {
return self.identifier == hotKey.identifier &&
self.keyCombo == hotKey.keyCombo &&
self.autoReRegisterOnKeyboardKeyCodesChange == hotKey.autoReRegisterOnKeyboardKeyCodesChange &&
self.pausesWhenMenuIsTracking == hotKey.pausesWhenMenuIsTracking &&
self.hotKeyId == hotKey.hotKeyId &&
self.hotKeyRef == hotKey.hotKeyRef
}
Expand Down
51 changes: 48 additions & 3 deletions Lib/Magnet/HotKeyCenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ open class HotKeyCenter {

private var hotKeys = [String: HotKey]()
private var hotKeyCount: UInt32 = 0
private var isMenuTracking = false
private let modifierEventHandler: ModifierEventHandler
private let notificationCenter: NotificationCenter

Expand All @@ -29,6 +30,7 @@ open class HotKeyCenter {
installModifiersChangedEventHandlerIfNeeded()
observeKeyboardKeyCodesChanges()
observeApplicationTerminate()
observeMenuTracking()
}

deinit {
Expand All @@ -45,6 +47,7 @@ extension HotKeyCenter {

hotKeys[hotKey.identifier] = hotKey
guard !hotKey.keyCombo.doubledModifiers else { return true }
guard !(isMenuTracking && hotKey.pausesWhenMenuIsTracking) else { return true }
guard registerEvent(with: hotKey) else {
unregister(with: hotKey)
return false
Expand All @@ -57,7 +60,7 @@ extension HotKeyCenter {
private func registerEvent(with hotKey: HotKey) -> Bool {
let hotKeyId = EventHotKeyID(signature: UTGetOSTypeFromString("Magnet" as CFString), id: hotKeyCount)
var carbonHotKey: EventHotKeyRef?
let currentKeyCode = hotKey.keyCombo.currentKeyCode
let currentKeyCode = hotKey.registeredKeyCode ?? hotKey.keyCombo.currentKeyCode
let error = RegisterEventHotKey(UInt32(currentKeyCode),
UInt32(hotKey.keyCombo.modifiers),
hotKeyId,
Expand Down Expand Up @@ -92,13 +95,15 @@ extension HotKeyCenter {
hotKeys.forEach { unregister(with: $1) }
}

private func unregisterEvent(with hotKey: HotKey) {
private func unregisterEvent(with hotKey: HotKey, clearKeyCode: Bool = true) {
if let carbonHotKey = hotKey.hotKeyRef {
UnregisterEventHotKey(carbonHotKey)
}
hotKey.hotKeyId = nil
hotKey.hotKeyRef = nil
hotKey.registeredKeyCode = nil
if clearKeyCode {
hotKey.registeredKeyCode = nil
}
}
}

Expand All @@ -118,6 +123,17 @@ extension HotKeyCenter {
object: nil)
}

private func observeMenuTracking() {
notificationCenter.addObserver(self,
selector: #selector(HotKeyCenter.menuDidBeginTracking),
name: NSMenu.didBeginTrackingNotification,
object: nil)
notificationCenter.addObserver(self,
selector: #selector(HotKeyCenter.menuDidEndTracking),
name: NSMenu.didEndTrackingNotification,
object: nil)
}

@objc func applicationWillTerminate() {
unregisterAll()
}
Expand All @@ -130,9 +146,38 @@ extension HotKeyCenter {
let currentKeyCode = hotKey.keyCombo.currentKeyCode
guard hotKey.registeredKeyCode != currentKeyCode else { return }
unregisterEvent(with: hotKey)
guard !(isMenuTracking && hotKey.pausesWhenMenuIsTracking) else { return }
registerEvent(with: hotKey)
}
}

@objc func menuDidBeginTracking() {
isMenuTracking = true
pauseHotKeysForMenuTracking()
}

@objc func menuDidEndTracking() {
isMenuTracking = false
resumeHotKeysPausedForMenuTracking()
}

private func pauseHotKeysForMenuTracking() {
hotKeys.values
.filter { $0.pausesWhenMenuIsTracking }
.filter { !$0.keyCombo.doubledModifiers }
.filter { $0.hotKeyRef != nil }
.forEach { hotKey in
unregisterEvent(with: hotKey, clearKeyCode: false)
}
}

private func resumeHotKeysPausedForMenuTracking() {
hotKeys.values
.filter { $0.pausesWhenMenuIsTracking }
.filter { !$0.keyCombo.doubledModifiers }
.filter { $0.hotKeyRef == nil }
.forEach { registerEvent(with: $0) }
}
}

// MARK: - HotKey Events
Expand Down
Loading