$Revision: 1.1.1.1.8.3 $
Basics
Menus provide a set of commands for your application's users. The menu bar is located
below the title bar of your window. Note: Only top-level windows can have a menu bar. A top-level window is a window that is owned by the screen or another top-level window and is not a child window. You can add a set of menus to your menu bar. In this case there are two: File and Edit. Each menu can have zero or more menu-items. For the File menu, there are 6 menu items. The horizontal line is a special type of menu-item called a separator. The menus of a menu bar are of type pull-down-menu because as you click on the title of the menu its menu items are displayed. |
In the title of a menu or menu item, you can define an access key. An access key lets your user open the menu or access the menu item using the keyboard. If you wanted to open the File menu, you would press ALT+F. To invoke the Exit menu item, you would press "x". Within a particular menu, you should have each access key be unique and mnemonic as possible.
Shortcut keys are another way for your users to access your menu commands. A shortcut key runs a menu item immediately when pressed. You should assign shortcut keys to frequently used commands. Windows reserves some shortcut keys for cross-application commands like Cut, Copy and Paste.
You may occasionally want a floating menu of commands presented to your
user. The most common implementation of floating (pop-up) menus is shortcut menus.
Shortcut menus are usually displayed after a right mouse click, and often provide
context-sensitive commands. In general, all commands that are displayed on a shortcut menu
should be available through other means in your user interface. In Allegro CL's Integrated
Development Environment, a pop-up menu is displayed when you use the Search |
Complete Symbol command. Floating menus are implemented using pop-up-menus. A pop-up-menu is similar to a pull-down-menu because it can have one or more menu items. The difference is that a pop-up menu is not activated by clicking on the menu's title or by using the ALT key. Usually, pop-up-menus are opened by using the right mouse click |
|
The Menu Editor only works with pull-down menus and cannot be used to define pop-up menus. Pop-up menus must be defined programmatically. |
Add a menu bar to your form by toggling its menu property.
Setting the menu property to a non-nil value causes the default menu bar to be added to
your form and displays the Menu Editor to modify it. As you work in the Menu Editor, the menu bar displayed on your form is automatically updated. You can click on Cancel at any time to discard your changes. Everything that you can do in the Menu Editor, you can also do programmatically and at run-time. The Menu Editor is provided as a convenience for specifying your initial menu bar. |
|
Once you finish specifying the menu bar, use the OK button to accept your changes. You can use the menu bar on your form to verify the contents of the menu items. The form's menu bar does not run your code. Instead clicking on a menu item displays the source code for the value of the menu item. This is analogous to the event handlers of your controls. In the form, the control does not execute the code for the event handler. If you want to test the code associated with your menu bar, then you should run the form. |
The first field in the Menu Editor is the name of the menu bar. You can use the name of the menu bar to uniquely identify it programmatically. The scrolling list in the middle of the Menu Editor determines all the menu items on your menu bar. The Menu Editor gives you a rudimentary starter menu and adds it to your form. You use the menu bar displayed on your form as a reference. The indentation in the menu item list reflects the hierarchy of your menu items. Notice that the File menu item is not indented. Therefore, it appears in your menu bar. The New menu item is indented under the File menu and appears as a menu item on the File menu.
The Menu Editor has a toolbar for adding, deleting, and arranging the menu items.
Insert | The Insert button adds a menu item after the selected menu item. If you want to add a menu item as the first in the menu bar, then you would select the first menu item and click on the Insert button. Then, you would need to click on the Move Up (up arrow) button to move it to the start of the menu bar. |
Delete | The Delete button deletes the selected menu item and all of its sub-items. If the File menu item is selected when the Delete Item button is pressed, then the File menu item will be deleted as well as the New menu item, Open menu item, etc. |
Delete All | The Delete All button removes all of the menu items in the menu bar. It is possible to have a menu bar with no menu items. |
Arrow Buttons | The arrow buttons let you reorder the selected menu item and reposition the menu item in the hierarchy. You can use the Indent and Unindent buttons to define cascading menus. |
The Menu Item group is updated as a menu item in the list is selected.
Item Name | The name of a menu item can be used programmatically to find the menu item. For the New menu item, its name is :new-text-editor. |
Title | The title of the menu item is a string. The naming conventions are that
words should be capitalized, and the title should be as concise as possible. You can indicate the access key of the menu item by placing "~" before the desired access-letter in the title. For the New menu item, its access key is "N". For menu items that need further input to complete, their titles should end in "...". For example, Edit | Find needs to get to the search string before it can be executed. Therefore, its title is "~Find...". Make sure that all the access keys within a menu are unique. You can re-use access keys across menus. As a special case, you can define separators by specifying "-" as the title of the menu item. Menu items that are separators are drawn as a horizontal line across the width of the menu. When you are working programmatically, there is a menu-separator variable that you can use rather than creating new menu items every time. The user cannot click a separator menu item. Separators are used as visual cues to grouped (related) menu-items. |
On Click | You can specify an on-click event handler only if the menu item has
sub-items. Otherwise, the on-click event handler is not valid. The on-click event handler
of a menu is the function that is called when one of its sub-items is clicked on. Using
the File menu as an example, its on-click event-handler is funcall-menu-item-with-window, which is the
default on-click event handler. If the user clicks on the New menu item,
then the on-click event handler of the File menu is executed with the New
menu item passed as an argument. When the user clicks on the New menu item, the funcall-menu-item-with-window function receives the File menu, the New menu item, and the current window as arguments. Then, funcall-menu-item-with-window uses the value of the New menu item as a function and calls it with the current window or the frame-child of the current window if it has one. The value of the New menu item is new-text-editor. So, the function, new-text-editor, would be called with the current window or its frame-child. |
Value | You can specify the value of a menu item when it does not have sub-items.
Otherwise, the value of the menu item is the menu containing its sub-items. Therefore, the
value of the File menu item is an instance of menu named :file-menu. When a menu item does not have sub-items, then the value of a menu item can be anything. Usually, the value of a menu item is the symbol name of a function to invoke when the menu item is clicked on. In many applications, there is a Window menu with a list of all the open windows. For this case, you may decide that the value of the menu item is the window. Then, you would need to define a different on-click event handler for the Window menu, which would retrieve the value of the selected menu item and call select-window on that value. |
Synonym | There are four fields associated with the synonym. The synonym fields let you specify the shortcut key of your menu item. The synonym drop down list lets you pick the letter, number or function key of the shortcut key. You can check or uncheck the CTRL, ALT, and SHIFT boxes depending on whether or not you want those modifier keys pressed to execute this menu item. Make sure that all your shortcut keys are unique for the entire menu bar. Otherwise, it is arbitrary which command will be invoked when the user presses the shortcut key combination. |
Available | By default, a menu item is available. Available menu items can be clicked on by the user. Unavailable menu items are displayed as dimmed and cannot be clicked on by your user. |
Selected | By default, a menu item is not selected. Selected menu items are displayed with a check mark before the title. Selected menu items indicate to your user that something is "on" or displayed. The View | Status Bar command in the Integrated Development Environment is an example of a menu item that toggles its selected state. |
Help String | This is the string that is displayed when the user points to a menu item before clicking on it. By default, the help string of the selected menu item is displayed in the status bar of your window. |
There are three possible actions for associating code with your menus.
The most common requirement is linking code to when the user invokes a menu command. There are several different ways that the user can invoke a menu command.
Whatever way that the user invokes a menu command, all the code funnels through handle-menu-on-click. The arguments to handle-menu-selection are the menu, menu item or its id, and the current window. If you want to customize the behavior of all menu commands, then you could subclass menu and add a method to handle-menu-selection for your class of menus.
handle-menu-selection does the following steps.
The on-click event handler of a menu is invoked whenever the user invokes one of its
commands by clicking, shortcut keys, etc. You can specify the on-click event handler of a
menu to be any function that you want. The default on-click event is funcall-menu-item-with-window.
funcall-menu-item-with-window has
arguments of the menu, selected menu item, and current window. You can specialize the
behavior of this function by adding a method specialized for your class of menus.
Typically, you can use the supplied behavior and specify values for the menu items
corresponding to your functions. funcall-menu-item-with-window
does the following steps.
When the user points at a menu on your menu-bar, the menu is opened. Also when the user does a right click, a shortcut menu may be opened. You can add code to specialize the behavior of opening a menu.
One example of specialized open behavior is the Edit menu in the Integrated Development Environment. Before the Edit menu is opened, you should determine if anything is on the clipboard to paste. That way, you can make the Paste menu item unavailable before the user sees the menu.
about-to-show-menu is called whenever a menu is opened. The arguments to about-to-show-menu are the current window and the menu to open. You can add methods specialized on your window and/or menu classes depending on your requirements. The default behavior does nothing.
The user can point to menu items in a menu before clicking. As the user scans through commands, you can provide help messages and other information to the user to help them make their choice. You can add code to specialize the menu item selected behavior. The function, menu-item-highlighted, is called whenever a menu command is highlighted. You can add methods specialized on your window and/or menu classes depending on your requirements. The default behavior displays the help string of the selected menu item in the status bar of the current window.
There are several classes related to menus. The following is
a class diagram showing all the menu classes. Notice that all menus inherit from window. Window is a very primitive window class that provides a handle to an OS window object. When you are working with windows related to forms, you use a subclass of basic-pane. The menu-item class is not shown because it does not inherit from menu because menu-items are not an OS visible object. Instead, menu-items inherit from standard-object. |
|||||||||||||
There are several different functions related to creating an instance of a menu; the table shows which function should be used for each menu class. | |||||||||||||
|
Creating and Modifying Menus at Run Time
Code similar to the following is generated if you accept the default menu bar that is created when you open the Menu Editor. It provides the provides the value of the menu keyword argument to make-window. If you want to see the code for yourself, then toggle the menu property on a form without a menu bar to on. In the Menu Editor, click on OK to accept the default menu bar. With the focus on the form, save it using the File | Save command. In the editor, open the .bil file and search for open-menu.
(open-menu (list (make-instance 'menu-item :name :file-menu :title "~File" :value (open-menu (list (make-instance 'menu-item :name 'new-text-editor :title "~New" :value 'new-text-editor :selected nil :available t :event-synonym '(control-key #\N) :help-string "New editor") (make-instance 'menu-item :name 'open-text-file :title "~Open" :value 'open-text-file :selected nil :available t :event-synonym '(control-key #\O) :help-string "Open a file") (make-instance 'menu-item :name :save :title "~Save" :value 'save-text-file :selected nil :available t :event-synonym '(control-key #\S) :help-string "Save to file") (make-instance 'menu-item :name 'save-as-text-file :title "Save ~As..." :value 'save-as-text-file :selected nil :available t :event-synonym nil :help-string "Save to new file") (make-instance 'menu-item :name nil :title "-" :value nil :selected nil :available nil :event-synonym nil :help-string nil) (make-instance 'menu-item :name 'user-close :title "E~xit" :value 'user-close :selected nil :available t :event-synonym '(alt-key vk-f4) :help-string "Exit application")) 'pull-down-menu (screen *system*) :name :file-menu :on-click 'funcall-menu-item-with-window) :selected nil :available t :event-synonym nil :help-string nil) (make-instance 'menu-item :name :edit-menu :title "~Edit" :value (open-menu (list (make-instance 'menu-item :name 'cut-command :title "~Cut" :value 'cut-command :selected nil :available t :event-synonym '(control-key #\X) :help-string "Copy contents to clipboard and delete") (make-instance 'menu-item :name 'copy-command :title "C~opy" :value 'copy-command :selected nil :available t :event-synonym '(control-key #\C) :help-string "Copy contents to clipboard") (make-instance 'menu-item :name 'paste-command :title "~Paste" :value 'paste-command :selected nil :available t :event-synonym '(control-key #\V) :help-string "Paste contents from clipboard")) 'pull-down-menu (screen *system*) :name :edit-menu :on-click 'funcall-menu-item-with-window) :selected nil :available t :event-synonym nil :help-string nil)) 'menu-bar (screen *system*) :name :default-menu :on-click 'funcall-menu-item)
open-menu
(open-menu (list ...) 'pull-down-menu (screen *system*) :on-click 'funcall-menu-item-with-window ...)
Use the open-menu function to create a pop-up-menu or pull-down-menu. The arguments to open-menu are the list of menu-items, the type of menu, the screen, and zero or more keyword arguments for the properties of the menu.
name | The symbolic name of the menu item |
title | The title of the command. Place a "~" before the
access key of the command. Use "-" to indicate a menu separator. |
on-click | The event handler that is invoked when the user clicks on a menu item in the menu |
make-instance
(make-instance 'menu-item :name :new :title "~New" ...)
For every command on a menu, you need to create an instance of menu-item. In general, you need to specify most of the properties of a menu-item to have it perform properly.
allow-during-modality | A Boolean value indicating whether or not the command can be invoked when a modal dialog is displayed |
available | A Boolean value indicating whether the command is displayed as available or dimmed |
event-synonym | The shortcut key(s) that can invoke the command. Values can be nil or a list of key constants/characters. Here are two examples, '(vk-f8) and '(control-key #\N). |
help-string | A string that is displayed when the user points to the menu item |
name | The symbolic name of the menu item |
selected | A Boolean value indicating whether or not the menu item is displayed with a check mark or not. |
title | The title of the command. Place a "~" before the access key of the command. Use "-" to indicate a menu separator. |
value | The value of the menu item. If the menu item has sub-items, then the value is a pull-down or pop-up menu. |
(make-instance 'menu-item :name nil :title "-")
Creates a menu separator. When the title of a menu-item is "-", then a menu separator is displayed as a horizontal line in the menu. You can also use the constant, menu-separator, instead of creating a menu-item. Menu items can be placed on more than one menu.
This short tutorial demonstrates creating and customizing an application's menu bar. The sample application is a mini-text editor that extends the default menu bar.
Setup
(defclass my-window (text-edit-window) ())
(defclass my-pane (text-edit-pane) ()) (defmethod default-pane-class ((window my-window)) 'my-pane)
(defmethod new-text-editor ((window my-pane)) (setf (file window) nil) (clear-page window))
(defmethod open-text-file ((window my-pane)) (let* ((pathname (ask-user-for-existing-pathname "Edit File" :stream window))) (cond ((or (null pathname) (not (probe-file pathname))) nil) (t (load-file window pathname)))))
(defmethod save-text-file ((window my-pane)) (save-file window (file (parent window)))) (defmethod save-as-text-file ((window my-pane)) (save-file window nil))
(defmethod about-to-show-menu ((window my-window) menu) (case (name menu) (:edit-menu (multiple-value-bind (start end) (get-selection (frame-child window)) (setf (available (find-named-object 'cut-command menu)) (not (eq start end))) (setf (available (find-named-object 'copy-command menu)) (not (eq start end))) (setf (available (find-named-object 'paste-command menu)) (first (first (clipboard *system*)))))) (t (call-next-method))))
On-Click Event Handler
(defmethod background-color-on-click (menu menu-item (window my-window)) (declare (ignore menu)) (setf (background-color (frame-child window)) (value menu-item)))
(defmethod about-to-show-menu ((window my-window) menu) (case (name menu) (:edit-menu (multiple-value-bind (start end) (get-selection (frame-child window)) (setf (available (find-named-object 'cut-command menu)) (not (equalp start end))) (setf (available (find-named-object 'copy-command menu)) (not (equalp start end))) (setf (available (find-named-object 'paste-command menu)) (first (first (clipboard *system*)))))) (:background-menu (let ((color (background-color (frame-child window)))) (dolist (item (menu-items menu)) (setf (selected item) (and (null (string-equal (title item) "-")) (or (and (null color) (null (value item))) (if (and color (value item)) (rgb-equal (symbol-value (value item)) color)))))))) (t (call-next-method))))
Floating menus are implemented using pop-up-menus. A pop-up-menu is similar to a pull-down-menu because it can have one or more menu items. The difference is that a pop-up menu is not activated by clicking on the menu's title or by using the ALT key. Usually, pop-up-menus are opened by using the right mouse click. You cannot use the Menu Editor to specify pop-up-menus.
Pop-up menus that are opened using a mouse right click are called shortcut menus. If you want to define a shortcut menu for your window, you do the following:
There is a more general exported function called pop-up-menu. If you use pop-up-menu, you are responsible for opening the menu, creating the menu items, and positioning the pop-up menu.
The utility function called pop-up-lettered-menu takes a list of strings and displays a pop-up-menu with alphabetized accelerator keys. If the user clicks on an option, then the string of the selected item is returned. Otherwise, nil is returned if no item is selected. pop-up-lettered-menu enables an end user to type a single key to select an item. It is used by the Search | Complete Symbol command in the Integrated Development Environment.
The following steps extend the previous mini-text editor example. For shortcut menus, you cannot use the Menu Editor and will have to work programmatically.
(defclass my-shortcut-menu (shortcut-menu) ())
(defmethod shortcut-menu-class ((window my-pane)) 'my-shortcut-menu)
(defmethod mouse-right-down ((window my-pane) buttons data) (declare (ignore buttons data)) (pop-up-shortcut-menu window))
(defmethod shortcut-commands ((window my-pane) (menu my-shortcut-menu)) (list (make-instance 'menu-item :name :inspect :value 'inspect-command :title "Inspect") menu-separator (make-instance 'menu-item :name :system-background :value 'system-background-color-command :title "System Background" :selected (not (background-color window))) (make-instance 'menu-item :name :blue-background :value 'blue-background-color-command :title "Blue Background" :selected (rgb-equal (background-color window) blue)))) (defmethod inspect-command ((window my-pane)) (inspect window)) (defmethod system-background-color-command ((window my-pane)) (setf (background-color window) nil)) (defmethod blue-background-color-command ((window my-pane)) (setf (background-color window) blue))
Sometimes you may want to use the more general pop-up-menu function rather than pop-up-shortcut-menu.
In the menu-example editor, you would need to rewrite the mouse-right-down function to use pop-up-menu instead. Since you are not using the shortcut menu functions, you are responsible for creating the menu and its menu items. The pop-up-menu function displays a pop-up menu at a specified location.
;; Redefined mouse-right-down that calls pop-up-menu. (defmethod mouse-right-down ((window my-pane) buttons data) (declare (ignore buttons data)) (let ((menu (open-menu (list (make-instance 'menu-item :name :inspect :value 'inspect-command :title "Inspect") menu-separator (make-instance 'menu-item :name :system-background :value 'system-background-color-command :title "System Specified Background" :selected (not (background-color window))) (make-instance 'menu-item :name :blue-background :value 'blue-background-color-command :title "Blue Background" :selected (rgb-equal (background-color window) blue))) 'my-shortcut-menu (screen *system*) :window window))) (prog1 (pop-up-menu menu (screen *system*)) (close menu))))
Copyright (C) 1998-1999, Franz Inc., Berkeley, CA. All Rights Reserved.