Professional GEM - Part VII - Menu structures
Professional GEM 49
P�PA�AR�RT�T -�- V�VI�II�I
M�Me�en�nu�u S�St�tr�ru�uc�ct�tu�ur�re�es�s
H�HA�AP�PP�PY�Y N�NE�EW�W Y�YE�EA�AR�R
This is article number seven in the ST PRO GEM series, and
the first for 1986. In this installment, I will be discussing
GEM menu structures and how to use them in your application.
There is also a short Feedback response section. You will find
the download file containing the code for this column in the
file GEMCL7.C in DL3 of the ATARI16 SIG (PCS-58).
M�ME�EN�NU�U B�BA�AS�SI�IC�CS�S
In ST GEM, the menu consists of a bar across the top of the
screen which displays several sub-menu titles. Touching one of
the titles causes it to highlight, and an associated "drop-down"
to be drawn directly below on the screen. This drop-down may
be dismissed by moving to another title, or by clicking the
mouse off of the drop-down.
To make a selection, the mouse is moved over the
drop-down. Each valid selection is highlighted when the mouse
touches it. Clicking the mouse while over one of these
selections picks that item. GEM then undraws the drop-down, and
sends a message to your application giving the object number of
the title bar entry, and the object number of the drop-down item
which were selected by the user. The selected title entry is
left highlighted while your code processes the request.
M�ME�EN�NU�U S�ST�TR�RU�UC�CT�TU�UR�RE�ES�S
The data structure which defines a GEM menu is (surprise!)
an object tree, just like the dialogs and panels which we have
discussed before. However, the operations of the GEM menu
manager are quite different from those of the form manager, so
the internal design of the menu tree has some curious
constraints.
The best way to understand these constraints is to look at
an example. The first item in the download is the object
structure (only) of the menu tree from the GEM Doodle/Demo
sample application.
The ROOT of a menu tree is sized to fit the entire screen.
To satisfy the visual hierarchy principle (see article #5), the
screen is divided into two parts: THE BAR, containing the menu
Professional GEM Part VII 50
titles, and THE SCREEN, while contains the drop-downs when they
are drawn. Each of these areas is defined by an object of the
same name, which are the only two objects linked directly below
the ROOT of a menu tree. You will notice an important
implication of this structure: The menu titles and their
associated drop-downs are stored in entirely different subtrees
of the menu!
While examining THE BAR in the example listing, you may
notice that its OBHEIGHT is very large (513). In hexadecimal
this is 0x0201. This defines a height for THE BAR of one
character plus two pixels used for spacing. THE BAR and its
subtree are the only objects which are drawn on the screen in
the menu's quiescent state.
The only offspring object of THE BAR is THE ACTIVE. This
object defines the part of THE BAR which is covered by menu
titles. The screen rectangle belonging to THE ACTIVE is used by
the GEM screen manager when it waits for the mouse to enter an
active menu title. Notice that THE ACTIVE and its offspring
also have OBHEIGHTs with pixel residues.
The actual menu titles are linked left to right in order
below THE ACTIVE. Their OBXs and OBWIDTHs are arranged so that
they completely cover THE ACTIVE. Normally, the title objects
are typed GTITLE, a special type which assures that the title
bar margins are correctly drawn.
THE SCREEN is the parent object of the drop-down boxes
themselves. They are linked left to right in an order identical
with their titles, so that the menu manager can make the
correct correspondence at run-time. The OBX of each drop-down
is set so that it is positioned below its title on the screen.
Notice that it is safe to overlap the drop-downs within a
menu, since only one of them will be displayed at any time.
There is one constraint on the boxes however: They must be no
greater than a quarter screen in total size. This is the size
of the off-screen blit buffer which is used by GEM to store the
screen contents when the drop-down is drawn. If you exceed this
size, not all the screen under the drop-down will be restored,
or the ST may crash!
The entries within a drop-down are usually GSTRINGs, which
are optimized for drawing speed. The rectangles of these
entries must completely cover the drop-down, or the entire
drop-down will be inverted when the mouse touches an uncovered
area! Techniques for using objects other than GSTRINGs are
discussed later in this column.
The first title and its corresponding drop-down are
special. The title name, by custom, is set to DESK. The
Professional GEM Part VII 51
drop-down must contain exactly eight GSTRING objects. The
first (again by custom) is the INFO entry, which usually leads
to a dialog displaying author and copyright information for your
application. The next is a separator string of dashes with the
DISABLED flag set. The following six objects are dummy strings
which GEM fills in with the names of desk accessories when your
menu is loaded.
The purpose of this description of menu trees is to give
you an understanding of what lies "behind the scenes" in the
next section, which describes the run-time menu library calls.
In practice, the Resource Construction Set provides "blank
menus" which include all of the required elements, and it also
enforces the constraints on internal structure. You only need
to worry about these if you modify the menu tree "on-the-fly".
U�US�SI�IN�NG�G T�TH�HE�E M�ME�EN�NU�U
once you have loaded the application's resource, you can
ask the AES to install your menu. You must first get the
address of the menu tree within the resource using:
rsrcgaddr(RTREE, MENUTREE, &admenu);
assuming that MENUTREE is the name you gave the menu in the RCS,
and that admenu is a LONG which will receive the address. Then
you call the AES to establish the menu:
menubar(admenu, TRUE);
At this point, the AES draws your menu bar on the screen and
animates it when the user moves the mouse into the title area.
The AES indicates that the user has made a menu selection
by sending your application a message. The message type is
MNSELECTED, which will be stored in msg[0], the first location
in the message returned by evntmulti().
The AES also stores the object number of the selected
menu's title in msg[3], and the object number of the selected
menu item in msg[4]. Generally, your application will process
menu messages with nested C switch statements. The outer switch
will have one case for each menu title, and the inner switch
statements will have a case for each entry within the selected
menu. (This implies that you must give a name to each title and
to each menu entry when you create the menu in the RCS.)
After the user has made a menu selection, the AES leaves
the title of the chosen menu in reverse video to indicate that
your application is busy processing the message. When you done
with whatever action is indicated, you need to return the title
Professional GEM Part VII 52
to a normal state. This is done with
menutnormal(admenu, msg[3], TRUE);
(Remember that msg[3] is the title's object number.)
When your application is ready to terminate, it should delete
its menu bar. Do this with the call: menubar(admenu, FALSE);
G�GE�ET�TT�TI�IN�NG�G F�FA�AN�NC�CY�Y
The techniques above represent the bare minimum to handle
menus. In most cases, however, you will want your menus to be
more "intelligent" in displaying the user's options. For
instance, you can prevent many user errors by disabling
inappropriate choices, or you can save space on drop-downs by
showing only one line for a toggle and altering its text or
placing and removing a check mark when the state is changed.
This section discusses these and other advanced techniques.
It is a truism of user interface design that the best way
to deal with an error is not to let it happen in the first
place. It many cases, you can apply this principle to GEM
menus by disabling choices which should not be used. If your
application uses a "selection precedes action" type of
interface, the type of object selected may give the information
needed to do this. Alternately, the state of the underlying
program may render certain menu choices illegal.
GEM provides a call to disable and re-enable menu options.
The call is:
menuienable(admenu, ENTRY, FALSE);
to disable a selection. The entry will be grayed out when it is
drawn, and will not invert under the mouse and will not be
selected by the user. Substituting TRUE for FALSE re-enables the
option. ENTRY is the name of the object which is being
affected, as assigned in the RCS.
Note that menuienable() will not normally affect the
appearance or operation of menu TITLE entries. However, there
is an undocumented feature which allows this. If ENTRY is
replaced by the object number of a title bar entry with its top
bit set, then the entire associated drop-down will be disabled
or re-enabled as requested, and the title's appearance will be
changed. But, be warned that this feature did not work reliably
in some early versions of GEM. Test it on your copy of ST GEM,
and use it with caution when you cannot control the version
under which your application may run.
Professional GEM Part VII 53
It is also possible to disable menu entries by directly
altering the DISABLED attribute within the OBSTATE word. The
routines enabobj() and disabobj() in the download show how this
is done. They are also used in setmenu(), which follows them
immediately.
Setmenu() is a utility which is useful when you wish to
simultaneously enable or disable many entries in the menu when
the program's state changes or a new object is selected by the
user. It is called with
setmenu(admenu, vector);
where vector is a pointer to an array of WORDs. The first word
of the array determines the default state of menu entries. If
it is TRUE, then setmenu() enables all entries in every
drop-down of the menu tree, except that the DESK drop-down is
unaffected. If it is FALSE, then every menu entry is disabled.
The following entries in the array are the numbers of menu
entries which are to be toggled to the reverse of the default
state. This list is terminated by a zero entry.
The advantage of setmenu() is that it allows you to build a
collection of menu state arrays, and associate one with each
type of user-selected object, program state, and so on.
Changing the status of the menu tree may then be accomplished
with a single call.
C�CH�HE�EC�CK�K P�PL�LE�EA�AS�SE�E?�?
One type of state indicator which may appear within a
drop-down is a checkmark next to an entry. You can add the
checkmark with the call:
menuicheck(admenu, ENTRY, TRUE);
and remove it by replacing the TRUE with FALSE. As above, ENTRY
is the name of the menu entry of interest. The checkmark
appears inside the left boundary of the entry object, so leave
some space for it.
The menuicheck() call is actually changing the state of the
CHECKED flag within the entry object's OBSTATE word. If
necessary, you may alter the flag directly using doobj() and
undoobj() from the download.
Professional GEM Part VII 54
N�NO�OW�W Y�YO�OU�U S�SE�EE�E I�IT�T N�NO�OW�W Y�YO�OU�U D�DO�ON�N'�'T�T
You can also alter the text which appears in a particular
menu entry (assuming that the entry is a GSTRING object). The
call
menutext(admenu, ENTRY, ADDR(text));
will substitute the null-terminated string pointed to by text
for whatever is currently in ENTRY. Remember to make the
drop-down wide enough to handle the largest text string which
you may substitute. In the interests of speed, GSTRINGs drawn
within drop-downs are not clipped, so you may get garbage
characters on the desktop if you do not size the drop-down
properly!
The menutext() call actually alters the OBSPEC field of the
menu entry object to point to the string which you specify.
Since the menu tree is a static data structure which may be
directly accessed by the AES at any time, be sure that the
string is also statically allocated and that it is not modified
without first being delinked from the menu tree. Failure to do
this may result in random crashes when the user accesses the
drop-down!
L�LU�UN�NC�CH�H A�AN�ND�D D�DI�IN�NN�NE�ER�R M�ME�EN�NU�US�S
Some applications may have such a wide range of operations
that they need more than one menu bar at different times.
There is no problem with having more than one menu tree in a
resource, but the AES can only keep track of one at a time.
Therefore, to switch menus you need to use menubar(admenu1,
FALSE); to release the first menu, then use menubar(admenu2,
TRUE); to load the second menu tree.
Changing the entire menu is a drastic action. Out of
consideration for your user, it should be associated with some
equally obvious change in the application which has just been
manually requested. An example might be changing from
spreadsheet to data graphing mode in a multi-function program.
D�DO�O I�IT�T Y�YO�OU�UR�RS�SE�EL�LF�F
In a future column, I will discuss how to set up
user-defined drawing objects. If you have already discovered
them on your own, you can use them within a drop-down or as a
title entry.
Professional GEM Part VII 55
If the user-defined object is within a drop-down, its
associated drawing code will be called once when the drop-down
is first drawn. It will then be called in "state-change" mode
when the entry is highlighted (inverted). This allows you to
use non-standard methods to show selection, such as outlines.
If you try to insert a user-defined object within the menu
title area, remember that the GTITLE object which you are
replacing includes part of the dark margin of the bar. You will
need to experiment with your object drawing code to replicate
this effect.
M�MA�AK�KE�E P�PR�RE�ET�TT�TY�Y
There are a number of menu formatting conventions which
have become standard practice. Using these gives your
application a recognizable "look-and-feel" and helps users learn
it. The following section reviews these conventions, and
supplies a few hints and tricks to obtain a better appearance
for you menus.
The second drop-down is customarily used as the FILE menu.
It contains options related to loading and saving the files
used by the application, as well as entries for clearing the
workspace and terminating the program.
You should avoid crowding the menu bar. Leave a couple of
spaces between each entry, and try not to use more than 70% of
the bar. Not only does this look better, but you will have
space for longer words if you translate your application to a
foreign language.
Similarly, avoid cluttering menu drop-downs. Try to keep
the number of options to no more than ten unless they are
clearly related, such as colors. Separate off dissimilar
entries with the standard disabled dashes line. (If you are
using setmenu(), remember to consider the separators when
setting up the state vectors.)
If the number of options grows beyond this bound, it may be
time to move them to a dialog box. If so, it is a convention to
put three dots following each menu entry which leads to a
dialog. Also, allow a margin on the menu entries. Two leading
blanks and a minimum of one trailing blank is standard, and
allows room for checkmarks if they are used.
Dangerous menu options should be far away from common used
entries, and are best separated with dashed lines. Such options
should either lead to a confirming go/no-go alert, or should
have associated "undo" options.
Professional GEM Part VII 56
After you have finished defining a menu drop-down with the
RCS, be sure that its entries cover the entire box. Then use
ctrl-click to select the drop-down itself, and SORT the entries
top to bottom. This way the drop-down draws in smoothly top to
bottom.
Finally, it is possible to put entries other than GSTRINGs
into drop-downs. In the RCS, you will need to import them via
the clipboard from the Dialog mode.
Some non-string object, such as icons and images, will look
odd when they are inverted under the mouse. There is a
standard trick for dealing with this problem. Insert the icon
or whatever in the drop-down first. Then get a GIBOX object and
position and size it so that it covers the first object as well
as the extra area you would like to be inverted.
Edit the GIBOX to remove its border, and assign the entry
name to it. Since the menu manager uses objcfind(), it will
detect and invert this second object when the mouse moves into
the drop-down. (To see why, refer to article #5.) Finally, DO
NOT SORT a drop-down which has been set up this way!
T�TH�HA�AT�T'�'S�S I�IT�T F�FO�OR�R N�NO�OW�W!�!
The next column will discuss some of the principles of
designing GEM interfaces for applications. This topic is
irreverantly known as GEM mythology or interface religion. The
subject for the following column is undecided. I am considering
mouse and keyboard messages, VDI drawing primitives, and the
file selector as topics. Let me know your preferences in the
Feedback!
Back to Professional_GEM