防止在点击NSWindow/NSView时激活应用程序

7
我正在开发一款 Mac 截屏应用,希望能够重新实现按下 Cmd-Ctrl-Shift-4 时的操作:交叉线样式的鼠标指针以及矩形选区截图。
我使用了一个自定义无边框 NSWindow 并置于其他所有窗口之上。我禁用了系统鼠标指针,以便绘制自己的鼠标指针和选区矩形。
我的问题在于,一旦我点击并拖动以截取截屏,我的应用程序会被激活(因为点击被我的保护窗口拦截)。
是否有方法可以在我的自定义视图 / 窗口中接收点击事件,并且不使我的应用程序被激活?
我尝试使用带有 NSNonactivatingPanelMask 标志的 NSPanel,但在这种情况下,我遇到了一个鼠标指针的问题:由于我不能隐藏其他应用程序的鼠标指针,因此在其他应用程序处于活动状态时,我无法绘制自己的鼠标指针。。。

我不确定你要实现什么,但也许你可以通过使用以下一种解决方案来代替重新实现截屏机制:https://dev59.com/RW855IYBdhLWcg3wIAoa - Brad Allred
@BradAllred 我正在尝试构建与Cmd-Shift-3相同的用户体验,但不需要用户先保存文件。似乎这就是您所引用的问题中答案所建议的。 - Mark
所以,FYI,你可以按住 ctrl 键将截图复制到剪贴板中...不知道这是否是你想要做的所有事情。否则,链接线程中的建议之一可能是最好的选择(或者是我下面的回答)。 - Brad Allred
设置 NSApplicationActivationPolicy.Prohibited 是另一种方法... 当然,这取决于应用程序的需求。 - Milos
4个回答

3

实际上,我有一个更好的答案来回答这个问题,其中包含更多未记录的好东西。以下是未来可参考的答案:

NSWindow 上有一个未记录的方法可以完全满足您的需求:

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

[myWindow _setPreventsActivation:true];

当用户点击窗口时,这将阻止它自己和其应用程序同时激活。

当然,使用未经记录的API时要注意标准警告:苹果公司可能会在某些时候更改此内容(尽管它已存在多个OS X版本,因此他们不太可能更改),使用此方法可能会使您的应用程序被 Mac 应用商店拒绝。


我该如何从Swift中调用这个? - Anshuman Sharma
这可能会对您有所帮助:https://dev59.com/xl4c5IYBdhLWcg3wOoAH - Bri Bri

2

说句实话,除了创建一个巨大的窗口来使光标全局不可见之外,还有另一种方法。如果您可以使用一些未记录的API,则可以采用以下方法:

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);
}

结合精明的事件挂钩来捕获和过滤鼠标点击,您可以创建与内置屏幕截图功能相同的效果。

0

希望现在有更好的方法来完成这个任务,但是当我需要做类似的事情时,我最终让我的窗口/视图忽略所有鼠标输入,然后使用CGEventTap(请参见Quarts事件服务文档)全局捕获鼠标事件(而不从事件队列中删除它们)。然后我手动将它们映射到我的窗口上,创建了一个自定义的NSEvent并手动将其分派到我的窗口。

这里的巨大缺点(除了复杂性)是我记得需要以root身份运行才能安装事件监听器。然而,我认为有一种方法可以通过通用访问获取权限。

我完全不确定直接向窗口分派自定义的NSEvent是否会产生激活应用程序的相同副作用;特别是自10.6以来许多事情已经发生了变化... 我建议进行简单的测试,以查看是否可行,然后再进行追求。


是的,我考虑过这个问题。我认为你是对的,用户首先必须允许辅助设备访问,否则这将无法工作... - Mark

0

还有一个想法,你可以在NSWindow子类中覆盖- (BOOL)_isNonactivatingPanel私有方法:

@implementation MyWindow

- (BOOL)_isNonactivatingPanel
{
    return YES;
}

@end

完成了,你现在拥有类似于NSPanel的行为 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接