如何通过单击外部关闭以弹出窗口形式加载的 NSWindow


如何

通过单击外部关闭以弹出窗口形式加载的 NSWindow?

我想处理鼠标事件,当光标在具有焦点的模态窗口之外(但仍在应用程序内)时。

您可以实现以下委托NSWindow方法来获取窗口失去焦点的通知。

- (void)windowDidResignKey:(NSNotification *)notification

并在内部检查,您的应用程序是否是最前面的应用程序。如果是,则相应地关闭。

当应用程序处于模式运行循环中时,它不会响应任何 其他事件(包括鼠标、键盘或窗口关闭事件) 除非它们与窗口相关联。它也不执行 与 模式运行循环。

您可以使用nextEventMatchingMask:untilDate:inMode:dequeue:方法。这将在模态循环中工作。

NSWindow和NSApplication都定义了该方法 nextEventMatchingMask:untilDate:inMode:dequeue:,它允许 对象,从事件队列中检索特定类型的事件。

如上所述,有必要重写 [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] 方法。就我而言(插件),我必须使用从NSApplication派生的第三方未知类的现有实例。我不能只是从中衍生出一个新类。因此,我使用 method_exchangeImplementations 将上面的命名方法与我自己的实现交换

+ (void)hijack
{
    Class appClass = [NSApplication class];
    Method originalMethod = class_getInstanceMethod(appClass, @selector(nextEventMatchingMask:untilDate:inMode:dequeue:));
    Method categoryMethod = class_getInstanceMethod(appClass, @selector(my_nextEventMatchingMask:untilDate:inMode:dequeue:));
    method_exchangeImplementations(originalMethod, categoryMethod);
}

如下所示:

- (NSEvent *)my_nextEventMatchingMask:(NSUInteger)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag
{
    NSEvent *event = [self my_nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:deqFlag];
    NSEventType type = [event type]; // 0 if event is nil
    if (type == NSLeftMouseDown || type == NSRightMouseDown)
    {
        if ([self modalWindow] != nil && [event window] != [self modalWindow])
        {
            [self stopModalWithCode:NSModalResponseCancel];
            event = nil;
        }
    }
    return event;
}

最后,模态窗口按如下方式调用:

[NSApplication hijack];
[NSApp runModalForWindow:window];
[NSApplication hijack];

显然,如果您可以覆盖NSApplication,则无需定义和调用hijack方法。

最新更新