SwiftUI context menu trigger by left click on macOS - macos

I have a gear button on my view, when user click it I want to show a menu of buttons. It seems the https://developer.apple.com/documentation/swiftui/view/contextmenu(menuitems:) is designed for this task but only right click can trigger the context menu.
How can I trigger the contextMenu by left-click on macOS? Thanks!

You can use a specific menu style for that type of UI. If you want to hide the indicator, just put in false.
Menu("Label") {
Button("Buttons") { }
}
.menuStyle(BorderlessButtonMenuStyle())
.menuIndicator(.hidden)
.fixedSize() // Otherwise will be the width of your menu options.

Related

Drop-down menu in NSToolbar like Mail.app

I'd like a toolbar button with an attached dropdown menu, like the "Flag" button in the toolbar in Mail.app:
I'd hoped that making a normal NSMenuItem and adding a menu as the menuFormRepresentation would do the trick, but that menu only appears when the button goes into overflow mode.
I had also hoped that adding an NSPopupButton as a custom view would work, but that makes the whole view a menu, whereas I want the left part of the component to behave like a normal button, and the right dropdown part bring up the menu.
Is there some trick to making the NSToolbarItem show a component like this, or is this two custom views stuck together?
There's nothing magical about NSToolbar here. That's just one of the ways you can set up NSSegmentedControl, regardless of whether it appears as a toolbar item's custom view or on its own.
You can't set this up in Interface Builder (storyboard), but NSSegmentedControl has APIs for assigning menus to segments:
segmentControl.setMenu(myMenu, forSegment: 1)
segmentControl.setShowsMenuIndicator(true, forSegment: 1) // for the little arrow
You probably want to set the tracking mode to momentary, since your segment control is acting as a set of visually-connected buttons, not a choose-one-of-N selector.
When the user clicks either segment, your action method will need to use the selectedSegment to decide whether to perform the action associated with the "button" side or ignore the click (letting the menu show for the other side).

How can I stop or fix Wakanda menu highlighting?

Wakanda's Menu Bar widget seems to not have a sense of hierarchy; that is, clicking an item will highlight it and will not un-highlight a clicked item of a different menu item group. Example below:
"Home" was clicked, and then the submenu item "View Requests" was clicked. "Home" remains highlighted, but it really should have un-highlighted.
It looks sloppy. Do you have any advice for making it nicer?
event.preventDefault() on the onClick event did not prevent the coloring change for the down state.
Edit: This is all one complex menu bar widget. It has menu bar widgets as submenu items. That's what I mean by a lack of sense of heirarchy. Wakanda fails to see the tree of menu bar widgets as ONE menu bar.
In the properties tab of the Menu Bar widget, change the value of "Show submenus" from 'On Mouse Over' (default) to 'On Mouse Click'.
Regards.

Open infinite window of Preferences Pane in Cocoa app when using segue

I want to open a preference pane in my Cocoa app when an user taps the Preferences... menu or typing ⌘+,. So I connected from the preferences menu item to the window controller in storyboard as a show segue.
However, while this opens the preference pane when an user taps Preferences..., if the user taps the menu item again before closing the pane, another pane is going to be launched and displayed on the screen.
I want to have only one preference pane in my screen but how can I do that? I want to set it up only on storyboard and avoid coding that makes the nest of nest of boring menu items...
In order to prevent the windows from being launched multiple times, you select Single from the Window Controller's Attribute Inspector on Storyboard.
The default value is Multiple, causing the infinite windows to be launched.
Alternatively, if you want to use the code, here is something I will do in AppDelegate.swift.
extension AppDelegate: NSSeguePerforming {
func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if identifier == OpenPreferencesIdentifier {
let windows = NSApp.windows
for window in windows {
if window.windowController?.className == MyPreferencesWindowController.className() {
return false
}
}
}
return true
}
}
Also do NOT forget to set the appropriate segue identifier on Storyboard.

Cannot seem to setEnabled:NO on NSMenuItem

I have subclassed NSMenu and connected a bunch of NSMenuItem's via Interface Builder. I have tested via the debugger to see that they really get initialized.
The menu is set to not auto enable items. Still when I set any of my NSMenuItem's to [myMenuItem setEnabled:NO] they continue to be enabled. Even if I call [self update] from within the NSMenu subclass.
What am I missing?
Had the same issue, so I thought I'd post my solution. NSMenu auto enables NSMenuButtons, so we have to override that.
In IB:
Or programmatically:
// Disable auto enable
[myMenu setAutoenablesItems:NO];
// Test it
[myMenuButton setEnabled:NO];
[myMenuButton setEnabled:YES];
I solved it with the help of a colleague, so I post it here for others that experience the same issue.
You should set your NSMenu-sublass to auto-enable items (default behaviour) and then implement this method in the NSMenu-sublass.
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
return [menuItem isEnabled];
}
You should uncheck Auto Enables Items on the closest parent NSMenu
You can solve this problem without subclassing.
If only you need is standard menu + some custom NSMenuItems that can be disabled on your control, than you can just:
Add one more menu node - just drag and drop Submenu Menu Item from Object library to your menu.
Add all NSMenuItems you want to manage to this new Menu node.
Open Attributes inspector for your New Menu node, and switch off Auto Enables Items option:
configure any other options of your menu & items.
Now you can write a code like:
#property (weak) IBOutlet NSMenuItem *hidePlateMenuItem;
...
[self.hidePlateMenuItem setEnabled:NO];
and it will works well.
Adding to the response of itsdavyh, if the menu item is located inside one or more submenus you only have to uncheck the 'Auto enables items'-property on the submenu of the menu item and not on any other parentmenus.
I tried all these solution but finally i found the real problem that also make more sense to me.
It also is the simplest way to handle disabled nsmenuitem that no needs to subclass or do code.
The nsmenuitem before to be child of menu itself is child of the main item for example "Save as..." is child of "File". Just select the parent item (in this example is File) and set off "auto enable menu items" in the menu ispector panel, and here you go!
Swift 3 answer:
I have a submenu item under the standard "View" menu called "Enable System Setup On Launch". I use the function below to enable or disable the menu item. Note: the view menu does need the "Auto Enable Items" in IB to be turned off.
func enableSystemSetupMenuItem(enabled:Bool) {
//set view menu item to enabled: value
//requires "Auto Enable Items" of "View" menu item to be turned off in IB
//because "View" menu is now turned off for "Auto Enable" we have to handle all
//of the "View" menu items ourselves
//just to avoid operating on menu separators I set all other menu items to TAG = -1
let main = NSApplication.shared().menu?.item(withTitle: "View")
let subMenuItems = main?.submenu?.items
for item in subMenuItems! {
if item.title == "Enable System Setup On Launch" {
item.isEnabled = enabled
} else if item.tag == -1 {
item.isEnabled = true
}
}
}
Try calling [myMenuItem setEnabled:NO] from a different place and making sure that it happens after the menu-containing nib is loaded. Maybe do it right in the subclassed NSMenu's awakeFromNib.

How to require explicit dismissal of a yui popup menu?

I have a page with a couple of widgets on it, each of which, when clicked, brings up a yui popup menu: If I click on widget 1, its menu comes up. If I now click on widget 2, widget 1's menu gets a hide event, and widget 2's menu gets a show event and comes up. I'd like to change this so that, when widget 1's menu is up, it must be explicitly dismissed by a click on the page background (and/or, perhaps, another click on the widget or the escape key) before the menu attached to widget 2 is allowed to appear.
I've set up some beforeShowEvent and beforeHideEvent handlers on the menus, hoping to be able to use some method (a global variable? ick) of keeping track of when a menu is present and showing or hiding accordingly, but it's not working -- these handlers can't tell the difference between a click on the page background and a click on widget 2 (at least, not as I've done it so far). Is there any way to do what I'm trying to do? Thanks!
I think that a combination of clicktohide: false
Boolean indicating if the Menu will automatically be hidden if the user clicks outside of it. This property is only applied when the "position" configuration property is set to dynamic and is automatically applied to all submenus.
and keepopen: true
Boolean indicating if the menu should remain open when clicked.
will take care of this.
http://developer.yahoo.com/yui/menu/#configref

Resources