NSMenuItem 中的自定义 NSView 未接收鼠标事件

发布于 2024-08-03 18:32:22 字数 1440 浏览 9 评论 0原文

我使用 popUpStatusItemMenu 从 NSStatusItem 中弹出一个 NSMenu。这些 NSMenuItems 显示了一堆不同的链接,每个链接都通过 setAction: 连接到目标的 openLink: 方法。这种安排长期以来一直运作良好。用户从菜单中选择一个链接,然后 openLink: 方法对其进行处理。

不幸的是,我最近决定尝试使用 NSMenuItem 的 setView: 方法来提供更好/更流畅的界面。基本上,我只是停止设置标题,创建 NSMenuItem,然后使用 setView: 显示自定义视图。这工作得很好,菜单项看起来很棒,并且显示了我的自定义视图。

但是,当用户选择菜单项并释放鼠标时,该操作将不再起作用(即不调用 openLink:)。如果我只是简单地注释掉 setView: 调用,那么操作会再次起作用(当然,菜单项是空白的,但操作会正确执行)。那么,我的第一个问题是为什么设置视图会破坏 NSMenuItem 的操作。

没问题,我想,我将通过检测自定义视图中的 mouseUp 事件并从那里调用我的操作方法来修复它。我将此方法添加到我的自定义视图中:

- (void)mouseUp:(NSEvent *)theEvent {
  NSLog(@"in mouseUp");
  }

没有骰子!这个方法永远不会被调用。

不过,我可以设置跟踪矩形并接收 mouseEntered: 事件。我在 mouseEntered 例程中进行了一些测试,如下所示:

if ([[self window] ignoresMouseEvents]) {  NSLog(@"ignoring mouse events");  }
else {  NSLog(@"not ignoring mouse events");  }
if ([[self window] canBecomeKeyWindow]) {  dNSLog((@"canBecomeKeyWindow"));  }
else {  NSLog(@"not canBecomeKeyWindow");  }
if ([[self window] isKeyWindow]) {  dNSLog((@"isKeyWindow"));  }
else {  NSLog(@"not isKeyWindow");  }

并得到以下响应:

not ignoring mouse events
canBecomeKeyWindow
not isKeyWindow

这是问题所在吗? “不是isKeyWindow”?大概这不好,因为苹果的文档说“如果用户单击不在关键窗口中的视图,默认情况下该窗口会向前移动并成为关键,但不会调度鼠标事件。”但必须有一种方法可以检测这些事件。如何?

添加:没有任何效果。

[[self window] makeKeyWindow];

尽管 canBecomeKeyWindow 为 YES,但

I have an NSMenu popping out of an NSStatusItem using popUpStatusItemMenu. These NSMenuItems show a bunch of different links, and each one is connected with setAction: to the openLink: method of a target. This arrangement has been working fine for a long time. The user chooses a link from the menu and the openLink: method then deals with it.

Unfortunately, I recently decided to experiment with using NSMenuItem's setView: method to provide a nicer/slicker interface. Basically, I just stopped setting the title, created the NSMenuItem, and then used setView: to display a custom view. This works perfectly, the menu items look great and my custom view is displayed.

However, when the user chooses a menu item and releases the mouse, the action no longer works (i.e., openLink: isn't called). If I just simply comment out the setView: call, then the actions work again (of course, the menu items are blank, but the action is executed properly). My first question, then, is why setting a view breaks the NSMenuItem's action.

No problem, I thought, I'll fix it by detecting the mouseUp event in my custom view and calling my action method from there. I added this method to my custom view:

- (void)mouseUp:(NSEvent *)theEvent {
  NSLog(@"in mouseUp");
  }

No dice! This method is never called.

I can set tracking rects and receive mouseEntered: events, though. I put a few tests in my mouseEntered routine, as follows:

if ([[self window] ignoresMouseEvents]) {  NSLog(@"ignoring mouse events");  }
else {  NSLog(@"not ignoring mouse events");  }
if ([[self window] canBecomeKeyWindow]) {  dNSLog((@"canBecomeKeyWindow"));  }
else {  NSLog(@"not canBecomeKeyWindow");  }
if ([[self window] isKeyWindow]) {  dNSLog((@"isKeyWindow"));  }
else {  NSLog(@"not isKeyWindow");  }

And got the following responses:

not ignoring mouse events
canBecomeKeyWindow
not isKeyWindow

Is this the problem? "not isKeyWindow"? Presumably this isn't good because Apple's docs say "If the user clicks a view that isn’t in the key window, by default the window is brought forward and made key, but the mouse event is not dispatched." But there must be a way do detect these events. HOW?

Adding:

[[self window] makeKeyWindow];

has no effect, despite the fact that canBecomeKeyWindow is YES.

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

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

发布评论

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

评论(7

落日海湾 2024-08-10 18:32:22

将此方法添加到您的自定义 NSView 中,它将可以很好地处理鼠标事件,

- (void)mouseUp:(NSEvent*) event {
    NSMenuItem* mitem = [self enclosingMenuItem];
    NSMenu* m = [mitem menu];
    [m cancelTracking];
    [m performActionForItemAtIndex: [m indexOfItem: mitem]];
}

但我在按键处理方面遇到问题,如果您解决了这个问题,也许您可​​以转到我的问题并帮助我一点。

Add this method to your custom NSView and it will work fine with mouse events

- (void)mouseUp:(NSEvent*) event {
    NSMenuItem* mitem = [self enclosingMenuItem];
    NSMenu* m = [mitem menu];
    [m cancelTracking];
    [m performActionForItemAtIndex: [m indexOfItem: mitem]];
}

But i'm having problems with keyhandling, if you solved this problem maybe you can go to my question and help me a little bit.

又怨 2024-08-10 18:32:22

我将此方法添加到我的自定义视图中,现在一切都运行良好:

- (void)viewDidMoveToWindow {
    [[self window] becomeKeyWindow];
}

希望这会有所帮助!

I added this method to my custom view, and now everything works beautifully:

- (void)viewDidMoveToWindow {
    [[self window] becomeKeyWindow];
}

Hope this helps!

深海少女心 2024-08-10 18:32:22

将其添加到您的自定义视图中,您应该没问题:

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
    return YES;
}

Add this to your custom view and you should be fine:

- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
    return YES;
}
清君侧 2024-08-10 18:32:22

我已经为 SwiftUI Swift 5.3 更新了此版本:

final class HostingView<Content: View>: NSHostingView<Content> {
    override func viewDidMoveToWindow() {
        window?.becomeKey()
    }
}

然后像这样使用:

let item = NSMenuItem()
let contentView = ContentView()
item.view = HostingView(rootView: contentView)

let menu = NSMenu()
menu.items = [item]

I've updated this version for SwiftUI Swift 5.3:

final class HostingView<Content: View>: NSHostingView<Content> {
    override func viewDidMoveToWindow() {
        window?.becomeKey()
    }
}

And then use like so:

let item = NSMenuItem()
let contentView = ContentView()
item.view = HostingView(rootView: contentView)

let menu = NSMenu()
menu.items = [item]
度的依靠╰つ 2024-08-10 18:32:22

到目前为止,实现该目标的唯一方法是在 updateTrackingAreas 中手动注册跟踪区域 - 值得庆幸的是,它被称为,如下所示:

override func updateTrackingAreas() {
    let trackingArea = NSTrackingArea(rect: bounds, options: [.enabledDuringMouseDrag, .mouseEnteredAndExited, .activeInActiveApp], owner: self, userInfo: nil)
    addTrackingArea(trackingArea)
}

So far, the only way to achieve the goal, is to register a tracking area manually in updateTrackingAreas - that is thankfully called, like this:

override func updateTrackingAreas() {
    let trackingArea = NSTrackingArea(rect: bounds, options: [.enabledDuringMouseDrag, .mouseEnteredAndExited, .activeInActiveApp], owner: self, userInfo: nil)
    addTrackingArea(trackingArea)
}
独守阴晴ぅ圆缺 2024-08-10 18:32:22

最近,我需要显示 NSStatusItem 的自定义视图,单击它时显示常规 NSMenu,并支持状态图标上的拖放操作。

我主要使用三个不同的来源解决了我的问题,这些来源可以在 这个问题。

希望它能帮助其他人。

Recently I needed to show a Custom view for a NSStatusItem, show a regular NSMenu when clicking on it and supporting drag and drop operations on the Status icon.

I solved my problem using, mainly, three different sources that can be found in this question.

Hope it helps other people.

素食主义者 2024-08-10 18:32:22

请参阅 Apple 名为 CustomMenus 的示例代码
在那里您将在 ImagePickerMenuItemView 类中找到一个很好的示例。

让菜单中的视图像普通的 NSMenuItem 一样工作并不简单或微不足道。
有一些真正的决定和编码要做。

See the sample code from Apple named CustomMenus
In there you'll find a good example in the ImagePickerMenuItemView class.

It's not simple or trivial to make a view in a menu act like a normal NSMenuItem.
There are some real decisions and coding to do.

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