Mark
Mark

Reputation: 6947

Prevent activating the application when clicking on NSWindow/NSView

I'm working on a screenshot Mac app. I'm trying to rebuilt what happens when you press Cmd-Ctrl-Shift-4: the cross hair cursor and the selection rectangle for the screenshot.

I'm using a custom borderless NSWindow on top of all other windows. I disabled the cursor to draw my own along with the selection rectangle.

My problem is that as soon as I click & drag to capture a screenshot, my app gets activated (because the click is intercepted by my shielding window).

Is there a way how I can receive the click in my custom view/window without having my app get activated?

I tried using an NSPanel with the NSNonactivatingPanelMask flag, but in this case, I have a problem with the cursor: I can't draw my own when another app is active, because I can't hide the cursor for other apps...

Upvotes: 7

Views: 1627

Answers (4)

Aleksey Prykhodko
Aleksey Prykhodko

Reputation: 11

One more idea, you can override - (BOOL)_isNonactivatingPanel private method in NSWindow subclass:

@implementation MyWindow

- (BOOL)_isNonactivatingPanel
{
    return YES;
}

@end

Voila, you got behaviour similar to NSPanel :)

Upvotes: 1

Bri Bri
Bri Bri

Reputation: 2270

Actually, I have a new, better answer to this question involving more undocumented goodies. Here it is for future posterity:

There is an undocumented method on NSWindow that does exactly what you want:

@interface NSWindow (Private)
- (void )_setPreventsActivation:(bool)preventsActivation;
@end

[myWindow _setPreventsActivation:true];

This stops the window from activating both itself and its application when the user clicks on it.

The standard warnings about using undocumented APIs of course apply: Apple may change this at some point (although it's been around for many OS X versions so there's a good chance they won't) and using this may get your app rejected from the Mac app store.

Upvotes: 3

Bri Bri
Bri Bri

Reputation: 2270

For what it's worth, there's another way to make the cursor invisible globally other than creating a giant window. It involves some undocumented APIs if that's something you can use:

extern "C" {
    typedef int CGSConnection;
    void CGSSetConnectionProperty(int, int, const void *, const void *);
    int CGSMainConnectionID();
}

void allowHidingCursorForBackgroundOnlyApp()
{
    CFStringRef propertyString = CFStringCreateCopy(NULL, CFSTR("SetsCursorInBackground"));
    CGSSetConnectionProperty(CGSMainConnectionID(), CGSMainConnectionID(), propertyString, kCFBooleanTrue);
    CFRelease((CFTypeRef)propertyString);
}

Combine that with judicious use of event taps to capture and filter out mouse clicks, and you can create the same effect as the built-in screen shot feature.

Upvotes: 2

Brad Allred
Brad Allred

Reputation: 7534

I pray that there is a better way to do this now, but when I had to do something similar I ended up letting my window/view ignore all mouse input, then I used a CGEventTap (see Quarts Event Services documentation) to capture mouse events globally(without removing them from the event queue). I them mapped them manually to my window, created a custom copy NSEvent and manually dispatched it to my window.

The huge downside here (aside from complexity) is that I recall needing to run as root to be able to install the event tap. However, I think there is a way to get permission though universal access.

I'm completely unsure if dispatching a custom NSEvent directly to the window will have the same side effect of activating your application; especially since many things have changed since 10.6... I would suggest a simple test to see if this is feasible before pursuing it.

Upvotes: 0

Related Questions