6—
Scrollbars
Scrollbars are used with widgets when there is more to see than can be shown at once. One or two scrollbars allow a user to scroll a widget's contents horizontally and/or vertically. You've seen a scrollbar on many different types of applications. Every major word processor has scrollbars. A drawing program has scrollbars. Even your web browser has scrollbars. This chapter will show you how you can use scrollbars with certain Perl/Tk widgets.
Defining Scrollbar Parts
Figure 6-1 shows all the different parts of a scrollbar and their names.
0124-01.gif
Figure 6-1.
Different parts of a scrollbar
The trough is the sunken part between the two arrows. It is divided into two parts, trough1 and trough2, by the slider. The slider is the rectangle that indicates how much of the window is available for scrolling. If you were in the middle of the list, you would see the slider rectangle in the center of the trough with space on either side of it. The arrows on either end are called arrow1 and arrow2. If the scrollbar were vertical (rotated 90 degrees clockwise), arrow1 would be the top arrow.
Clicking on either arrow will move the information in the associated widget one unit at a time. What the unit is depends on what type of widget the scrollbar is associated with. With an entry widget, the units are characters. With a listbox widget and a vertical scrollbar, the units are lines. Clicking in the trough on either side of the slider will page the information in the widget in that direction. You can also click directly on the slider and, holding the mouse button down, move it directly.
Scrollbars can be horizontal or vertical. Typically, they reside on the bottom and/or to the right of the widget they are scrolling, but not always.
Some of the Perl/Tk widgets that can be configured for use with scrollbars are text, listbox, canvas, entry, ghostview, hlist, and tiler. Only the first four widgets (text, listbox, canvas, entry) are covered in this book. See Figures 6-2 through 6-5 for examples of scrollbars with each of the covered widgets.
0125-01.gif
Figure 6-2.
Entry widget with a scrollbar
0125-02.gif
Figure 6-3.
Listbox widget with scrollbar
There are two ways to create and configure scrollbars for use with widgets. You can use the Scrollbar widget creation command, or you can use the Scrolled method to create the widget and associated scrollbars. Both have their advantages and disadvantages. Using Scrolled is much less work and requires less coding, but it won't let you do anything fancy like associate the same scrollbar with two different widgets. Creating the scrollbar widgets yourself takes more code, but you can do much fancier things with them since you'll have direct control over where
0126-01.gif
Figure 6-4.
Text widget with scrollbar. Text widget is displaying Scrollbar.pm file
0126-02.gif
Figure 6-5.
Canvas widget with scrollbars
they go and which widget(s) they are associated with. This chapter will cover both methods of creating scrollbars.
The Scrolled Method
To create a widget and scrollbars at the same time, use the Scrolled method. Scrolled returns a pointer to the widget created. It is the easiest way to add scrollbars to a scrollable widget. The method creates a frame, which contains the widget and scrollbar(s). You create them all in one command.
The usage for the Scrolled method is:
$widget = $parent->Scrolled('Widget',
                             -scrollbars => 'string' [, options ]);
The first argument is the widget to create, such as "Listbox" or "Canvas". The other argument you'll need to use is the -scrollbars option, which takes a string that tells it which scrollbars to create and where to put them.
The possible values for -scrollbars are "n", "s", "e", "w", or "on", "os", "oe", "ow", or some combination of those that combines n or s with an e or w. The "n" means to put a horizontal scrollbar above the widget. An "s" means to put a horizontal scrollbar below the widget. The "e" means to put a vertical scrollbar to the right of the widget. The "w" means to put a vertical scrollbar to the left of the widget.
You can have a maximum of two scrollbars for each widget. For instance, we can create one scrollbar on the "n" side of the widget. It is possible to use "nw" to create two scrollbars, one on the top and one on the left of the widget. It is not legal to use "ns" because "n" and "s" scroll in the same direction.
The "o" in front of the direction makes that scrollbar optional. Optional scrollbars will only display when the size of the widget makes it necessary to scroll the information in the widget. Always list the north or south value first (if you use either) to avoid complaints from the subroutine. Here are some examples to make this clearer:
# Create optional scrollbar east (to the right) of widget
$lb = $mw->Scrolled("Listbox", -scrollbars => 'oe')->pack;

# Create scrollbars to south (below) and east (to the right) of widget
$lb = $mw->Scrolled("Listbox", -scrollbars => 'se')->pack;

# Create optional scrollbars south (below) and east (right) of widget
$lb = $mw->Scrolled("Listbox", -scrollbars => 'osoe')->pack;

# Create scrollbars to the north (above) and west (to the left) of widget
$lb = $mw->Scrolled("Listbox", -scrollbars => 'nw')->pack;
Configuring the Scrollbar(s) Created with Scrolled
Any other options sent with the Scrolled method will configure only the widget created. If you need to configure the scrollbars, use the Subwidget method from the widget reference. The Subwidget method can be used because a Scrolled widget is really a composite widget. Composite widgets are covered in Chapter 15, Composite Widgets.
To turn the background of your horizontal scrollbar green, use this code:
$lb->Subwidget("xscrollbar")->configure(-background => "green");
To configure a vertical scrollbar, use "yscrollbar" in place of "xscrollbar". If you try to configure a scrollbar that you didn't create (for instance, you used -scrollbars => "e" and tried to configure the "xscrollbar"), nothing will happen.
To configure just the widget, you can use $widget->configure after calling Scrolled(), or you can use:
$widget->Subwidget("widget")->configure(...);
Using Subwidget this way is silly because you can just use $widget. The "widget" string is the same as the first argument sent to Scrolled, except it's all lowercase. For instance, in the preceding example we called Scrolled with "Listbox", but we would use "listbox" with the Subwidget method.
The Scrollbar Widget
0128-01.gif Instead of automatically creating one or more scrollbars with the Scrolled method, you can use the Scrollbar widget method and perform the configuration yourself. It is better to create and configure your own scrollbars when you need to do something nonstandard, such as having one scrollbar scroll two listboxes.
Creating a Scrollbar Widget
To create the scrollbar, invoke the Scrollbar method from the parent widget. It returns a reference to the newly created scrollbar that you can use for configuration:
$scrollbar = $mw->Scrollbar([ options ...])
There are at least two other things you need to do to get a scrollbar working with another widget. First, create the to-be-scrolled widget and use the scrollbar with its -xscrollcommand or -yscrollcommand option. Then configure the scrollbar so that it knows to talk to that widget. Here's an example that creates a listbox widget (don't worry if you don't quite follow all of this now; I just want to show a complete example before we go on to talk about all the options):
# Create the vertical scrollbar
$scrollbar = $mw->Scrollbar();
$lb = $mw->Listbox(-yscrollcommand => ['set' => $scrollbar]);
#Configure the scrollbar to talk to the listbox widget
$scrollbar->configure(-command => ['yview' => $lb]);

#Pack the scrollbar first so that it doesn't disappear when we resize
$scrollbar->pack(-side => 'right', -fill => 'y');
$lb->pack(-side => 'left', -fill => 'both');
Creating the scrollbar is pretty simple; we want all the default options for it. As we create the listbox, we have to set up a callback so the listbox can communicate with the scrollbar when the contents of the listbox move around. Our scrollbar is vertical, so the -yscrollcommand option has the set command and our scrollbar assigned to it (if it is horizontal, use -xscrollcommand). When the contents of the listbox are scrolled by the user without using the scrollbar, the listbox will alert the scrollbar by invoking $scrollbar->set (...).
The line $scrollbar->configure(-command => ['yview' => $lb]) does almost the opposite; it configures the scrollbar to communicate with the listbox. When the user clicks on the scrollbar, the scrollbar will invoke $lb->yview(...) to tell the listbox how to change the view of the contents. We use the "y" version of the view command because this is a vertical scrollbar.
There is more information on the details of yview in "How the Scrollbar Communicates with Other Widgets," later in this chapter. The last two lines in this example pack the scrollbar and the listbox in the window so that the scrollbar is the same height as the listbox and lies to the right of the listbox.
Always pack your scrollbars first within the window or frame. This allows the scrollbars to remain visible when the user resizes the window smaller. It will then resize the listbox (or other widget) but leave the scrollbars visible on the edges of the screen.
Now that we've seen a complete example of how to create a scrollbar and how to set up the widget it will scroll, we can go over the options with an idea of how they are used.
Scrollbar Options
This list contains the options available with a scrollbar, and their quick definitions. The important options are discussed in more detail later in this chapter.
-activebackground => color
Sets the color the scrollbar should be when the mouse pointer is over it.
-activerelief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken'
The -activerelief option determines how active elements are drawn. The elements in question are arrow1, arrow2, and the slider.
-background => color
Sets the background color of the scrollbar (not the trough color).
-borderwidth => amount
Sets the width of the edges of the scrollbar and the
arrow1, arrow2, and slider elements.
-command => callback
Sets the callback that is invoked when the scrollbar is clicked.
-cursor => cursorname
Sets the cursor that is displayed when the mouse pointer is over the scrollbar.
-elementborderwidth => amount
Sets the width of the borders of the
arrow1, arrow2, and slider elements.
-highlightbackground => color
Sets the color the highlight rectangle around the scrollbar widget should be when it does not have the keyboard focus.
-highlightcolor => color
Sets the color the highlight rectangle around the scrollbar should be when it does have the keyboard focus.
-highlightthickness => amount
Sets the thickness of the highlight rectangle. Default is 2.
-jump => 0 | 1
Indicates whether or not the scrollbar will jump scroll.
-orient => "horizontal" | "vertical"
Sets the orientation of the scrollbar.
-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken' | 'solid'
Changes the edges of the widget.
-repeatdelay => time
Sets the number of milliseconds required to hold down an arrow before it will auto-repeat. Default is 300 ms.
-repeatinterval => time
Sets the number of milliseconds in between auto-repeats. Default is 100 ms.
-takefocus => 0 | 1 | undef
Controls whether the scrollbar can obtain the keyboard focus.
-troughcolor => color
Changes the color of the trough (both trough1 and trough2).
-width => amount
Sets the width of the scrollbar.
Scrollbar Colors
Within the scrollbar, we have a new part of the widget called a trough. This trough gets its own coloring through the -troughcolor option. The trough is considered the part behind the arrows and slider. Figure 6-6 shows an example.
0131-01.gif
Figure 6-6.
Scrollbar with -troughcolor set to 'green'
The background of the scrollbar consists of the arrows, the slider, and a small portion around the outside of the trough. You change the color of the background by using the -background option. The -activebackground option controls the color that is displayed when the mouse cursor is over one of the arrows or the slider. Figure 6-7 shows two examples of -background; the second window uses both -background and -troughcolor.
0131-02.gif
Figure 6-7.
Examples of -background option
Scrollbar Style
The -relief and -borderwidth options affect both the outside edges of the scrollbar and the arrow1, arrow2, and slider elements. This is similar to how the checkbutton and radiobutton widgets are affected by the -relief and -borderwidth options. See Figure 6-8 for a screen shot of different values for these two options.
0131-03.gif
Figure 6-8.
First row shows different relief values; second row different relief values with
-borderwidth => 4
The -activerelief option affects the decoration of three elements-arrow1, arrow2, and the slider-when the mouse cursor is over them. The -elementborderwidth also affects the same three elements: arrow1, arrow2, and the slider. The width of the edges of these elements can be changed with this option. The -borderwidth option also changes the width of these elements but also changes the width of the edges of the widget. Notice in Figure 6-9 how the edges of the scrollbar remain at a width of 2.
0132-01.gif
Figure 6-9.
Example of -elementborderwidth set to 4
The -width of the scrollbar is the distance across the skinny part of the scrollbar, not including the borders. Figure 6-10 demonstrates how the scrollbar changes when you alter the -width.
0132-02.gif
Figure 6-10.
Top scrollbar has default width of 15, bottom scrollbar has width of 20
Scrollbar Orientation
As mentioned earlier, a scrollbar can be vertical or horizontal. The default for a scrollbar is 'vertical'. To change this, use the -orient option:
$scrollbar = $mw->Scrollbar(-orient => 'horizontal');
You could also use -orient => 'vertical', but since this is the default, it is not necessary.
Using the Arrows and Slider
When you click on one of the arrows in a scrollbar, you cause the slider to move in that direction by one unit. If you continue to hold the mouse button down, after a bit of a delay, the slider will auto-repeat that movement. The amount of time you must wait before the auto-repeat kicks in is determined by the -repeatdelay option. The default is 300 milliseconds.
Once you have held the mouse button down long enough to start auto-repeating, there is a short delay between each time it repeats the action. This delay is controlled by the -repeatinterval option. The default for -repeatinterval is 100 milliseconds.
Normally, when you click on the slider and move it around, the data within the widget will move accordingly. This is because the scrollbar is updating the widget continuously as you move the slider. To change the scrollbar so that it will only update the widget when you let go of the slider, use the -jump option and set it to 1. The default for -jump is 0. You would most likely want to use -jump => 1
when your scrolled widget contains a large amount of data, and waiting for the screen to update while you slide through it would make the application seem slow.
Assigning a Callback
When you create a scrollbar, you tell it which widget to talk to and which method in that widget to call by using the -command option with an anonymous list. The list contains the name of the method to call and the widget from which that method should be invoked. In this code snippet, we can see that we want to use the yview command to scroll the widget $lb (a listbox):
$scrollbar->configure(-command => ['yview' => $lb])
Now when the scrollbar gets clicked on by the user, it will invoke $lb->yview. We know that the scrollbar associated with $lb is vertical because it uses the yview command. For a horizontal scrollbar, use xview. Both yview and xview tell the widget to move the widget contents an amount that is determined by where the user clicked in the scrollbar. The yview and xview methods are covered in the next section.
How the Scrollbar Communicates with Other Widgets
As described earlier, you use the -command option with the scrollbar so it knows which widget and method to use when the scrollbar is clicked. The command should be xview for horizontal scrollbars and yview for vertical scrollbars. You can call these methods yourself, but most of the time you won't want to.
Both xview and yview take the same type of arguments. Where the user clicked in the scrollbar determines the value used, but the value will always be sent as one of the following forms:
$widget->xviewMoveto(fraction); # or
$widget->yviewMoveto(fraction);
This form is used when the user clicks on the slider, moves it around, and drops it again. The argument is a fraction, a real number from 0 to 1 that represents the first part of the data to be shown within the widget. If the user moved the slider all the way to the top or left of the scrollbar, the very first part of the data in the widget should be seen on the screen. This means the argument should be 0:
$widget->xviewMoveto(0);
If the slider were moved to the center of the scrollbar, the argument is 0.5:
$widget->xviewMoveto(0.5);
$widget->xviewScroll(number, "units"); # or
$widget->yviewScroll(number, "units");
This form is used when the user clicks on one of the arrow elements in the scrollbar. The widget should move its data up/down or left/right unit by unit.
The first argument is the number of units to scroll by. The value for number can be any number, but it's typically either 1 or -1. A value of 1 means that the next unit of data on the bottom or right of the widget becomes visible (scrolling one unit of data off the left or top). A value of -1 means that a previous unit of data will become visible in the top or right of the widget (one unit will scroll off the bottom or right of the widget). For example, every time the user clicks on the down arrow in a vertical scrollbar associated with a listbox, a new line shows up at the bottom of the listbox.
The second argument is the string "units". What a unit is depends on the widget. In a listbox, a unit would mean one line of text. In an entry widget, it would be one character.
Here are some example calls:
# User clicked down arrow
$listbox->yviewScroll(1, "units");

# User clicked up arrow
$listbox->yviewScroll(-1, "units");

# User clicked right arrow
$entry->xviewScroll(1, "units");
$widget->xviewScroll(number, "page"); # or
$widget->yviewScroll(number, "page");
This form is exactly like our previous one except the last argument is "page" instead of "units". When users click in the trough area of the scrollbar (between the slider and arrows), they expect to see the data move by an entire page.
The type of page is defined by the widget being scrolled. For example, a listbox would page up/down by the number of lines shown in the listbox. It would page right/left by the width of the listbox.
Scrollbar Configuration
You can get and set any of the options available with a scrollbar by using cget and configure. See Appendix A, Configuring Widgets with configure and cget, for complete details on these methods.
Defining What We Can See
The set method, which we tell the scrolled widget about when we create it, defines what is visible. In our first example, we created a listbox and told it to use our scrollbar and the set method:
$scrollbar = $mw->Scrollbar();   # Vertical scrollbar
$lb = $mw->Listbox(-yscrollcommand => ['set' => $scrollbar ]);
When the widget invokes the set command, it sends two fractions (first and last) as the arguments:
$scrollbar->set (first, last);
This will change the position in the data that we are seeing. The arguments first and last are real numbers between 0 and 1. They represent the position of the first data item we can see and the position of the last data item we can see, respectively. If we can see all of the data in our widget, they would be 0 and 1. The first value gets larger as more data is scrolled off the top, and the last value gets smaller as more data is scrolled off the bottom. You will probably never find a case in which to call set yourself, so just try to get an idea of what it does behind the scenes.
0135-01.gif
Figure 6-11.
View of data through widget by set method (assumes vertical scrollbar)
Figure 6-11 shows a hypothetical document that we are viewing with a vertically scrolled widget. The dashed rectangle represents the view of what we can currently see within the widget. When the widget calls set, it determines how far into the document the first viewable item is and sends this as the first argument. In Figure 6-11, this would be 10%, or 0.10. The second argument to set() is how far into the document the last viewable item is. From our example, this would be 80%, or 0.80.
Getting the Current View
The get method returns in a list whatever the latest arguments to set were:
($first, $last) = $scrollbar->get();
This data can change if the widget requests a change in position of the data or if the scrollbar requests a change.
Activating Elements in a Scrollbar
To determine which part of the scrollbar is active, you can use the activate method:
$elem = $scrollbar->activate();
The value returned is an empty string (which means no element is currently active) or the name of the currently active element. The possible elements are "arrow1", "arrow2", or "slider".
If you sent an element name as the argument to activate, that element will change to the color and relief specified by the -activebackground and -activerelief options. The element will continue to display that color and relief until an event (such as the mouse cursor passing over the element) causes it to change. Contrary to what you might believe, using activate does not invoke that element. Here are some examples:
$scrollbar->activate("arrow1");
$scrollbar->activate("arrow2");
$scrollbar->activate("slider");
There is no activate for "trough" because the trough doesn't change color when the mouse is over it.
Calculating Change from Pixels
The number returned by delta indicates how much the scrollbar must change to move the slider deltax pixels for horizontal scrollbars and deltay pixels for vertical scrollbars. (The inapplicable argument is ignored for each type of scrollbar).
$amount = $scrollbar->delta(deltax, deltay)
The amount returned can be positive or negative.
Locating a Point in the Trough
Given a point at (x,y), fraction will return a real number between 0 and 1 indicating where that coordinate point would fall in the trough of the scrollbar:
$loc = $scrollbar->fraction(x, y);
The point (x,y) must be relative to the scrollbar. Figure 6-12 shows the location of three possible results from fraction: 0.0, 0.5 and 1.0.
0137-01.gif
Figure 6-12.
Example of values returned by the fraction method
Identifying Elements
The identify method returns a string containing the name of the element located at the x,y coordinate:
$elem = $scrollbar->identify(x,y);
If x,y is not in any element, the string will be empty. Both x and y must be pixel coordinates relative to the scrollbar. The possible element names are "arrow1", "arrow2", "trough", and "slider".
Examples
These examples are included to hopefully clear up any confusion about how to use scrollbars in the real world. Each example uses the Scrolled method if possible; then we do the same thing manually. We haven't covered all the widget types we are using here, but we aren't doing anything fancy with them either. If you see an option or method you don't recognize, just see the appropriate chapter for that widget to learn more.
Entry Widget
The entry widget can only be scrolled horizontally. The entry can only contain one line of text at most, so a vertical scrollbar would do nothing. Using Scrolled to create a scrolled entry widget is easy:
$mw->Scrolled("Entry", -scrollbars => "s", -width => 30)->pack();
If you want to make the scrollbar only show when the data in the entry widget requires it, use -scrollbars => "os". Using the Scrollbar method is a bit more work:
$scrollbar = $mw->Scrollbar(-orient => 'horizontal');
$entry = $mw->Entry(-width => 30,
                    -xscrollcommand => ['set', $scrollbar]);
$scrollbar->configure(-command => ['xview', $entry]);
$scrollbar->pack(-side => 'bottom', -fill => 'x');
$entry->pack(-side => 'bottom', -fill => 'x');
Both will create an entry that looks similar to the one in Figure 6-13.
0138-01.gif
Figure 6-13.
Entry widget with a scrollbar
Listbox, Text, and Canvas Widgets
A listbox widget can be scrolled both horizontally and vertically, although you might not always want to use both options. If you know how wide your data is going to be and the window can accommodate it, a horizontal scrollbar is unnecessary. Our first example uses the Scrolled method and creates two scrollbars:
$mw->Scrolled("Listbox", -scrollbars => "se",
              -width => 50, -height => 12)->pack();
To do the same thing manually, we need to use Scrollbar to create two scrollbars and configure them to work with the widget:
$f = $mw->Frame()->pack(-side => 'top', expand => 1, -fill => 'both');
$xscroll = $f->Scrollbar(-orient => 'horizontal');
$yscroll = $f->Scrollbar();
$lb = $f->Listbox(-width => 50, -height => 12,
                   -yscrollcommand => ['set', $yscroll],
                   -xscrollcommand => ['set', $xscroll]);
$xscroll->configure(-command => ['xview', $lb]);
$yscroll->configure(-command => ['yview', $lb]);
$xscroll->pack(-side => 'bottom', -fill => 'x');
$yscroll->pack(-side => 'right', -fill => 'y');
$lb->pack(-side => 'bottom', -fill => 'both', -expand => 1);
As you can see, using Scrolled saves a lot of extra work. In Figure 6-14, we see a listbox with two scrollbars, one on the south and one on the east. This window was created using Scrolled. There is a subtle difference: the small square of open space where the two scrollbars meet in the southeast corner. When we create the scrollbars ourselves, we don't get that small space (whichever scrollbar gets packed first takes it).
Scrolled text and canvas widgets are created the same exact way a scrolled listbox widget is created, so we won't bother repeating the same code again.
One Scrollbar, Multiple Widgets
There are times when you want to use one scrollbar with more than one widget. When the user clicks on the scrollbar, it should scroll all the widgets in the same direction at the same time. For this example, we will create three listboxes, each
0139-01.gif
Figure 6-14.
A listbox with two scrollbars
with eleven items. There will be one scrollbar that will scroll all three lists when the user clicks on it. When the user tabs to the listboxes and scrolls up and down by using the arrow keys or the pageup/pagedown keys, the other listboxes are also scrolled. Figure 6-15 shows what the window looks like.
0139-02.gif
Figure 6-15.
A window with three listboxes all controlled by the same scrollbar
The code for Figure 6-15 is as follows:
use Tk;

$mw = MainWindow->new();
$mw->title("One Scrollbar/Three Listboxes");
$mw->Button(-text => "Exit",
            -command => sub { exit })->pack(-side => 'bottom');

$scroll = $mw->Scrollbar();
# Anonymous array of the three listboxes
$listboxes = [ $mw->Listbox(), $mw->Listbox(), $mw->Listbox() ];
 
# This method is called when one listbox is scrolled with the keyboard
# It makes the scrollbar reflect the change, and scrolls the other lists
sub scroll_listboxes {
  my ($sb, $scrolled, $lbs, @args) = @_;
  $sb->set(@args); # tell the scrollbar what to display
  my ($top, $bottom) = $scrolled->yview();
  foreach $list (@$lbs) {
    $list->yviewMoveto($top); # adjust each lb
  }
}

# Configure each listbox to call &scroll_listboxes
foreach $list (@$listboxes) {
  $list->configure(-yscrollcommand => [ \&scroll_listboxes, $scroll,
                                       $list, $listboxes ]);
}

# Configure the scrollbar to scroll each listbox
$scroll->configure(-command => sub { foreach $list (@$listboxes) {
                                       $list->yview(@_);
                                      }});

# Pack the scrollbar and listboxes
$scroll->pack(-side => 'left', -fill => 'y');
foreach $list (@$listboxes) {
  $list->pack(-side => 'left');
  $list->insert('end', "one", "two", "three", "four", "five", "six",
                       "seven", "eight", "nine", "ten", "eleven");
}

MainLoop;
In order to connect multiple widgets to one scrollbar, we first use the Scrollbar command to create the scrollbar. Then we configure the scrollbar so it calls yview for each of the listboxes we are scrolling (the listboxes are kept in an anonymous array so that all methods can reference them easily). The other part that makes the listboxes truly connected is to configure each listbox to call a special subroutine that scrolls all three listboxes in addition to adjusting the scrollbar. Normally, -yscrollcommand would only have ['set', $lb] assigned to it. Instead, we use a callback to \&scroll_listboxes and call set from within that subroutine.
Fun Things to Try
• Create two of each scrollable widget type, make one Scrolled, and create your own scrollbars for the second of each type. This will show you which method you prefer to use.
• Create two scrollbars and attach them to the same widget (the opposite of our one scrollbar/multiple widgets example). For instance, create a listbox with a scrollbar on the left and one on the right, both of which will scroll the listbox vertically.