Designing for Mouse and
Keyboard Copyright © 1999-2006 Clayton Jones |
by Clayton Jones |
One of the challenges we face in creating interfaces for database applications is providing for efficient operation using the keyboard as well as for the mouse. The many types of events, handled differently by various components and often passed to event handlers through the parent/child chain, can be challenging to master. I would like to present here a method which has proven to be very effective for data entry forms. It is based upon three design principles which can make this task faster, easier and free from conflicts. |
||||||||||||||||||||
The First Principle |
||||||||||||||||||||
The first principle: All action code is located in a single event handler
routine. This is similar to the concept we are familiar with in Clipper
of having only one exit point in a function. The centrally located code is easier
to maintain and less prone to conflicts. |
||||||||||||||||||||
The Second Principle |
||||||||||||||||||||
To help make order out of chaos, we have the second principle: Translate all events into a single type before sending them to the central event handler. With the PostAppEvent() function we have the ability to send any type of event we wish to any object. Because of the large number of possible events, I have found the simplest approach is to have the event handler trap for keyboard events only. The most efficient way to do this is to have a keyhandler function for the dialog window and redirect all mouse and keyboard events from components into it, with all non-keyboard events being "translated" into keyboard events. Our task in writing action codeblocks on the component level becomes one of simply sending the appropriate keyboard event into the handler where the action code is executed. This process can be made more difficult, however, by the fact that some components pass certain keystrokes up the parent/child chain while retaining others. For example, when an xbpPushbutton has focus, it handles keystrokes in three different ways. Table 1 describes these three behaviors and the related keystrokes:
|
||||||||||||||||||||
Table 1 |
||||||||||||||||||||
The Third Principle | ||||||||||||||||||||
Knowledge of these behaviors leads us to the third principle: Always
have a single entry point into the keyhandler. It is tempting to ask,
"Why not call the keyhandler directly from a component codeblock? Why go the
long way around by sending it to the window first?" |
||||||||||||||||||||
The Code |
||||||||||||||||||||
Looking first at the action code in the keyhandler function, TVkeys(), we see that only two keystrokes are trapped, Enter and Esc: IF nKey == xbeK_ESC At the top of tdTabView where the window is created, we find that the codeblock for the window's :keyboard slot has a direct call to the keyhandler. Any keyboard event posted to the window will end up in this codeblock and be sent to TVkeys for handling: oModal:keyBoard := ; oBrow:itemSelected := {|u1,u2,self|
; ******* Select |
||||||||||||||||||||
The Results |
||||||||||||||||||||
At this point we have assigned code blocks to all of the objects: the window, the browse, and the two buttons. Table 2 lists nine possible user actions for making a decision, and how they are handled:
|
||||||||||||||||||||
Table 2 |
||||||||||||||||||||
From these nine
actions only two different keystrokes are sent to TVkeys: Enter and Esc.
This saves us from having to write complex logic to trap for many different
possibilities. Any other keystrokes which might be passed up the chain are also sent
to TVkeys, but are ignored. |
||||||||||||||||||||
Conclusion |
||||||||||||||||||||
This example
is an extremely simple one and serves well to illustrate the principles. It may
even seem like overkill to apply such logic to something so simple. However, with a
busy form containing group boxes, tab pages, and numerous buttons and data-aware objects,
the complexity can easily get out of control. Then the value of this method really
proves itself. I find it helpful when designing a form to make a list of the components and what actions they will perform, along with all possible user actions. This helps to get a clear picture of what keystrokes need to be trapped, which components need codeblocks, and what should be in them. Doing this in advance more than makes up for the time it takes. It virtually eliminates the possibility of conflicts that can keep a developer busy long after the form is written. |
Copyright © 1999-2006 Clayton Jones |
Return to: Articles Page |Top-Down Page | Software And Services
| Home