GPS: Feb 12, 2004 - In reponse to a question asked on this Wiki about panning widgets I wrote a demonstration. This could fairly easily be turned into a reusable megawidget by someone with some time. :)
proc update.panner.view {win panner type args} { foreach {x1 y1 x2 y2} [$panner coords panner] {} if {"x" == $type} { set x1 [expr {[winfo width $panner] * [lindex $args 0]}] set x2 [expr {[winfo width $panner] * [lindex $args 1]}] } else { set y1 [expr {[winfo height $panner] * [lindex $args 0]}] set y2 [expr {[winfo height $panner] * [lindex $args 1]}] } $panner coords panner [list $x1 $y1 $x2 $y2] } proc start.drag {x y} { variable start_x variable start_y set start_x $x set start_y $y } proc drag {win panner x y} { variable start_x variable start_y set x_diff [expr {$x - $start_x}] set y_diff [expr {$y - $start_y}] foreach {sx1 sy1 sx2 sy2} [$win cget -scrollregion] {} foreach {xv1 xv2} [$win xview] {} set xview_limit [expr {1.0 - ($xv2 - $xv1)}] foreach {yv1 yv2} [$win yview] {} set yview_limit [expr {1.0 - ($yv2 - $yv1)}] set pxview [expr {((1.0 / [winfo width $panner]) * $x_diff) + $xv1}] if {$pxview < 0.0} { $win xview moveto 0.0 } elseif {$pxview > $xview_limit} { $win xview moveto $xview_limit } else { $win xview moveto $pxview set start_x $x } set pyview [expr {((1.0 / [winfo height $panner]) * $y_diff) + $yv1}] if {$pyview < 0.0} { $win yview moveto 0.0 } elseif {$pyview > $yview_limit} { $win yview moveto $yview_limit } else { $win yview moveto $pyview set start_y $y } } proc main {} { canvas .m -width 400 -height 400 -bg blue canvas .p -width 100 -height 100 -bg orange grid .p -row 0 -column 0 grid .m -row 0 -rowspan 2 -column 1 -sticky news grid rowconfigure . 1 -weight 100 grid columnconfigure . 1 -weight 100 .p create rectangle 0 0 100 100 -tags panner -fill purple .m create rectangle -300 20 -200 50 -fill green .m create rectangle -50 500 50 600 -fill red text .m.t -width 60 -height 20 .m create window 200 200 -window .m.t .m.t insert end "Hello, I'm a text widget embedded in a canvas with a\ panner.\n\nThe orange window represents the total area of the canvas.\ The purple area represents the current view area. You may\ shrink/enlarge the window and the panner will compensate.\n\ \nYou may use/copy/modify the code under the same terms as Tcl.\n\ \nBy George Peter Staplin." .m config -xscrollcommand {update.panner.view .m .p x} .m config -yscrollcommand {update.panner.view .m .p y} .m config -scrollregion {-500 0 500 1000} entry .e -textvariable ::eval_me grid .e bind .e <KeyPress-Return> {puts [uplevel #0 $::eval_me]} bind .p <ButtonPress-1> {start.drag %X %Y} bind .p <B1-Motion> {drag .m .p %X %Y} } main
EB: Such widget exists in BWidget, named ScrollView [L1 ].