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.
frame
orbounds
? – Damico