Click Through Custom Nswindow

Click through custom NSWindow

Answering my own question:

On OS X, in order to have windows with custom shapes with click through on transparent areas following conditions must be met:

  1. The window must be created with only NSBorderlessWindowMask set by [window setStyleMask: NSBorderlessWindowMask] for example.

  2. You MUST NOT call [window setIgnoresMouseEvent: NO] on it. As that method clearly contains a bug on Apple's side.

  3. contentView of the window MUST NOT use layers. So something like this [[window contentView] setWantsLayer: YES] effectively disables click through too.

Just in case: all this was about layered windows handling in Sciter on OS X

"Sciter clock" sample on Windows, OS X and Linux

Discard mouse events on NSWindow based on click position

So after a few weeks, I came to the following results:

A) Proxy window:
Make use of a non layer-backed proxy window, which is placed on top of the target window as a child window. The proxy window has the same shape, as the target window, and since it is not layer-backed, it will properly receive and ignore events. The proxy window delegates all events to the target window by overwriting sendEvent:. The target window is set to ignore all mouse events.

B) Global Mouse Pointer observation:
Install both a global and local event monitor for NSMouseMovedMask|NSLeftMouseDraggedMask events using addGlobalMonitorForEventsMatchingMask and addLocalMonitorForEventsMatchingMask. The event monitors disable and enable ignoring mouse events on all registered target windows based on the current global mouse position. In the case of circular windows, the distance between the mouse pointer and every target window must be calculated.

Both approaches work well in generally, but I've been experiencing some unpredictable misbehaviors of the child window approach (where the child window is 'out-of-sync' of its parent's position).

UPDATE: Both approaches have some significant disadvantages:
In A), the proxy window sometimes may be out of sync and may be placed slightly off the actual window.

In B), the event monitor has a big impact on battery life while moving the mouse, even if the app is not the front-most application.

Clicking through NSWindow/CALayer

My solution in this scenario was actually to use:

[self ignoresMouseEvents:YES];

I originally was hoping to be able to retain the mouse events on the specific CALayer that I'm animating, but upon some further research I understand this comes with the cost of custom drawing everything from scratch, which is not ideal for this particular project.

How to pass click on NSView through to app window beneath it?

Shady by Matt Gemmell does exactly the same, take a look at the source:
http://instinctivecode.com/shady/

It does this by sending the following message to the window:

[window setIgnoresMouseEvents:YES];

NSWindow: Passing mouse events through window to whatever is underneath

IIRC, you can just use [window setIgnoresMouseEvents:YES].

Simulate a mouse click in an NSWindow

It's hard to know exactly how much fidelity you're looking for with what happens for an actual click. For example, do you want the click to activate the app? Do you want the click to bring a window to the top? To make it key or main? If the location is in the title bar, do you want it to potentially close, minimize, zoom, or move the window?

As John Caswell noted, if you pass an appropriately-constructed NSEvent to -[NSApplication sendEvent:] that will closely simulate the processing of a real event. In most cases, NSApplication will forward the event to the event's window and its -[NSWindow sendEvent:] method. If you want to avoid any chance of NSApplication doing something else, you could dispatch directly to the window's -sendEvent: method. But that may defeat some desirable behavior, depending on exactly what you desire.

What happens if the clicked window's or view's response is to run an internal event-tracking loop? It's going to be synchronous; that is, the code that calls -sendEvent: is not going to get control back until after that loop has completed and it might not complete if you aren't able to deliver subsequent events. In fact, such a loop is going to look for subsequent events via -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:], so if your synthetic events are not in the queue, they won't be seen. Therefore, an even better simulation of the handling of real events would probably require that you post events (mouse-down, mouse drags, mouse-up) to the queue using -[NSApplication postEvent:atStart:].

I think your first task is to really think deeply about what you're trying to accomplish, all the potential pitfalls and corner cases, and decide how you want to handle those.

With respect to the CGEvent... stuff, you can post an event to a specific process using CGEventPostToPSN() and that won't click on other app's windows, even if they are in front of the target window. However, it may still click on a different window within the target app.

Passing Click Through Transparent Window

Setting IgnoresMouseEvents to YES should do the trick..

  • (void)setIgnoresMouseEvents:(BOOL)ignoreMouseEvents

Specifies whether the window is transparent to mouse clicks and other mouse events, allowing overlay windows.

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/Reference/Reference.html#//apple_ref/occ/instm/NSWindow/setIgnoresMouseEvents:



Related Topics



Leave a reply



Submit