Designing a good user interface requires logical grouping of items. It also requires that parts not often used is hidden away, but still easy to access. By making use of Gtk2::NoteBook
and Gtk2::Expander
, you can accomplish this goal.
This lesson will expose the reader to the capabilities of the Gtk2::NoteBook
and Gtk2::Expander
classes, which are both Gtk2::Container
subclasses.
Terse descriptions of the classes this lesson is about.
The Gtk2::NoteBook
widget is a Gtk2::Container
whose children are pages that can be switched between using tab labels along one edge.
A Gtk2::Expander
allows the user to hide or show its child by clicking on an expander triangle similar to the triangles used in a Gtk2::TreeView
.
This sample will add a Gtk2::NoteBook
as the child of a Gtk2::Expander
. We will manipulate various properties of the Gtk2::NoteBook
to show their influence.
Table 19-1. Gtk2
object classes used
Gtk2 Class Name |
---|
Gtk2::Window |
Gtk2::VBox |
Gtk2::Expander |
Gtk2::NoteBook |
Gtk2::HSeparator |
Gtk2::Label |
Gtk2::Table |
Gtk2::ComboBox |
Gtk2::CheckButton |
The program can be found here: 'gtk2_expander.pl'
1 #! /usr/bin/perl -w 2 3 use strict; 4 use Gtk2 '-init'; 5 use Glib qw/TRUE FALSE/; 6 7 #standard window creation, placement, and signal connecting 8 my $window = Gtk2::Window->new('toplevel'); 9 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; }); 10 $window->set_border_width(5); 11 $window->set_position('center_always'); 12 13 #add and show the vbox 14 $window->add(&ret_vbox); 15 $window->show(); 16 17 #our main event-loop 18 Gtk2->main(); 19 20 sub ret_vbox { 21 22 my $vbox = Gtk2::VBox->new(FALSE,5); 23 24 #create a instance of the Gtk2::Expander class 25 my $expander = Gtk2::Expander->new_with_mnemonic('Expand Me'); 26 27 #create the child that we want to add to it. 28 #---------------------------------------------- 29 #NOTE: If we want to resize 30 #the widget containing the Gtk2::Expander Widget 31 #to the size that it was BEFORE the expansion 32 #we have to "add / remove" the child on each "expanded / closed" cycle 33 #If we just add it, it will be shown and hidden, but we will not be able 34 #to shrink the parent containing the Gtk2::Expander instance when hidden 35 #------------------------------------------------- 36 my $nb = &ret_notebook; 37 $nb->show_all; 38 39 $expander->signal_connect_after('activate' => sub { 40 41 if($expander->get_expanded){ 42 $expander->set_label('Close Me'); 43 $expander->add($nb); 44 45 }else{ 46 $expander->set_label('Expand Me'); 47 $expander->remove($nb); 48 $window->resize(4,4); 49 } 50 return FALSE; 51 }); 52 53 54 $vbox->pack_start($expander,FALSE,FALSE,0); 55 56 $vbox->show_all(); 57 return $vbox; 58 } 59 60 sub ret_notebook { 61 62 #this will create a vbox containing a Notebook 63 #and some widgets to manipulate the Notebook's 64 #properties. 65 66 my $vbox_nb = Gtk2::VBox->new(FALSE,5); 67 $vbox_nb->set_size_request (500, 300); 68 69 my $nb = Gtk2::Notebook->new; 70 71 #pre-set some properties 72 $nb->set_scrollable (TRUE); 73 $nb->popup_enable; 74 for (0..10) { 75 76 my $child = Gtk2::Frame->new("Frame of Tab $_"); 77 78 #________ 79 #The tab's label can be a widget, and does not 80 #need to be a label, here we create a hbox containing 81 #a label and close button 82 my $hbox = Gtk2::HBox->new(FALSE,0); 83 $hbox->pack_start(Gtk2::Label->new("Tab $_"),FALSE,FALSE,0); 84 85 my $btn = Gtk2::Button->new(''); 86 $btn->set_image(Gtk2::Image->new_from_stock('gtk-close','menu')); 87 $btn->signal_connect('clicked' => sub { 88 89 $nb->remove_page ($nb->page_num($child)); 90 }); 91 92 $hbox->pack_end($btn,FALSE,FALSE,0); 93 $hbox->show_all; 94 #________ 95 96 $nb->append_page ($child,$hbox); 97 98 99 } 100 101 $vbox_nb->pack_start($nb,TRUE,TRUE,0); 102 $vbox_nb->pack_start(Gtk2::HSeparator->new(),FALSE,FALSE,5); 103 104 #call the sub that will create the table containing 105 #controls to manipulate the notebook 106 $vbox_nb->pack_end(&nb_controls($nb),FALSE,FALSE,0); 107 108 return $vbox_nb; 109 } 110 111 112 sub nb_controls { 113 114 my ($nb) = @_; 115 116 my $table = Gtk2::Table->new(3,2,TRUE); 117 118 $table->attach_defaults(Gtk2::Label->new('Tab position:'),0,1,0,1); 119 120 my $cb_position = Gtk2::ComboBox->new_text; 121 122 foreach my $val (qw/left right top bottom/){ 123 124 $cb_position->append_text($val); 125 } 126 127 $cb_position->signal_connect("changed" => sub { 128 129 $nb->set_tab_pos($cb_position->get_active_text); 130 131 }); 132 133 $cb_position->set_active(2); 134 135 $table->attach_defaults($cb_position,1,2,0,1); 136 137 my $show_tabs = Gtk2::CheckButton->new("Show Tabs"); 138 $show_tabs->set_active(TRUE); 139 $show_tabs->signal_connect('toggled' =>sub { 140 141 $nb->set_show_tabs($show_tabs->get_active); 142 143 }); 144 $table->attach_defaults($show_tabs,0,1,1,2); 145 146 my $scrollable = Gtk2::CheckButton->new("Scrollable"); 147 $scrollable->set_active(TRUE); 148 $scrollable->signal_connect('toggled' =>sub { 149 150 $nb->set_scrollable($scrollable->get_active); 151 152 }); 153 $table->attach_defaults($scrollable,1,2,1,2); 154 155 my $popup = Gtk2::CheckButton->new("Popup Menu (right click on tab)"); 156 $popup->set_active(TRUE); 157 $popup->signal_connect('toggled' =>sub { 158 159 ($popup->get_active)&&($nb->popup_enable); 160 ($popup->get_active)||($nb->popup_disable); 161 }); 162 $table->attach_defaults($popup,0,1,2,3); 163 164 return $table; 165 166 }
Gtk2::Expander
When you connect to the 'activate'
signal, using 'signal_connect'
, and you query the expanded state of Gtk2::Expander
, the state reported will differ from the actual state of the widget.
This is because user-connected signal handlers run BEFORE the default handlers, so your callback is running BEFORE the Gtk2::Expander
's state has been changed.
To overcome this problem, use the 'signal_connect_after'
method. This will run AFTER the Gtk2::Expander
's state changed, reporting the right state.
A Gtk2::Expander
shows and hides its child, but you may want to shrink the parent container containing Gtk2::Expander
when the child is hidden. To do this, you actually have to 'remove'
the child from the Gtk2::Expander
instance each time it gets hidden, and 'add'
it again when it is expanded.
There is a much easier way, but using it takes away the ability for the user to resize the window. $window->set_resizable(FALSE);will cause the window to always "auto shrink" around the packed widgets. |
Gtk2::NoteBook
Remember that the label you specify for a Gtk2::NoteBook
is just a convenience method. The Gtk2::NoteBook
tab can actually contain any Gtk2::Widget
. We make use of this feature, by adding a Gtk2::HBox
with some contents in our sample.
The rest of the program is just used to set various properties of Gtk2::NoteBook
. Available Gtk2::NoteBook
properties, and methods to get and set them are listed on Gtk2::NoteBook
's man page.