Arlen Anderson
Arlen Anderson

Reputation: 2496

How to find if the mouse is over a view

I need to find if the mouse position is inside an NSView's rect.

I'd use NSPointInRect(point, rect), but I'd need to convert the rect coordinates to screen coordinates and I'm not sure how. Any help would be much appreciated!

Upvotes: 21

Views: 13328

Answers (6)

Honghao Z
Honghao Z

Reputation: 1527

It seems like many answers don't consider isFlipped flag. Here is a way that takes the view's isFlipped into account:

public extension NSView {

  /// Checks if the mouse cursor is in self, without an event.
  func containsMouseCursor() -> Bool {
    guard let window, let windowContentView = window.contentView else {
      return false
    }

    var mouseLocationInWindowContentView = window.mouseLocationOutsideOfEventStream // returns a coordinate in bottom-left origin coordinate system
    if windowContentView.isFlipped {
      // correct the coordinates if `contentView` is flipped
      mouseLocationInWindowContentView = CGPoint(mouseLocationInWindowContentView.x, windowContentView.frame.height - mouseLocationInWindowContentView.y)
    }
    // convert the cursor point into self's coordinate system
    let pointInSelf = windowContentView.convert(mouseLocationInWindowContentView, to: self)

    return isMousePoint(pointInSelf, in: bounds)
  }
}

Upvotes: 0

Ryan H
Ryan H

Reputation: 1746

The NSView isMousePoint function works for me, without having to worry about anything CG.

func isMouseInView(view: NSView) -> Bool? {
    if let window = view.window {
        return view.isMousePoint(window.mouseLocationOutsideOfEventStream, in: view.frame)
    }
    return nil
}

Upvotes: 7

Sentry.co
Sentry.co

Reputation: 5569

None of these answers take into account that a view might not be rectangular.

Here is an universal method that works on non-rectangular views as well. Think watch-face or a complex path shape.

This code snippet will give you a localPoint from a globalPoint (aka globalToLocal). (GlobalPoint in 0,0 window space) If you change the argument to "toView" then you get a "localToGlobal" point.

let localPoint = convertPoint(aPoint, fromView: self.window?.contentView)    

The bellow code would return true or false if the point was inside the path or not.

CGPathContainsPoint(someCGPath,nil,localPoint,true)

NOTE: aPoint is a globalPoint derived from the parameter in hitTest, it can also be derived from:

(self.window?.mouseLocationOutsideOfEventStream)!

NOTE: There might be a more correct way of doing this. But this works,and doesn't care about flipped views.

Upvotes: 1

aepryus
aepryus

Reputation: 4825

Some code making use of mouse:inRect: (the recommended way; that accounts for flipped coordinates)

CGPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
CGRect rect = [self bounds];
if ([self mouse:point inRect:rect]) {
}

Upvotes: 5

Matt Bierner
Matt Bierner

Reputation: 65223

Use NSView's convertPoint:fromView: method with fromView as nil to covert a point in window coordinates to a view's coordinate system.

After converting the mouse point to the view's coordinate system, I would use NSView's mouse:inRect: method instead of NSPointInRect as the former also accounts for flipped coordinate systems.

Alternatively, you could use a Tracking Area Object.

Upvotes: 26

Jon Steinmetz
Jon Steinmetz

Reputation: 4124

Something like this should work for you:

NSView* myView; // The view you are converting coordinates to
NSPoint globalLocation = [ NSEvent mouseLocation ];
NSPoint windowLocation = [ [ myView window ] convertScreenToBase: globalLocation ];
NSPoint viewLocation = [ myView convertPoint: windowLocation fromView: nil ];
if( NSPointInRect( viewLocation, [ myView bounds ] ) ) {
    // Do your thing here
}

I don't personally use this many local variables but hopefully this make this example clearer.

Upvotes: 34

Related Questions