macos NSButton 的弹出菜单实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9716385/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 08:58:34  来源:igfitidea点击:

Popup menu implementation from NSButton

objective-cmacoscocoa

提问by Dr.Kameleon

How should I go about it?

我该怎么办?

I was thinking about...

我在想...

[NSMenu popUpContextMenu:menu withEvent:event forView:(NSButton *)sender];

回答by arun.s

Yup.

对。

On button action call

按钮操作调用

[NSMenu popUpContextMenu:menu withEvent:event forView:(NSButton *)sender];

where

在哪里

  • menu: menu you want to show
  • sender: button you clicked
  • event: a new NSEventyou create
  • menu: 要显示的菜单
  • sender:你点击的按钮
  • event: 一个NSEvent你创造的新

When you create the new NSEvent, specify the location as to where you want the popup menu to be shown.

创建新的 时NSEvent,请指定要显示弹出菜单的位置。

回答by Kaunteya

Swift version of accepted answer

已接受答案的 Swift 版本

@IBAction func actionOccurred(sender: NSButton) {
    if let event = NSApplication.sharedApplication().currentEvent {
        NSMenu.popUpContextMenu(sender.menu!, withEvent: event, forView: sender)
    }
}


Updated answer

更新答案

Swift 5.1 version

斯威夫特 5.1 版本

Add NSMenu to the NSViewController in storyboard as seen in the image

如图所示,将 NSMenu 添加到故事板中的 NSViewController

Image

图片

@IBOutlet var userMenu: NSMenu!

@IBAction func menuClicked(_ sender: NSButton) {
    var location = NSEvent.mouseLocation
    location.x -= 10; location.y -= 10 // Menu appears below the button
    userMenu.popUp(positioning: userMenu.item(at: 0), at: location, in: nil)
}

回答by Jonathan Mitchell

As I have commented I find the ButtonMadness example less than perfect. My implementation seems to work better. The menu is shown on mouse down, the button remains depressed throughout, the menu position can be specified and the menu is dismissed without subsequent spurious display.

正如我所评论的,我发现 ButtonMadness 示例并不完美。我的实现似乎效果更好。菜单在鼠标按下时显示,按钮始终保持按下状态,可以指定菜单位置并取消菜单,而不会出现后续虚假显示。

To be honest NSPopupButton is a better choice in the majority of situations. I use this code mainly because of the convenience of having one class for buttons and popups and because the menu does not contain the popup control image and title. I load the menu from a separate nib and reuse it as is elsewhere in the app as required.

老实说,在大多数情况下,NSPopupButton 是更好的选择。我使用这段代码主要是因为有一个按钮和弹出窗口的类很方便,而且菜单不包含弹出控件图像和标题。我从一个单独的笔尖加载菜单,并根据需要在应用程序的其他地方重新使用它。

Note that it is trivial to add additional support for say a popover as well as menu.

请注意,为弹出框和菜单添加额外支持是微不足道的。

NSButton subclass:

- (void)mouseDown:(NSEvent *)theEvent {

    // if a menu is defined let the cell handle its display
    if (self.menu) {
        if ([theEvent type] == NSLeftMouseDown) {
            [[self cell] setMenu:[self menu]];
        } else {
            [[self cell] setMenu:nil];
        }
    }

    [super mouseDown:theEvent];
}

NSButtonCell subclass:

- (BOOL)trackMouse:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp
{
    // if menu defined show on left mouse
    if ([event type] == NSLeftMouseDown && [self menu]) {

        NSPoint result = [controlView convertPoint:NSMakePoint(NSMidX(cellFrame), NSMidY(cellFrame)) toView:nil];

        NSEvent *newEvent = [NSEvent mouseEventWithType: [event type]
                                               location: result
                                          modifierFlags: [event modifierFlags]
                                              timestamp: [event timestamp]
                                           windowNumber: [event windowNumber]
                                                context: [event context]
                                            eventNumber: [event eventNumber]
                                             clickCount: [event clickCount]
                                               pressure: [event pressure]];

        // need to generate a new event otherwise selection of button
        // after menu display fails
        [NSMenu popUpContextMenu:[self menu] withEvent:newEvent forView:controlView];

        return YES;
    }

    return [super trackMouse:event inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp];
}

回答by sim

Recently, I was trying to implement it and I came, as I think, with a simpler solution

最近,我正在尝试实施它,正如我所想的那样,我带来了一个更简单的解决方案

-(IBAction)buttonClick:(id)sender {
    NSButton * b = (NSButton*)sender;
    NSPoint l = [ self.window convertBaseToScreen:b.frame.origin ];

    [ self.menu popUpMenuPositioningItem:nil atLocation:l inView:nil ];
}

Update

更新

convertBaseToScreenis deprecated starting from 10.7, instead of it use convertRectToScreenin the following way:

convertBaseToScreen从 10.7 开始弃用,而不是convertRectToScreen按以下方式使用:

NSPoint l = [self.window convertRectToScreen:b.frame].origin; 

回答by Pierre Houston

Using a context menu on the action call isn't a great way to do it because the menu doesn't show until mouseUp - you don't get the hold & drag menu behavior. Apple's ButtonMadness sample shows how to really do this in a subclass of NSButton, see DropDownButton. https://developer.apple.com/library/mac/samplecode/ButtonMadness/Introduction/Intro.html

在操作调用上使用上下文菜单并不是一个好方法,因为菜单直到 mouseUp 才会显示 - 您不会获得按住和拖动菜单行为。Apple 的 ButtonMadness 示例展示了如何在 NSButton 的子类中真正做到这一点,请参阅 DropDownButton。https://developer.apple.com/library/mac/samplecode/ButtonMadness/Introduction/Intro.html

Summarizing that subclass: create a NSPopUpButtonCell with pullsDown set to YES & preferredEdge to NSMaxYEdge, copy your menu to add a blank top item and set it as that cell's menu, on mouseDown call [thePopUpCell performClickWithFrame:self.bounds inView:self] and set self.needsDisplay

总结该子类:创建一个 NSPopUpButtonCell 并将 pullsDown 设置为 YES & preferredEdge 到 NSMaxYEdge,复制您的菜单以添加一个空白的顶部项目并将其设置为该单元格的菜单,在 mouseDown 调用 [thePopUpCell performClickWithFrame:self.bounds inView:self] 并设置自我需求展示

回答by ByungBok Lee

Do like that.

这样做。

    -(IBAction)onClickSourceAdd:(id)sender {
       NSMenu *mainMenu = [NSApp mainMenu];
       NSMenu *sourceMenu = [[mainMenu itemAtIndex:2] submenu];
       NSMenu *addMenu = [[sourceMenu itemAtIndex:0] submenu];
       [NSMenu popUpContextMenu:addMenu 
               withEvent:[NSApp currentEvent] 
               forView:(NSButton *)sender]; 
     }