mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
BugFix: Gesture open close even when cursor kisses the top
This commit is contained in:
parent
04426d8af7
commit
2766d250e9
2 changed files with 38 additions and 3 deletions
|
|
@ -56,9 +56,13 @@ private struct ScrollMonitor: NSViewRepresentable {
|
||||||
private let threshold: CGFloat
|
private let threshold: CGFloat
|
||||||
private let action: (CGFloat, NSEvent.Phase) -> Void
|
private let action: (CGFloat, NSEvent.Phase) -> Void
|
||||||
private var monitor: Any?
|
private var monitor: Any?
|
||||||
|
private var globalMonitor: Any?
|
||||||
private var accumulated: CGFloat = 0
|
private var accumulated: CGFloat = 0
|
||||||
private var active = false
|
private var active = false
|
||||||
private let noiseThreshold: CGFloat = 0.2
|
private let noiseThreshold: CGFloat = 0.2
|
||||||
|
private weak var observedView: NSView?
|
||||||
|
private let hoverInset: CGFloat = 14
|
||||||
|
private var lastEventTimestamp: TimeInterval = 0
|
||||||
|
|
||||||
init(direction: PanDirection, threshold: CGFloat, action: @escaping (CGFloat, NSEvent.Phase) -> Void) {
|
init(direction: PanDirection, threshold: CGFloat, action: @escaping (CGFloat, NSEvent.Phase) -> Void) {
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
|
@ -68,11 +72,15 @@ private struct ScrollMonitor: NSViewRepresentable {
|
||||||
|
|
||||||
func installMonitor(on view: NSView) {
|
func installMonitor(on view: NSView) {
|
||||||
removeMonitor()
|
removeMonitor()
|
||||||
monitor = NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { [weak self, weak view] event in
|
observedView = view
|
||||||
guard let self = self, event.window === view?.window else { return event }
|
monitor = NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { [weak self] event in
|
||||||
|
guard let self else { return event }
|
||||||
self.handleScroll(event)
|
self.handleScroll(event)
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
globalMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.scrollWheel]) { [weak self] event in
|
||||||
|
self?.handleScroll(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeMonitor() {
|
func removeMonitor() {
|
||||||
|
|
@ -80,11 +88,21 @@ private struct ScrollMonitor: NSViewRepresentable {
|
||||||
NSEvent.removeMonitor(monitor)
|
NSEvent.removeMonitor(monitor)
|
||||||
self.monitor = nil
|
self.monitor = nil
|
||||||
}
|
}
|
||||||
|
if let globalMonitor = globalMonitor {
|
||||||
|
NSEvent.removeMonitor(globalMonitor)
|
||||||
|
self.globalMonitor = nil
|
||||||
|
}
|
||||||
accumulated = 0
|
accumulated = 0
|
||||||
active = false
|
active = false
|
||||||
|
observedView = nil
|
||||||
|
lastEventTimestamp = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleScroll(_ event: NSEvent) {
|
private func handleScroll(_ event: NSEvent) {
|
||||||
|
guard lastEventTimestamp != event.timestamp else { return }
|
||||||
|
lastEventTimestamp = event.timestamp
|
||||||
|
guard isCursorNearObservedView(using: event) else { return }
|
||||||
|
|
||||||
if event.phase == .ended || event.momentumPhase == .ended {
|
if event.phase == .ended || event.momentumPhase == .ended {
|
||||||
if active {
|
if active {
|
||||||
action(accumulated.magnitude, .ended)
|
action(accumulated.magnitude, .ended)
|
||||||
|
|
@ -107,5 +125,22 @@ private struct ScrollMonitor: NSViewRepresentable {
|
||||||
action(accumulated.magnitude, .changed)
|
action(accumulated.magnitude, .changed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func isCursorNearObservedView(using event: NSEvent) -> Bool {
|
||||||
|
guard let view = observedView, let window = view.window else { return false }
|
||||||
|
|
||||||
|
let screenPoint: NSPoint
|
||||||
|
if let eventWindow = event.window {
|
||||||
|
let rect = NSRect(origin: event.locationInWindow, size: .zero)
|
||||||
|
screenPoint = eventWindow.convertToScreen(rect).origin
|
||||||
|
} else {
|
||||||
|
screenPoint = NSEvent.mouseLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
let windowPoint = window.convertPoint(fromScreen: screenPoint)
|
||||||
|
let localPoint = view.convert(windowPoint, from: nil)
|
||||||
|
let hitArea = view.bounds.insetBy(dx: -hoverInset, dy: -hoverInset)
|
||||||
|
return hitArea.contains(localPoint)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1031,7 +1031,7 @@ private enum WeatherSymbolMapper {
|
||||||
return "cloud.rain.fill"
|
return "cloud.rain.fill"
|
||||||
case 179, 182, 185, 311, 314, 317, 320, 362, 365:
|
case 179, 182, 185, 311, 314, 317, 320, 362, 365:
|
||||||
return "cloud.sleet.fill"
|
return "cloud.sleet.fill"
|
||||||
case 227, 230, 281, 284, 317, 320, 323, 326, 329, 332, 335, 338, 368, 371, 374, 377:
|
case 227, 230, 281, 284, 323, 326, 329, 332, 335, 338, 368, 371, 374, 377:
|
||||||
return "cloud.snow.fill"
|
return "cloud.snow.fill"
|
||||||
case 200, 386, 389, 392, 395:
|
case 200, 386, 389, 392, 395:
|
||||||
return "cloud.bolt.rain.fill"
|
return "cloud.bolt.rain.fill"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue