The category of range widgets includes the ubiquitous scrollbar widget and the less common "scale" widget. Though these two types of widgets are generally used for different purposes, they are quite similar in function and implementation. All range widgets share a set of common graphic elements, each of which has its own X window and receives events. They all contain a "trough" and a "slider" (what is sometimes called a "thumbwheel" in other GUI environments). Dragging the slider with the pointer moves it back and forth within the trough, while clicking in the trough advances the slider towards the location of the click, either completely, or by a designated amount, depending on which mouse button is used.
As mentioned in Adjustments above, all range widgets are associated with an adjustment object, from which they calculate the length of the slider and its position within the trough. When the user manipulates the slider, the range widget will change the value of the adjustment.
These are your standard, run-of-the-mill scrollbars. These should be used only for scrolling some other widget, such as a list, a text box, or a viewport (and it's generally easier to use the scrolled window widget in most cases). For other purposes, you should use scale widgets, as they are friendlier and more featureful.
There are separate classes for horizontal and vertical scrollbars. There really isn't much to say about these. You create them with the following creation procedures, defined in GTK_HSCROLLBAR and GTK_VSCROLLBAR
make ( adjustment : GTK_ADJUSTMENT )
and that's about it (if you don't believe me, look in the source
files!). The adjustment
argument can either be a an existing
GTK_ADJUSTMENT, or Void, in which case one will be created for you.
Specifying Void might actually be useful in this case, if you wish to
pass the newly-created adjustment to the creation procedure of some
other widget which will configure it for you, such as a text widget.
Scale widgets are used to allow the user to visually select and manipulate a value within a specific range. You might want to use a scale widget, for example, to adjust the magnification level on a zoomed preview of a picture, or to control the brightness of a colour, or to specify the number of minutes of inactivity before a screensaver takes over the screen.
As with scrollbars, there are separate widget classes for horizontal and vertical scale widgets. (Most programmers seem to favour horizontal scale widgets). Since they work essentially the same way, there's no need to treat them separately here. The following creation procedures, are defined in classes GTK_VSCALE and GTK_HSCALE
make ( adjustment : GTK_ADJUSTMENT )
The adjustment
argument can either be an adjustment which has
already been created , or Void
, in which case, an anonymous
GTK_ADJUSTMENT is created with all of its values set to
0.0
(which isn't very useful in this case). In order to avoid
confusing yourself, you probably want to create your adjustment with a
page_size
of 0.0
so that its upper
value actually
corresponds to the highest value the user can select. (If you're
already thoroughly confused, read the section on
Adjustments again for an explanation of
what exactly adjustments do and how to create and manipulate them).
Scale widgets can display their current value as a number beside the trough. The default behaviour is to show the value, but you can change this with this procedure:
set_draw_value ( draw_value : BOOLEAN )
As you might have guessed, draw_value
is either True
or
False
, with predictable consequences for either one.
The value displayed by a scale widget is rounded to one decimal point
by default, as is the value
field in its GTK_ADJUSTMENT. You can
change this with:
set_digits ( digits : INTEGER )
where digits
is the number of decimal places you want. You can
set digits
to anything you like, but no more than 13 decimal
places will actually be drawn on screen.
Finally, the value can be drawn in different positions relative to the trough:
set_value_pos ( pos : GTK_POSITION_TYPE ) is
The argument pos
is of class GTK_POSITION_TYPE
, which
is defined in GTK_CONSTANTS
, and can take one of the
following values:
If you position the value on the "side" of the trough (e.g. on the top or bottom of a horizontal scale widget), then it will follow the slider up and down the trough.
The GTK_RANGE widget class is fairly complicated internally, but, like all the "base class" widgets, most of its complexity is only interesting if you want to hack on it. Also, almost all of the functions and signals it defines are only really used in writing derived widgets. There are, however, a few useful procedures that are defined in class GTK_RANGE and will work on all range widgets.
The "update policy" of a range widget defines at what points during
user interaction it will change the value
field of its
GTK_ADJUSTMENT and emit the "value_changed" signal on this
GTK_ADJUSTMENT. The update policies, defined in GTK_UPDATE_TYPE
are:
The update policy of a range widget can be set by passing it to this procedure:
set_update_policy ( policy : GTK_UPDATE_TYPE )
Getting and setting the adjustment for a range widget "on the fly" is done, predictably, with:
get_adjustment : GTK_ADJUSTMENT
set_adjustment ( adjustment : GTK_ADJUSTMENT )
get_adjustment()
returns an adjustment to which range
is
connected.
set_adjustment()
does absolutely nothing if you pass it the
adjustment that range
is already using, regardless of whether you
changed any of its fields or not. If you pass it a new GTK_ADJUSTMENT,
it will unreference the old one if it exists (possibly destroying it),
connect the appropriate signals to the new one, and call the private
procedure adjustment_changed()
, which will (or at least, is
supposed to...) recalculate the size and/or position of the slider and
redraw if necessary. As mentioned in the section on adjustments, if
you wish to reuse the same GTK_ADJUSTMENT, when you modify its values
directly, you should emit the "changed" signal on it, like this:
signal_emit_by_name (adjustment, "changed");
All of the GTK+ range widgets react to mouse clicks in more or less
the same way. Clicking button-1 in the trough will cause its
adjustment's page_increment
to be added or subtracted from its
value
, and the slider to be moved accordingly. Clicking mouse
button-2 in the trough will jump the slider to the point at which the
button was clicked. Clicking any button on a scrollbar's arrows will
cause its adjustment's value to change step_increment
at a time.
It may take a little while to get used to, but by default, scrollbars as well as scale widgets can take the keyboard focus in GTK+. If you think your users will find this too confusing, you can always disable this by unsetting the GTK_CAN_FOCUS flag on the scrollbar, like this:
scrollbar.unset_flags (GTK_CAN_FOCUS);
The key bindings (which are, of course, only active when the widget has focus) are slightly different between horizontal and vertical range widgets, for obvious reasons. They are also not quite the same for scale widgets as they are for scrollbars, for somewhat less obvious reasons (possibly to avoid confusion between the keys for horizontal and vertical scrollbars in scrolled windows, where both operate on the same area).
All vertical range widgets can be operated with the up and down arrow
keys, as well as with the Page Up
and Page Down
keys. The
arrows move the slider up and down by step_increment
, while
Page Up
and Page Down
move it by page_increment
.
The user can also move the slider all the way to one end or the other
of the trough using the keyboard. With the GtkVScale widget, this is
done with the Home
and End
keys, whereas with the
GtkVScrollbar widget, this is done by typing Control-Page Up
and Control-Page Down
.
The left and right arrow keys work as you might expect in these
widgets, moving the slider back and forth by step_increment
. The
Home
and End
keys move the slider to the ends of the trough.
For the GtkHScale widget, moving the slider by page_increment
is
accomplished with Control-Left
and Control-Right
,
while for GtkHScrollbar, it's done with Control-Home
and
Control-End
.
This example is a somewhat modified version of the "range controls"
test from testgtk
. It basically puts up a window with three
range widgets all connected to the same adjustment, and a couple of
controls for adjusting some of the parameters mentioned above and in
the seciton on adjustments, so you can see how they affect the way
these widgets work for the user.
-- example-start rangewidgets rangewidgets.e
class RANGEWIDGETS
inherit
-- All constants we need are defined in the class below.
-- eg gtk_window_toplevel etc.
GTK_CONSTANTS
-- This class contains common features like vegtk_init and so
VEGTK_MAIN
-- If we need to handle callbacks we must inherit from the below
-- class
VEGTK_CALLBACK_HANDLER
creation
make
feature -- Callback handlers
close_application is
-- This callback quits the program
do
gtk_main_quit;
end
cb_pos_menu_select (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
local
pos : GTK_POSITION_TYPE;
do
pos ?= data;
-- Set the value position on both scale widgets
hscale.set_value_pos (pos);
vscale.set_value_pos (pos);
end
cb_update_menu_select (data : ANY) is
local
policy : GTK_UPDATE_TYPE;
do
policy ?= data;
-- Set the update policy for both scale widgets
hscale.set_update_policy (policy);
vscale.set_update_policy (policy);
end
cb_digits_scale (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
local
adj : GTK_ADJUSTMENT;
do
adj ?= cb_data.get_widget;
-- Set the number of decimal places to which adj.value is rounded
hscale.set_digits (adj.value.rounded);
vscale.set_digits (adj.value.rounded);
end
cb_page_size (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
local
get,set : GTK_ADJUSTMENT;
-- signals : expanded GTK_SIGNAL_EXTERNALS;
do
get ?= cb_data.get_widget;
set ?= data;
-- Set the page size and page increment size of the sample
-- adjustment to the value specified by the "Page Size" scale
set.set_page_size (get.value);
set.set_page_increment (get.value);
-- Now emit the "changed" signal to reconfigure all the widgets that
-- are attached to this adjustment
set.changed;
-- signals.gtk_signal_emit_by_name(set.to_external,("changed").to_external);
end
cb_draw_value (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
local
button : GTK_CHECK_BUTTON;
do
button ?= cb_data.get_widget;
-- Turn the value display on the scale widgets off or on depending
-- on the state of the checkbutton
hscale.set_draw_value (button.get_active);
vscale.set_draw_value (button.get_active);
end
feature -- Convenience procedures
make_menu_item(name : STRING; callback : POINTER ;
data : ANY) : GTK_MENU_ITEM is
do
!!Result.make_with_label(name);
signal_connect_with_data (Result, "activate", callback, data);
Result.show;
end
scale_set_default_values (scale : GTK_SCALE) is
do
scale.set_update_policy (Gtk_update_continuous);
scale.set_digits (1);
scale.set_value_pos (Gtk_pos_top);
scale.set_draw_value (True);
end
create_range_controls is
-- makes the sample window
local
window : GTK_WINDOW;
box1, box2, box3 : GTK_BOX;
button : GTK_BUTTON;
check_button : GTK_CHECK_BUTTON;
scrollbar : GTK_HSCROLLBAR;
separator : GTK_HSEPARATOR;
opt : GTK_OPTION_MENU
menu : GTK_MENU;
item : GTK_MENU_ITEM;
label : GTK_LABEL;
scale : GTK_HSCALE;
adj1, adj2 : GTK_ADJUSTMENT;
do
-- Standard window-creating stuff
!!window.make(Gtk_window_toplevel);
signal_connect (window, "destroy",$close_application);
window.set_title ("range controls");
!GTK_VBOX!box1.make (False, 0);
window.add (box1);
box1.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
box1.pack_start (box2, True, True, 0);
box2.show;
-- calue, lower, upper, step_increment, page_increment, page_size
-- Note that the page_size value only makes a difference for
-- scrollbar widgets, and the highest value you'll get is actually
-- (upper - page_size).
!!adj1.make(0.0, 0.0, 101.0, 0.1, 1.0, 1.0);
!!vscale.make (adj1);
scale_set_default_values (vscale);
box2.pack_start (vscale, True, True, 0);
vscale.show;
!GTK_VBOX!box3.make (False, 10);
box2.pack_start (box3, True, True, 0);
box3.show;
-- Reuse the same adjustment
!!hscale.make(adj1);
hscale.set_usize (200, 30);
scale_set_default_values (hscale);
box3.pack_start (hscale, True, True, 0);
hscale.show;
-- Reuse the same adjustment again
!!scrollbar.make (adj1);
-- Notice how this causes the scales to always be updated
-- continuously when the scrollbar is moved
scrollbar.set_update_policy (Gtk_update_continuous);
box3.pack_start (scrollbar, True, True, 0);
scrollbar.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
box1.pack_start (box2, True, True, 0);
box2.show;
-- A checkbutton to control whether the value is displayed or not
!!check_button.make_with_label("Display value on scale widgets");
check_button.set_active (True);
signal_connect (check_button, "toggled",$cb_draw_value);
box2.pack_start ( check_button, True, True, 0);
check_button.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
-- An option menu to change the position of the value
!!label.make ("Scale Value Position:");
box2.pack_start (label, False, False, 0);
label.show;
!!opt.make;
!!menu.make;
item := make_menu_item ("Top",$cb_pos_menu_select,Gtk_pos_top);
menu.append (item);
item := make_menu_item ("Bottom",$cb_pos_menu_select,Gtk_pos_bottom);
menu.append (item);
item := make_menu_item ("Left", $cb_pos_menu_select,Gtk_pos_left);
menu.append (item);
item := make_menu_item ("Right", $cb_pos_menu_select,Gtk_pos_right);
menu.append (item);
opt.set_menu (menu);
box2.pack_start (opt, True, True, 0);
opt.show;
box1.pack_start (box2, True, True, 0);
box2.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
-- Yet another option menu, this time for the update policy of the
-- scale widgets
!!label.make ("Scale Update Policy:");
box2.pack_start (label, False, False, 0);
label.show;
!!opt.make;
!!menu.make;
item := make_menu_item ("Continuous",$cb_update_menu_select,
Gtk_update_continuous);
menu.append (item);
item := make_menu_item ("Discontinuous",$cb_update_menu_select,
Gtk_update_discontinuous);
menu.append (item);
item := make_menu_item ("Delayed",$cb_update_menu_select,
Gtk_update_delayed);
menu.append (item);
opt.set_menu (menu);
box2.pack_start (opt, True, True, 0);
opt.show;
box1.pack_start (box2, True, True, 0);
box2.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
-- A GTK_HSCALE widget for adjusting the number of digits on the
-- sample scales.
!!label.make ("Scale Digits:");
box2.pack_start (label, False, False, 0);
label.show;
!!adj2.make (1.0, 0.0, 5.0, 1.0, 1.0, 0.0);
signal_connect (adj2, "value_changed",$cb_digits_scale);
!!scale.make (adj2);
scale.set_digits (0);
box2.pack_start (scale, True, True, 0);
scale.show;
box1.pack_start (box2, True, True, 0);
box2.show;
!GTK_HBOX!box2.make (False, 10);
box2.set_border_width (10);
-- And, one last GTK_HSCALE widget for adjusting the page size of the
-- scrollbar.
!!label.make ("Scrollbar Page Size:");
box2.pack_start (label, False, False, 0);
label.show;
!!adj2.make (1.0, 1.0, 101.0, 1.0, 1.0, 0.0);
signal_connect_with_data (adj2, "value_changed",$cb_page_size, adj1);
!!scale.make(adj2);
scale.set_digits (0);
box2.pack_start (scale, True, True, 0);
scale.show;
box1.pack_start (box2, True, True, 0);
box2.show;
!!separator.make;
box1.pack_start (separator, False, True, 0);
separator.show;
!GTK_VBOX!box2.make (False, 10);
box2.set_border_width (10);
box1.pack_start (box2, False, True, 0);
box2.show;
!!button.make_with_label ("Quit");
signal_connect (button, "clicked",$close_application)
box2.pack_start (button, True, True, 0);
button.set_flags (Gtk_can_default);
button.grab_default;
button.show;
window.show;
end -- create_range_controls
feature -- Creation
make is
local
window : GTK_WINDOW;
rbutton : GTK_RADIO_BUTTON;
button : GTK_BUTTON;
box1,box2 : GTK_VBOX;
separator : GTK_HSEPARATOR;
do
-- This is called in all GTK applications. Arguments are parsed
-- from the command line and are returned to the application.
vegtk_init;
create_range_controls;
gtk_main;
end -- make
feature {NONE}
hscale : GTK_HSCALE;
vscale : GTK_HSCALE;
end -- class RANGEWIDGETS
-- example-end