Search code examples
macoscocoawebviewnscursor

How to disable all NSCursor updates in my application?


In my macOS application, I have a custom WebView. When the user moves the mouse cursor over different elements in the WebView's webpage like text, links, etc. (which are implemented as subviews, I think), the cursor updates automatically, based on the type of the element.

My requirement is that I want to set a custom cursor for the WebView (even for my entire application if possible), and not let it change whatsoever, at least till I programmatically change it in some other part of the code. I tried to set the NSCursor in the AppDelegate's applicationDidFinishLaunching: method, but it gets instantly reset.

I have tried many solutions like setting an NSTrackingArea, sending disableCursorRects to both my NSApplication and NSWindow, and even monitoring cursor update events, without much success.

So is there a way to disable all NSCursor updates within my application? I would set it to my custom cursor in applicationDidFinishLaunching: and want it to remain the same as long as the application runs, or I change it in code.


Solution

  • Found a solution that worked for me! Here's how I did it:

    Subclass NSView and override the resetCursorRects method, in which send addCursorRect:cursor: with its bounds as the cursor rect and the required cursor (described here). Then make an instance of this custom NSView with the same frame as the WebView, and add it to the later as a subview. This will act as a transparent overlay over the WebView. It affects only the cursor and does not interfere with mouse clicks or keypresses.

    Here's the code in PyObjC (the concept should apply in Objective C as well):

    # Overlay View (NSView subclass)
    class OverLayView(AppKit.NSView):
       def resetCursorRects(self):
           self.addCursorRect_cursor_(self.bounds(), my_custom_cursor)
    
    # In my main class
        ...
        self.overlay = OverLayView.alloc().initWithFrame_(self.webview.bounds())
        self.webview.addSubview_(self.overlay)
        ...
    

    If I ever want to reset the cursor back to auto, I can just remove this overlay view, or even better, set it temporarily hidden by sending it the setHidden: message:

    self.overlay.setHidden_(Foundation.YES)
    

    Some additional things to note:

    • Using an NSTrackingArea instead of the cursor rect doesn't seem to work well. For me it didn't.
    • I had tried the above method on the custom WebView itself, but it didn't work. The WebView's default tracking areas/cursor rects always interfere. Adding a subview on top of it blocks this behaviour, as the above code does.
    • Finally the overlay view should be a subview of the WebView. If both are sibling views of the same superview, Cocoa doesn't guarantee which one will be on top of the other.

    If someone has a better solution, or can improve this one, feel free to post it.

    Hope this helps someone!