当弹出窗口位于状态栏中时,NSPopover 瞬态

发布于 2024-12-29 12:11:22 字数 268 浏览 1 评论 0原文

我正在制作一个位于状态栏中的应用程序。单击状态项时,会弹出 NSPopover。

它看起来像这样:

在此处输入图像描述

问题是:我希望它是“瞬态”的,也就是说,如果我单击弹出窗口之外的任意位置,它将关闭。虽然当弹出窗口位于窗口中时 NSPopoverBehaviorTransient 工作正常,但当它位于状态栏中时它不起作用。

我自己该如何实施这种行为?

I'm making an app which lives in status bar. When status item is clicked, NSPopover pops up.

It looks like this:

enter image description here

Here's the problem: I want it to be "transient", that is if I click anywhere outside of the popover, it will close. And while NSPopoverBehaviorTransient works fine when popover is in a window, it doesn't work when it's in status bar.

How can I implement such behavior myself?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

秉烛思 2025-01-05 12:11:22

事实证明这很简单:

- (IBAction)openPopover:(id)sender
{
    // (open popover)

    if(popoverTransiencyMonitor == nil)
    {
        popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask handler:^(NSEvent* event)
                                    {
                                        [self closePopover:sender];
                                    }];
    }
}

- (IBAction)closePopover:(id)sender
{
    if(popoverTransiencyMonitor)
    {
        [NSEvent removeMonitor:popoverTransiencyMonitor];

        popoverTransiencyMonitor = nil;
    }

    // (close popover)
}

但并不容易的是,从 NSStatusItem 中弹出弹出窗口会出现令人讨厌的问题(当调用任务控制或空间切换到全屏时,它的行为不符合预期)窗户)。我必须实现一个始终浮动在 NSStatusItem 上方的自定义窗口,并处理切换到全屏窗口等。这看起来很简单,但显然状态项不是为类似的事情而设计的;)

It turned out to be easy:

- (IBAction)openPopover:(id)sender
{
    // (open popover)

    if(popoverTransiencyMonitor == nil)
    {
        popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask handler:^(NSEvent* event)
                                    {
                                        [self closePopover:sender];
                                    }];
    }
}

- (IBAction)closePopover:(id)sender
{
    if(popoverTransiencyMonitor)
    {
        [NSEvent removeMonitor:popoverTransiencyMonitor];

        popoverTransiencyMonitor = nil;
    }

    // (close popover)
}

What wasn't easy, though, is that there are nasty issues with having a popover pop out of NSStatusItem (it didn't behave as desired when Mission Control was invoked or space switched to a full-screen window). I had to implement a custom window that always floats above the NSStatusItem and deals with switching to a full-screen window etc. It seemed easy, but clearly status items weren't designed for something like that ;)

蓦然回首 2025-01-05 12:11:22

我使用的方法与上面的答案类似,只是我将所有内容合并到一种方法中,而不是使用两个单独的 IBActions。

首先,我声明以下属性,

@property (strong, nonatomic) NSStatusItem *statusItem;
@property (strong, nonatomic) NSEvent *popoverTransiencyMonitor;
@property (weak, nonatomic) IBOutlet NSPopover *popover;
@property (weak, nonatomic) IBOutlet NSView *popoverView;

然后在 awakeFromNib 中设置状态栏项目,

- (void)awakeFromNib {

    self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];

    self.statusItem.title = @"Title";
    self.statusItem.highlightMode = YES;
    self.statusItem.action = @selector(itemClicked:);
}

然后设置单击状态栏项目时调用的方法

- (void)itemClicked:(id)sender {

    [[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];

    if (self.popoverTransiencyMonitor == nil) {
        self.popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
            [NSEvent removeMonitor:self.popoverTransiencyMonitor];
            self.popoverTransiencyMonitor = nil;
            [self.popover close];
        }];
    }
}

,这使得弹出窗口出现,并且当用户在视图外部单击时也会关闭。

请注意,在 Interface Builder 中,您必须将弹出窗口的行为设置为 Transient,以便当用户单击状态项时弹出窗口将关闭。

The approach that I use is similar to the above answer except I have everything combined into one method instead of using two separate IBActions.

First, I declare the following properties

@property (strong, nonatomic) NSStatusItem *statusItem;
@property (strong, nonatomic) NSEvent *popoverTransiencyMonitor;
@property (weak, nonatomic) IBOutlet NSPopover *popover;
@property (weak, nonatomic) IBOutlet NSView *popoverView;

then in awakeFromNib I set up the status bar item

- (void)awakeFromNib {

    self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];

    self.statusItem.title = @"Title";
    self.statusItem.highlightMode = YES;
    self.statusItem.action = @selector(itemClicked:);
}

followed by the method that is called when the status bar item is clicked

- (void)itemClicked:(id)sender {

    [[self popover] showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];

    if (self.popoverTransiencyMonitor == nil) {
        self.popoverTransiencyMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDownMask | NSRightMouseDownMask | NSKeyUpMask) handler:^(NSEvent* event) {
            [NSEvent removeMonitor:self.popoverTransiencyMonitor];
            self.popoverTransiencyMonitor = nil;
            [self.popover close];
        }];
    }
}

which makes the popover appear and also close when the user clicks outside the view.

Note that in Interface Builder you must set the behavior of the popover to Transient so the popover will close when the user clicks the status item.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文