Show Nsmenu Only on Nsstatusbarbutton Right Click

Show NSMenu only on NSStatusBarButton right click?

The usual way a to show a menu is to assign a menu to the status item, where it will be shown when the status item button is clicked. Since popUpMenu is deprecated, another way is needed to show the menu under different conditions. If you want the right click to use an actual status item menu instead of just showing a contextual menu at the status item location, the status item menu property can be kept nil until you want to show it.

I've adapted your code to keep the statusBarItem and statusBarMenu references separate, only adding the menu to the status item in the clicked action method. In the action method, once the menu is added, a normal click is performed on the status button to drop the menu. Since the status item will then always show its menu when the button is clicked, an NSMenuDelegate method is added to set the menu property to nil when the menu is closed, restoring the original operation:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
// keep status item and menu separate
var statusBarItem: NSStatusItem!
var statusBarMenu: NSMenu!

func applicationDidFinishLaunching(_ aNotification: Notification) {
let statusBar = NSStatusBar.system
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
statusBarItem.button?.title = "br>
statusBarItem.button?.action = #selector(self.statusBarButtonClicked(sender:))
statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])

statusBarMenu = NSMenu(title: "Status Bar Menu")
statusBarMenu.delegate = self
statusBarMenu.addItem(
withTitle: "Order an apple",
action: #selector(AppDelegate.orderAnApple),
keyEquivalent: "")
statusBarMenu.addItem(
withTitle: "Cancel apple order",
action: #selector(AppDelegate.cancelAppleOrder),
keyEquivalent: "")
}

@objc func statusBarButtonClicked(sender: NSStatusBarButton) {
let event = NSApp.currentEvent!
if event.type == NSEvent.EventType.rightMouseUp {
print("Right click!")
statusBarItem.menu = statusBarMenu // add menu to button...
statusBarItem.button?.performClick(nil) // ...and click
} else {
print("Left click!")
}
}

@objc func menuDidClose(_ menu: NSMenu) {
statusBarItem.menu = nil // remove menu so button works as before
}

@objc func orderAnApple() {
print("Ordering a apple!")
}

@objc func cancelAppleOrder() {
print("Canceling your order :(")
}

}

Call Action when NSStatusBarButton is right-clicked

You can subclass and override the mouseDown method, but since Mac OS X 10.10 (Yosemite), there has been an easier way: NSGestureRecognizer and its subclasses:

func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
if let button = statusItem.button {
button.image = NSImage(named: "myImage")
button.alternateImage = NSImage(named: "myImage")
button.action = Selector("myAction")

// Add right click functionality
let gesture = NSClickGestureRecognizer()
gesture.buttonMask = 0x2 // right mouse
gesture.target = self
gesture.action = "myRightClickAction:"
button.addGestureRecognizer(gesture)
}
}

func myRightClickAction(sender: NSGestureRecognizer) {
if let button = sender.view as? NSButton {
// Handle your right click event here
}
}

Swift: NSStatusItem menu behaviour in 10.10 (e.g. show only on right mouse click)

Here's the solution I came up with. It works fairly well, though there's one thing I'm not happy with: the status item stays highlighted after you choose an option in the right-click menu. The highlight goes away as soon as you interact with something else.

Also note that popUpStatusItemMenu: is "softly deprecated" as of OS X 10.10 (Yosemite), and will be formally deprecated in a future release. For now, it works and won't give you any warnings. Hopefully we'll have a fully supported way to do this before it's formally deprecated—I'd recommend filing a bug report if you agree.

First you'll need a few properties and an enum:

typedef NS_ENUM(NSUInteger,JUNStatusItemActionType) {
JUNStatusItemActionNone,
JUNStatusItemActionPrimary,
JUNStatusItemActionSecondary
};

@property (nonatomic, strong) NSStatusItem *statusItem;
@property (nonatomic, strong) NSMenu *statusItemMenu;
@property (nonatomic) JUNStatusItemActionType statusItemAction;

Then at some point you'll want to set up the status item:

NSStatusItem *item = [[NSStatusBar systemStatusBar] statusItemWithLength:29.0];
NSStatusBarButton *button = item.button;
button.image = [NSImage imageNamed:@"Menu-Icon"];
button.target = self;
button.action = @selector(handleStatusItemAction:);
[button sendActionOn:(NSLeftMouseDownMask|NSRightMouseDownMask|NSLeftMouseUpMask|NSRightMouseUpMask)];
self.statusItem = item;

Then you just need to handle the actions sent by the status item button:

- (void)handleStatusItemAction:(id)sender {

const NSUInteger buttonMask = [NSEvent pressedMouseButtons];
BOOL primaryDown = ((buttonMask & (1 << 0)) != 0);
BOOL secondaryDown = ((buttonMask & (1 << 1)) != 0);
// Treat a control-click as a secondary click
if (primaryDown && ([NSEvent modifierFlags] & NSControlKeyMask)) {
primaryDown = NO;
secondaryDown = YES;
}

if (primaryDown) {
self.statusItemAction = JUNStatusItemActionPrimary;
} else if (secondaryDown) {
self.statusItemAction = JUNStatusItemActionSecondary;
if (self.statusItemMenu == nil) {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
[menu addItemWithTitle:NSLocalizedString(@"Quit",nil) action:@selector(terminate:) keyEquivalent:@""];
self.statusItemMenu = menu;
}
[self.statusItem popUpStatusItemMenu:self.statusItemMenu];
} else {
self.statusItemAction = JUNStatusItemActionNone;
if (self.statusItemAction == JUNStatusItemActionPrimary) {
// TODO: add whatever you like for the primary action here
}
}

}

So basically, handleStatusItemAction: is called on mouse down and mouse up for both mouse buttons. When a button is down, it keeps track of whether it should do the primary or secondary action. If it's a secondary action, that's handled immediately, since menus normally appear on mouse down. If it's a primary action, that's handled on mouse up.

NSStatusItem right click menu

NSStatusItem popUpStatusItemMenu: did the trick. I am calling it from my right click action and passing in the menu I want to show and it's showing it! This is not what I would have expected this function to do, but it's working.

Here's the important parts of what my code looks like:

- (void)showMenu{
// check if we are showing the highlighted state of the custom status item view
if(self.statusItemView.clicked){
// show the right click menu
[self.statusItem popUpStatusItemMenu:self.rightClickMenu];
}
}

// menu delegate method to unhighlight the custom status bar item view
- (void)menuDidClose:(NSMenu *)menu{
[self.statusItemView setHighlightState:NO];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
// setup custom view that implements mouseDown: and rightMouseDown:
self.statusItemView = [[ISStatusItemView alloc] init];
self.statusItemView.image = [NSImage imageNamed:@"menu.png"];
self.statusItemView.alternateImage = [NSImage imageNamed:@"menu_alt.png"];
self.statusItemView.target = self;
self.statusItemView.action = @selector(mainAction);
self.statusItemView.rightAction = @selector(showMenu);

// set menu delegate
[self.rightClickMenu setDelegate:self];

// use the custom view in the status bar item
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
[self.statusItem setView:self.statusItemView];
}

Here is the implementation for the custom view:

@implementation ISStatusItemView

@synthesize image = _image;
@synthesize alternateImage = _alternateImage;
@synthesize clicked = _clicked;
@synthesize action = _action;
@synthesize rightAction = _rightAction;
@synthesize target = _target;

- (void)setHighlightState:(BOOL)state{
if(self.clicked != state){
self.clicked = state;
[self setNeedsDisplay:YES];
}
}

- (void)drawImage:(NSImage *)aImage centeredInRect:(NSRect)aRect{
NSRect imageRect = NSMakeRect((CGFloat)round(aRect.size.width*0.5f-aImage.size.width*0.5f),
(CGFloat)round(aRect.size.height*0.5f-aImage.size.height*0.5f),
aImage.size.width,
aImage.size.height);
[aImage drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0f];
}

- (void)drawRect:(NSRect)rect{
if(self.clicked){
[[NSColor selectedMenuItemColor] set];
NSRectFill(rect);
if(self.alternateImage){
[self drawImage:self.alternateImage centeredInRect:rect];
}else if(self.image){
[self drawImage:self.image centeredInRect:rect];
}
}else if(self.image){
[self drawImage:self.image centeredInRect:rect];
}
}

- (void)mouseDown:(NSEvent *)theEvent{
[super mouseDown:theEvent];
[self setHighlightState:!self.clicked];
if ([theEvent modifierFlags] & NSCommandKeyMask){
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}else{
[self.target performSelectorOnMainThread:self.action withObject:nil waitUntilDone:NO];
}
}

- (void)rightMouseDown:(NSEvent *)theEvent{
[super rightMouseDown:theEvent];
[self setHighlightState:!self.clicked];
[self.target performSelectorOnMainThread:self.rightAction withObject:nil waitUntilDone:NO];
}

- (void)dealloc{
self.target = nil;
self.action = nil;
self.rightAction = nil;
[super dealloc];
}

@end

How to show Menu on button press instead of right mouse click in os x

I got it working finally using this code:

@IBOutlet var meeenu: NSMenu!
@IBAction func Options(sender: NSButtonCell) {
meeenu.popUpMenuPositioningItem(meeenu.itemAtIndex(0), atLocation: NSEvent.mouseLocation(), inView: nil)
}

Mac OS Menu Bar Add Right Click To Show Another View

Instead of overriding functions in NSStatusBarButton, you should detect the left and right click behavior within the MenuButtonToggle function.

First, you should register the right-click action, inside your the if-let inside applicationDidFinishLaunching, like so:

if let menuButton = statusItem?.button {
menuButton.image = NSImage(systemSymbolName: "note", accessibilityDescription: nil)
menuButton.action = #selector(MenuButtonToggle)
menuButton.sendAction(on: [.leftMouseUp, .rightMouseUp]) // register action on right click too
}

Then, you want to update the MenuButtonToggle function to act differently depending on left and right clicks.

@objc func menuButtonToggle(sender: AnyObject){
// ... previous logic here

if let event = NSApp.currentEvent {
if event.type == NSEventType.rightMouseUp {
// Right button click
self.popOver.show(...)
} else {
// Left button click
self.popOverOptions.show(...)
}
}
}


Related Topics



Leave a reply



Submit