Simply use [self convertPoint:event_location fromView:nil];
can be inaccurate, this can happen when event's window and view's window are not the same. Following is a more robust way to get the event location even in different window/screens:
public extension NSEvent {
/// Get the event mouse location in `view`.
func location(in view: NSView) -> CGPoint {
if let eventWindow = window, let viewWindow = view.window {
if eventWindow.windowNumber == viewWindow.windowNumber {
// same window, just convert
return view.convert(locationInWindow, from: nil)
} else {
// window not equal, check screen
if let eventScreen = eventWindow.screen, let viewScreen = viewWindow.screen {
if eventScreen.isEqual(to: viewScreen) {
// same screen, try to convert between windows
// screen coordinate zero point is at bottom left corner
let eventLocationInScreen = locationInWindow.translate(dx: eventWindow.frame.origin.x, dy: eventWindow.frame.origin.y)
let viewFrameInScreen = view.frameInWindow.translate(dx: viewWindow.frame.origin.x, dy: viewWindow.frame.origin.y)
return eventLocationInScreen.translate(dx: -viewFrameInScreen.origin.x, dy: -viewFrameInScreen.origin.y)
} else {
// different screen, try to convert to unified coordinate
let eventLocationInScreen = locationInWindow.translate(dx: eventWindow.frame.origin.x, dy: eventWindow.frame.origin.y)
let eventLocationInBase = eventLocationInScreen.translate(dx: eventScreen.frame.origin.x, dy: eventScreen.frame.origin.y)
let viewFrameInScreen = view.frameInWindow.translate(dx: viewWindow.frame.origin.x, dy: viewWindow.frame.origin.y)
let viewFrameInBase = viewFrameInScreen.translate(dx: viewScreen.frame.origin.x, dy: viewScreen.frame.origin.y)
return eventLocationInBase.translate(dx: -viewFrameInBase.origin.x, dy: -viewFrameInBase.origin.y)
}
}
}
}
// other unexpected cases, fall back to use `convert(_:from:)`
return view.convert(locationInWindow, from: nil)
}
}
public extension NSView {
/// The view's frame in its window.
var frameInWindow: CGRect {
convert(bounds, to: nil)
}
}
public extension CGRect {
/// Move/translate a `CGRect` by its origin..
/// - Parameters:
/// - dx: The delta x.
/// - dy: The delta y.
/// - Returns: A new `CGRect` with moved origin.
func translate(dx: CGFloat = 0, dy: CGFloat = 0) -> CGRect {
CGRect(origin: origin.translate(dx: dx, dy: dy), size: size)
}
}
public extension CGPoint {
/// Translate the point.
/// - Parameters:
/// - dx: The delta x.
/// - dy: The delta y.
/// - Returns: The translated point.
@inlinable
@inline(__always)
func translate(dx: CGFloat = 0, dy: CGFloat = 0) -> CGPoint {
CGPoint(x: x + dx, y: y + dy)
}
}
As you can see, it checks if both event and view's window is the same one, if not, the logic tries to compare the screen and try to convert manually.