forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFloatingBarMouseInterception.swift
More file actions
97 lines (88 loc) · 3.13 KB
/
Copy pathFloatingBarMouseInterception.swift
File metadata and controls
97 lines (88 loc) · 3.13 KB
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
import AppKit
import Combine
enum FloatingBarMouseInterceptionPolicy {
static func shouldIgnoreMouseEvents(
mouseLocation: NSPoint,
windowFrame: NSRect,
isVisible: Bool,
acceptsMouseHit: (NSPoint) -> Bool
) -> Bool {
guard isVisible else { return true }
guard windowFrame.contains(mouseLocation) else { return false }
let local = NSPoint(
x: mouseLocation.x - windowFrame.minX,
y: mouseLocation.y - windowFrame.minY)
return !acceptsMouseHit(local)
}
}
@MainActor
final class FloatingBarMouseInterceptionReconciler {
private nonisolated(unsafe) var monitors: [Any] = []
private var cancellables = Set<AnyCancellable>()
private var pollingCancellable: AnyCancellable?
init(state: FloatingControlBarState, reconcile: @escaping @MainActor @Sendable () -> Void) {
let scheduleReconciliation: @Sendable () -> Void = {
DispatchQueue.main.async { reconcile() }
}
if let global = NSEvent.addGlobalMonitorForEvents(
matching: [.mouseMoved, .leftMouseDragged],
handler: { _ in scheduleReconciliation() })
{
monitors.append(global)
}
if let local = NSEvent.addLocalMonitorForEvents(
matching: [.mouseMoved],
handler: { event in
scheduleReconciliation()
return event
})
{
monitors.append(local)
}
let ownershipPublishers: [AnyPublisher<Void, Never>] = [
state.$showingAIConversation.map { _ in () }.eraseToAnyPublisher(),
state.$showingAIResponse.map { _ in () }.eraseToAnyPublisher(),
state.$currentNotification.map { _ in () }.eraseToAnyPublisher(),
state.$inputViewHeight.map { _ in () }.eraseToAnyPublisher(),
state.$notchHoverMenuOpen.map { _ in () }.eraseToAnyPublisher(),
]
Publishers.MergeMany(ownershipPublishers)
.sink { scheduleReconciliation() }
.store(in: &cancellables)
}
func setPollingActive(_ active: Bool, reconcile: @escaping @MainActor @Sendable () -> Void) {
guard active != (pollingCancellable != nil) else { return }
pollingCancellable =
active
? Timer.publish(every: 1.0 / 30.0, on: .main, in: .common)
.autoconnect()
.sink { _ in reconcile() }
: nil
}
deinit {
monitors.forEach(NSEvent.removeMonitor)
}
}
extension FloatingControlBarWindow {
func syncMouseInterception(mouseLocation: NSPoint = NSEvent.mouseLocation) {
setMouseInterceptionPollingActive(isVisible)
let shouldIgnore = FloatingBarMouseInterceptionPolicy.shouldIgnoreMouseEvents(
mouseLocation: mouseLocation,
windowFrame: frame,
isVisible: isVisible,
acceptsMouseHit: acceptsMouseHit(inContentPoint:))
if ignoresMouseEvents != shouldIgnore {
ignoresMouseEvents = shouldIgnore
}
}
func installMouseInterceptionSync() {
mouseInterceptionReconciler = FloatingBarMouseInterceptionReconciler(
state: state,
reconcile: { [weak self] in self?.syncMouseInterception() })
syncMouseInterception()
}
func setMouseInterceptionPollingActive(_ active: Bool) {
mouseInterceptionReconciler?.setPollingActive(
active, reconcile: { [weak self] in self?.syncMouseInterception() })
}
}