home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.lang.tcl
- Path: sparky!uunet!eco.twg.com!twg.com!news
- From: "David Herron" <david@twg.com>
- Subject: Keyboard focus and keyboard driven traversal
- Message-ID: <1992Jul28.214959.19452@twg.com>
- Sensitivity: Personal
- Encoding: 62 TEXT , 193 TEXT
- Sender: news@twg.com (USENET News System)
- Conversion: Prohibited
- Organization: The Wollongong Group, Inc., Palo Alto, CA
- Conversion-With-Loss: Prohibited
- Date: Tue, 28 Jul 1992 21:51:43 GMT
- Lines: 256
-
- Greetings!
-
- One of the fundamental things in Motif is that every application must be
- equally drivable from either keyboard or mouse. There are at least two
- reasons for this:
-
- - Some people are either unwilling or unable to use a mouse (effectively).
- While one might not have much sympathy for those unwilling to use mice,
- the ones `unable' to do so deserve lots of sympathy.
-
- - Some work environments are too busy and crowded to put a mouse on the
- persons desk. Take a look at a stock traders work area sometime. They
- have umpty-umpt screens in front of them, and a keyboard for each one
- lying around, a TV screen (or more), telephones, etc. No Room for extravagences
- like mice.
-
- There is a complicated set of rules which a Motif application must follow
- to allow this. It is all detailed in the style guide, but I have not had
- the time to learn it. Fortunately the Motif toolkit implements either the
- real thing, or something pretty close.
-
- The basic idea is that TAB, BACK-TAB, and the arrow keys move keyboard focus
- around the screen. So if you have a data entry form to fill out the keyboard
- use is: type value, TAB, type value, TAB, type value, ... When done TAB
- and ARROW to the right button, and hit RETURN. Voila' no mouse activity
- and much easier than: type, POSITION, type, POSITION, type, POSITION, ..., CLICK.
-
- The other part is that each widget has a way to trigger actions using the
- keyboard. Above I mentioned RETURN to activate a button, SPACE works as well.
- The arrow keys scroll listboxes or edit boxes. The arrow keys also move
- panes in paned windows. Hitting F10 enable keyboard driving of the
- menu bar, LEFT and RIGHT change which menu pane is pulled down, while UP
- and DOWN change which menu button is enabled. etc...
-
-
- For the application I'm writing at home (SEARCH) it seems that a lot of time
- is being spent moving back and forth between keyboard and mouse. To cut
- this down I wanted to drive as many things as possible from the keyboard.
- This meant having a mechanism for moving keyboard focus around the screen.
- Once keyboard focus is on a widget, to indicate this to the user.
-
- So I wrote up a TK module to handle focus setting. This has gone through
- at least 3 design-coding iterations, and is pretty useful at the moment.
-
- It does not match up with Motif's style guide in a couple of ways:
-
- - I could not figure out how to tell TK to bind an action to Shift<Key>Tab.
- So instead of BACK-TAB, this module uses Control-b.
-
- - It indicates focus differently from Motif. Motif draws a border around
- the outside of the widget. FOCUS changes the relief of a widget.
-
- - It does not use LEFT, RIGHT, UP or DOWN for some traversals, Motif does this
- when there are a group of buttons in the same area.
-
- - You cannot reach all widgets in the interface. Only those described to FOCUS.
-
-
- A weirdity in FOCUS is that you must initialize it by calling FOCUS:traverse
- after setting up all the traversals. (Or maybe it's FOCUS:enter?) Otherwise
- it doesn't work.
-
-
-
-
- #
- # $Id: focus.tcl,v 1.3 1992/07/21 15:36:33 david Exp $
- # focus -- Functions for implementing keyboard-driven focus traversal
- #
- # This module deals with FocusObjects as its unit-of-discourse.
- # A FocusObject is something which may have focus, and may
- # be traversed through. It has a couple of subfields as so:
- #
- # widget to which keyboard focus is to be directed.
- #
- # widget whose relief is to be modified.
- #
- # When a widget has focus, the relief-widget is made `raised'. When
- # it loses focus, it is made `flat'. Normally one will want
- # this relief-widget to be a frame widget. This is largely because
- # each widget has a `natural & preferred' relief which is part of
- # what identifies that widget to the user. In order for the relief
- # to show up, the frame must have -borderwidth > 0.
- #
- # Three main entry points exist:
- #
- # FOCUS:OBJ:create -- Creates an FocusObject instance.
- # FOCUS:OBJ:obj -- Get widget name for the FocusObject's object.
- # FOCUS:OBJ:frame -- Get the widget name for its frame.
- #
- # FOCUS:circle -- Sets up a circular chain of FocusObject traversals.
- # FOCUS:bind -- Sets up more specialized traversals.
- # FOCUS:now -- Return the FocusObject name where it is NOW.
- #
- # The FocusObjects are stored in a global array called FOCUS_OBJ_heap.
- # The `name' of the FocusObject is used to reference into the heap.
- # Each is stored as a TCL list, with members in the order specified above.
- #
- #
- #
- # $Log: focus.tcl,v $
- # Revision 1.3 1992/07/21 15:36:33 david
- # Create functions to return parts of FocusObject structure, and find
- # the FocusObject which corresponds to a certain widget. Add support
- # for rebinding the bindings. Remember where focus currently is.
- #
- # Revision 1.2 1992/07/19 19:41:02 david
- # Change indicator of when a widget has focus. Used to set background
- # color, now change -raised option on a frame widget.
- # Revamped API to focus module to allow for more terse descriptions
- # of the traversals. Did this through creation of the FocusObject
- # concept.
- #
- # Revision 1.1 1992/05/27 06:51:32 david
- # Initial revision.
- #
- #
- #
-
- proc FOCUS:OBJ:create {{name} {object} {frame}} {
- global FOCUS_OBJ_heap
- set FOCUS_OBJ_heap($name) [list $object $frame ]
- }
-
- proc FOCUS:OBJ:obj name {
- global FOCUS_OBJ_heap
- return [lindex $FOCUS_OBJ_heap($name) 0]
- }
-
-
- proc FOCUS:OBJ:frame name {
- global FOCUS_OBJ_heap
- return [lindex $FOCUS_OBJ_heap($name) 1]
- }
-
- proc FOCUS:OBJ:find object {
- global FOCUS_OBJ_heap
-
- foreach name [array names FOCUS_OBJ_heap] {
- set obj $FOCUS_OBJ_heap($name)
- if {"$object" == [lindex $obj 1] || \
- "$object" == [lindex $obj 0]} {
- return $name
- }
- }
- return ""
- }
-
- # FOCUS:circle -- Create a chain of FOCUS:traversals for the
- # Objects specified in the arg-list. Use <Tab> to move forward
- # through the circle, and <Control-b> to move backwards. We'd
- # **LIKE** to use BackTab for moving backwards, but cannot figure
- # out how to specify that to TK.
- #
- # We destroy any preexisting binding.
- #
- proc FOCUS:circle {args} {
- global FOCUS_OBJ_heap
-
- foreach arg $args {
- if {![info exists first]} {
- set first $arg
- set last $arg
- continue
- }
-
- set to $FOCUS_OBJ_heap($arg)
- set from $FOCUS_OBJ_heap($last)
-
- set to_w [lindex $to 0]
- set from_w [lindex $from 0]
-
- catch {bind $from_w <Tab>}
- bind $from_w <Tab> "FOCUS:traverse $last $arg"
- catch {bind $to_w <Control-b>}
- bind $to_w <Control-b> "FOCUS:traverse $arg $last"
-
- set last $arg
- }
-
- if {[info exists last]} {
- if {"$first" != "$last"} {
- set to $FOCUS_OBJ_heap($first)
- set from $FOCUS_OBJ_heap($last)
-
- set to_w [lindex $to 0]
- set from_w [lindex $from 0]
-
- catch {bind $from_w <Tab>}
- bind $from_w <Tab> "FOCUS:traverse $last $first"
- catch {bind $to_w <Control-b>}
- bind $to_w <Control-b> "FOCUS:traverse $first $last"
- }
- }
- }
-
- # FOCUS:bind -- Create special purpose bindings from a particular Object.
- # The Object is specified in $from_nm.
- # For each of the args, they are a TCL list of the following members
- # binding
- # destination FocusObject name
- #
- # We destroy any preexisting binding.
- #
- proc FOCUS:bind {{from_nm} args} {
- global FOCUS_OBJ_heap
-
- set from $FOCUS_OBJ_heap($from_nm)
- set from_w [lindex $from 0]
-
- foreach arg $args {
- set binding [lindex $arg 0]
- set to_nm [lindex $arg 1]
-
- set to $FOCUS_OBJ_heap($to_nm)
- set to_w [lindex $to 0]
-
- catch {bind $from_w $binding}
- bind $from_w $binding "FOCUS:traverse $from_nm $to_nm"
- }
- }
-
- # FOCUS:enter -- Utility to perform the `entering' part of the
- # focus traversal.
- #
- proc FOCUS:enter {{w} {f}} {
- focus $w
- $f configure -relief raised
- }
-
- # FOCUS:leave -- Handle the `leaving' part of traversal.
- #
- proc FOCUS:leave {{w} {f}} {
- $f configure -relief flat
- }
-
- # FOCUS:traverse -- Do the job of traversing from one widget to
- # the other. Simply call FOCUS:enter and FOCUS:leave
- # to do the trick.
- #
- proc FOCUS:traverse {{from_nm} {to_nm}} {
- global FOCUS_OBJ_heap FOCUS_now
-
- set to $FOCUS_OBJ_heap($to_nm)
- set from $FOCUS_OBJ_heap($from_nm)
-
- FOCUS:leave [lindex $from 0] [lindex $from 1]
- FOCUS:enter [lindex $to 0] [lindex $to 1]
-
- set FOCUS_now $to_nm
- }
-
- proc FOCUS:now {} {
- global FOCUS_now
- return $FOCUS_now
- }
-