bammi@cwruecmp.UUCP (Jwahar R. Bammi) (06/27/86)
Here are the next two installments of Tim Orens Professional Gem Seminar. I am including toc.prf and macros.prf again. #!/bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #!/bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # wind13.prf # apndx10.prf # This archive created: Fri Jun 27 02:51:51 1986 # By: Jwahar R. Bammi () export PATH; PATH=/bin:$PATH echo shar: extracting "'wind13.prf'" '(18936 characters)' if test -f 'wind13.prf' then echo shar: over-writing existing file "'wind13.prf'" fi sed 's/^X//' << \SHAR_EOF > 'wind13.prf' X.!**************************************************************************** X.! X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION. X.! X.! ** Professional GEM ** by Tim Oren X.! X.! Proff File by ST enthusiasts at X.! Case Western Reserve University X.! Cleveland, Ohio X.! uucp : decvax!cwruecmp!bammi X.! csnet: bammi@case X.! arpa : bammi%case@csnet-relay X.! compuserve: 71515,155 X.! X.!**************************************************************************** X.! X.! X.!**************************************************************************** X.! X.! Begin Part XIII X.! X.!**************************************************************************** X.! X.PART XIII A New Form Manager X.PP XThis is the 13th installment of ST PRO GEM, and the first Xdevoted to explaining a large piece of code. This article is also Xthe second in a series of three concerning GEM user interface Xtechniques. The code is an alternate form (dialog) manager for XGEM. It is stored as GMCL13.C in DL3 of PCS-58. You should go Xand download it now, or you will have no hope of following this Xdiscussion. X.PP XWhat is unique about this version of the form manager? XFirst, it implements all of the functions of the standard GEM Xform_do routine, as well as adding a "hot spots" feature which Xcauses selectable objects to become mouse-sensitive, just like Xthe entries in menu dropdowns. The second (and obvious) Xdifference is that this form manager is provided in source code Xform. This gives you the freedom to examine it and change it to Xsuit your own needs. X.PP XI have several purposes in presenting this code. It is Xintended as an example of GEM program structure, and an Xapplication of some of the techniques presented in earlier Xcolumns. It is also relevant to the continuing thread discussing Xthe necessity of feedback when constructing a user interface. X.PP XAlso, this issue represents the beginning of a fundamental Xchange in direction for ST PRO GEM. Since this column began last XAugust, Atari ST developers have increased not only in number, but Xin sophistication. A number of books, as well as back issues of XST PRO GEM, are now available to explain the basics of the ST and XGEM. Quick answers to common questions are available here on XCompuserve's PCS-57 from Atari itself, or from helpful members of Xthe developer community. X.PP XTo reflect these changes, future columns will discuss more Xadvanced topics in greater depth, with an accent on code which can Xbe adapted by developers. The program presented in this issue Xwill be a basis for a number of these discussions. There will be Xfewer "encyclopediac" treatments of AES and VDI function calls. XFinally, to give me the time required to create this code or clean Xup tools from my "bag of tricks", ST PRO GEM will probably convert Xto a monthly format around the start of summer. X.SH ON WITH THE SHOW XTaking your listing in hand, you will Xquickly notice two things. First, this program uses the infamous Xportability macros, so that it may be used with Intel versions of XGEM. Second, the routines are arranged "bottom up", with the main Xat the end, and subroutines going toward the beginning. (This is Xa carry-over from my days with ALGOL and PASCAL.) You should now Xturn to the form_do entry point near the end of the code. X.PP XOne change has been made in the standard calling sequence for Xm_do. The starting edit field is now a pointer to a value, Xrather than the value itself. The new form_do overwrites the Xinitial value with the number of the object being edited when the Xdialog terminated. Using this information, your program can Xrestore the situation when the dialog is next called. As before, Xif there is NO editable field, the initial value should be zero. X.PP XThere are several local variables which maintain vital state Xinformation during the dialog interaction. Edit_obj is the number Xof the editable object currently receiving keystrokes. Next_obj Xis set when the mouse is clicked over an object. If the object Xhappens to be editable, next_obj becomes the new edit_obj. X.PP XThree variables are associated with the "hot-spot" feature. XIf hot_mode is set to M1_ENTER, then the mouse is outside the area Xof the dialog. If it equals M1_EXIT, then the mouse is currently Xin the dialog. If it is in the dialog, hot_obj indicates whether Xthere is an active "hot" object. If its value is NIL (-1), then Xthere is no active object. Otherwise, it is equal to the number Xof the object which is currently "hot", that is, inverted on the Xscreen. Finally, hot_rect is the current wait rectangle. If the Xmouse is outside of the window, then the wait rectangle equals the Xdialog's ROOT. If there is a current hot object, then hot_rect Xequals that object's screen rectangle. If the mouse is in the Xdialog, but not within a hot object, then the wait rectangle Xdefines the area within which no further collision checks are Xnecessary. This is arrived at through an algorithm explained Xbelow. X.PP XForm_do's initialization code sets up the hot-spot variables Xto trigger if the mouse is within the dialog. It also sets Xstarting values for edit_obj and next_obj which will cause the Xedit startup code to be activated. X.PP XThe main portion of form_do is a loop, exhibiting the type of Xevent driven structure discussed in the last column. Before Xentering the evnt_multi wait, the status of next_obj and edit_obj Xare checked to see if a new object should be initialized for Xediting. If so, objc_edit is called with the EDINIT function Xcode. NOTE: the objc_edit calling sequence used in this program Xdiffers from the one given in the AES manual, which is incorrect! XYou should check the bindings you are using to be sure they will Xwork with this code, and modify as necessary. X.PP XThe evnt_multi is set up to wait for a mouse click (single or Xdouble), for a keyboard input, or for the mouse to make a X"significant" movement, as discussed above. Notice that since Xform_do is used as a subroutine, it does not handle messages which Xare normally processed by the main loop of your application. XNotice that this creates a mode, and that this routine as written Xhandles modal dialogs. You could, however, use this code as the Xbasis for a non-modal dialog handler by drawing the dialog within Xa window, and combining the main loop of form_do with the main Xloop of your application. (This possibility may be examined in Xfuture columns. In the meantime, it is left as an exercise for Xthe reader.) X.PP XThe event bit vector is returned to the variable "which". XSince events are not mutually exclusive, each possible event type Xmust be examined in turn before returning to the evnt_multi call. XThe form manager's event handling routines are form_hot, for mouse Xrectangle event, form_keybd, for character input, and form_button, Xfor mouse clicks. Form_keybd and form_button are allowed to Xterminate the dialog by returning a value of false to the loop Xcontrol variable "cont". If termination is imminent, or the user Xhas clicked on a new editable object, objc_edit is called with XEDEND to remove the cursor from the old object. The normal flow Xof control then returns to edit setup and evnt_multi. X.PP XA few cleanup actions are performed upon termination. If the Xterminating object (stored in next_obj) is not the same as the Xhot_obj, then a race condition has occured and the hot object must Xbe cleared with objc_toggle before exiting. After this test, the Xfinal edit_obj value is passed back via the parameter, and the Xterminating object is returned as the function value. X.SH RELAXEN UND WATCHEN DAS BLINKENLICHTE XForm_hot is Xresponsible for maintaining on-screen hot-spots, and correctly Xupdating the internal hot-spot variables. It is about halfway Xthrough the listing. X.PP Xhe first action in form_hot is to determine if the mouse has Xjust exited an object which is "hot. In this case, objc_toggle is Xcalled to unhighlight the object and reset the SELECTED flag. X.PP XThe current mouse position is passed to form_hot by form_do. XIt is checked against the root rectangle of the dialog to see if Xthe mouse is inside the dialog. If not, the program must wait for Xit to re-enter, so form_hot sets the rectangle and waiting mode Xaccordingly, and returns NIL as the new hot_obj. X.PP XWhen the mouse is within the dialog, a regular objc_find call Xdetermines the object at which it is pointing. For an object to Xbe mouse-sensitive, it must be SELECTABLE and not DISABLED. If Xthe found object meets these tests, the mouse will "hover" over Xthe object, waiting to leave its screen rectangle. Since the Xobject might already be SELECTED (and hence drawn reversed), this Xis checked before objc_toggle is called to do the highlighting and Xselection of the object, which becomes the hot_obj. (If the Xobject was already SELECTED, the hot_obj becomes NIL.) X.PP XThe toughest condition is when the mouse is within the Xdialog, but not over a mouse-sensitive object. The regular GEM Xevent structure will not work, because it can only wait on two Xrectangles, and there may be many more selectable objects in a Xdialog tree. I have found a way around this limitation using a Xcombination of the map_tree utility (introduced in ST PRO GEM #5) Xwith the principle of visual hierarchy in object trees. X.PP XIn summary, the algorithm attempts to find the largest Xbounding rectangle around the current mouse position, within which Xthere are no mouse-sensitive objects. It starts with a rectangle Xequal to the dialog root, and successively "breaks" it with the Xrectangle of each mouse-sensitive object. The next few paragraphs Xexamine this method in detail. X.PP XSince C lacks the dynamic scoping of LISP, from which Xmap_tree was derived, it is necessary to set up some globals to be Xused during the rectangle break process. Br_rect is the GRECT of Xthe current bounding rectangle. Br_mx and br_my hold the current Xmouse position. Br_togl is a switch which determines whether the Xnext break will be attempted horizontally or vertically. After Xinitializing these variables, form_hot uses map_tree to invoke the Xbreak_obj routine for every object in the dialog. X.PP XBreak_obj first intersects the rectangle of the object with Xthe current bounding rectangle. If they are disjoint, then Xneither the object nor any of its offspring can possible affect Xthe operation, so FALSE is returned, causing map_tree to ignore Xthe subtree. X.PP XThe object is next checked to see if it is mouse-sensitive. XAs before, it must be SELECTABLE and not DISABLED, and it must not Xbe hidden (this was checked automatically by objc_find before). XIf these conditions are met, then the object intrudes into the Xcurrent bounding rectangle. To maintain the desired condition, Xpart of the rectangle must be removed or "broken away". X.PP XIn many cases, the break operation can be done either Xhorizontally or vertically. Since we have no prior information as Xto which way the mouse will move next, break_obj uses the br_togl Xflag to alternate which direction it will try first. This should Xyield the most nearly square rectangle. X.PP XThe break_x and break_y routines are very similar. In each Xcase, the segment occupied by the breaking object is compared to Xthe point occupied by the mouse. If the point is within the Xsegment, there is no possible break in this dimension, and FALSE Xis returned. If the point lies outside the segment, then the Xrectangle may be successfully broken by reducing this dimension. XThis is done, and TRUE is returned to report success. X.PP XThe break_y routine also employs a look-ahead test to prevent Xa possible infinite loop. It is conceivable, though not likely, Xthat someone might nest a non-SELECTABLE object completely within Xanother SELECTABLE object(s). If the mouse point is within such Xan object, the algorithm will not be able to select a break Xdimension. In the current version, the mouse rectangle is simply Xforced to a single pixel for this case. (Note that is is NOT Xsufficent to simply wait on the non-selectable object's rectangle, Xsince other SELECTABLE objects may overlap it and follow it in Xtree order.) X.PP XSince map_tree examines all possible objects, br_rect will be Xthe correct bounding rectangle at completion. Note that you can Xreadily adapt this technique to other uses, such as hot-spotting Xwhile dragging objects. It is much less demanding of CPU Xresources than other methods, such as repetitive objc_finds. X.SH WHAT A CHARACTER! XThe form_keybd routine acts as a filter on Xcharacter input. When it recognizes a control character, it Xprocesses it and zeroes the keyboard word. Other chararacters are Xpassed on to objc_edit to be inserted in edit_obj. If there is no Xediting object, the character goes to the bit bucket. X.PP XThe form_keybd given implements the standard GEM Xfunctionality with two minor additions. First, a carriage return Xin a dialog with no DEFAULT exit object is taken as a tab. This Xallows <CR> to be used "naturally" in dialogs with several lines Xof text input. Second, tabs and backtabs "wrap around" from top Xto bottom of the dialog, and are done by "walking the tree", Xrather than relying on the LASTOB flag to signal the end of the Xdialog. This allows the new form manager to handle dialog trees Xwhich are not contiguous in memory. X.PP XThe code sets up several global variables for use by mapped Xfunctions. Fn_obj is the output from both find_tab and find_def. XFn_dir is an input to find_tab. Fn_last, fn_prev, and fn_last are Xused while searching for tab characters. X.PP XA carriage return results in a search of the entire tree, Xusing map_tree and find_def, for an object with its DEFAULT flag Xset. Its SELECTED flag is set and it is inverted on the screen to Xindicate the action taken. Form_keybd returns a FALSE to force Xtermination of the main form_do loop. If no DEFAULT is found, Xcontrol passes to the tab code. X.PP XThe tabbing procedure is somewhat complicated because the Xsame code is used for forward and backward tabbing. The old value Xof edit_obj (the object being tabbed FROM) is placed into fn_last. XFn_dir is set to one for a forward tab, and zero for a backward Xtab. X.PP XThe general strategy is to scan the entire tree for EDITABLE Xobjects, always saving the last one found in fn_prev. When Xtabbing forward fn_last is checked against fn_prev. A match Xindicates that the current object is the one desired. When Xtabbing backward the current object is checked against fn_last. XIf they match, fn_prev is the desired object. This procedure Xrequires two passes when the tab "wraps around" the tree, that is, Xwhen the desired object as at the opposite end of the traverse Xfrom the old editing object. X.PP XThe result of the tab operation is written back into Xform_do's next_obj parameter, and becomes the new editing object Xat the beginning of the next loop. X.SH BUTTON DOWN XThe form_button procedure is lengthy because it Xmust recognize and handle mouse clicks on several types of Xobjects: EDITABLE, SELECTABLE, and TOUCHEXIT. The first section Xof code rejects other objects, which cannot accept a click. X.PP XThe next piece of form_button makes a special check for a Xdouble click on a TOUCHEXIT object. This will cause the high bit Xof the returned object number to be set. (By the way, this also Xoccurs in the standard form_do.) This flag allows user dialog Xcode to perform special processing on the object. X.PP XThe largest piece of form_button handles the various cases in Xwhich the SELECTABLE flag may be set. Setting the RBUTTON (radio Xbutton) flag causes all of the object's siblings in the tree to be Xdeselected at the time the object is clicked. The do_radio Xroutine uses the get_parent utility to find the ancestor, and then Xperforms the deselect/select operation. X.PP XIf the SELECTABLE object is not TOUCHEXIT, then graf_watchbox Xis used to make sure that the mouse button comes back up while it Xis within the object. Otherwise, the click is cancelled. Care is Xnecessary here, since the hot-spot code may have already set the XSELECTED flag for the object. (We cannot be sure of this, for a Xrace condition may have occurred!) X.PP XIf the SELECTABLE object is TOUCHEXIT, then the application Xhas requested that form_do exit without waiting for the button to Xgo back up. In both this and regular form_do, TOUCHEXIT objects Xare used when you want to provide immediate response (animation) Xwithin the context of a dialog. X.PP XThe final parts of form_button do cleanup. If the clicked Xobject was already hot-spotted, hot_obj must be reset to NIL, Xotherwise form_do will carefully unselect the object which has Xjust been selected! X.PP XIf the EXIT or TOUCHEXIT flags are in force, form_button Xreturns FALSE to force the completion of form_do. For EDITABLE Xobjects, next_obj is left intact to replace edit_obj during the Xnext loop. Otherwise, next_obj has done its job and is zeroed, Xand form_button returns TRUE for continuation. X.PP XThis concludes the tour of the alternate form_do. The best Xcure for any confusion in this explanation is to compile the code Xinto an application and watch how it runs with different Xresources, or attack it with a debugger. X.SH OPERATORS ARE STANDING BY XI encourage you to modify this Xcode to meet your particular needs and incorporate it into your Xapplication. I would like to request than anyone who comes up Xwith significant improvements (or bug fixes) send them to me so Xthey can be made generally available. You can do this via the XANTIC ONLINE Feedback, or by sending E-mail to 76703,202. X.PP XSpeaking of Feedback, I would also like comments on the Xproposed change of direction for the column, and more suggestions Xfor future topics. The next installment will be a further Xdiscussion of interface design. Topics now queued for future Xarticles include the file selector and DOS error handling, a new Xobject editor, and customized drag box and rubber box routines. XDiscussions on VDI workstations and printer output are on hold Xpending release of the GDOS by Atari. If there are items which Xyou want to appear here, you must let me know! X.! X.! X.!***************************************************************************** X.!* * X.!* End Part XIII * X.!* * X.!***************************************************************************** SHAR_EOF if test 18936 -ne "`wc -c 'wind13.prf'`" then echo shar: error transmitting "'wind13.prf'" '(should have been 18936 characters)' fi echo shar: extracting "'apndx10.prf'" '(14690 characters)' if test -f 'apndx10.prf' then echo shar: over-writing existing file "'apndx10.prf'" fi sed 's/^X//' << \SHAR_EOF > 'apndx10.prf' X.!**************************************************************************** X.! X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION. X.! X.! ** Professional GEM ** by Tim Oren X.! X.! Proff File by ST enthusiasts at X.! Case Western Reserve University X.! Cleveland, Ohio X.! uucp : decvax!cwruecmp!bammi X.! csnet: bammi@case X.! arpa : bammi%case@csnet-relay X.! compuserve: 71515,155 X.! X.!**************************************************************************** X.! X.! Begin Appendix 10 X.! X.!*************************************************************************** X.! X.! X.AP X Sample Code for Part XIII X#include "portab.h" /* portable coding conv */ X#include "machine.h" /* machine depndnt conv */ X#include "obdefs.h" /* object definitions */ X#include "gembind.h" /* gem binding structs */ X#include "taddr.h" X X#define M1_ENTER 0x0000 X#define M1_EXIT 0x0001 X X#define BS 0x0008 X#define TAB 0x0009 X#define CR 0x000D X#define ESC 0x001B X#define BTAB 0x0f00 X#define UP 0x4800 X#define DOWN 0x5000 X#define DEL 0x5300 X /* Global variables used by */ X /* 'mapped' functions */ XMLOCAL GRECT br_rect; /* Current break rectangle */ XMLOCAL WORD br_mx, br_my, br_togl; /* Break mouse posn & flag */ XMLOCAL WORD fn_obj; /* Found tabable object */ XMLOCAL WORD fn_last; /* Object tabbing from */ XMLOCAL WORD fn_prev; /* Last EDITABLE obj seen */ XMLOCAL WORD fn_dir; /* 1 = TAB, 0 = BACKTAB */ X X/************* Utility routines for new forms manager ***************/ X X VOID Xobjc_toggle(tree, obj) /* Reverse the SELECT state */ X LONG tree; /* of an object, and redraw */ X WORD obj; /* it immediately. */ X { X WORD state, newstate; X GRECT root, ob_rect; X X objc_xywh(tree, ROOT, &root); X state = LWGET(OB_STATE(obj)); X newstate = state ^ SELECTED; X objc_change(tree, obj, 0, root.g_x, root.g_y, X root.g_w, root.g_h, newstate, 1); X } X X.bp XVOID /* If the object is not already */ Xobjc_sel(tree, obj) /* SELECTED, make it so. */ X LONG tree; X WORD obj; X { X if ( !(LWGET(OB_STATE(obj)) & SELECTED) ) X objc_toggle(tree, obj); X } X X VOID /* If the object is SELECTED, */ Xobjc_dsel(tree, obj) /* deselect it. */ X LONG tree; X WORD obj; X { X if (LWGET(OB_STATE(obj)) & SELECTED) X objc_toggle(tree, obj); X } X X VOID /* Return the object's GRECT */ Xobjc_xywh(tree, obj, p) /* through 'p' */ X LONG tree; X WORD obj; X GRECT *p; X { X objc_offset(tree, obj, &p->g_x, &p->g_y); X p->g_w = LWGET(OB_WIDTH(obj)); X p->g_h = LWGET(OB_HEIGHT(obj)); X } X.bp X VOID /* Non-cursive traverse of an */ Xmap_tree(tree, this, last, routine) /* object tree. This routine */ X LONG tree; /* is described in PRO GEM #5. */ X WORD this, last; X WORD (*routine)(); X { X WORD tmp1; X X tmp1 = this; /* Initialize to impossible value: */ X /* TAIL won't point to self! */ X /* Look until final node, or off */ X /* the end of tree */ X while (this != last && this != NIL) X /* Did we 'pop' into this node */ X /* for the second time? */ X if (LWGET(OB_TAIL(this)) != tmp1) X { X tmp1 = this; /* This is a new node */ X this = NIL; X /* Apply operation, testing */ X /* for rejection of sub-tree */ X if ((*routine)(tree, tmp1)) X this = LWGET(OB_HEAD(tmp1)); X /* Subtree path not taken, */ X /* so traverse right */ X if (this == NIL) X this = LWGET(OB_NEXT(tmp1)); X } X else /* Revisiting parent: */ X /* No operation, move right */ X { X tmp1 = this; X this = LWGET(OB_NEXT(tmp1)); X } X } X.bp X WORD /* Find the parent object of */ Xget_parent(tree, obj) /* by traversing right until */ X LONG tree; /* we find nodes whose NEXT */ X WORD obj; /* and TAIL links point to */ X { /* each other. */ X WORD pobj; X X if (obj == NIL) X return (NIL); X pobj = LWGET(OB_NEXT(obj)); X if (pobj != NIL) X { X while( LWGET(OB_TAIL(pobj)) != obj ) X { X obj = pobj; X pobj = LWGET(OB_NEXT(obj)); X } X } X return(pobj); X } X X WORD Xinside(x, y, pt) /* determine if x,y is in rectangle */ X WORD x, y; X GRECT *pt; X { X if ( (x >= pt->g_x) && (y >= pt->g_y) && X (x < pt->g_x + pt->g_w) && (y < pt->g_y + pt->g_h) ) X return(TRUE); X else X return(FALSE); X } X X WORD Xrc_intersect(p1, p2) /* compute intersection of two GRECTs */ X GRECT *p1, *p2; X { X WORD tx, ty, tw, th; X X tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w); X th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h); X tx = max(p2->g_x, p1->g_x); X ty = max(p2->g_y, p1->g_y); X p2->g_x = tx; X p2->g_y = ty; X p2->g_w = tw - tx; X p2->g_h = th - ty; X return( (tw > tx) && (th > ty) ); X } X.bp X VOID Xrc_copy(psbox, pdbox) /* copy source to destination rectangle */ X GRECT *psbox; X GRECT *pdbox; X { X pdbox->g_x = psbox->g_x; X pdbox->g_y = psbox->g_y; X pdbox->g_w = psbox->g_w; X pdbox->g_h = psbox->g_h; X } X X/************* "Hot-spot" manager and subroutines ***************/ X X WORD Xbreak_x(pxy) X WORD *pxy; X { /* Breaking object is right of */ X if (br_mx < pxy[0]) /* mouse. Reduce width of */ X { /* bounding rectangle. */ X br_rect.g_w = pxy[0] - br_rect.g_x; X return (TRUE); X } X if (br_mx > pxy[2]) /* Object to left. Reduce width*/ X { /* and move rect. to right */ X br_rect.g_w += br_rect.g_x - pxy[2] - 1; X br_rect.g_x = pxy[2] + 1; X return (TRUE); X } X return (FALSE); /* Mouse within object segment. */ X } /* Break attempt fails. */ X.bp X WORD Xbreak_y(pxy) X WORD *pxy; X { X if (br_my < pxy[1]) /* Object below mouse. Reduce */ X { /* height of bounding rect. */ X br_rect.g_h = pxy[1] - br_rect.g_y; X return (TRUE); X } X if (br_my > pxy[3]) /* Object above mouse. Reduce */ X { /* height and shift downward. */ X br_rect.g_h += br_rect.g_y - pxy[3] - 1; X br_rect.g_y = pxy[3] + 1; X return (TRUE); X } X /* Emergency escape test! Protection vs. turkeys who nest */ X /* non-selectable objects inside of selectables. */ X if (br_mx >= pxy[0] && br_mx <= pxy[1]) X { /* Will X break fail? */ X br_rect.g_x = br_mx; /* If so, punt! */ X br_rect.g_y = br_my; X br_rect.g_w = br_rect.g_h = 1; X return (TRUE); X } X return (FALSE); X } X.bp X WORD Xbreak_obj(tree, obj) /* Called once per object to */ X LONG tree; /* check if the bounding rect. */ X WORD obj; /* needs to be modified. */ X { X GRECT s; X WORD flags, broken, pxy[4]; X X objc_xywh(tree, obj, &s); X grect_to_array(&s, pxy); X if (!rc_intersect(&br_rect, &s)) X return (FALSE); /* Trivial rejection case */ X X flags = LWGET(OB_FLAGS(obj)); /* Is this object a potential */ X if (flags & HIDETREE) /* hot-spot? */ X return (FALSE); X if ( !(flags & SELECTABLE) ) X return (TRUE); X if (LWGET(OB_STATE(obj)) & DISABLED) X return (TRUE); X X for (broken = FALSE; !broken; ) /* This could take two passes */ X { /* if the first break fails. */ X if (br_togl) X broken = break_x(pxy); X else X broken = break_y(pxy); X br_togl = !br_togl; X } X return (TRUE); X } X.bp X WORD /* Manages mouse rectangle events */ Xform_hot(tree, hot_obj, mx, my, rect, mode) X LONG tree; X WORD hot_obj, mx, my, *mode; X GRECT *rect; X { X GRECT root; X WORD state; X X objc_xywh(tree, ROOT, &root); /* If there is already a hot-spot */ X if (hot_obj != NIL) /* turn it off. */ X objc_toggle(tree, hot_obj); X X if (!(inside(mx, my, &root)) ) /* Mouse has moved outside of */ X { /* the dialog. Wait for return. */ X *mode = M1_ENTER; X rc_copy(&root, rect); X return (NIL); X } X /* What object is mouse over? */ X /* (Hit is guaranteed.) */ X hot_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my); X /* Is this object a hot-spot? */ X state = LWGET(OB_STATE(hot_obj)); X if (LWGET(OB_FLAGS(hot_obj)) & SELECTABLE) X if ( !(state & DISABLED) ) X { /* Yes! Set up wait state. */ X *mode = M1_EXIT; X objc_xywh(tree, hot_obj, rect); X if (state & SELECTED) /* But only toggle if it's not */ X return (NIL); /* already SELECTED! */ X else X { X objc_toggle(tree, hot_obj); X return (hot_obj); X } X } X X rc_copy(&root, &br_rect); /* No hot object, so compute */ X br_mx = mx; /* mouse bounding rectangle. */ X br_my = my; X br_togl = 0; X map_tree(tree, ROOT, NIL, break_obj); X rc_copy(&br_rect, rect); /* Then return to wait state. */ X *mode = M1_EXIT; X return (NIL); X } X.bp X/************* Keyboard manager and subroutines ***************/ X X WORD Xfind_def(tree, obj) /* Check if the object is DEFAULT */ X LONG tree; X WORD obj; X { /* Is sub-tree hidden? */ X if (HIDETREE & LWGET(OB_FLAGS(obj))) X return (FALSE); X /* Must be DEFAULT and not DISABLED */ X if (DEFAULT & LWGET(OB_FLAGS(obj))) X if ( !(DISABLED & LWGET(OB_STATE(obj))) ) X fn_obj = obj; /* Record object number */ X return (TRUE); X } X X WORD Xfind_tab(tree, obj) /* Look for target of TAB operation. */ X LONG tree; X WORD obj; X { /* Check for hiddens subtree. */ X if (HIDETREE & LWGET(OB_FLAGS(obj))) X return (FALSE); X /* If not EDITABLE, who cares? */ X if ( !(EDITABLE & LWGET(OB_FLAGS(obj))) ) X return (TRUE); X /* Check for forward tab match */ X if (fn_dir && fn_prev == fn_last) X fn_obj = obj; X /* Check for backward tab match */ X if (!fn_dir && obj == fn_last) X fn_obj = fn_prev; X fn_prev = obj; /* Record object for next call. */ X return (TRUE); X } X X WORD Xform_keybd(tree, edit_obj, next_obj, kr, out_obj, okr) X LONG tree; X WORD edit_obj, next_obj, kr, *out_obj, *okr; X { X if (LLOBT(kr)) /* If lower byte valid, mask out */ X kr &= 0xff; /* extended code byte. */ X fn_dir = 0; /* Default tab direction if backward. */ X switch (kr) { X case CR: /* Zap character. */ X *okr = 0; X /* Look for a DEFAULT object. */ X fn_obj = NIL; X map_tree(tree, ROOT, NIL, find_def); X /* If found, SELECT and force exit. */ X if (fn_obj != NIL) X { X objc_sel(tree, fn_obj); X *out_obj = fn_obj; X return (FALSE); X } /* Falls through to */ X case TAB: /* tab if no default */ X case DOWN: X fn_dir = 1; /* Set fwd direction */ X case BTAB: X case UP: X *okr = 0; /* Zap character */ X fn_last = edit_obj; X fn_prev = fn_obj = NIL; /* Look for TAB object */ X map_tree(tree, ROOT, NIL, find_tab); X if (fn_obj == NIL) /* try to wrap around */ X map_tree(tree, ROOT, NIL, find_tab); X if (fn_obj != NIL) X *out_obj = fn_obj; X break; X default: /* Pass other chars */ X return (TRUE); X } X return (TRUE); X } X.bp X/************* Mouse button manager and subroutines ***************/ X X WORD Xdo_radio(tree, obj) X LONG tree; X WORD obj; X { X GRECT root; X WORD pobj, sobj, state; X X objc_xywh(tree, ROOT, &root); X pobj = get_parent(tree, obj); /* Get the object's parent */ X X for (sobj = LWGET(OB_HEAD(pobj)); sobj != pobj; X sobj = LWGET(OB_NEXT(sobj)) ) X { /* Deselect all but... */ X if (sobj != obj) X objc_dsel(tree, sobj); X } X objc_sel(tree, obj); /* the one being SELECTED */ X } X X WORD /* Mouse button handler */ Xform_button(tree, obj, clicks, next_obj, hot_obj) X LONG tree; X WORD obj, clicks, *next_obj, *hot_obj; X { X WORD flags, state, hibit, texit, sble, dsbld, edit; X WORD in_out, in_state; X X flags = LWGET(OB_FLAGS(obj)); /* Get flags and states */ X state = LWGET(OB_STATE(obj)); X texit = flags & TOUCHEXIT; X sble = flags & SELECTABLE; X dsbld = state & DISABLED; X edit = flags & EDITABLE; X X if (!texit && (!sble || dsbld) && !edit) /* This is not an */ X { /* interesting object */ X *next_obj = 0; X return (TRUE); X } X X if (texit && clicks == 2) /* Preset special flag */ X hibit = 0x8000; X else X hibit = 0x0; X X if (sble && !dsbld) /* Hot stuff! */ X { X if (flags & RBUTTON) /* Process radio buttons*/ X do_radio(tree, obj); /* immediately! */ X else if (!texit) X { X in_state = (obj == *hot_obj)? /* Already toggled ? */ X state: state ^ SELECTED; X if (!graf_watchbox(tree, obj, in_state, X in_state ^ SELECTED)) X { /* He gave up... */ X *next_obj = 0; X *hot_obj = NIL; X return (TRUE); X } X } X else /* if (texit) */ X if (obj != *hot_obj) /* Force SELECTED */ X objc_toggle(tree, obj); X } X X if (obj == *hot_obj) /* We're gonna do it! So don't */ X *hot_obj = NIL; /* turn it off later. */ X X if (texit || (flags & EXIT) ) /* Exit conditions. */ X { X *next_obj = obj | hibit; X return (FALSE); /* Time to leave! */ X } X else if (!edit) /* Clear object unless tabbing */ X *next_obj = 0; X X return (TRUE); X } X.bp X/************* New forms manager: Entry point and main loop *************/ X X WORD Xform_do(tree, start_fld) X REG LONG tree; X WORD *start_fld; X { X REG WORD edit_obj; X WORD next_obj, hot_obj, hot_mode; X WORD which, cont; X WORD idx; X WORD mx, my, mb, ks, kr, br; X GRECT hot_rect; X WORD (*valid)(); X /* Init. editing */ X next_obj = *start_fld; X edit_obj = 0; X /* Initial hotspot cndx */ X hot_obj = NIL; hot_mode = M1_ENTER; X objc_xywh(tree, ROOT, &hot_rect); X /* Main event loop */ X cont = TRUE; X while (cont) X { X /* position cursor on */ X /* the selected */ X /* editting field */ X if (edit_obj != next_obj) X if (next_obj != 0) X { X edit_obj = next_obj; X next_obj = 0; X objc_edit(tree, edit_obj, 0, &idx, EDINIT); X } X /* wait for button or */ X /* key or rectangle */ X which = evnt_multi(MU_KEYBD | MU_BUTTON | MU_M1, X 0x02, 0x01, 0x01, X hot_mode, hot_rect.g_x, hot_rect.g_y, X hot_rect.g_w, hot_rect.g_h, X 0, 0, 0, 0, 0, X 0x0L, X 0, 0, X &mx, &my, &mb, &ks, &kr, &br); X X if (which & MU_M1) /* handle rect. event */ X hot_obj = form_hot(tree, hot_obj, mx, my, &hot_rect, &hot_mode); X /* handle keyboard event*/ X if (which & MU_KEYBD) X { /* Control char filter */ X cont = form_keybd(tree, edit_obj, next_obj, kr, &next_obj, &kr); X if (kr && edit_obj) /* Add others to object */ X objc_edit(tree, edit_obj, kr, &idx, EDCHAR); X } X /* handle button event */ X if (which & MU_BUTTON) X { /* Which object hit? */ X next_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my); X if (next_obj == NIL) X next_obj = 0; X else /* Process a click */ X cont = form_button(tree, next_obj, br, X &next_obj, &hot_obj); X } X /* handle end of field */ X /* clean up */ X if (!cont || (next_obj != edit_obj && next_obj != 0)) X if (edit_obj != 0) X objc_edit(tree, edit_obj, 0, &idx, EDEND); X } X /* If defaulted, may */ X /* need to clear hotspot*/ X if (hot_obj != (next_obj & 0x7fff)) X if (hot_obj != NIL) X objc_toggle(tree, hot_obj); X /* return exit object */ X /* hi bit may be set */ X /* if exit obj. was */ X /* double-clicked */ X *start_fld = edit_obj; X return(next_obj); X } X.! X.!**************************************************************************** X.! X.! End Appendix 10 X.! X.!**************************************************************************** SHAR_EOF if test 14690 -ne "`wc -c 'apndx10.prf'`" then echo shar: error transmitting "'apndx10.prf'" '(should have been 14690 characters)' fi # End of shell archive exit 0 -- Jwahar R. Bammi Usenet: .....!decvax!cwruecmp!bammi CSnet: bammi@case Arpa: bammi%case@csnet-relay CompuServe: 71515,155
bammi@cwruecmp.UUCP (Jwahar R. Bammi) (06/27/86)
#!/bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #!/bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # wind14.prf # tut.prf # macros.prf # This archive created: Fri Jun 27 02:53:58 1986 # By: Jwahar R. Bammi () export PATH; PATH=/bin:$PATH echo shar: extracting "'wind14.prf'" '(22068 characters)' if test -f 'wind14.prf' then echo shar: over-writing existing file "'wind14.prf'" fi sed 's/^X//' << \SHAR_EOF > 'wind14.prf' X.!**************************************************************************** X.! X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION. X.! X.! ** Professional GEM ** by Tim Oren X.! X.! Proff File by ST enthusiasts at X.! Case Western Reserve University X.! Cleveland, Ohio X.! uucp : decvax!cwruecmp!bammi X.! csnet: bammi@case X.! arpa : bammi%case@csnet-relay X.! compuserve: 71515,155 X.! X.!**************************************************************************** X.! X.! X.!**************************************************************************** X.! X.! Begin Part XIV X.! X.!**************************************************************************** X.! X.PART XIV USER INTERFACES - Part 2 X.PP XThis issue of ST PRO GEM (#14) continues the discussion of Xuser interface design which began in episode eight. It begins Xwhere we left off, with a further treatment of the mode problem, Xand proceeds into topics such as visual grammar and layered Xinterfaces. X.PP X Note that there is no download for this column. The Xdownloads will return with the next issue, a discussion of using Xthe GEM DOS file system within a GEM application. Specifically, Xit will include sample code for using the file selector, the GEM Xform_error alerts, and some utilities for manipulating file and Xpath names. There will also be a feedback section. The following Xtwo columns will be devoted to "graphics potpourri", a collection Xof small but useful GEM utilities such as popup menus, string Xediting, and source code for drag and rubber box operations. X.SH MODES AGAIN XIf a program is modeless, it acts predictably, Xwhich turns out to be very important. On the other hand, a good Xdefinition for "modes" is hard to find. In column eight, I Xsuggested that a mode exists when you cannot use all of the Xcapabilities of the program without performing some intermediate Xstep. If this is less than clear, here are two alternate Xdefinitions offering different views of the problem. X.SH THE "TWO USER TEST" XConsider the following thought Xexperiment: Imagine that your ST (and GEM) had two mice, two Xcursors, and two users. Could they both effectively use the Xprogram at the same time? If so, the application is modeless. If Xthere are points where one user can be "locked out" by the actions Xof the other, then a mode exists at that point. Let's consider Xsome examples of this test. X.PP XIn any program which uses the GEM menu system, one user could Xstop the other by touching a menu hotspot and dropping a menu. XThis constitutes an inherent mode in the GEM architecture. X.PP XOn the GEM Desktop, two users could open windows and view Xfiles without interference. However, as soon as one person tries Xto delete a file (assuming the verify option is on), the other is Xbrought to a halt as a dialog appears. Thus, we have found a Xmodal dialog. X.PP XIn many "Paint-type" programs, such as MacPaint, PC Paint, Xand GEM Paint, two artists could co-exist quite well, utilizing Xthe on-screen palette and tool selection. Of course, these Xprograms also contain modal dialogs for such operations as file Xand brush shape selection. In contrast, consider the paint Xprogram DEGAS for the ST. Here, two artists could only work Xtogether as long as neither wanted to change tool or color. Then Xthe display would have to be flipped to the selection screen, Xstopping the other user. This is a mode in the DEGAS interface. X.PP X(By the way, this test is not just academic. The grand-daddy Xof all mouse based systems, NLS, demonstrated by Doug Englebart in X1968, had two mice and two users, one of whom was physically Xremote. Cooperative techniques such as this are still largely Xunexplored and unexploited.) X.SH ONE LINER XHere's a terse definition by Jef Raskin: A Xprogram is modeless if a given action has one and only one result. XAgain, let's run a few examples. X.PP XThe menu dropdowns are clearly modal by this definition. XBefore the menu was activated, window control points could be Xactivated with a click. However, when the dropdown is visible, a Xclick action is interpreted as a menu selection or a dismissal of Xthe dropdown. Similarly, dialogs are modal because the action of Xmoving the mouse into the menu bar no longer causes the dropdown Xto appear. X.PP XI am typing this using the First Word editor program. It has Xa nice desktop level box full of characters where I can click to Xget symbols which the ST keyboard won't produce. However, if I Xinvoke the find or replace string dialog, the click-in-the-box Xaction doesn't work anymore. This is a mode in the First Word Xinterface. X.PP XFinally, consider an "old style" menu program, the kind where Xyou type in the number of the desired action from a list. Since Xthe number "2" might mean "Insert the record" in one menu, and X"Purge the file" in another, such a program is clearly modal by XRaskin's definition. X.PP XThese three definitions say almost the same thing, but from Xdifferent viewpoints. Depending on the situation, one or the Xother may be more intuitive for you. The goal of this type of Xanalysis is to root out unnecessary modes, and to make sure that Xthose which remain only appear when requested by the user, offer Xsome visual cue such as a rubber line or standard dialog box, and Xare used consistently throughout the application. X.SH PREDICTABILITY FOREVER AND EVER AND EVER XAs Raskin's Xdefinition makes clear, when the modes go away, the interface Xbecomes predictable. Predictability leads to the formation of Xhabits of use. Habits reduce "think time" and become Xprogressively faster due to the Power Law of Practice discussed in Xcolumn eight. This is exactly what we want! X.PP XThere is another benefit of predictability. A habit learned Xin one part of a program with a consistent interface can be Xtransferred and used elsewhere in the application. If several Xprograms share the same style of interface, the same habits can be Xused across a complete set of products. Learning time for the new Xfunctions becomes shorter, and the user is more likely to use the Xnew feature. X.SH IS A BOGEYMAN! XMost casual users are scared silly of Xcomputers and programs. (If you have any doubt, eavesdrop on a Xsecretary with a new word processor, or the doctor's receptionist Xcoping with an insurance data entry program.) In most cases, they Xhave a right to be frightened. Even experienced programmers, Xprone to toss the manuals and hack away, know that moderate Xparanoia is the best way to deal with an unknown program. How Xmust this feel to someone whose ability to perform (or lose) their Xjob depends on an unpredictable (aha!) black box. X.PP XSo here's another way in which predictability works. But to Xproduce a truly fearless user, we need other qualities as well. XOne is robustness, meaning that the program will not crash given Xnormal or even bizarre actions by the user. Another is feedback, Xwhich shuts off invalid options, reinforces correct actions, and Xgives reassurance that an operation is proceeding normally. XFinally, we need forgiveness, in the form of inverse operations or XUndo options, when the inevitable mistake is made. X.PP XThe ultimate goal is make the program discoverable. This Xmeans the user should be able to safely "wing it" after a short Xsession with the application and its interface. This practice Xought to be considered the norm anyway, since the manual is always Xacross the office or missing when an esoteric and half-forgotten Xfeature is needed. If it is possible to muddle through such a Xsituation by trial and error, without causing damage, the Ximmediate problem will be solved, and the user will gain Xconfidence. X.SH GOOD GRAMMAR OR... XSo exactly what are these habits that are Xsupposed to be so helpful? One of the most useful patterns is a Xconsistent command grammar for the program. This may sound Xstrange, since we have supposedly abandoned command line Xinterfaces in the graphics world, but in fact, the same type of Xrules apply. For instance, in the world of A> we might issue the Xcommand: X.br X.sp X.ce Xcopy a:foobar.txt b: X.PP XBy analogy to Englist grammar, this command contains a verb, X"copy", a file as subject: "a:foobar.txt", and a location as Xan object: "b:". The equivalent GEM Desktop operation is: X.BO X- Move mouse to foobar.txt icon in a: window X.EO X.BO X- Press mouse button X.EO X.BO X- Move mouse to b: icon X.EO X.BO X- Release mouse button X.EO X.PP XThe operation can be described as a select-drag-drop sequence, Xwith the select designating the subject file, the drag denoting Xthe operation (copy), and the location of the drop showing the Xobject. A grammar still exists, but its "terminal symbols" are Xcomposed of mouse actions interpreted in the context of the Xcurrent screen display, rather than typed characters. X.PP XOne useful way to analyze simple grammars, including those Xused as command languages, is to separate them into prefix, Xpostfix, and infix forms. In a prefix grammer, the operation to Xbe performed precedes its operands, that is, its subject(s) and Xobject(s). The DOS copy command given above is an example of a Xprefix command. LISP is an example of a language which uses Xprefix specification for its commands. X.PP XPostfix grammars specify the action after all of the operands Xhave been given. This command pattern is familiar to many as the Xway in which Hewlett-Packard calculators work. FORTH is an Xexample of a language which uses a postfix grammar. X.PP XInfix notation places the verb, or operator, between its Xsubject and object. Conventional algebraic notation is infix, as Xare most computer languages such as C or PASCAL. The example GEM Xcommand given above is also infix, since the selection of a Xsubject file preceded the action, which was followed by the Xdesignation of an object. X.PP XThe "standard" GEM command grammar, as used in the products Xproduced by Digital Research, is in fact infix. This is not to Xsay that GEM enforces such a convention, or that it is rigorously Xfollowed. However, when there is no pressing reason for a change, Xadoption of an infix command grammar will make your application Xfeel most like others which users may have seen. X.PP XThe general problem of specifying a graphic command language Xcan be difficult, but much of the problem has already been handled Xon the ST. Part of the solution is by constraint: the input and Xoutput hardware of the ST are predefined, so most developers will Xnot need to worry about choosing a pointing device or screen Xresolution. The other part of the standard solution is the GEM Xconvention for mouse usage. I am going to review these rules, and Xthen describe of the situations in which they have been bent, and Xfinally some alternate approaches which may prove useful to some Xdevelopers. X.SH SPECIFYING A SUBJECT XThere are really two sets of methods Xfor designating what is to be affected by an operation. One set Xis used when distinct objects are to be affected. Examples are Xfile and disk icons in the Desktop and trees in the RCS. Another Xset of designation methods is used when continuous material, such Xas text or bit images, is being handled. X.PP XWhen dealing with objects, a single mouse click (down and up) Xover the object selects it. The application should show that the Xselection has occurred by changing the appearance of the object. XThe most common methods are inverting the object, or drawing X"handles" around it. X.PP XMany operations allow "plural", or multiple object, Xselections. The GEM convention is that a click on an object while Xthe shift key is held down extends the selection by adding that Xobject. If the shift-clicked object was already selected, it is Xdeleted from the selection list. X.PP XAnother way to select multiple objects is to use a "rubber Xbox" to enclose them. This operation begins with drag on a part Xof the view where no object is present. The application then Xanimates a rubber box on the screen as long as the mouse button is Xheld down. When the button is released, all objects within the Xcurrent extent of the box are selected. A shift-drag combination Xcould be used to add the objects to an existing selection list. X.PP XSelecting part of a text or bit plane display is also done Xwith a rubber box. Since there are no "objects" in the view, any Xmouse drag is interpreted as the beginning of a selection Xoperation. In the simplest case, a bit plane, the rectangle Xwithin the box when the button is released is the selected extent. X.PP XWhen the underlying data has structure, such as words and Xlines of text, the display should reflect this fact during the Xselection operation. Typically, text selection is indicated by Xinversion of the characters rather than a rubber box. The Xselection extends along the starting line so long as the mouse Xstays within the line. If the mouse move off the starting text Xline, the implied selection is all characters between the starting Xcharacter and the character currently under the mouse, which is Xnot necessarily a rectangular area. X.PP XAn extended "plural" selection may be supported in text Xediting. The use of the shift key is also conventional in this Xapplication. X.SH ACTION XWith the subject designated, the user can now choose Xan operation. In many cases, this will be picked from the menu, Xin which case the entire command is complete. Some menu Xselections will lead to dialogs, in which the interaction methods Xare regulated by the GEM form manager. When the command is Xcompleted, it is often helpful if the application leaves the Xobjects (or areas) selected and ready for another operation. A Xsingle click away from any object is interpreted as cancelling the Xselections. X.PP XMany operations are indicated by gestures on the screen. XUsually, this is some variant of a drag operation. The Xinterpretation of the gesture may depend on the type and location Xof the selected subject, which part of it is under the mouse, and Xin what location the drag terminates. X.PP X"Handles" are small boxes or dot displayed around an object Xwhen it is selected. A drag beginning with the mouse on a handle Xis usually interpreted as a resizing operation, if this is Xappropriate. The pointing finger mouse form is displayed to Xindicate the operation in progress, and a rubber version of the Xobject is animated on the screen to show the user the result if Xthe button were released. In some cases, where an underlying X"snap" grid exists, the animated object may change size in Xdiscrete steps. X.PP XDragging a non-handle area of a selected object is usually Xinterpreted as the beginning of a move function. In most Xapplications, a move of a single object may be started without Xpre-selection. Simply beginning the drag on the object is taken Xto imply selection. The spread hand, or "grabber", mouse form is Xtypically displayed during a drag operation. X.PP XDragging may denote copying or movement, or more complex Xfunctions such as instantiation or generalization. The operation Ximplied by movement on the screen will differ among applications, Xand often within the same application, depending on target Xlocation. This target is the recipient of the command's action, Xor its object, in an English grammar sense. X.PP XFor example, a drag from window to window in the Desktop Xdenotes a copy. On the other hand, dragging the same icon to the Xtrashcan deletes it completely. Dragging an object from the RCS Xpartbox to the editing view creates a new copy of that prototype Xobject. Dragging the same object within the edit view simply Xchanges its placement. X.PP XThere are some mouse actions which are conventional X"abbreviations". A double click on an object is interpreted as Xboth a selection and an action. Usually, the double click action Xis the same as the Open entry in the "File" menu. X.PP XWhen the usual interpretation of a drag is movement, then Xshift-drag may be used as an enhanced varient implying copying. XFor instance, shift-dragging an object within the RCS editing Xwindow makes a copy of the object and places it in the final Xlocation. X.PP XTo return to the beginning of this discussion, the reason for Xadopting these conventional usages is to build an interface that Xpromotes habits. Particularly, a standard grammar for giving Xcommands helps answer the question "What comes next?". It breaks Xthe user's actions into logical phrases, or chunks, which may be Xthought of a whole, rather than one action at a time. X.SH DIFFERENT FOLKS, DIFFERENT STROKES X.PP XThere are always Xexceptions to a rule, or so it seems. In this case, consistency Xof the interface grammar is sometimes traded off against Xconsistency of metaphor, preservation of screen space, and "fast Xpath" methods for experts. X.PP XOne example is the use of "tools" in Paint and Draw programs. XIn such programs, an initial click is made on a tool icon, Xdenoting the operation to be applied to all following selections. XThis is an prefix style of grammar, and stands in contrast to the Xusual method of selecting subject object(s) first. Because of Xthis contrast, it is sometimes called "moding the cursor". (Try Xapplying the tests above to be sure it really is a mode.) X.PP XIn these cases, there are two reasons for accepting the Xnonstandard method. The first is consistency of metaphor. The X"user model" portrayed in the programs is an artist's work table, Xwith tools, palette, and so on. The cursor moding action is Xequivalent to picking up a working tool. The second reason is Xspeed. In a Paint program, the "canvas" is often modified, and Xspeed in creating or changing the bits is important. In more Xobject oriented applications such as Desktop or RCS, the objects Xare more persistent. Speed is then more essential when adding or Xchanging properties of the objects. X.PP XWhen command styles are mixed in this fashion, you must Xdesign very carefully to avoid conflicts or apparent side-effects Xin the command language. For example, in GEM Draw picking an Xaction from the Edit menu cancels the current cursor mode without Xwarning. Confusion from such side-effects may cancel out the Xbenefits of the mixed grammar. X.PP XThe subject of command speed deserves further attention. XWhile the novice approaching a program needs full feedback, a Xperson who uses it day in and day out will learn the program, and Xwant faster ways to get the job done, even if they are more Xarcane. The gives rise to a "layered" style of interface. X.PP XA layered interface is designed so that the visual grammar is Xobvious, as we have discussed. However, there are one or more Xsets of "accelerators" built into the program, which may be harder Xto find but faster to use. One example is condensed mouse actions Xsuch as the double-click. For instance, attempting to select a Xblock of text which extends beyond a window is impossible using Xthe basic metaphor. The novice will simply do the operation in Xpieces. A layered interface might put a less obvious Mark Begin Xand Mark End option in the menus. Another way is to take a drag Xwhich extends outside the window as a request to begin scrolling Xin that direction, while extending the current selection. X.PP XOne of the most common and useful accelerator methods is Xfunction keys. Using this approach, single key equivalents to Xactions are listed in the menu. Striking this key when an object Xis selected will cause the action to occur. Note that this is Xmost useful if some keyboard driven method of object selection, Xsuch as tabbing, is also available. Otherwise, the time switching Xfrom the mouse, used to select the object, to the keyboard for Xcommand input, may well cancel any advantage. X.PP XFinally, radical departures from the GEM metaphor may be Xuseful when attempting to replicate the look of another system, or Xtrying to meet severe constraints, such as display space. One Xexample would be discarding the standard GEM menus in favor of X"popup" menus which appear next to the current mouse position in Xresponse to a click on the second button. This method has the Xadvantage of preserving the menu space at the top of the screen, Xand is potentially faster because the menu appears right next to Xthe current mouse position. The drawbacks are lack of a visual Xcue for naive users trying to find the commands, and the need for Xcustom coding to build the popups. X.SH MORE TO COME XWe have reached the end of the second sermon Xon user interface. In a future column, I will look at "higher Xlevel" topics relating to the design of the application's user Xmetaphor. These include issues of object orientation, direct Xmanipulation, and the construction of microworlds. In the Xmeantime, several of the more practical columns will present Ximplementions of techniques such as accelarator keys and popup Xmenus which I have discussed this time. X.PP XTHANKS AND APOLOGIES to the following people whose public and Xpublished remarks have formed part of the basis of this Xdiscussion: Jef Raskin, Bill Buxton, Adele Goldberg, James Foley, Xand Ben Schneidermann. As always, any errors are my own. X.! X.! X.!***************************************************************************** X.!* * X.!* End Part XIV * X.!* * X.!***************************************************************************** SHAR_EOF if test 22068 -ne "`wc -c 'wind14.prf'`" then echo shar: error transmitting "'wind14.prf'" '(should have been 22068 characters)' fi echo shar: extracting "'tut.prf'" '(1236 characters)' if test -f 'tut.prf' then echo shar: over-writing existing file "'tut.prf'" fi sed 's/^X//' << \SHAR_EOF > 'tut.prf' X.!**************************************************************************** X.! X.! ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION. X.! X.! ** Professional GEM ** by Tim Oren X.! X.! Proff File by ST enthusiasts at X.! Case Western Reserve University X.! Cleveland, Ohio X.! uucp : decvax!cwruecmp!bammi X.! csnet: bammi@case X.! arpa : bammi%case@csnet-relay X.! compuserve: 71515,155 X.! X.!**************************************************************************** X.! X.! Begin Tutorial X.! X.!*************************************************************************** X.! X.! X.so macros.prf X.so wind1.prf X.so wind2.prf X.so wind3.prf X.so wind4.prf X.so wind5.prf X.so wind6.prf X.so wind7.prf X.so wind8.prf X.so wind10.prf X.so wind11.prf X.so wind12.prf X.so wind13.prf X.so wind14.prf X.! X.! Add any further Parts Above X.! X.cl 0 APPENDICES X.so apndx1.prf X.so apndx2.prf X.so apndx3.prf X.so apndx4.prf X.so apndx5.prf X.so apndx6.prf X.so apndx7.prf X.so apndx8.prf X.so apndx9.prf X.so apndx10.prf X.! X.! Add any further Appendicies Above X.! X.so toc.prf X.! X.! X.!**************************************************************************** X.! X.! End Tutorial X.! X.!*************************************************************************** SHAR_EOF if test 1236 -ne "`wc -c 'tut.prf'`" then echo shar: error transmitting "'tut.prf'" '(should have been 1236 characters)' fi echo shar: extracting "'macros.prf'" '(1170 characters)' if test -f 'macros.prf' then echo shar: over-writing existing file "'macros.prf'" fi sed 's/^X//' << \SHAR_EOF > 'macros.prf' X.!**************************************************************************** X.! X.! Macros X.! X.!**************************************************************************** X.! X.! Begin New Part X.! X.define PART X.he /Professional GEM//#/ X.bp X.bold on X.ce 1 XPART - $1 X.br X.sp 1 X.ce 1 X$2 $3 $4 $5 $6 $7 $8 $9 X.bold off X.cl 0 PART - $1. $2 $3 $4 $5 $6 $7 $8 $9 X.he /Professional GEM/Part $1/#/ X.sp 1 X.en X.! X.! Section Heading X.! X.define SH X.cl 1 $1 $2 $3 $4 $5 $6 $7 $8 $9 X.ne 5 X.sp 2 X.bold 1 X$1 $2 $3 $4 $5 $6 $7 $8 $9 X.br X.sp 1 X.ti +5 X.en X.! X.! Figure Begin X.! X.define FB X.ne 3 X.br X.cl 2 $1 X.sp 1 X.ce on X.en X.! X.! Figure End X.! X.define FE X.br X.ce off X.sp 1 X.en X.! X.! Paragraph X.! X.define PP X.ne 3 X.sp 1 X.ti +5 X.en X.! X.! Begin Option X.! X.define BO X.sp 1 X.in +4 X.en X.! X.! End Option X.define EO X.in -4 X.en X.! X.! X.define AP X.fi X.he /Professional GEM/Appendix $1/#/ X.bp X.cl 1 Appendix - $1 $2 $3 $4 $5 $6 X.ce on X.bold on XAppendix - $1 X.br X$2 $3 $4 $5 $6 X.bold off X.ce off X.sp 3 X.nf X.en X.! X.! X.!**************************************************************************** X.! X.! End Macros X.! X.!**************************************************************************** SHAR_EOF if test 1170 -ne "`wc -c 'macros.prf'`" then echo shar: error transmitting "'macros.prf'" '(should have been 1170 characters)' fi # End of shell archive exit 0 -- Jwahar R. Bammi Usenet: .....!decvax!cwruecmp!bammi CSnet: bammi@case Arpa: bammi%case@csnet-relay CompuServe: 71515,155