UserGuide

Desktop Menus

From Xojo Documentation

Most apps have a Menu Bar. The location of the menu bar varies by platform. On Mac, there is only a single menu bar and it appears at the top of the screen. On Windows, each window can have its own menu bar. Linux can work either way, depending on the distribution.

When you create a desktop project, a default menu bar is added automatically, called MainMenuBar. For most applications this is usually sufficient. You create additional menu bars by adding them to your project using the Insert ↠ Menu Bar command on the toolbar or menu.

Menu Editor

The Menu Editor is used to create your menu bars. A menu bar consists of top-level menus (called menus) and their items (called menu items). Regardless, both are subclasses of MenuItem.

The default menu bar, MainMenuBar, has two menus: File and Edit, each with their own menu items.

Using Menus with Desktop Apps

Use the toolbar buttons to add menus and menu items.

Menu items that are added to a menu have several properties that are useful. In particular, you can set the Text for the menu (this is what appears in the menu itself) and an icon for the menu. By default menu items have their AutoEnable property set to ON. This means that the menu will automatically be enabled when the menu is clicked, if the menu handler (see below) contains code. Lastly, you can specify the keyboard shortcut for the menu.

Keyboard Shortcuts

You can assign keyboard shortcuts to menu items, but remember that the operating system looks for a shortcut starting from the leftmost menu. That means that if you assign the same keyboard shortcut to two different menu items, one of them won’t work. There are also several specific keyboard shortcuts that are reserved for specific functions.

You do not have to assign keyboard shortcuts to all menu items.

In the Behavior section, specify the Key to use for the keyboard shortcut. You will also typically turn on the MenuModifier property which is "Command" on macOS and "Control" on Windows/Linux. AlternateMenuModifier is "Shift" on all platforms.

To to set a keyboard shortcut for "Command-S" (macOS) or "Control-S" (Windows/Linux) you would set the Key to "S" and turn the MenuModifier to ON. There are several other modifiers you can also use with the shortcut:

  • HasMacOptionKey: Use the Option key on macOS. Turning this on has no effect on Windows/Linux.
  • HasMacControlKey: Use the Control key on macOS. Turning this on has no effect on Windows/Linux, which use the Control key by default when MenuModifier is turned ON.
  • HasAltKey: Uses the Alternate (or Alt) key on Windows/Linux. Turning this on has no effect on macOS.

You can also use these special values in the Key property to set non-alphanumeric shortcut keys:

F1-F15, Tab, Enter, Space, Del (Delete), Return, Bksp (Backspace), Esc, Clear, PageUp, PageDown, Left, Right, Up, Down, Help, and Ins (Insert)

Menu Inspector for F4 Shortcut Key
You can also change the shortcut key in code by using the ShortcutKey property for the MenuItem. This example sets the HelpAbout MenuItem shortcut to be F5:
HelpAbout.ShortcutKey = "F5"

If you wanted to set its shortcut to be Ctrl+1 you would do this:

HelpAbout.ShortcutKey = "Ctrl-1"

Refer to the MenuItem page for more details on how you can set up shortcut keys in code.

Accelerator Keys

Windows and Linux also have the concept of keyboard accelerators. In addition to the keyboard equivalent, which work on all platforms, you can also add a keyboard accelerator for each menu and menu item. When you designate a key as the keyboard accelerator, it is underlined in the menu name or menu item. The user can display the menu or invoke the menu item by holding down Alt and pressing the accelerator key. With a comprehensive system of accelerators, a user can use the menu system without using the mouse at all.

To designate the accelerator key, precede the letter by an ampersand ("&") in the menu or MenuItem Text property. For example, if you are creating a menu named "Actions" and you want to make the keyboard accelerator the “A”, you would enter “&Actions” in the Text property. To make the "t" the accelerator key, enter "Ac&tions".

To display an ampersand in the menu, you need to double it like this: "&&".

Accelerators do not work on Mac and are not displayed, but you still need to use a double-ampersand “&&” to display a single ampersand in a menu.

Adding a Submenu

Submenus are menu items that display an additional menu to their right. The menu item itself is not selectable, clicking it displays the submenu whose items can be selected.

If you wish, you can continue to add a submenu to an existing submenu, creating a three-level hierarchical menu system. However, submenus can be difficult to navigate for most users. Deeply nested submenus are not a good design because they are hard for most users to navigate and make it harder to find menu items.

Adding a Menu Item to the Mac Apple and Application Menus

Mac apps add a new menu between the Apple menu and the standard File menu. When you create a Mac app, this menu gets the Mac App Name entered in the macOS Build Settings.

Although both the Apple menu and the Application menu appear in the Menu Editor, you cannot directly add menu items to them. Instead you use these MenuItem subclasses so that the menu is moved at runtime:

  • PrefsMenuItem: For your preferences menu item, put the menu item where you want it to appear under Windows and Linux and set its Super class to PrefsMenuItem. A MenuItem based on the PrefsMenuItem class will be moved automatically to the Application menu when the Mac app runs.
  • ApplicationMenuItem: Similarly, use the ApplicationMenuItem subclass for any menu items that should appear in the application menu on Mac.
  • Refer to Special Menus below for additional information.

The Exit (or Quit) menu item

The QuitMenuItem class is intended only for the Quit (or Exit) menu item. When a QuitMenuItem is selected, your application quits. It also moves the menu item to the application menu for Mac apps.

Moving Menus and Menu Items

A menu item can be moved to a new position in the menu by dragging the menu item.

You can also move menus within the menu bar. In a similar fashion, drag a menu in the menu bar and move it to the left or right. Drop the menu when it is between the desired menus.

Converting a Menu Item to a Menu

To convert a Menu Item into a Menu, select the menu item and then click the Convert To Menu button in the Menu Editor toolbar. The menu item is then removed from its menu and appears in the menubar. From there you can drag it to another position in the menu bar if you wish.

Removing Menu Items

To remove a menu item from a menu, select it in the menu (not the Navigator) and press the Delete key or choose Edit->Delete from the menu.

Adding A Menu Item Separator

Menu item separators are lines that appear in between menu items to logically group items together. To add a menu item separator, simply select a menu item and click the Add Separator button in the Menu Editor toolbar. The separator will appear just below the selected menu item. If you wish, you can drag it vertically to a new location.

Default Menu Bar

The default menu bar, MainMenuBar, is automatically set as the MenuBar for the App and for each new window that you create. The menu that appears for a window depends on the platform being used. Since each window on Windows has a menu bar, you must specify the MenuBar property on the window in order for a menu bar to appear. Linux is similar.

On Mac, if the MenuBar property for a window is not specified, then the App.MenuBar property is used for the menu instead.

Implicit Instance

You may have noticed that you can refer to a MenuBar globally by its name. This is because an "implicit instance" is automatically created for you. If you use this global name, then you get the same MenuBar instance everywhere you use it. If you modify the MenuBar in code, the modification will appear everywhere the MenuBar is used.

If you would rather have separate instances of the MenuBar, you should assign it in code manually in the Window.Open event:

Self.MenuBar = New MainMenuBar

With Mac apps, if a window does not have a MenuBar specified, then the window uses the MenuBar specified on Application.MenuBar. With Windows and Linux, if a window does not have a MenuBar specified, then the window displays without a MenuBar even if one is specified in Application.MenuBar.

Menu Handlers

When a user clicks a menu, a Menu Handler gets called. Menu Handlers are added to your window manually for the specific menu items that the window needs to handle.

Click the Add button on the Window and select "Menu Handler". This adds an empty Menu Handler to the window. In the Inspector you can change the MenuItem Name (by using the ComboBox) to select the name of an existing Menu Item in the menu bar that is assigned to the window.

In the menu handler itself, you write the code that should run when the menu is selected. If the menu item has its AutoEnable property set to True (or ON), then the menu will automatically be enabled once you add code. This is how you will most often work with menus.

You can also manually control when menu items are enabled. To do this, its AutoEnable property must be False (or OFF). In this case, the menu item will always appear as disabled. In order to enable it, you have to add code to the MenuSelected event handler for the containing Window. For example, you might have code that enables a Save menu item only when the document has been changed:

If Self.ContentsChanged Then
SaveMenu.Enable
End If

To enable a menu item, you can call its Enable method or set its Enabled property to True.

Dynamic Menus

Sometimes you may need to create a menu dynamically. Examples of this include a menu that shows recently opened files, a menu that shows the names of the currently open windows or a menu that shows the name of available fonts.

To do this you create a MenuItem subclass and use the Append and Insert methods of the MenuItem class to add instances of your subclass to the menu bar. Here is an example that adds a Font menu. Create a new MenuItem subclass (called FontMenuItem). In its MenuItemSelected event add this code:

MessageBox("Selected Font: " + Self.Value)

Reminder: To create a MenuItem subclass, create a new class and set its Super to MenuItem.

Now you can use this subclass to create a font menu. In the Open event of the default window, add this code:

mFontMenu = New MenuItem("Font")
MenuBar1.AddMenu(mFontMenu)
Var fCount As Integer = FontCount
Var fMenu As FontMenuItem
For i As Integer = 0 To fCount - 1
fMenu = New FontMenuItem(Font(i))
mFontMenu.AddMenu(fMenu)
Next

This code creates a new top-level menu called "Font" and adds to it a menu item for each font that is installed on your system. When you run the application and click the Font menu, you will see a list of all the fonts. Click on a font and a dialog appears telling you the name of the one you clicked.

Menu Control Set

Another way to create menus dynamically is to use a Menu Control Set, although the method described above (using Append) is preferred. To create a Menu Control Set, add an item to a menu and in the Inspector set its Index value to a 0 (normally it will be blank).

When you add the Menu Handler for this menu item you will now see that it has a parameter: index As Integer. You can use this to tell which menu item was selected when there are multiple ones.

When you create a new instance of this menu item it will add a new entry to the menu in which it is contained. For example, if you have called the menu item WindowItem then you can use this code to add another entry to the menu:

Var m As New WindowItem
m.Value = "Menu Text"

Creating a Recent Items Menu

You can use the above dynamic menus technique to create your own "Recent Items" menu. For example, the steps below describe how to create Recent Items menu for tracking recently opened files.

Create a class called "OpenRecentMenuItem" and set its Super to MenuItem.

In its MenuItemSelected event add some code to display the name of the file that was selected (it's stored in the Tag for the MenuItem):

MessageBox("Recent Item: " + FolderItem(Me.Tag).NativePath)

Return True

In your MainMenuBar, add a menu to the File menu and call it "FileOpenRecent" and set its Text to "Open Recent" and its Submenu property to ON. With that in place, you can now add menu items to FileOpenRecent when a new file is open. As a test, you can add a button to a Window with code that lets the user select a file and then adds the file they selected to the FileOpenRecent menu:

// Choose a file
Var file As FolderItem = FolderItem.ShowOpenFileDialog("")
If file Is Nil Then Return

// Make one of our Open Recents subclasses and
// set it's text to being something (in this
// case, it's a number that we'll increment
// below)
Var recentItem As New OpenRecentMenuItem(file.NativePath, file)

// Put the latest item at the top of
// of the open recents menu on
// the File menu.
FileOpenRecent.AddMenuAt(0, recentItem)

To remove an item from the FileOpenRecent menu, just remove the last one in the list like this:

// If the recent menu has items, then remove the last one
If FileOpenRecent.Count > 0 Then
FileOpenRecent.RemoveMenuAt(FileOpenRecent.Count - 1)
End If

You can find a working version of this in the Xojo examples here: Examples/Desktop/Menus/OpenRecentMenu

Contextual Menus

Contextual menus are menus that appear when the user choose to see them. This is most often by right-clicking (or Ctrl-clicking on Mac) somewhere, but contextual menus can also be displayed using a keyboard shortcut on Windows.

All controls have two events that you use to create and handle contextual menus: ConstructContextualMenu and ContextualMenuAction.

In ConstructContextualMenu, you can dynamically create the contextual menu (by appending menu items to the base parameter). Return True from the event to display the contextual menu:

base.AddMenu(New MenuItem("Test 1"))
base.AddMenu(New MenuItem("Test 2"))
base.AddMenu(New MenuItem("Test 3"))

Return True

In ContextualMenuAction, you can test the HitItem parameter to perform actions:

If hitItem <> Nil Then
MessageBox(hitItem.Value)
End If

Return True

Special Menus

There a few menu items that should have specific names in order for you to get automatic OS-provided functionality.

Help

If you add a Help menu, you should make sure its text property is set to just "Help" (or the localized equivalent) to allow Mac to automatically provide the Spotlight "Search" menu item that lets the user search all the menu items for specific text.

Edit

A top-level menu with the text "Edit" (or the localized equivalent), automatically get "Start Dictation" and "Insert Special Characters..." menus added on Mac.

Additionally, items in the EditMenu should also retain the names they are given by default if you want them to automatically work in TextFields, TextAreas and other controls. These names are: EditCut, EditCopy, EditPaste, EditClear, EditSelectAll, EditUndo and EditRedo. You can change their Value property, but do not change the Name property.

ApplicationMenuItem

Desktop applications usually have an About menu that displays an About window with the name of the application and its copyright information. On Windows and Linux, this About menu appears in a Help menu. You can add a help menu by clicking the “Add Menu” button in the Menu Editor to add a new top-level menu. Name this menu "HelpMenu" and drag it so that it is rightmost. Now you can add the About menu item to the help menu.

On Mac, the About menu instead should appear in the Application menu rather than the help menu. To make it automatically move to the Application menu, you add your About menu to the Help menu so that it appears as expected for Windows and Linux. And then change its Super property in the Inspector from “MenuItem” to “ApplicationMenuItem”. Any menu that uses this class will be automatically moved to the Application menu when it is run on macOS.

PrefsMenuItem

Similarly, the Preferences menu is also located in the Application menu on Mac. On Windows and Linux, the Preferences menu is often located in the Edit menu and is instead called “Options”.

There is always a fixed Preferences menu in the Application menu on Mac, but is is disabled by default. To attach your Preferences menu to it, you set its Super property to “PrefsMenuItem”. Only one menu in your project should be set to PreferencesMenuItem.

To change the name of the Preferences menu for Mac and Windows/Linux, you use a constant. By default there are several constants on the App class that control the text for Edit ↠ Clear/Delete and File ↠ Quit/Exit. You can add another to handle preferences.

To do so, add a new constant and call it kPreferences, setting its default value to "&Options...". In the Constant Editor, click the “+” to add a new entry and select “macOS” as the Platform. For the Value, enter "Preferences..."

This creates a constant that uses the value "&Options..." on Windows and Linux, but the value "Preferences..." when run on Mac.

Now that you have created the constant, you can use it as the text of the menu. Add a new MenuItem to the Edit menu and set its Text property to “#App.kPreferences”. This tells it to use the value of the constant. Also set its Super to “PrefsMenuItem”.

You can use the preview buttons in the Menu Editor toolbar to see the text change between Mac, Windows and Linux. In addition, when you run the application on Mac, the Preferences menu appears in the Application menu instead of the Edit menu.

See Also

MenuBar, MenuItem classes; UserGuide:Menu Editor topic; Using Menus with Desktop and Web Apps. Using Menus with Desktop Apps videos; GettingStarted:Everything About Desktop Menus lesson