home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / comp / lang / tcl / 1061 < prev    next >
Encoding:
Text File  |  1992-07-28  |  8.5 KB  |  271 lines

  1. Newsgroups: comp.lang.tcl
  2. Path: sparky!uunet!eco.twg.com!twg.com!news
  3. From: "David Herron" <david@twg.com>
  4. Subject: Keyboard focus and keyboard driven traversal
  5. Message-ID: <1992Jul28.214959.19452@twg.com>
  6. Sensitivity: Personal
  7. Encoding:  62 TEXT , 193 TEXT 
  8. Sender: news@twg.com (USENET News System)
  9. Conversion: Prohibited
  10. Organization: The Wollongong Group, Inc., Palo Alto, CA
  11. Conversion-With-Loss: Prohibited
  12. Date: Tue, 28 Jul 1992 21:51:43 GMT
  13. Lines: 256
  14.  
  15. Greetings!
  16.  
  17. One of the fundamental things in Motif is that every application must be
  18. equally drivable from either keyboard or mouse.  There are at least two
  19. reasons for this:
  20.  
  21. - Some people are either unwilling or unable to use a mouse (effectively).
  22.   While one might not have much sympathy for those unwilling to use mice,
  23.   the ones `unable' to do so deserve lots of sympathy.
  24.  
  25. - Some work environments are too busy and crowded to put a mouse on the
  26.   persons desk.  Take a look at a stock traders work area sometime.  They
  27.   have umpty-umpt screens in front of them, and a keyboard for each one
  28.   lying around, a TV screen (or more), telephones, etc.  No Room for extravagences
  29.   like mice.
  30.  
  31. There is a complicated set of rules which a Motif application must follow
  32. to allow this.  It is all detailed in the style guide, but I have not had
  33. the time to learn it.  Fortunately the Motif toolkit implements either the
  34. real thing, or something pretty close.
  35.  
  36. The basic idea is that TAB, BACK-TAB, and the arrow keys move keyboard focus
  37. around the screen.  So if you have a data entry form to fill out the keyboard
  38. use is: type value, TAB, type value, TAB, type value, ...  When done TAB
  39. and ARROW to the right button, and hit RETURN.  Voila' no mouse activity
  40. and much easier than: type, POSITION, type, POSITION, type, POSITION, ..., CLICK.
  41.  
  42. The other part is that each widget has a way to trigger actions using the 
  43. keyboard.  Above I mentioned RETURN to activate a button, SPACE works as well.
  44. The arrow keys scroll listboxes or edit boxes.  The arrow keys also move
  45. panes in paned windows.  Hitting F10 enable keyboard driving of the
  46. menu bar, LEFT and RIGHT change which menu pane is pulled down, while UP
  47. and DOWN change which menu button is enabled.  etc...
  48.  
  49.  
  50. For the application I'm writing at home (SEARCH) it seems that a lot of time
  51. is being spent moving back and forth between keyboard and mouse.  To cut
  52. this down I wanted to drive as many things as possible from the keyboard.
  53. This meant having a mechanism for moving keyboard focus around the screen.
  54. Once keyboard focus is on a widget, to indicate this to the user.
  55.  
  56. So I wrote up a TK module to handle focus setting.  This has gone through
  57. at least 3 design-coding iterations, and is pretty useful at the moment.
  58.  
  59. It does not match up with Motif's style guide in a couple of ways:
  60.  
  61. - I could not figure out how to tell TK to bind an action to Shift<Key>Tab.
  62.   So instead of BACK-TAB, this module uses Control-b.
  63.  
  64. - It indicates focus differently from Motif.  Motif draws a border around
  65.   the outside of the widget.  FOCUS changes the relief of a widget.
  66.  
  67. - It does not use LEFT, RIGHT, UP or DOWN for some traversals, Motif does this
  68.   when there are a group of buttons in the same area.
  69.  
  70. - You cannot reach all widgets in the interface.  Only those described to FOCUS.
  71.  
  72.  
  73. A weirdity in FOCUS is that you must initialize it by calling FOCUS:traverse
  74. after setting up all the traversals.  (Or maybe it's FOCUS:enter?)  Otherwise
  75. it doesn't work.
  76.  
  77.  
  78.  
  79.  
  80. #
  81. # $Id: focus.tcl,v 1.3 1992/07/21 15:36:33 david Exp $
  82. # focus -- Functions for implementing keyboard-driven focus traversal
  83. #
  84. # This module deals with FocusObjects as its unit-of-discourse.
  85. # A FocusObject is something which may have focus, and may
  86. # be traversed through.  It has a couple of subfields as so:
  87. #
  88. #    widget to which keyboard focus is to be directed.
  89. #
  90. #    widget whose relief is to be modified.
  91. #
  92. # When a widget has focus, the relief-widget is made `raised'.  When
  93. # it loses focus, it is made `flat'.  Normally one will want
  94. # this relief-widget to be a frame widget.  This is largely because
  95. # each widget has a `natural & preferred' relief which is part of
  96. # what identifies that widget to the user.  In order for the relief
  97. # to show up, the frame must have -borderwidth > 0.
  98. #
  99. # Three main entry points exist:
  100. #
  101. # FOCUS:OBJ:create -- Creates an FocusObject instance.
  102. # FOCUS:OBJ:obj    -- Get widget name for the FocusObject's object.
  103. # FOCUS:OBJ:frame  -- Get the widget name for its frame.
  104. #
  105. # FOCUS:circle     -- Sets up a circular chain of FocusObject traversals.
  106. # FOCUS:bind       -- Sets up more specialized traversals.
  107. # FOCUS:now        -- Return the FocusObject name where it is NOW.
  108. #
  109. # The FocusObjects are stored in a global array called FOCUS_OBJ_heap.
  110. # The `name' of the FocusObject is used to reference into the heap.
  111. # Each is stored as a TCL list, with members in the order specified above.
  112. #
  113. #
  114. #
  115. # $Log: focus.tcl,v $
  116. # Revision 1.3  1992/07/21  15:36:33  david
  117. # Create functions to return parts of FocusObject structure, and find
  118. # the FocusObject which corresponds to a certain widget.  Add support
  119. # for rebinding the bindings.  Remember where focus currently is.
  120. #
  121. # Revision 1.2  1992/07/19  19:41:02  david
  122. # Change indicator of when a widget has focus.  Used to set background
  123. # color, now change -raised option on a frame widget.
  124. # Revamped API to focus module to allow for more terse descriptions
  125. # of the traversals.  Did this through creation of the FocusObject
  126. # concept.
  127. #
  128. # Revision 1.1  1992/05/27  06:51:32  david
  129. # Initial revision.
  130. #
  131. #
  132. #
  133.  
  134. proc FOCUS:OBJ:create {{name} {object} {frame}} {
  135.     global FOCUS_OBJ_heap
  136.     set FOCUS_OBJ_heap($name) [list $object $frame ]
  137. }
  138.  
  139. proc FOCUS:OBJ:obj name {
  140.     global FOCUS_OBJ_heap
  141.     return [lindex $FOCUS_OBJ_heap($name) 0]
  142. }
  143.  
  144.  
  145. proc FOCUS:OBJ:frame name {
  146.     global FOCUS_OBJ_heap
  147.     return [lindex $FOCUS_OBJ_heap($name) 1]
  148. }
  149.  
  150. proc FOCUS:OBJ:find object {
  151.     global FOCUS_OBJ_heap
  152.  
  153.     foreach name [array names FOCUS_OBJ_heap] {
  154.         set obj $FOCUS_OBJ_heap($name)
  155.         if {"$object" == [lindex $obj 1] || \
  156.             "$object" == [lindex $obj 0]} {
  157.             return $name
  158.         }
  159.     }
  160.     return ""
  161. }
  162.  
  163. # FOCUS:circle -- Create a chain of FOCUS:traversals for the
  164. #    Objects specified in the arg-list.  Use <Tab> to move forward
  165. #    through the circle, and <Control-b> to move backwards.  We'd
  166. #    **LIKE** to use BackTab for moving backwards, but cannot figure
  167. #    out how to specify that to TK.
  168. #
  169. # We destroy any preexisting binding.
  170. #
  171. proc FOCUS:circle {args} {
  172.     global FOCUS_OBJ_heap
  173.  
  174.     foreach arg $args {
  175.         if {![info exists first]} {
  176.             set first $arg
  177.             set last $arg
  178.             continue
  179.         }
  180.  
  181.         set to $FOCUS_OBJ_heap($arg)
  182.         set from $FOCUS_OBJ_heap($last)
  183.  
  184.         set to_w   [lindex $to   0]
  185.         set from_w [lindex $from 0]
  186.  
  187.         catch {bind $from_w <Tab>}
  188.         bind $from_w <Tab>       "FOCUS:traverse $last $arg"
  189.         catch {bind $to_w   <Control-b>}
  190.         bind $to_w   <Control-b> "FOCUS:traverse $arg  $last"
  191.  
  192.         set last $arg
  193.     }
  194.  
  195.     if {[info exists last]} {
  196.         if {"$first" != "$last"} {
  197.             set to $FOCUS_OBJ_heap($first)
  198.             set from $FOCUS_OBJ_heap($last)
  199.  
  200.             set to_w   [lindex $to   0]
  201.             set from_w [lindex $from 0]
  202.  
  203.             catch {bind $from_w <Tab>}
  204.             bind $from_w <Tab>       "FOCUS:traverse $last  $first"
  205.             catch {bind $to_w   <Control-b>}
  206.             bind $to_w   <Control-b> "FOCUS:traverse $first $last"
  207.         }
  208.     }
  209. }
  210.  
  211. # FOCUS:bind -- Create special purpose bindings from a particular Object.
  212. #    The Object is specified in $from_nm.
  213. #    For each of the args, they are a TCL list of the following members
  214. #        binding
  215. #        destination FocusObject name
  216. #
  217. # We destroy any preexisting binding.
  218. #
  219. proc FOCUS:bind {{from_nm} args} {
  220.     global FOCUS_OBJ_heap
  221.  
  222.     set from $FOCUS_OBJ_heap($from_nm)
  223.     set from_w [lindex $from 0]
  224.  
  225.     foreach arg $args {
  226.         set binding [lindex $arg 0]
  227.         set to_nm   [lindex $arg 1]
  228.  
  229.         set to   $FOCUS_OBJ_heap($to_nm)
  230.         set to_w [lindex $to   0]
  231.  
  232.         catch {bind $from_w $binding}
  233.         bind $from_w $binding "FOCUS:traverse $from_nm $to_nm"
  234.     }
  235. }
  236.  
  237. # FOCUS:enter -- Utility to perform the `entering' part of the
  238. #    focus traversal.
  239. #
  240. proc FOCUS:enter {{w} {f}} {
  241.     focus $w
  242.     $f configure -relief raised
  243. }
  244.  
  245. # FOCUS:leave -- Handle the `leaving' part of traversal.
  246. #
  247. proc FOCUS:leave {{w} {f}} {
  248.     $f configure -relief flat
  249. }
  250.  
  251. # FOCUS:traverse -- Do the job of traversing from one widget to
  252. #    the other.  Simply call FOCUS:enter and FOCUS:leave
  253. #    to do the trick.
  254. #
  255. proc FOCUS:traverse {{from_nm} {to_nm}} {
  256.     global FOCUS_OBJ_heap FOCUS_now
  257.  
  258.     set to   $FOCUS_OBJ_heap($to_nm)
  259.     set from $FOCUS_OBJ_heap($from_nm)
  260.  
  261.     FOCUS:leave [lindex $from 0] [lindex $from 1]
  262.     FOCUS:enter [lindex $to   0] [lindex $to   1]
  263.  
  264.     set FOCUS_now $to_nm
  265. }
  266.  
  267. proc FOCUS:now {} {
  268.     global FOCUS_now
  269.     return $FOCUS_now
  270. }
  271.