Skip to content

Commit 6e7dc6f

Browse files
committed
Update package
1 parent ffce84c commit 6e7dc6f

File tree

5 files changed

+216
-73
lines changed

5 files changed

+216
-73
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// Copyright (c) Vatsal Manot
3+
//
4+
5+
import Swift
6+
import SwiftUI
7+
8+
@available(iOS 14.0, macOS 11.0, *)
9+
@available(tvOS, unavailable)
10+
@available(watchOS, unavailable)
11+
extension KeyEquivalent {
12+
public static func ~= (lhs: KeyEquivalent, rhs: KeyEquivalent) -> Bool {
13+
lhs.character == rhs.character
14+
}
15+
16+
public static func == (lhs: KeyEquivalent, rhs: KeyEquivalent) -> Bool {
17+
lhs.character == rhs.character
18+
}
19+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// Copyright (c) Vatsal Manot
3+
//
4+
5+
import Swift
6+
import SwiftUI
7+
8+
#if os(macOS)
9+
10+
@available(iOS 14.0, macOS 11.0, *)
11+
@available(tvOS, unavailable)
12+
@available(watchOS, unavailable)
13+
extension KeyboardShortcut {
14+
public var appKitKeyEquivalent: (key: Character, modifiers: NSEvent.ModifierFlags) {
15+
return (key.character, modifiers.appKitModifierFlags)
16+
}
17+
18+
public init?(from event: NSEvent) {
19+
guard let key = event.charactersIgnoringModifiers.map(Character.init) else {
20+
return nil
21+
}
22+
23+
self.init(KeyEquivalent(key), modifiers: EventModifiers(from: event.modifierFlags))
24+
}
25+
26+
public static func ~= (lhs: KeyboardShortcut, rhs: KeyboardShortcut) -> Bool {
27+
lhs.key ~= rhs.key && rhs.modifiers.contains(lhs.modifiers)
28+
}
29+
}
30+
31+
// MARK: - Auxiliary Implementation -
32+
33+
extension EventModifiers {
34+
fileprivate var appKitModifierFlags: NSEvent.ModifierFlags {
35+
switch self {
36+
case .capsLock:
37+
return .capsLock
38+
case .shift:
39+
return .shift
40+
case .control:
41+
return .control
42+
case .option:
43+
return .control
44+
case .command:
45+
return .command
46+
case .numericPad:
47+
return .numericPad
48+
case .function:
49+
return .function
50+
default:
51+
fatalError()
52+
}
53+
}
54+
55+
fileprivate init(from modifierFlags: NSEvent.ModifierFlags) {
56+
self.init()
57+
58+
if modifierFlags.contains(.capsLock) {
59+
insert(.capsLock)
60+
}
61+
62+
if modifierFlags.contains(.shift) {
63+
insert(.shift)
64+
}
65+
66+
if modifierFlags.contains(.control) {
67+
insert(.control)
68+
}
69+
70+
if modifierFlags.contains(.command) {
71+
insert(.command)
72+
}
73+
74+
if modifierFlags.contains(.numericPad) {
75+
insert(.numericPad)
76+
}
77+
78+
if modifierFlags.contains(.function) {
79+
insert(.function)
80+
}
81+
}
82+
}
83+
84+
#endif

Sources/Intramodular/Miscellaneous/NSEventGlobalMonitor.swift

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// Copyright (c) Vatsal Manot
3+
//
4+
5+
#if os(macOS)
6+
7+
import AppKit
8+
import Combine
9+
import Swift
10+
import SwiftUI
11+
12+
public final class NSEventMonitor {
13+
public enum Context {
14+
case local
15+
case global
16+
}
17+
18+
private let context: Context
19+
private let mask: NSEvent.EventTypeMask
20+
private var monitor: Any?
21+
22+
public var eventHandler: (NSEvent) -> NSEvent? = { $0 }
23+
24+
public init(
25+
context: Context,
26+
matching mask: NSEvent.EventTypeMask
27+
) {
28+
self.context = context
29+
self.mask = mask
30+
31+
start()
32+
}
33+
34+
private func start() {
35+
switch self.context {
36+
case .local:
37+
monitor = NSEvent.addLocalMonitorForEvents(matching: mask) { [weak self] event in
38+
guard let `self` = self else {
39+
return event
40+
}
41+
42+
return self.eventHandler(event)
43+
}
44+
case .global:
45+
monitor = NSEvent.addGlobalMonitorForEvents(matching: mask) { [weak self] event in
46+
_ = self?.eventHandler(event)
47+
}
48+
}
49+
}
50+
51+
private func stop() {
52+
if let monitor = monitor {
53+
NSEvent.removeMonitor(monitor)
54+
55+
self.monitor = nil
56+
}
57+
}
58+
59+
deinit {
60+
stop()
61+
}
62+
}
63+
64+
// MARK: - API -
65+
66+
@available(macOS 11.0, *)
67+
extension View {
68+
public func onAppKitEvent(
69+
context: NSEventMonitor.Context = .local,
70+
matching mask: NSEvent.EventTypeMask,
71+
peform action: @escaping (NSEvent) -> NSEvent?
72+
) -> some View {
73+
modifier(
74+
_AttachNSEventMonitor(
75+
eventMonitor: .init(context: context, matching: mask),
76+
handleEvent: action
77+
)
78+
)
79+
}
80+
81+
public func onAppKitKeyboardShortcutEvent(
82+
context: NSEventMonitor.Context = .local,
83+
perform action: @escaping (KeyboardShortcut) -> Bool
84+
) -> some View {
85+
onAppKitEvent(context: context, matching: [.keyDown]) { event in
86+
guard let shortcut = KeyboardShortcut(from: event) else {
87+
return event
88+
}
89+
90+
let wasEventHandled = action(shortcut)
91+
92+
return wasEventHandled ? nil : event
93+
}
94+
}
95+
}
96+
97+
// MARK: - Auxiliary Implementation -
98+
99+
private struct _AttachNSEventMonitor: ViewModifier {
100+
@State var eventMonitor: NSEventMonitor
101+
102+
let handleEvent: (NSEvent) -> NSEvent?
103+
104+
func body(content: Content) -> some View {
105+
content.background {
106+
PerformAction {
107+
eventMonitor.eventHandler = handleEvent
108+
}
109+
}
110+
}
111+
}
112+
113+
#endif

Sources/Intramodular/Search/SearchBar.swift

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -232,20 +232,7 @@ extension SearchBar: UIViewRepresentable {
232232
@available(tvOSApplicationExtension, unavailable)
233233
extension SearchBar: NSViewRepresentable {
234234
public final class NSViewType: NSSearchField {
235-
class ClosureResponder: NSView {
236-
override func becomeFirstResponder() -> Bool {
237-
print("Foo")
238-
239-
return true
240-
}
241-
242-
override func resignFirstResponder() -> Bool {
243-
return true
244-
}
245-
}
246-
247235
var isFirstResponderBinding: Binding<Bool>?
248-
var closureResponder = ClosureResponder()
249236

250237
override public func becomeFirstResponder() -> Bool {
251238
let result = super.becomeFirstResponder()
@@ -276,9 +263,6 @@ extension SearchBar: NSViewRepresentable {
276263
nsView.isBordered = false
277264
nsView.isBezeled = true
278265

279-
nsView.nextResponder = nsView.closureResponder
280-
nsView.nextKeyView = nsView.closureResponder
281-
282266
return nsView
283267
}
284268

0 commit comments

Comments
 (0)