Version 1 of Move cursor by display line in a text widget

Updated 2002-03-05 20:25:57

Christian Heide Damm responded in comp.lang.tcl [L1 ] on how to move the cursor up and down visible lines instead of real text widget lines:

...namely binding up/down to something like this:

     # Find the coordinates of the cursor and set the new height
     # manually. Note: errors rounding off, since
     # coordinates don't match character positions exactly.
     lset {lines char} [split [$textWidget index insert] .]
     lset {x y textWidth textHeight} [$textWidget bbox [$textWidget
     index insert]]
     lset {_ maxy _ _} [$textWidget bbox "end - 1 char"]
     # When updating position, make sure y is within text boundaries
     switch -- $upOrDown {
       "up" {
          set y [max [expr $y-$textHeight] 0]
       }
       "down" {
          set y [min [expr $y+$textHeight] $maxy]
       }
     }
     lset {newx newy width _} [$textWidget bbox [$textWidget index
     @$x,$y]]
     # Test on which side of the character
     # we should position the cursor
     if {$x>[expr $newx+$width/2]} {
       set x [expr $newx+$width+1]
     }
     set newIndex [$textWidget index @$x,$y]

Here is the above code in a function, with the non-existent function lset replaced with calls to scan and missing functions min/max defined. Note that this code needs to be modified to handle more than a screenful of text:

 proc min args {lindex [lsort -real $args] 0}
 proc max args {lindex [lsort -real $args] end}
 proc moveUpDown {textWidget upOrDown} {
     # Find the coordinates of the cursor and set the new height
     # manually. Note: errors rounding off, since
     # coordinates don't match character positions exactly.
     scan [$textWidget index insert] {%d.%d} lines char
     scan [$textWidget bbox [$textWidget index insert]] {%d %d %d %d} x y textWidth textHeight
     scan [$textWidget bbox "end - 1 char"] {%*d %d %*d %*d} maxy
     # When updating position, make sure y is within text boundaries
     switch -- $upOrDown {
       "up" {
          set y [max [expr $y-$textHeight] 0]
       }
       "down" {
          set y [min [expr $y+$textHeight] $maxy]
       }
     }
     scan [$textWidget bbox [$textWidget index @$x,$y]] {%d %d %d %*d} newx newy width

     # Test on which side of the character
     # we should position the cursor
     if {$x>[expr $newx+$width/2]} {
       set x [expr $newx+$width+1]
     }
     return [$textWidget index @$x,$y]
 }

 # Replace the default Text widget bindings to try it out
 bind Text <Up> {
    tkTextSetCursor %W [moveUpDown %W up]
 }
 bind Text <Down> {
    tkTextSetCursor %W [moveUpDown %W down]
 }