forked from home-assistant/iOS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMacBridgeStatusItem.swift
128 lines (102 loc) · 4.03 KB
/
MacBridgeStatusItem.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import AppKit
class MacBridgeStatusItem: NSObject, NSMenuDelegate {
private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
private var lastConfiguration: MacBridgeStatusItemConfiguration?
override init() {
super.init()
statusItem.button?.imagePosition = .imageLeading
statusItem.button?.target = self
statusItem.button?.action = #selector(statusItemTapped(_:))
statusItem.button?.sendAction(on: [.leftMouseUp, .leftMouseDown, .rightMouseDown])
}
func configure(title: String) {
statusItem.button?.title = title
}
func configure(using configuration: MacBridgeStatusItemConfiguration) {
lastConfiguration = configuration
statusItem.isVisible = configuration.isVisible
statusItem.button?.setAccessibilityLabel(configuration.accessibilityLabel)
let image = NSImage(cgImage: configuration.image, size: configuration.imageSize)
image.isTemplate = true
statusItem.button?.image = image
}
@objc private func statusItemTapped(_ sender: NSStatusBarButton) {
guard let configuration = lastConfiguration, let event = NSApp.currentEvent else { return }
if event.isRightClickEquivalentEvent {
let mainMenu = menu(for: configuration.items)
mainMenu.delegate = self
statusItem.menu = mainMenu
sender.performClick(sender)
} else if event.type == .leftMouseUp {
// leftMouseDown also fires, but we only want to do that for ctrl-clicks
configuration.primaryActionHandler(MacBridgeStatusItemCallbackInfoImpl(sender: sender))
}
}
func menuDidClose(_ menu: NSMenu) {
statusItem.menu = nil
}
private func modifierKeys(for uglyMask: Int) -> NSEvent.ModifierFlags {
var modifierMask: NSEvent.ModifierFlags = []
let pairings: [(MacBridgeStatusModifierMask, NSEvent.ModifierFlags)] = [
(.capsLock, .capsLock),
(.shift, .shift),
(.control, .control),
(.option, .option),
(.command, .command),
(.numericPad, .numericPad),
(.help, .help),
(.function, .function),
]
for (ugly, good) in pairings where uglyMask & ugly.rawValue != 0 {
modifierMask.insert(good)
}
return modifierMask
}
private func menu(for items: [MacBridgeStatusItemMenuItem]) -> NSMenu {
let menu = NSMenu()
for item in items {
guard !item.isSeparator else {
menu.addItem(.separator())
continue
}
let menuItem = NSMenuItem(
title: item.name,
action: #selector(actionTapped(_:)),
keyEquivalent: item.keyEquivalent
)
menu.addItem(menuItem)
menuItem.keyEquivalentModifierMask = modifierKeys(for: item.keyEquivalentModifierMask)
menuItem.target = self
menuItem.representedObject = item
if let image = item.image, case let imageSize = item.imageSize, imageSize != .zero {
menuItem.image = NSImage(cgImage: image, size: imageSize)
}
if !item.subitems.isEmpty {
menuItem.submenu = self.menu(for: item.subitems)
}
}
return menu
}
@objc private func actionTapped(_ sender: NSMenuItem) {
guard let representedObject = sender.representedObject as? MacBridgeStatusItemMenuItem else { return }
representedObject.primaryActionHandler(MacBridgeStatusItemCallbackInfoImpl(sender: sender))
}
}
class MacBridgeStatusItemCallbackInfoImpl: MacBridgeStatusItemCallbackInfo {
let sender: Any?
init(sender: Any?) {
self.sender = sender
}
var isActive: Bool {
NSApp.isActive
}
func activate() {
NSApp.activate(ignoringOtherApps: true)
}
func deactivate() {
NSApp.hide(sender)
}
func terminate() {
NSApp.terminate(sender)
}
}