70 lines
2.2 KiB
Swift
70 lines
2.2 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
|
|
/// Lightweight NSView-based mouse tracking with local coordinates.
|
|
///
|
|
/// Why: SwiftUI's `onHover` doesn't provide location, but we want "hover a bar to see values" on macOS.
|
|
@MainActor
|
|
struct MouseLocationReader: NSViewRepresentable {
|
|
let onMoved: (CGPoint?) -> Void
|
|
|
|
func makeNSView(context: Context) -> TrackingView {
|
|
let view = TrackingView()
|
|
view.onMoved = self.onMoved
|
|
return view
|
|
}
|
|
|
|
func updateNSView(_ nsView: TrackingView, context: Context) {
|
|
nsView.onMoved = self.onMoved
|
|
}
|
|
|
|
final class TrackingView: NSView {
|
|
var onMoved: ((CGPoint?) -> Void)?
|
|
private var trackingArea: NSTrackingArea?
|
|
|
|
override var isFlipped: Bool {
|
|
true
|
|
}
|
|
|
|
override func viewDidMoveToWindow() {
|
|
super.viewDidMoveToWindow()
|
|
self.window?.acceptsMouseMovedEvents = true
|
|
self.updateTrackingAreas()
|
|
}
|
|
|
|
override func updateTrackingAreas() {
|
|
super.updateTrackingAreas()
|
|
if let trackingArea {
|
|
self.removeTrackingArea(trackingArea)
|
|
}
|
|
|
|
let options: NSTrackingArea.Options = [
|
|
// NSMenu popups aren't "key windows", so `.activeInKeyWindow` would drop events and cause hover
|
|
// state to flicker. `.activeAlways` keeps tracking stable while the menu is open.
|
|
.activeAlways,
|
|
.inVisibleRect,
|
|
.mouseEnteredAndExited,
|
|
.mouseMoved,
|
|
]
|
|
let area = NSTrackingArea(rect: .zero, options: options, owner: self, userInfo: nil)
|
|
self.addTrackingArea(area)
|
|
self.trackingArea = area
|
|
}
|
|
|
|
override func mouseEntered(with event: NSEvent) {
|
|
super.mouseEntered(with: event)
|
|
self.onMoved?(self.convert(event.locationInWindow, from: nil))
|
|
}
|
|
|
|
override func mouseMoved(with event: NSEvent) {
|
|
super.mouseMoved(with: event)
|
|
self.onMoved?(self.convert(event.locationInWindow, from: nil))
|
|
}
|
|
|
|
override func mouseExited(with event: NSEvent) {
|
|
super.mouseExited(with: event)
|
|
self.onMoved?(nil)
|
|
}
|
|
}
|
|
}
|