[Home]  [Prev]  [Next]    A guide, a tutorial for developing well-designed cross-platform applications

3. Menus

First of all only top level menus have a menu bar, child windows don't. A child window usually does just a specific task, easily handled by some buttons. If this isn't sufficient for a child window it should be changed into a top level window itself.

Menu entries provide access to any action or command an application provides within a graphical user Interface (GUI). While each action usually has a menu or a submenu entry, it is common practice to move seldom used actions into dialog boxes, etc. Menu entries which do this are shown with "..." appended.

Menu entries are a kind of minimal help which show which actions an application provides. Therefore actions (i.e. their menu entries) which don't apply are still shown but disabled.

Menu entries should be generally being static, inserting and deleting entries should only be performed if other conditions (such as a changing set of files) require it. Entries which are currently unavailable should be disabled.

Menus have a limited count of entries ranging from 1 to about 15 depending on the size of the application top level window. A larger window may allow for more a smaller for less entries. To reduce the size of a menu, submenus for related entries may be used. Or if the functionality allows a menu may be split into several. Be carefully it's obvious for the user to find out where an entry is located.

There is a convention of several standard menu entries in a defined structure which an application should follow. If a standard action is never available in an application the corresponding entry should be removed. Such entries can't be reused by other actions unless these actions follow a similar principle.

Menus are very important for the GUI of an application. Since the menus for MacOS diverts rather heavy some platform specific precautions have been taken.


3.1 File menu

This is a standard menu, always the first. Most of the entries in this menu are also standard, sometimes some might be missing and some might be additional (i.e. not all application have recent file entries). Usually this menu looks rather similar between different applications.

In case an application doesn't act on files this menu might be removed or renamed to a more appropriate name. I.e. games should use a "Game" menu, databases should use a "DB" menu.

The entries in the file menu have their defined command keys as shown in the sample, if they have any. These keys are reserved and may not used elsewhere, not even if the corresponding menu entry is missing.

Sample code

#include "filelist.h"    // FileList control

const wxString RECENTFILES = _T("Recent Files/file");
...

BEGIN_EVENT_TABLE (AppFrame, wxFrame)
    ...
    EVT_MENU (wxID_SAVE,             AppFrame::...)
    EVT_UPDATE_UI (wxID_SAVE,        AppFrame::OnFileSaveUI)
    ...
    EVT_MENU_RANGE (wxID_FILE1,
                    wxID_FILE9,      AppFrame::OnFileRecents)
    ...
END_EVENT_TABLE ()

AppFrame::AppFrame (...
    ...
    m_recents = new wxFileList (4,
                                myFLIST_STARTSEPARATOR |
                                myFLIST_CTRL_SHIFT_KEYS |
                                myFLIST_LRUORDER);
    m_recents->Load (RECENTFILES);
    ...
    m_menuBar = new wxMenuBar;
    CreateMenu ();
    ...
    }

AppFrame::~AppFrame () {
    ...
    delete m_recents;
    ...
}

void AppFrame::OnClose (wxCloseEvent &event) {
    ...
    m_recents->Save (RECENTFILES);
    ...
}

void AppFrame::OnFileRecents (wxCommandEvent &event) {
    wxArrayString fnames;
    fnames.Clear();
    fnames.Add (m_recents->Item (event.GetId() - wxID_FILE1));
    FileOpen (fnames);
}

void AppFrame::OnFileSaveUI (wxUpdateUIEvent &event) {
    event.Enable (...); // enables/disables the menu entry for wxID_SAVE
}

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuFile = new wxMenu;
    menuFile->Append (wxID_NEW, _("&New\tCtrl+N"));
    menuFile->Append (wxID_OPEN, _("&Open ...\tCtrl+O"));
    menuFile->Append (wxID_SAVE, _("&Save\tCtrl+S"));
    menuFile->Append (wxID_SAVEAS, _("Save &as ...\tCtrl+Shift+S"));
    menuFile->Append (wxID_CLOSE, _("&Close\tCtrl+W"));
    m_recents->UseMenu (menuFile, wxID_FILE1, -1, 24, myPARTIAL_BEGIN | myPARTIAL_END);
    menuFile->AppendSeparator();
    menuFile->Append (myID_PROPERTIES, _("Proper&ties\tCtrl+I"));
    menuFile->AppendSeparator();
    menuFile->Append (wxID_PRINT_SETUP, _("Print Set&up ..."));
    menuFile->Append (wxID_PREVIEW, _("Print Pre&view\tCtrl+Shift+P"));
    menuFile->Append (wxID_PRINT, _("&Print ...\tCtrl+P"));
#ifndef __WXMAC__
    menuFile->AppendSeparator();
#else
    wxApp::s_macExitMenuItemId = wxID_EXIT;
#endif
    menuFile->Append (wxID_EXIT, _("&Quit\tCtrl+Q"));
    ...
    m_menuBar->Append (menuFile, _("&File"));
    ...
    SetMenuBar (m_menuBar);
}

Get the "wxFileList" from here and here.


3.2 Edit menu

This is a standard menu, always the second. All the clipboard entries are standard, the others are optional depending on the functionality of the application. Also depending on the application there might be several additional entries.

If there were lots of entries in this menu, all the find and replace entries could be packed into a submenu. This applies also to the additional application specific entries.

The preferences entry depends highly on the used platform and might be located in a tools menu (Windows) or in the file menu (Macintosh). It is up to you if choose the native form or the cross-platform form but never us a native one on another platform. Cross platform applications should keep the preferences as the last entry in the edit menu.

A similar problem arises with the shortcut key for the "Redo" entry, it's either Ctrl-Y (Windows) or Ctrl-Shift-Z (all others). Do the same as above.

The standard entries in the file menu have their defined command keys as shown in the sample, if they have any. These keys are reserved and may not used elsewhere, not even if the corresponding menu entry is missing.

Sample code

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuEdit = new wxMenu;
    menuEdit->Append (wxID_UNDO, _("&Undo\tCtrl+Z"));
#ifndef __WXMSW__
    menuEdit->Append (wxID_REDO, _("&Redo\tCtrl+Shift+Z"));
#else
    menuEdit->Append (wxID_REDO, _("&Redo\tCtrl+Y"));
#endif
    menuEdit->AppendSeparator();
    menuEdit->Append (wxID_CUT, _("Cu&t\tCtrl+X"));
    menuEdit->Append (wxID_COPY, _("&Copy\tCtrl+C"));
    menuEdit->Append (wxID_PASTE, _("&Paste\tCtrl+V"));
    menuEdit->Append (wxID_CLEAR, _("&Delete\tDel"));
    menuEdit->AppendSeparator();
    menuEdit->Append (wxID_FIND, _("&Find\tCtrl+F"));
    menuEdit->Append (myID_FINDNEXT, _("Find &next\tF3"));
    menuEdit->Append (myID_REPLACE, _("Rep&lace\tCtrl+H"));
    menuEdit->Append (myID_REPLACENEXT, _("Replace &again\tShift+F4"));
    menuEdit->AppendSeparator();
    menuEdit->Append (myID_GOTO, _("&Goto\tCtrl+G"));
    menuEdit->AppendSeparator();
    menuEdit->Append (wxID_SELECTALL, _("&Select all\tCtrl+A"));
#ifndef __WXMAC__
    menuEdit->AppendSeparator();
#else
    wxApp::s_macPreferencesMenuItemId = myID_PREFS;
#endif
    menuEdit->Append (myID_PREFS, _("Pr&eferences ..."));
    ...
    m_menuBar->Append (menuEdit, _("&Edit"));
    ...
    SetMenuBar (m_menuBar);
}


3.3 View menu

This is an optional menu, always the third if present. All the entries in this menu are optional.

Sample code

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuView = new wxMenu;
    menuView->AppendCheckItem (myID_TOOLBARS, _("Tool &bar"));
    menuView->AppendCheckItem (myID_STATUSBAR, _("Status b&ar"));
    ...
    m_menuBar->Append (menuView, _("&View"));
    ...
    SetMenuBar (m_menuBar);
}


3.4 Application specific menus

These are optional menus. Usually they have some common names like "Extra", "Tools", etc.

Sample code

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuExtra = new wxMenu;
    ...
    m_menuBar->Append (menuExtra, _("E&xtra"));
    ...
    SetMenuBar (m_menuBar);
}


3.5 Window menu

This is an optional menu, always the second last if present.

Sample code

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuWindow = new wxMenu;
    menuWindow->Append (myID_PAGEPREV, _("&Previous\tCtrl+Shift+Tab"));
    menuWindow->Append (myID_PAGENEXT, _("&Next\tCtrl+Tab"));
    m_files->UseMenu (menuWindow, myID_WINDOW1, -1, 24, myPARTIAL_BEGIN | myPARTIAL_END);
    menuWindow->AppendSeparator();
    menuWindow->Append (myID_WINDOWS, _("&All windows ..."));
    menuWindow->AppendSeparator();
    menuWindow->Append (myID_FRAMELAYOUT, _("Save current layout"));
    ...
    m_menuBar->Append (menuWindow, _("&Window"));
    SetMenuBar (m_menuBar);
}


3.6 Help menu

This is a standard menu, always the last.

Sample code

void AppFrame::CreateMenu () {
    ...
    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append (wxID_HELP, _("&Index\tF1"));
#ifndef __WXMAC__
    menuHelp->AppendSeparator();
#else
    wxApp::s_macAboutMenuItemId = wxID_ABOUT;
#endif
    menuHelp->Append (wxID_ABOUT, _("&About ...\tShift+F1"));
    ...
#ifdef __WXMAC__
    wxApp::s_macHelpMenuTitleName = _("&Help");
#endif
    m_menuBar->Append (menuHelp, _("&Help"));
    SetMenuBar (m_menuBar);
}


3.7 Menu shortcuts

The shortcuts for standard menu entries are always reserved and may not be used for other entries to prevent any miss understandings. I.e. if there is no print action and therefore the "Ctrl-P" not used, it should not be used for another action.

Shortcuts shouldn't be translated in international version as long as the used character set allows it. I.e. the action print, in German "Drucken" still has the shortcut "Ctrl-P". Shortcuts are for power users who know them by heart. It doesn't matter much which shortcut is used but that it's always the same.

Shortcuts always follows the platform dependent notion, this means "Ctrl" is on Windows the control key, on Linux/Unix the meta key and on Mac the command key.