Professional GEM - Part VII - Menu structures

From Atari Wiki
Revision as of 12:12, 13 September 2006 by Zorro 2 (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search


        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