Documentation

Menu Bars, Menus, and Menu Items

Any reasonably modern user-interface offers various methods of accomplishing the same goals, such as keyboard commands, tool bar and form buttons, and menus. Some user-interface design guides go so far as to say that any functionality that is reachable via a tool bar button or a form button, should always provide a menu item to accomplish the same thing. Part of the reason for this is that many users may not be inclined to use a mouse, or under certain circumstances the user may not have a mouse available. Also, providing menus and menu items allows the user to look around in a (hopefully) well-sorted and logically devised set of various groups of functionality. It is an easy way to get to know a product if the menus provide a clear overview of what can be done with the program.

The wxWidgets-based menu support that is part of SIMPOL offers the usual menu capabilities: menus, sub-menus, and menu items that can also be either checkable or one of a group of options. An example menu program is show below:

Example 18.8. A wxmenu Example

function main()
  wxwindow w
  wxmenubar mb
  wxmenu filemenu, printmenu
  integer iErrnum
  string sResult

  iErrnum = 0
  w =@ wxwindow.new(0, 0, 640, 480, captiontext="Main test \
                    window", error=iErrnum)
  if w =@= .nul
    sResult = "Error number: " + .tostr(iErrnum, 10) + \
              " opening main window{d}{a}"
  else
    w.onvisibilitychange.function =@ quit

    mb =@ wxmenubar.new()
    filemenu =@ wxmenu.new()
    mb.insert(filemenu, "&File", name="filemenu")
    filemenu.insert("", "&New", enabled=.false, name="new")
    filemenu.insert("separator")

    printmenu =@ wxmenu.new()
    printmenu.insert("radio", "&Laser Printer", checked=.true, \
                     name="laserprinter")
    printmenu.insert("radio", "&Inkjet Printer", \
                     name="inkjetprinter")
    printmenu.insert("radio", "La&bel Printer", \
                     name="labelprinter")

    filemenu.insert("submenu", "&Printer", submenu=printmenu, \
                    name="printmenu")
    filemenu.insert("checkable", "Print Second &Copy", \
                    checked=.true, name="secondcopy")
    filemenu.insert("separator")
    filemenu.insert("", "E&xit{9}Ctrl+Q", name="exit")
    filemenu!exit.onselect.function =@ quit

    mb.setwindow(w)
    wxprocess(.inf)
  end if
end function sResult


function quit(type(*) me)
  wxbreak()
end function


The program above demonstrates most of the capabilities of the menu support in SIMPOL. A menubar (like all other SIMPOL GUI objects) exists indepently of its representation in a window. A menubar can be assigned to a window and then be replaced by another. Looking at the preceding program, we first create a menu bar and then an empty menu that we insert into the menu bar. It is not necessary to do it this way, we could just as easily have filled the menu first and then inserted it into the menu bar. Next we insert a menu item that is set to disabled from the start. Following this, a separator is inserted and then a printer menu is created that contains three radio items, only one of which can be selected and we pre-select the first one. The printer menu is then inserted into the file menu as a sub menu. This is then followed by a checkable item, entitled "Print Second Copy". Next we add another separator and the item to exit the program. This item is also given a keyboard accelerator (by adding a tab character and the desired accelerator combination). In closing the quit() function is assigned to the onselect event of the exit item. Finally the menu bar is set into the window and the program then waits for events. A picture of the menu can be seen below:

An example of a wxmenu

An example of the wxmenu in action.

The trickiest part of working with menus may be learning how to correctly address the various parts. The containership model of the menus is as follows: wxmenubar contains objects of type wxmenubarentry. That contains objects of type wxmenu. The wxmenu objects contain objects of type wxmenuitem, which can themselves contain objects of type wxmenu. So, assuming that there is a variable called mb and the menu bar from the program above, to access the labelprinter item, the following code would be used:

mb!filemenu.menu!printmenu.submenu!labelprinter

The member operator (!) is used to access the wxmenubarentry that contains a reference to the filemenu menu object. The member operator is again used to access the wxmenuitem represented by the printmenu object, and then accesses the labelprinter item of the sub menu by using the member operator on the submenu property of the printmenu menu item.

Creating menus by hand can be quite boring, but until there is a menu editor available, the menu editor provided by the older Superbase product (including the downloadable demo) can be used, together with the conversion utility provided for converting Superbase menu programs to SIMPOL source code. The menu conversion utility is in the utilities directory and the program is called: ngmengen.sbp.