12

The problem

I have a transparent NSView on a transparent NSWindow. The view's drawRect: method draws some content (NSImages, NSBezierPaths and NSStrings) on the view but leaves parts of it transparent.

Clicking on the regions of the view that have been drawn on invokes the usual mouse event handling methods (mouseDown: and mouseUp:).

Clicking on the transparent areas gives focus to whatever window is behind my transparent window.

I would like to make parts of the transparent region clickable so that accidentally clicking between the elements drawn on my view does not cause the window to lose focus.


Solutions already attempted

  • Overriding the NSView's hitTest: method. Found that hitTest: was only called when clicking on a non-transparent area of the view.
  • Overriding the NSView's opaqueAncestor method. Found that this was not called when clicking on any part of the view.
  • Filling portions of the transparent area with [NSColor clearColor] in the drawRect: method, and with an almost-but-not-quite-transparent colour. This had no effect.
  • Experimented with the NSTrackingArea class. This appears to only add support for mouseEntered:, mouseExited:, mouseMoved:, and cursorUpdate: methods, not mouseUp: and mouseDown:.
Chris Hanson
  • 54,380
  • 8
  • 73
  • 102
georgebrock
  • 28,393
  • 13
  • 77
  • 72

7 Answers7

12

I had the same problem. It looks like [window setIgnoresMouseEvents:NO] will do it.

(On Lion, at least. See http://www.cocoabuilder.com/archive/cocoa/306910-lion-breaks-the-ability-to-click-through-transparent-window-areas-when-the-window-is-resizable.html)

justin k.
  • 504
  • 1
  • 6
  • 15
6

As far as I know, click events to transparent portions of windows aren't delivered to your application at all, so none of the normal event-chain overrides (i.e -hitTest:, -sendEvent:, etc) will work. The only way I can think of off the top of my head is to use Quartz Event Taps to capture all mouse clicks and then figure out if they're over a transparent area of your window manually. That, frankly, sounds like a huge PITA for not much gain.

Boaz Stuller
  • 2,534
  • 21
  • 16
2

George : you mentioned that you tried filling portions with an almost but not quite transparent color. In my testing, it only seems to work if the alpha value is above 0.05, so you might have some luck with something like this:

[[NSColor colorWithCalibratedRed:0.01 green:0.01 blue:0.01 alpha:0.05] set];

It's an ugly kludge, but it might work well enough to avoid using an event tap.

1

You can use an event monitor to catch events outside a window/view. https://developer.apple.com/library/mac/documentation/cocoa/conceptual/eventoverview/MonitoringEvents/MonitoringEvents.html

Yoav
  • 5,962
  • 5
  • 39
  • 61
1

Did you try overriding

- (NSView *)hitTest:(NSPoint)aPoint 

in your NSView sublcass?

tristan
  • 523
  • 4
  • 5
  • Thanks Tristan. I did try this earlier but my subclass's hitTest: method wasn't getting called unless I clicked on a non-transparent area. – georgebrock Jan 12 '09 at 16:18
0

You can do:

NSView* windowContent = [window contentView];
[windowContent setWantsLayer:YES]

Making sure that the background is transparent:

[[windowContent layer] setBackgroundColor:[[NSColor clearColor] CGColor]];

Another option would be to add a transparent background image that fills the contentView.

josephrider
  • 933
  • 5
  • 9
0

You can override the hitTest method in your NSView so that it always returns itself.

According to the NSView documentation, the hitTest method will either return the NSView that the user has clicked on, or nil if the point is not inside the NSView. In this case, I would invoke [super hitTest:], and then return the current view only if the result would otherwise be nil (just in case your custom view contains subviews).

- (NSView *)hitTest:(NSPoint)aPoint
{
    NSView * clickedView = [super hitTest:aPoint];
    if (clickedView == nil)
    {
        clickedView = self;
    }

    return clickedView;
}
e.James
  • 116,942
  • 41
  • 177
  • 214