Scrollbars
An important UI widget, which is implemented in Ruby/Tk
in a very elegant way. We will repeat the Ruby Book
example here, written even shorter.
root = TkRoot.new() { title "(Sc)rolling, (sc)rolling), (sc)rolling..." }
bar = TkScrollbar.new(root).pack('side'=>'right', 'fill'=>'y')
list = TkListbox.new(root).pack('side'=>'left', 'fill'=>'both', 'expand'=>true)
(0..20).each { |i|
list.insert('end', i)
}
Looks fine, does nothing. That is, the scrollbar does not do anything, but the
list itself can be scrolled by dragging the mouse above or below the list
widget.
That is inconvenient when the list gets longer, so here goes:
bar.command(proc { |args|
list.yview(*args)
})
list.yscrollcommand(proc { |first, last|
bar.set(first, last)
})
This is a very elegant way to pass action from the two widgets to each other.
If you're adventurous, print args, it is nice to
see what the scrollbar actually does.
But it can be even shorter; instead of the above two commands, give:
list.yscrollbar(bar)
Text
The above can be repeated for a TkText
widget. Upon vertical scrolling, a text widget can also perform horizontal
scrolling when wrapping is disabled:
root = TkRoot.new() { title "(Sc)rolling, (sc)rolling), (sc)rolling..." }
bar = TkScrollbar.new(root, 'orient'=>'hor').pack('side'=>'bottom', 'fill'=>'x')
text = TkText.new(root, 'wrap'=>'none', 'width'=>20).pack('fill'=>'both', 'expand'=>true)
text.insert('end', "A string that is longer than fits on one line...")
bar.command(proc { |args|
text.xview(*args)
})
text.xscrollcommand(proc { |first, last|
bar.set(first, last)
})
Differences are the orient going horizontal and the text view/scroll commands using x
instead of y.
Canvas
Slightly trickier, the TkCanvas:
root = TkRoot.new() { title "(Sc)rolling, (sc)rolling), (sc)rolling..." }
bar = TkScrollbar.new(root, 'orient'=>'hor').pack('side'=>'bottom', 'fill'=>'x')
canvas = TkCanvas.new(root, 'width'=>320, 'height'=>200).pack('fill'=>'both', 'expand'=>true)
TkcLine.new(canvas, 0, 0, 400, 400)
TkcLine.new(canvas, 0, 400, 400, 0)
bar.command(proc { |args|
canvas.xview(*args)
})
canvas.xscrollcommand(proc { |first, last|
bar.set(first, last)
})
Which shows what we expect, a window of size 320x200, displaying parts of the
400x400 cross we drew on it. Though the scrollbar looks like it won't work, it
does partially. Just press the small triangles. No, dragging the canvas like you
could drag a TkText is impossible.
canvas.configure('scrollregion'=>'0 0 400 400')
does the trick.
The second scrollbar
To get the second scrollbar where we want, we need to leave a small piece of
space in the total window open. This is impossible with packing, so we use a
grid:
root = TkRoot.new { title "Canvas, Grid, and Scrollbars" }
hbar = TkScrollbar.new(root) { orient 'horiz' }
vbar = TkScrollbar.new(root) { orient 'vert' }
canvas = TkCanvas.new(root) {
width 320
height 200
scrollregion '0 0 400 400'
}
canvas.xscrollbar(hbar)
canvas.yscrollbar(vbar)
TkcLine.new(canvas, 0, 0, 400, 400)
TkcLine.new(canvas, 0, 400, 400, 0)
TkGrid.grid(canvas, vbar, 'sticky'=>'ns')
TkGrid.grid(hbar, 'sticky'=>'ew')
TkGrid.columnconfigure(root, 0, 'weight'=>1)
TkGrid.rowconfigure( root, 0, 'weight'=>1)
(thanks to Mike Hall)
What we see here is:
- A shortcut to bind scrollbars to a widget (replacing the mutual setting
commands),
- A grid with two widgets in the top row and one in the bottom row (done
almost implicitely by the two grid commands; a new command starts a new row),
- The sticky setting subsumes the anchor and fill of
pack, which...
- ...together with the weight set for the
canvas, makes the canvas fill the space it gets, while the scrollbars (default
weight 0) get no extra space to fill.
Kero