Prevent activating the application when clicking on NSWindow/NSView
Asked Answered
C

4

7

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...

Chord answered 8/9, 2014 at 15:46 Comment(4)
I dont know exactly what you are trying to acomplish, but maybe instead of re-implementing the screenshot mechanism yourself you can get by with one of these solutions: #4517352Scevour
@BradAllred I'm trying to build the same user experience as Cmd-Shift-3, but without the user having to save the file first. Seems like that's what the answers suggest in the question you referenced.Chord
So, FYI, you can hold the ctrl key to take a screenshot to the clipboard... dont know if that is all you are trying to do. otherwise, yes, one of the suggestions in the linked thread may be the best (or my answer below)Scevour
Setting NSApplicationActivationPolicy.Prohibited is another approach... depending on the app's needs, of course.Kharkov
G
3

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.

Gibeonite answered 21/6, 2016 at 4:47 Comment(2)
How would I call this from Swift?Lilalilac
This may help you: #28175041Gibeonite
G
2

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.

Gibeonite answered 21/6, 2016 at 1:52 Comment(0)
F
1

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 :)

Forenoon answered 6/1, 2021 at 10:18 Comment(0)
S
0

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.

Scevour answered 8/9, 2014 at 16:18 Comment(1)
Yes, I thought about this. I think you're right, the user would first have to allow Access for Assistive Devices, otherwise this would not work...Chord

© 2022 - 2024 — McMap. All rights reserved.