home *** CD-ROM | disk | FTP | other *** search
- *PROFESSIONAL GEM*
- By Tim Oren
- Column #7 - Menu Structures
-
- HAPPY NEW YEAR! 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).
-
- MENU BASICS. 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.
-
- MENU STRUCTURES. 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 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 OB_HEIGHT 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 OB_HEIGHTs with pixel residues.
-
- The actual menu titles are linked left to right in order
- below THE ACTIVE. Their OB_Xs and OB_WIDTHs are arranged so
- that they completely cover THE ACTIVE. Normally, the title
- objects are typed G_TITLE, 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 OB_X 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 G_STRINGs,
- 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
- G_STRINGs 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
- drop-down must contain exactly eight G_STRING 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".
-
- USING THE MENU. 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:
-
- rsrc_gaddr(R_TREE, MENUTREE, &ad_menu);
-
- assuming that MENUTREE is the name you gave the menu in the
- RCS, and that ad_menu is a LONG which will receive the
- address. Then you call the AES to establish the menu:
-
- menu_bar(ad_menu, 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
- MN_SELECTED, which will be stored in msg[0], the first
- location in the message returned by evnt_multi().
-
- 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 to a normal state. This is done with
-
- menu_tnormal(ad_menu, 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:
- menu_bar(ad_menu, FALSE);
-
- GETTING FANCY. 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:
-
- menu_ienable(ad_menu, 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 menu_ienable() 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.
-
- It is also possible to disable menu entries by directly
- altering the DISABLED attribute within the OB_STATE word.
- The routines enab_obj() and disab_obj() in the download show
- how this is done. They are also used in set_menu(), which
- follows them immediately.
-
- Set_menu() 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
-
- set_menu(ad_menu, 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 set_menu() 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 set_menu() 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.
-
- CHECK, PLEASE? 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:
-
- menu_icheck(ad_menu, 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 menu_icheck() call is actually changing the state of
- the CHECKED flag within the entry object's OB_STATE word. If
- necessary, you may alter the flag directly using do_obj() and
- undo_obj() from the download.
-
- NOW YOU SEE IT, NOW YOU DON'T. You can also alter the
- text which appears in a particular menu entry (assuming that
- the entry is a G_STRING object). The call
-
- menu_text(ad_menu, 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,
- G_STRINGs 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 menu_text() call actually alters the OB_SPEC 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!
-
- LUNCH AND DINNER MENUS. 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 menu_bar(ad_menu1, FALSE); to release the first
- menu, then use menu_bar(ad_menu2, 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.
-
- DO IT YOURSELF. 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.
-
- 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 G_TITLE 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.
-
- MAKE PRETTY. 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 set_menu(), 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.
-
- 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
- G_STRINGs 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 G_IBOX
- 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 G_IBOX to remove its border, and assign the
- entry name to it. Since the menu manager uses objc_find(),
- 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!
-
- THAT'S IT FOR NOW! 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!
-