Create  Edit  FrontPage  Index  Search  Changes  History  RSS  Login

tut-gtk2-txtw-scrolledwin

Scrolled Windows

Before you can learn about the Gtk::TextView widget, you need to learn about two container widgets called Gtk::ScrolledWindow and Gtk::Viewport. The scrollbars allow a widget to take more space than is visible on the screen. A widget that allows a Gtk::TextView widget to contain documents larger than the screen view is called Gtk::ScrolledWindow. As you know it contains two scrollbars, both of which have an associated Gtk::Adjustment object. The adjustment objects are used to track the current position and range of a scroll bar. Adjustments work "magically" behind the scene and in most situations one does not need to access adjustments directly.

A scrollbar's adjustment holds information about scroll bounds, steps, and its current position.

Gtk::Adjustment.new(value, lower, upper, step_inc, page_inc, page_size)
Creates a new Gtk::Adjustment. All argument types are Float.
  • value: the initial value. Value is the current position of the scrollbar between the bonds. This value must always be between the lower and upper values, which are the bounds of the adjustment.
  • lower: the minimum value (Lower bound of the adjustment).
  • upper: the maximum value (Upper bound of the adjustment).
  • step_increment: the increment to use to make minor changes to the value. In a Gtk::Scrollbar this increment is used when the mouse is clicked on the arrows at the top and bottom of the scrollbar, to scroll by a small amount.
  • page_increment: the increment to use to make major changes to the value. In a Gtk::Scrollbar this increment is used when the mouse is clicked in the trough, to scroll by a large amount.
  • page_size: the page size. In a Gtk::Scrollbar this is the size of the area which is currently visible.
The API for Gtk::ScrolledWindow best explains the relationships between scrolling related widgets (Gtk::ScrolledWindow, Gtk::Viewport, Gtk::Scrollbar and Gtk::Adjustment):

Gtk::ScrolledWindow is a Gtk::Bin subclass: it is a container that accepts a single child widget. Gtk::ScrolledWindow adds scrollbars to the child widget and optionally draws a beveled frame around the child widget.

The scrolled window can work in two ways. Some widgets have native scrolling support; these widgets have "slots" for Gtk::Adjustment objects. Widgets with native scroll support include Gtk::TreeView, Gtk::TextView, Gtk::IconView, and Gtk::Layout.

For widgets that lack native scrolling support, the Gtk::Viewport widget acts as an adaptor class, implementing scrollability for child widgets that lack their own scrolling capabilities. Use Gtk::Viewport to scroll child widgets such as Gtk::Table, Gtk::Box, and so on.

If a widget has native scrolling abilities, it can be added to the Gtk::ScrolledWindow with Gtk::Container#add. If a widget is lacking native scrolling, you must first add the widget to a Gtk::Viewport, then add the Gtk::Viewport to the scrolled window. The convenience method Gtk::ScrolledWindow#add_with_viewport does exactly this, so you can ignore the presence of the viewport.

The position of the scrollbars is controlled by the scroll adjustments. See Gtk::Adjustment for the fields in an adjustment - for Gtk::Scrollbar, used by Gtk::ScrolledWindow, the "value" field represents the position of the scrollbar, which must be between the "lower" field and "upper - page_size." The "page_size" field represents the size of the visible scrollable area. The "step_increment" and "page_increment" fields are used when the user asks to step down (using the small stepper arrows) or page down (using for example the PageDown key).

The difference between the two implementations of scrollbars

Let's see the difference between the two implementations of scrollbars, first for children without the built in support for scrolling, and then for children with it. In the first example program, called 'scrolling-with-help-of-viewport.rb', the child widget we intend to use in a scrolled window will be a table, which as you know has no built in support for scrolling, hence we will need to employ the viewport widget. However, there are two ways of doing this: the short way and the long way.

The short way uses the Gtk::ScrolledWindow#add_with_viewport method:

swin.add_with_viewport(table1)

The long way does the same thing in three steps:

viewport = Gtk::Viewport.new(swin.hadjustment, swin.vadjustment)
viewport.add(table1)
swin.add(viewport)
NOTE:
When you create a view port you either have to supply horizontal and vertical adjustments, or nil(s) instead (you may not just call Gtk::Viewport.new without the arguments - nil(s) are mandatory).

To see the difference we are exploring here, you should compare the first program example (scrolling-with-help-of-viewport.rb) with the second. The second exampleis called "txtv-scrollingw-demo.rb". We will borrow this program from the following page, because in it we use the widgets with the built in or so called native scrolling support, namely, the Gtk::TextView and Gtk::TextBuffer widgets, detailed explanations of which we have postponed until we learn about adjustments, scrolling and viewports. However, this borrowed example is so simple, that the introduction of the new text view and buffer widgets here without proper explanations will in no way impede our comparison of the differences between the two implementations of scroll-bars as announced above in the title at the beginning of this paragraph.

Let's look at the two examples:

First example, a child without scrolling support:

scroll-w-help-of-wport.png

This program displays hundred buttons in a table widget, which does not have native scrolling abilities, therefore we need to first add the table to a viewport widget, and only then add the wiewport to scrolled window. As mentioned this is the long way of using viewport. We could instead just use the Gtk::ScrolledWindow#add_with_viewport(child) instance method. The two approaches can be seen in the listing between the "-- (start/end)" comment lines, where, the short way is commented out. This part of the program is only needed when the widget you are adding to scrolled window does not include native support for scrolling.

scrolling-with-help-of-viewport.rb:

#!/usr/bin/env ruby
require 'gtk2'
window = Gtk::Window.new("Scrolling Widgets With Help Of Viewports")
window.resizable = true
window.border_width = 10
window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(400, 200)
table = Gtk::Table.new(10, 10, true)
table.row_spacings = 5
table.column_spacings = 5
options = Gtk::EXPAND|Gtk::FILL
x = 10; y = 10
# Pack the table with 100 buttons.
x.times do |i|
  y.times do |j|
    b1 = Gtk::Button.new("B:%2d,%2d" % [i,j])
    b1.signal_connect('clicked') { |w| puts "Button (#{w.label}) pressed" }
             # child, x1,    x2, y1,    y2, x-opt,   y-opt, xpad, ypad
    table.attach(b1,  i, i + 1,  j, j + 1, options, options, 0, 0)
  end
end
swin = Gtk::ScrolledWindow.new
swin.border_width = 5
swin.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)

### the short way:   --------------------------- (start)
###
###    swin.add_with_viewport(table)
### or
###    the long way:
# Connect {{ swin }} scrollbars to {{ viewport }}
viewport  = Gtk::Viewport.new(swin.hadjustment, swin.vadjustment)
#viewport = Gtk::Viewport.new(nil, nil)  # either {{ new }} with the adjustments
                                         # or with nil(s) is fine.
viewport.add(table)
swin.add(viewport)
###   ------------------------------------------- (end)

window.add(swin)
window.show_all
Gtk.main


Second example, a child with built-in scrolling support:

txtv-scrollingw-demo.png

In this program we use text view and buffer widgets because text view includes native srolling support. Please ignore the fact that we have not yet covered these two widgets. The program is simple enough so you can without too much trouble infer what the two newly introduced widgets do in the presented context. The obvious thing to notice here is the omission of the viewport widget, and that here we add the widget we want to scroll (textview in this case) directly to scrolled window.

txtv-scrollingw-demo.rb (ahead of time):

#!/usr/bin/env ruby
require 'gtk2'

window = Gtk::Window.new("Scrolling Win. Demo")
window.resizable = true
window.border_width = 10
window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(250, 150)

textview = Gtk::TextView.new
textview.buffer.text = "Your very first TextView widget!\n" +
                       "Scrolling window demo\n" +
                       "line2\nline3\nline4\nline5\nline6\n" +
                       "Last line"

scrolledw = Gtk::ScrolledWindow.new
scrolledw.border_width = 5
scrolledw.add(textview)
scrolledw.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS)

window.add(scrolledw)
window.show_all
Gtk.main



viewports-w2tables.png

The following listing demonstrates how to use scrolled windows and viewports, and how the two relate to each other. The program is called "viewports-w2tables.rb", because it implements two scolling tables. However, what is interesting is that only one of the two tables is adorned with scrollbars, and even more peculiar is that these very same scrolbars are actually scrolling both tables. (Not, that you will often, or even ever, need to do such a thing, we nevertheless include this program in the tutorial, since it does expose hidden relationship between these widgets to us).

As the scrollbar is moved, the viewport will scroll as well because adjustments are synchronized. Namely, new scrolled windows are created with: Gtk::ScrolledWindow.new(hadjustment = nil, vadjustment = nil), where the two arguments are the scrolled window's adjustments; these will be shared with the scrollbars and the child widget keeping the two widgets in sync. Note, that unlike for Gtk::Viewport.new, where you must always specify all arguments, even when they are nil, for Gtk::ScrolledWindow.new, you do not have to supply both adjustment arguments. So so if you previously created adjustments, obviously, you supply them to Gtk::ScrolledWindow.new method, however if you would like the scrolled window to create them for you, then you do not have to specify any arguments at all, hence using the default nil(s) instead.

In our example the adjustments are used when viewport is created with Gtk::Viewport.new(horizontal, vertical). The viewport adjustments are initialized with those from the scrolled window, ensuring that both containers will be scrolled at the same time.

One additional decision you often would like to make when setting up a scrolled window is to specify when the scrollbars will be visible. This is accomplished with Gtk::ScrolledWindow#set_policy(hscrollbar_policy, vscrollbar_policy). The policy arguments take one of the values defined in GtkPolicyType. Though unlikely, another thing you may wish to set is the placement of the scrollbars. For that you have Gtk::Scrollbar#window_placement=(window_placement) method at your disposal. Look for the correct argument values at GtkCornerType. For your convenience I list them also here:

GtkCornerType:
Specifies which corner a child widget should be placed in when packed into a Gtk::ScrolledWindow. This is effectively the opposite of where the scroll bars are placed.

After you have setup a scrolled window you should add a child widget which you intend to slide around with your scrollbars. There are two possible ways of doing this, and the method is chosen based on the type of child widget. If you are adding to your scrolling window a Gtk::TextView, Gtk::TreeView, Gtk::IconView, or Gtk::Layout widget you should use Gtk::Container#add method, since all five of these containers include native scrolling support. All other GTK+ widgets, such as our Gtk::Table, which do not have scrolling support, you must first add to a Gtk::Viewport widget, and then add the Gtk::Viewport to the scrolled window. This longer method of adding a widget without the scrolling support to the scrolled window, demonstrates precisely what happens, when you use a convenience method Gtk::ScrolledWindow#add_with_viewport, which does exactly what we have just described. Namely, it adds the scrolling ability to a child widget such as for instance to our table, adds it (the table) to the viewport, and finally adds this viewport with the child (table) to the scrolling window.


viewports-w2tables.rb

#!/usr/bin/env ruby
require 'gtk2'

window = Gtk::Window.new("Scrlld. Viewports With Two Tables")
window.resizable = true
window.border_width = 10
window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(350, 300)

table1 = Gtk::Table.new(10, 10, true)
table2 = Gtk::Table.new(10, 10, true)
table1.row_spacings = 5
table2.row_spacings = 5
table1.column_spacings = 5
table2.column_spacings = 5

options = Gtk::EXPAND|Gtk::FILL
x = 10; y = 10
# Pack each table with 100 buttons.
x.times do |i|
  y.times do |j|
    b1 = Gtk::Button.new("S:%2d,%2d" % [i,j])
    b2 = Gtk::Button.new("V:%2d,%2d" % [i,j])
    b1.signal_connect('clicked') { |w| puts "Button (#{w.label}) pressed" }
    b2.signal_connect('clicked') { |w| puts "Button (#{w.label}) pressed" }
             # child, x1,    x2, y1,    y2, x-opt,   y-opt, xpad, ypad
    table1.attach(b1,  i, i + 1,  j, j + 1, options, options, 0, 0)
    table2.attach(b2,  i, i + 1,  j, j + 1, options, options, 0, 0)
  end
end
# Create a scrolled window and a viewport, each with one table.
# Use the adjustments in the scrolled window to synchronize
# both containers.
swin       = Gtk::ScrolledWindow.new
swin.border_width = 5
# Gtk::POLICY_ALWAYS/Gtk::POLICY_AUTOMATIC ... when to show scrollbars
swin.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS)
swin.add_with_viewport(table1)

# Connect {{ swin }} scrollbars to {{ viewport }}
viewport   = Gtk::Viewport.new(swin.hadjustment, swin.vadjustment)

# Creates new adjustment (not connected as above), but seems better
# viewport   = Gtk::Viewport.new(nil, nil)	# better than above

viewport.border_width = 5
viewport.add(table2)

# Pack the widgets into a GtkVBox and then into the window.
vbox = Gtk::VBox.new(true, 5)

vbox.pack_start_defaults(swin)
vbox.pack_start_defaults(viewport)

window.add(vbox)
window.show_all
Gtk.main

The scrolled window is simply a container with scrollbars. Neither the container nor the scrollbars perform any action by themselves. Scrolling is handled by the child widget, which is why the child widget must have a scrolling support. As you know by now, most GTK+ widgets originally do not include such a support - we mentioned only five that do (Gtk::TextView, Gtk::TreeView, Gtk::IconView, or Gtk::Layout). For all the other widgets, without native scrolling support, scrolling behaviour is added with the help of portview.

Adjustments are tightly related to scrolling. When user clicks and drags a scrollbar, the value in adjustment object changes and causesvalue-changedsignal to be emitted, which subsequently will cause the child to render itself accordingly.

When you add a child widget with scrolling support to a scrolled window, adjustments for both x and y axis are added. If the child does not have scrolling capabilities nothing will happen, which is why widgets without these capabilities are first added to a viewport. Since viewport does not have any scrollbars the adjustments must be added to it:

viewport = Gtk::Viewport.new(horizontal, vertical)

If you pass nil rather than an existing adjustment to the adjustment new method such as Gtk::Viewport.new(nil, nil), new adjustments are created for the created viewport.

Last modified:2012/12/04 03:57:08
Keyword(s):
References:[tut-gtk2-txtw] [tut-gtk]