Create NSMenu for NSStatusBar.systemStatusBar programmatically - objective-c

I'm trying to create a simple menu in the System Status Bar using code only. I'm not receiving any compilation or runtime errors but I see no effect at all.
- (void)awakeFromNib
{
NSMenu *stackMenu = [[NSMenu alloc] initWithTitle:#"Status Menu"];
NSMenuItem *soMenuItem =
[[NSMenuItem alloc] initWithTitle:#"Status Menu Item" action:nil keyEquivalent:#"S"];
[soMenuItem setEnabled:YES];
[stackMenu addItem:soMenuItem];
statusItem = [[[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength]
retain];
[statusItem setMenu:stackMenu];
}

I don't believe the NSStatusItem will implicitly take on the title of the NSMenu associated with it (which is what I am guessing you want to happen.) Try explicitly setting the NSStatusItem's title (and/or its image).
e.x.
[statusItem setTitle:[stackMenu title]];

Related

NSMenu with views in a modal NSWindow

I have an issue with selectors not being performed for custom views inside an NSMenuItem when they are displayed from a button within a modal NSWindow.
This appears to be a reproducible issue and I've simplified the issue as much as I can.
Modal window is displayed via.
[NSApp runModalForWindow:_modalWindow];
The modal window only has a button, and the button is attached to the following selector.
- (IBAction)modalButtonClicked:(id)sender
{
NSMenu* aMenu = [[NSMenu alloc] initWithTitle:#"Menu"];
NSMenuItem* aItemA = [[NSMenuItem alloc] initWithTitle:#"" action:nil keyEquivalent:#""];
NSMenuItem* aItemB = [[NSMenuItem alloc] initWithTitle:#"" action:nil keyEquivalent:#""];
NSMenuItem* aItemC = [[NSMenuItem alloc] initWithTitle:#"" action:nil keyEquivalent:#""];
[aItemA setView:[NSButton buttonWithTitle:#"Item A" target:self action:#selector(menuButtonClicked:)]];
[aItemB setView:[NSButton buttonWithTitle:#"Item B" target:self action:#selector(menuButtonClicked:)]];
[aItemC setView:[NSButton buttonWithTitle:#"Item C" target:self action:#selector(menuButtonClicked:)]];
[aMenu addItem:aItemA];
[aMenu addItem:aItemB];
[aMenu addItem:aItemC];
[NSMenu popUpContextMenu:aMenu withEvent:[NSApp currentEvent] forView:sender];
}
and the menu click event with a breakpoint:
- (void)menuButtonClicked:(id)sender
{
NSLog(#"%#", sender);
}
Clicking on the button will display a menu with 3 buttons, however nothing happens when you click any of those buttons. #(menuButtonClicked:) is never called. This is only an issue with modal windows but there's no obvious reason why.
The documention https://developer.apple.com/documentation/appkit/nsmenuitem/1514843-target?language=objc states:
To ensure that a menu item’s target can receive commands while a modal
dialog is open, the target object should return YES in worksWhenModal.
And indeed if one adds:
- (BOOL)worksWhenModal {
return YES;
}
then it works and your method menuButtonClicked gives out something like:
2019-10-03 22:47:27.892005+0200 MenuTest[12876:454071] <NSButton: 0x600003505760>

NSMenuItem with custom view dose not respond after leaving application

My application resides in the status bar. When the application first starts my search field responds fine, and allows me to click and enter text and look like this.
But after clicking out of the application into another application like "xcode" and back into my menu the search field and the rest of the custom view appears dim and does not respond unless I click multiple times.
In my applicationDidFinishLaunching I set up the statusItem and the NSMenuItem with the custom view.
//set up hotkeys
[self registerHotKeys];
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
NSImage *menuIcon = [NSImage imageNamed:#"Menu Icon"];
NSImage *highlightIcon = [NSImage imageNamed:#"Menu Icon"];
[highlightIcon setTemplate:YES];
[[self statusItem] setImage:menuIcon];
[[self statusItem] setAlternateImage:highlightIcon];
[[self statusItem] setMenu:[self menu]];
[[self statusItem] setHighlightMode:YES];
// search menu item
NSMenuItem *searchMenuItem = [[NSMenuItem alloc] initWithTitle:#"search" action:nil keyEquivalent:#""];
self.searchMenuItemView = [[SearchMenuItemView alloc] initWithNibName:#"SearchMenuItemView" bundle:nil];
self.searchMenuItemView.delegate = self;
[searchMenuItem setView:self.searchMenuItemView.view];
[searchMenuItem setTarget:self];
[searchMenuItem setEnabled:YES];
[_menu insertItem:searchMenuItem atIndex:0];
The view is coming from an UIViewController, but I have tried setting the view from just an NSView with no luck using "this" from someone with a similar issue but this did not solve mine. Auto enable is on.
Is there a way to bring the NSMenuItem back to the state it was in when the application is first lunched?

NSMenuItem's not selectable after opening window

I'm having an issue very similar to this thread.
I am programatically creating a NSMenu and adding my items. One selection of an item it shows a window. This works as intended. However, when I close the window I can no longer select any of the options in the menu.
AppDelegate.m
- (void)createMenu {
NSMenu *statusMenu = [[NSMenu alloc] initWithTitle:#""];
NSMenuItem *historyItem = [[NSMenuItem alloc] initWithTitle:#"History" action:#selector(onHistory:) keyEquivalent:#""];
[statusMenu addItem:historyItem];
NSImage *statusImage = [NSImage imageNamed:#"icon.png"];
[_item setImage:statusImage];
[_item setMenu:statusMenu];
}
- (void)onHistory:(id)sender {
OBHistoryWindowController *historyWindowController = [[OBHistoryWindowController alloc] initWithWindowNibName:#"OBHistoryWindowController"];
historyWindowController.managedContext = self.managedObjectContext;
[historyWindowController showWindow];
}
OBHistoryWindowController.m
- (void)showWindow {
[NSApp runModalForWindow:self.window];
}
I'm guessing I need to somehow on close of the window give focus back to the menu but I can't for the life of me figure out how.
It sounds like you haven't stopped the modal loop. As the docs for runModalForWindow: say, "You can exit the modal loop by calling the stopModal, stopModalWithCode:, or abortModal methods from your modal window code."

NSMenu inside custom NSView class for NSStatusItem not appearing at bottom of status bar

I created a custom view class because I wanted to have a status item you could drag items to.
Here's the definition of the view:
#interface DragStatusView : NSImageView <NSMenuDelegate>{
BOOL highlight;
}
#end
In my ApplicationDelegate.m I instantiate a NSStatusItem, and an instance of my DragStatusView. I set the image on the DragStatusView, and also set its menu to an instance of NSMenu containing a few NSMenuItems.
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Install icon into the menu bar
statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
NSImage *statusImage = [NSImage imageNamed:#"Status"];
[statusItem setImage:statusImage];
[menuItem setTitle:NSLocalizedString(#"Special Status", #"imgur menu item text")];
CGFloat itemHeight = [[NSStatusBar systemStatusBar] thickness];
NSRect itemRect = NSMakeRect(0.0, 0.0, NSSquareStatusItemLength, itemHeight);
DragStatusView* dragView = [[DragStatusView alloc] initWithFrame:itemRect];
[dragView retain];
[dragView setImage:statusImage];
[dragView setMenu:menu];
[statusItem setHighlightMode:YES];
[statusItem setView:dragView];
}
Here is the method in the DragStatusView controller that triggers the menu to pop up:
- (void)mouseDown:(NSEvent *)event {
[[[NSApp delegate] statusItem] popUpStatusItemMenu:[self menu]]; // or another method that returns a menu
}
This mostly works, however the menu appears too high when you click on the status item.
How it looks before clicking: http://imgur.com/fpJcd,quS3c#1
How it looks after clicking: http://imgur.com/fpJcd,quS3c#0 (the menu appears at the top of the screen -- ahh!)
How can I make the menu appear at the bottom of the status bar?
Thanks!
How are you popping up the menu? What you want to do is something like this:
- (void)mouseDown:(NSEvent *)event {
[statusItem popUpStatusItemMenu:[self menu]];
}
Of course, your view will need a reference to the status item, then.
Finally got it working.
Code can be seen here:
https://github.com/zbuc/imgurBar/blob/master/imgur/ApplicationDelegate.m
https://github.com/zbuc/imgurBar/blob/master/imgur/StatusItemView.m
I'm not quite sure why this code works though, which makes me uncomfortable. It does things with the bounding rects slightly differently but I'm not seeing what exactly makes this work now.

Custom view in NSMenuItem disables the NSPopUpButton selection

I want to customize an an NSPopUpButton so I have implemented an CustomMenuItemView which right now only has the following code (for testing purposes):
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor redColor] set];
NSRectFill(dirtyRect);
}
Now, for every NSMenuItem i add to the NSMenu in myPopUpButton.menu I set the view to my custom view:
NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:#"Some title" action:NULL keyEquivalent:#""];
menuItem.view = [[CustomMenuItemView alloc] initWithFrame:NSMakeRect(0, 0, 100, 25)];
When I run my program and open the popup button the menuitem selection seems disabled (i.e. nothing happens when I click on it).
I am guessing that it is not actually disabled; it just doesn't respond to events anymore. Do I need to add some event handling in my custom view? If so, how?
I solved the problem by adding the mouseUp method to my CustomMenuItemView:
- (void)mouseUp:(NSEvent*) event
{
NSMenu *menu = self.enclosingMenuItem.menu;
[menu cancelTracking];
[menu performActionForItemAtIndex:[menu indexOfItem:self.enclosingMenuItem]];
}