Internal scrollbars

(This moved from Bag of Tk algorithms.)


Widget and accompanying scrollbar in one border

FW: On Windows (not sure about the other platforms) the scrollbar for a widget is always within that widget's border rather than simply next to it, to emphasize that they act as one unit. The only time I have ever seen a violation of this convention is in Tk applications. I have a fetish for a perfect platform-native look in an application, so in that interest, plus the fact that I simply find this look more pleasing aesthetically, I present this procedure. Before invoking this procedure, you should create your scrollbar and scrollable widget as usual within a frame, then provide the frame and widget in question in that order as arguments. It will remove the border from the main widget and set the frame's border to have the same attributes.

 proc scroll_combine {frame widget} {
   set border_width [$widget cget -borderwidth]
   $widget config -borderwidth 0
   $frame config -borderwidth $border_width -relief [$widget cget -relief]
 } ;# FW

LES: Some will call it "violation", others will call it "freedom". I once made an app with a widget and its scroll bar separated from each other. In that app's particular context, it seemed appropriate.

DKF: Interestingly, many people don't think of scrollbars as separate widgets, either attaching them to the widgets they scroll (as seems to be assumed here) or embedding ordinary widgets within a scrollable portal. The first is fine for simple cases, but tricky to extend to complex things, and the second is hardly better in that regard and also severely limits the area that can be scrolled (if you have a lot of data, 32k-pixels in any direction can be very limiting indeed!)


MGS [2003/08/10] - I use the following proc in several places, so I thought I would share. It creates a wrapper frame around a scrollable widget, and gives that internal look to its scrollbars.

 # scroll --

 # Description
 #   Create a scrollable-widget frame

 # Arguments
 #   type : type of  scrollable widget (e.g. listbox)
 #   W    : name for scrollable widget
 #   args : arguments to be passed to creation of scrollable widget

 # Return
 #   name of scrollable widget

 proc scroll {type W args} {

 # ----------------------------------------------------------------------

   set w $W.$type
   set x $W.x
   set y $W.y

 # ----------------------------------------------------------------------

   array set arg [list \
     -borderwidth 0 \
     -highlightthickness 0 \
     -relief flat \
     -xscrollcommand [list $x set] \
     -yscrollcommand [list $y set] \
   ]
   array set arg $args

 # ----------------------------------------------------------------------

   frame $W \
     -borderwidth 1 \
     -class Scroll \
     -highlightthickness 1 \
     -relief sunken \
     -takefocus 0

   # create the scrollable widget
   uplevel [linsert [array get arg] 0 $type $w]

   scrollbar $x \
     -borderwidth 0 \
     -elementborderwidth 1 \
     -orient horizontal \
     -takefocus 0 \
     -highlightthickness 0 \
     -command [list $w xview]

   scrollbar $y \
     -borderwidth 0 \
     -elementborderwidth 1 \
     -orient vertical \
     -takefocus 0 \
     -highlightthickness 0 \
     -command [list $w yview]

   grid columnconfigure $W 1 -weight 1
   grid    rowconfigure $W 1 -weight 1

   grid $w -column 1 -row 1 -sticky nsew
   grid $x -column 1 -row 2 -sticky nsew
   grid $y -column 2 -row 1 -sticky nsew

 # ----------------------------------------------------------------------

   return $w

 }

 # demo code
 set text [scroll text .scroll -wrap none]
 pack .scroll -side top -expand 1 -fill both
 for {set i 1} {$i <= 30} {incr i} {
   $text insert end "This is line $i of text widget $text\n"
 }

KBK 10 Aug 2003 - Very pretty. You might also want to consider scroll bars that appear only when needed.

MGS [2005/01/05] - I've packaged this up. See scroll.