How do I change the main menu in Cocoa? - objective-c

I have a cocoa app with two types windows each of which requires a different main menu to be displayed.
In my MainMenu.xib I have the default MainMenu. In Window1.xib I have Window1 and in Window2.xib I have Window2 and it's MainMenu.
When I have the first Window open I have the default Menu, when I open Window2 I get it's menu.
However, when I switch back to Window1 I still see Window2's menu. How do I make the menu that is displayed follow the key window?

Generally, you shouldn't replace the entire main menu every time. It's more compliant with the Human Interface Guidelines to simply disable any menu items that don't apply to the current window. And if you really should have a completely different set of menus in the menu bar, maybe you should split that part of your application into a separate application.

NSApplication has a method, - (void)setMainMenu:(NSMenu *)aMenu. You can pass it a reference to the correct menu in the appropriate window controller, by implementing - (void)windowDidBecomeKey:(NSNotification *)notification.
Keep in mind it may be easier to change just the submenus instead of swapping out the entire main menu, since you won't have to maintain two different copies of the application, help, and other menus that won't change between the two windows.

Related

Adding a toolbar button to Finder.app programmatically (macOS)

I am looking for a way to add several toolbar buttons to Finder, which, when clicked, perform certain actions.
My research shows that injecting code into Finder process is impossible on latest versions of macOS due to SIP, yet this would be the most seamless way for the user.
There is a possibility to add a toolbar item by creating a Finder Sync extension. However there are 2 problems:
There can be only one toolbar button per extension (I need several buttons)
The toolbar button will have a dropdown arrow (see an image below). I do not need to show a menu, however, and therefore this arrow makes the button misleading. It must be a simple plain button that matches the current system theme and performs an action upon click.
So this is what I don't need (because of the drop down arrow):
Update:
One of the ways to add a button, is drag and drop an .app bundle, holding Command key.
This approach has the following problems:
This button wouldn't match the other toolbar buttons look&feel, as the icon for such button is taken from the .app bundle (so it wouldn't switch based on macOS light/dark theme, for example)
It is impossible to add several toolbar buttons like that (as there needs to be one .app per 1 button). However, I need multiple buttons.
I am wondering if FinderSync allows creating "normal" (non menu) buttons
Is there a way to add a regular button to Finder's toolbar?
Please check out my Finder buttons:
https://github.com/lexrus/LTFinderButtons
Basically, I made every task a button app.
I'm trying Finder Sync Extension to do the same thing. All issues you found are true. Furthermore, there's problem #3:
You can not submit an app with Finder Sync Extension to the App Store.

Close an NSWindow without activating the window below

Consider the following scenario. I have an app with two windows:
the main window which contains all of the app's functionality;
and an auxiliary window that can be invoked using a global shortcut or by clicking app's status bar icon. It looks like this:
It's a simple window for quickly adding data to the application without bringing up the main window — possibly changing spaces and disrupting user's workflow.
The window floats above everything else (it's on NSModalPanelWindowLevel) and can join all spaces (NSWindowCollectionBehaviorCanJoinAllSpaces)
Here's the problem: when the quick-add window is ordered out (the X button or Esc is pressed), the main window is made key and ordered front (provided that it's somewhere on the window list in the current space).
This isn't the behavior I want. Normally, when an app's window is closed, yes, you want the app's window below to be activated, but not here — this is a "helper" window that should work more-or-less independently from the app itself. When I'm in Safari and invoke the quick-add window, I only want to add some data, click Return, and I want it to go away — I want the previous window (Safari) to be key and on top, not the main window of my app (unless of course the main window of my app was key before invoking quick-add).
So, how to close an NSWindow without activating the window below?
I can't figure this out. The only idea I had is that maybe you could make a helper application that would display this auxiliary window — but that sounds like a lot of work (sending data back and forth between processes, all that…). There must be a simpler way!
i don't think this can be (usefully) done with an NSWindow.
As in this answer, i would recommend trying to use an NSPanel with the style NSNonactivatingPanelMask instead of creating an NSWindow.

Show NSWindow as dropdown from NSStatusItem?

How can I show an NSWindow when clicking on an NSStatusItem, such that it shows over other applications when appearing, but without causing them to lose active status?
I have seen MAAttachedWindow, and it is neat but it does not show on top of other applications unless its own application is active.
I would check out this excellent tutorial and sample project:
Cocoa Popup window in the Status bar
That example shows how to "attach" a window to a status bar item, but you could position the window wherever you want. Basically, if you define your app as a LSUIElement you can display floating utility windows despite your app never activating or showing a dock icon, and other apps still remain key.
if you use MAAttachedWindow, you can set the winow level as NSStatusWindowLevel. Then it will show on top of other application.

How to get the Spotlight-like text input effect in menu bar?

I want to have an icon in the menubar in my Mac app - and the icon should spawn a menu upon clicking. While having more entries in the menu, I would like to have a top row as a universal text entry field - like it is in Spotlight:
http://dl.dropbox.com/u/3943878/_mine/Screen%20shot%202011-07-16%20at%2012.29.18.png
Is it possible to add such a field to NSMenu? Or should I do it as a panel-type window?
If you're using xcode 4 , make a custom view in interface builder and add a textfield or anything you want to it. In IB also drag and drop a "Menu" from the objects library with as many items as you want in it. Then simply ctrl+click the menu item you want to make into the text field (In your case it would be the top one) and drag to the custom view and select "view". Now when you open the menu, instead of showing a menu item in that space, it shows whatever was in your custom view.
EDIT: As for your comment here's what you should do. Make your menu an outlet by opening the assistant editor view and ctrl+click from your menu to the header file that you want to use. now, simply make a method that will run whenever the menu will open, conveniently apple already made this, it's called menuWillOpen.
- (void)menuWillOpen: nameOfYourMenu{
[self performSelector:#selector(methodExecutedWhenMenuIsClicked) withObject:nil afterDelay:0.0 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
the delay at 0 will make it happen immediately, it must be done in the common modes run loop so that the menu will be updated even while it's open. Now just make the methodExecutedWhenMenuIsClicked and set it so the text field responds.
- (void)methodExecutedWhenMenuIsClicked{
[[yourTextfiled window] makeFirstResponder:yourTextField];
You can put any view in a menu using -[NSMenuItem setView:]. See the long comment in NSMenuItem.h and the section Views in Menus in Application Menu and Pop-up List Programming Topics.
You're probably going to struggle quite a bit. I just tried doing the same thing, and reading the Views in Menus in Application Menu and Pop-up List Programming Topics document referenced by Ahruman, I found this:
A view in a menu item can receive all mouse events as normal, but keyboard events are not supported. During “non-sticky” menu tracking (that is, manipulating menus with the mouse button held down), a view in a menu item receives mouseDragged: events.
I think we're SOL. Apparently Spotlight pops up a borderless window instead.

How can I keep an NSPopUpButton open after the user selects a menu item?

I have an NSPopUpButton providing the NSMenu for a status item with a custom view. The popup button displays a list of links. When the user selects a link from the list, the link is displayed in the user's browser (in the background).
Naturally, the menu closes every time the user selects a link.
I would like to change this: I want the menu to stay open while the user clicks on various links, all of which can be opened in the background. The menu can then go away when the user clicks elsewhere.
How can this be accomplished? Should I subclass NSMenuItem and intercept the mouse clicks somehow? Overlay a transparent NSView on the popped-up menu and, again, intercept the clicks somehow? I make these suggestions blithely, but I would have trouble implementing either of these...pointers to the right methods for override would be appreciated.
Instead of using a menu, one might use a collapsible box.I have seen that in many apps ( also provided by Apple) , so I guess this is the recommended style guide for multiple selections.
The collapsible box expands when you click the disclosure button, and it gives free all items desired - like a tableview with checkboxes.
Views below this box must move down in this case, not to interfere with the box.
Clicking again on the disclosure button will shrink the box back to its origin. The effect is similar to closing a menu.
Usually you should not bend a control too far past it's original intent. Users expect pop up buttons to close after making a selection. I don't think you should, or can, force NSPopUpButtonCell to behave in this way. If you do, you'll be subclassing and modifying the control so heavily that it might change/break with a future version of Mac OS X. You'd also have to worry about the usability problem of users thinking the menu will close after making a selection.
You might consider writing you're own subclass of NSView to work like the menu button you're describing. After the user clicks on the button. You'll want to create a new NSWindow, with no border by using NSBorderlessWindowMask as the style mask. The content view of that window should be another custom view of yours that you implement the menu selection in.