Adding Menus To The Application#
Now it is time to consider adding some menus to your application. There are two basic ways that you can create a system of menus for your application:
Design a hierarchical series of panes using the
<menu-bar>
,<menu>
, and various menu buttons classes, and glue the elements of this design together in the correct order.Use a command table.
In this chapter, the first of these methods is demonstrated. For information about command tables, refer to Using Command Tables. Before discussing the first method listed above, the overall design of the menu system for the task list manager is discussed.
A description of the menu system#
Before implementing the menus for the task list manager, it is worth describing what you are going to implement. The menu system of the task list manager comprises four menus: a File menu, Edit menu, Task menu, and Help. Each of these menus contains a number of commands, as follows:
File menu The File menu contains four commands that operate on the files loaded into the task list manager. The Open command opens a new file. The Save command saves the currently loaded file to disk. The Save As command saves the currently loaded file to disk under a new name. The Exit command quits the task application completely.
Edit menu The Edit menu contains the standard clipboard commands: Cut, Copy, and Paste.
Task menu The Task menu contains two commands that operate on individual tasks. The Add command adds a new task to the list. The Remove command removes the selected task from the list.
Help menu In a full-blown application, you would use commands in the Help menu as one hook into your online help system (other hooks being provided by buttons in dialog boxes and the F1 key). In this application, the Help menu contains a single command that simply displays a simple About dialog for the application.
Creating a menu hierarchy#
As you might expect, creating a menu hierarchy in a frame definition is
a matter of defining a series of panes for the frame. At the top-most
level in the menu hierarchy is the menu bar itself. The menu bar
contains each menu defined for the application and each menu contains
the menu commands that themselves perform operations. Once the panes
have been defined, the menu bar needs to be included in the frame using
the menu-bar
clause.
First of all, you can create a pane that defines the menu bar itself as follows:
pane task-menu-bar (frame)
make(<menu-bar>,
children: vector(frame.file-menu,
frame.edit-menu,
frame.task-menu,
frame.help-menu));
Next, define the File and Tasks menus themselves:
pane file-menu (frame)
make(<menu>, label: "File",
children: vector(frame.open-menu-button,
frame.save-menu-button,
frame.save-as-menu-button,
frame.exit-menu-button));
pane edit-menu (frame)
make(<menu>, label: "Edit",
children: vector(frame.cut-menu-button,
frame.copy-menu-button,
frame.paste-menu-button));
pane task-menu (frame)
make(<menu>, label: "Task",
children: vector(frame.add-menu-button,
frame.remove-menu-button));
pane help-menu (frame)
make(<menu>, label: "Help",
children: vector(frame.about-menu-button));
Finally, you need to define the menu commands themselves. A command that
appears on a menu is defined as an instance of <menu-button>
, and so
there is a strong similarity between these buttons and some of the
buttons already defined. DUIM also generates mnemonics for each menu
item; thus, the items appear as File and Edit, and so forth. (Note
that the make-keyboard-gesture
function that appears below is defined
in Keyboard accelerators.)
// Commands in the File menu
pane open-menu-button (frame)
make(<menu-button>, label: "Open...",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"o", #"control"),
documentation: "Opens an existing file.");
pane save-menu-button (frame)
make(<menu-button>, label: "Save",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"s", #"control"),
documentation: "Saves the current file to disk.");
pane save-as-menu-button (frame)
make(<menu-button>, label: "Save As...",
activate-callback: save-as-file,
documentation: "Saves the current file with a new name.");
pane exit-menu-button (frame)
make(<menu-button>, label: "Exit",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"f4", #"alt"),
documentation: "Exits the application.");
//Commands in the Edit menu
pane cut-menu-button (frame)
make(<menu-button>, label: "Cut",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"x", #"control"),
documentation: "Cut the selection to the clipboard.");
pane copy-menu-button (frame)
make(<menu-button>, label: "Copy",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"c", #"control"),
documentation: "Copy the selection to the clipboard.");
pane paste-menu-button (frame)
make(<menu-button>, label: "Paste",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"v", #"control"),
documentation: "Paste the selection in the clipboard at the current position.");
//Commands in the Task menu
pane add-menu-button (frame)
make(<menu-button>, label: "Add...",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture
(#"a", #"control", #"shift"),
documentation: "Add a new task.");
pane remove-menu-button (frame)
make(<menu-button>, label: "Remove",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture
(#"d", #"control", #"shift"),
documentation: "Remove the selected task from the list.");
//Commands in the Help menu
pane about-menu-button (frame)
make(<menu-button>, label: "About",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"f1"),
documentation:
"Display information about the application.");
Once you have defined the menu bar and all the children that it is to contain, you need to activate the menu bar in the frame by including the following towards the end of the frame definition.
menu-bar (frame) frame.task-menu-bar;
The definitions of these menu buttons demonstrate two interesting new features: the use of keyboard accelerators, and the use of documentation strings.
Documentation strings#
Documentation strings let you provide brief online help for gadgets such
as menu buttons. You can specify a documentation string for any gadget
using the documentation:
init-keyword. Although you can make whatever
use you want of these strings, using the gadget-documentation
and
gadget-documentation-setter
methods, documentation strings for menu
buttons are used in status bars without any need for special action on
your part. If you display a menu and move the mouse pointer over the
items in the menu, then the documentation string defined for each item
is displayed in the status bar of the frame for as long as the mouse
pointer is over the menu item. It is generally good practice to supply
documentation strings for all the menu items in a frame. Documentation
strings for other gadgets become tooltips in Windows.
Keyboard accelerators#
Keyboard accelerators let you define a combination of keys that can be pressed in order to invoke the activate callback of a gadget. This means that you can access the functionality of an application without having to choose commands from menus using the mouse, and can make it much quicker to use an application you are familiar with.
To specify a keyboard accelerator, you need to specify an alphanumeric
character, or a function key, together with any modifier keys (such as
the CONTROL or ALT keys) that should be held down while the alphanumeric
character is pressed. You actually create a keyboard accelerator by
calling the make
method on <keyboard-gesture>
, though to make it a
little easier, define the function below, which is used in the
definition of each menu button.
define function make-keyboard-gesture
(keysym :: <symbol>, #rest modifiers)
=> (gesture :: <keyboard-gesture>)
make(<keyboard-gesture>, keysym: keysym, modifiers: modifiers)
end function make-keyboard-gesture;
Add this definition to the file frame.dylan.
The keyboard accelerators defined demonstrate the several useful points about keyboard accelerators:
Whenever possible, use standard keyboard accelerators for standard application commands on your platform. Here, you use CONTROL+O to open a file, CONTROL+S to save a file, and CONTROL+X, CONTROL+C, and CONTROL+V respectively for Cut, Copy, and Paste.
As well as standard alphanumeric characters, you can use function keys as keyboard accelerators.
As well as the more common CONTROL key, you can use the ALT and SHIFT keys as modifiers, though you should not use the SHIFT key as the sole modifier.
You can use more than one modifier key at once.
If you wish, you need not use any modifier keys at all, as is the case with the (slightly non-standard) keyboard accelerator for the About command.
Gluing the final design together#
You can now add the definitions of the menu bar, menus, and menu
buttons, to the definition of the <task-frame>
class, to give the code
shown below. At this stage, the only thing missing from the final
application are real callback functions. Callbacks are dealt with in
Adding Callbacks to the Application.
Note that the final definition of <task-frame>
includes the
definition of a slot: frame-task-list
. This takes an instance of
the class <task-list>
as a value, the default value being an empty
<task-list>
. Although it has not been referred to so far, this
class will be used as the basic data structure in which task lists are
stored, and a more complete description of these data structures is
given in Defining the underlying data structures for tasks. It
transpires that defining the frame-task-list
slot is essential for
some of the file handling routines that are described in Handling
files in the task list manager.
define frame <task-frame> (<simple-frame>)
slot frame-task-list :: <task-list> = make(<task-list>);
// definition of menu bar
pane task-menu-bar (frame)
make(<menu-bar>,
children: vector(frame.file-menu,
frame.edit-menu,
frame.task-menu,
frame.help-menu));
// definition of menus
pane file-menu (frame)
make(<menu>, label: "File",
children: vector(frame.open-menu-button,
frame.save-menu-button,
frame.save-as-menu-button,
frame.exit-menu-button));
pane edit-menu (frame)
make(<menu>, label: "Edit",
children: vector(frame.cut-menu-button,
frame.copy-menu-button,
frame.paste-menu-button));
pane task-menu (frame)
make(<menu>, label: "Task",
children: vector(frame.add-menu-button,
frame.remove-menu-button));
pane help-menu (frame)
make(<menu>, label: "Help",
children: vector(frame.about-menu-button));
// definition of menu buttons
// Commands in the File menu
pane open-menu-button (frame)
make(<menu-button>, label: "Open...",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"o", #"control"),
documentation: "Opens an existing file.");
pane save-menu-button (frame)
make(<menu-button>, label: "Save",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"s", #"control"),
documentation: "Saves the current file to disk.");
pane save-as-menu-button (frame)
make(<menu-button>, label: "Save As...",
activate-callback: save-as-file,
documentation:
"Saves the current file with a new name.");
pane exit-menu-button (frame)
make(<menu-button>, label: "Exit",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"f4", #"alt"),
documentation: "Exits the application.");
//Commands in the Edit menu
pane cut-menu-button (frame)
make(<menu-button>, label: "Cut",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"x", #"control"),
documentation: "Cut the selection to the clipboard.");
pane copy-menu-button (frame)
make(<menu-button>, label: "Copy",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"c", #"control"),
documentation: "Copy the selection to the clipboard.");
pane paste-menu-button (frame)
make(<menu-button>, label: "Paste",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"v", #"control"),
documentation:
"Paste the selection in the clipboard at the current position.");
//Commands in the Task menu
pane add-menu-button (frame)
make(<menu-button>, label: "Add...",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture
(#"a", #"control", #"shift"),
documentation: "Add a new task.");
pane remove-menu-button (frame)
make(<menu-button>, label: "Remove",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture
(#"d", #"control", #"shift"),
documentation:
"Remove the selected task from the list.");
//Commands in the Help menu
pane about-menu-button (frame)
make(<menu-button>, label: "About",
activate-callback: not-yet-implemented,
accelerator: make-keyboard-gesture(#"f1"),
documentation:
"Display information about the application.");
// definition of buttons
pane add-button (frame)
make(<push-button>, label: "Add task",
activate-callback: not-yet-implemented);
pane remove-button (frame)
make(<push-button>, label: "Remove task",
activate-callback: not-yet-implemented);
pane open-button (frame)
make(<push-button>, label: "Open file",
activate-callback: not-yet-implemented);
pane save-button (frame)
make(<push-button>, label: "Save file",
activate-callback: not-yet-implemented);
// definition of radio box
pane priority-box (frame)
make (<radio-box>,
items: $priority-items,
orientation: #"horizontal",
label-key: first,
value-key: second,
value: #"medium",
activate-callback: not-yet-implemented);
// definition of tool bar
pane task-tool-bar (frame)
make(<tool-bar>,
child: horizontally ()
frame.open-button;
frame.save-button;
frame.add-button;
frame.remove-button
end);
// definition of status bar
pane task-status-bar (frame)
make(<status-bar>, label: "Task Manager");
// definition of list
pane task-list (frame)
make (<list-box>, items: #(), lines: 15,
activate-callback: not-yet-implemented);
// main layout
pane task-layout (frame)
vertically ()
frame.task-list;
frame.priority-box;
end;
// activation of frame elements
layout (frame) frame.task-layout;
tool-bar (frame) frame.task-tool-bar;
status-bar (frame) frame.task-status-bar;
menu-bar (frame) frame.task-menu-bar;
// frame title
keyword title: = "Task List Manager";
end frame <task-frame>;