Xfce Foundation classes
 « Main Page | Index

Packing Boxes


Containers use a notion of packing. Packing refers to adding widgets with reference to a particular position in a container. The container most commonly used to pack widgets is the box.  A box organizes widgets into a rectangular area that has two reference positions: the start and the end of the box. There are two types of boxes: horizontal boxes and vertical boxes.

For a horizontal box the start is defined as the left side and the end is defined as the right side. For a vertical box, the start is defined as the top of the box and the end is defined as the bottom. Horizontal boxes can pack widgets left to right (from the start) or right to left (from the end). Vertical boxes can pack widgets top to bottom (from the start) or bottom to top (from the end). All Child widgets packed in a box are organized into a single row so they have one dimension in common. For horizontal boxes, child widgets all have the same height. For vertical boxes, child widgets all have the same width. The other dimension depends on a widget's requested size, the flags specified when a widget is added to a box and the number widgets packed into the box.

Gtk::Box is an abstract widget which encapsulates the functionality of the box. A box is a 'no window' widget so it is always invisible. Horizontal boxes are represented by the class Gtk::HBox and vertical boxes are represented by the class Gtk::VBox.

You can create an instance of a box with these two constructors:

HBox(bool homogeneous = false, int spacing = 0);
VBox(bool homogeneous = false, int spacing = 0);

The 'homogeneous' argument controls whether each widget in an horizontal box has the same width, or whether each widget in an vertical box has the same height. The 'spacing' argument sets the default space, in pixels, to place between widgets in the box. If homogenous is set to true, the Gtk::Box pack methods function essentially as if the expand argument was always turned on (see later).
 
Child widgets can be packed into a box with either of the following Gtk::Box pack methods:

void pack_start(Gtk::Widget& child, bool expand = true, bool fill = true, unsigned int padding = 0);
void pack_end(Gtk::Widget& child, bool expand = true, bool fill = true, unsigned int padding = 0);

Use repeated calls to pack_start() to pack child widgets into a horizontal box from left to right or into a vertical box from top to bottom. Use pack_end() to pack child widgets into a horizontal box from right to left and into a vertical box from bottom to top.

The 'child' argument is the child widget to add to a box. The 'expand' argument specifies whether extra space given to a box should be allocated to the widget. The default value is true. Any extra space given to a box, such as when a box is resized, is allocated equally among widgets that set expand to true. Setting expand to false will allow you to justify child widgets to the left or right. Otherwise they will expand to fit the box.The 'fill' argument specifies whether the extra space is allocated to the widget itself or is allocated as padding on either side of the widget. The default value is true. fill only has an effect if the expand argument is also true. The 'padding' argument sets the extra space in pixels to put between this child and its neighbours. The default value is zero.

Child widgets can also be inserted into a box at a given position:

void insert_start(Gtk::Widget& child, int position, bool expand = true, bool fill = true, unsigned int padding = 0);
void insert_end(Gtk::Widget& child, int position, bool expand = true, bool fill = true, unsigned int padding = 0);

As you can see the insert methods are almost indentical to the pack methods. The only difference is the 'position' argument which lets you specify the zero-based position at which to insert the new widget.

Because Gtk::Box is also a container, you can use Gtk::Container::add() to pack widgets into a box. If you do, the widgets will be packed the same as with pack_start() using the default arguments. You should also use Gtk::Container::remove() to remove widgets from the box.

The Packbox Example

Setting appropriate packing flags when you pack a widget into a box lets GTK+ know where you want to place your widgets. It also tells GTK+ how it should do automatic resizing when the parent box is resized. There are a number of options as to how your widgets can be packed inside a box. As you can imagine, these options give you quite a bit of flexibility when and creating and placing widgets. Because of this flexibility, packing boxes can be confusing at first. There are a lot of options, and it's not immediately obvious how they all fit together. In the end, however, there are basically five different styles.

The header file for the Packbox example is <packbox.hh>:

#include <xfc/main.hh>
#include <xfc/gtk/box.hh>
#include <xfc/gtk/window.hh>

using namespace Xfc;

class PackBox : public Gtk::HBox
{
public:
    PackBox(bool homogeneous, int spacing, bool expand, bool fill, unsigned int padding);
    ~PackBox();
};

class PackBoxWindow : public Gtk::Window
{
public:
    PackBoxWindow(int which);
    ~PackBoxWindow();

    void example_1(Gtk::VBox *vbox);
    void example_2(Gtk::VBox *vbox);
    void example_3(Gtk::VBox *vbox);
};


and the source file is <packbox.cc>:

#include "packbox.hh"
#include <xfc/gtk/button.hh>
#include <xfc/gtk/label.hh>
#include <xfc/gtk/separator.hh>
#include <cstdio>

// PackBox

PackBox::PackBox(bool homogeneous, int spacing, bool expand, bool fill, unsigned int padding)
: Gtk::HBox(homogeneous, spacing)
{
// Make a new hbox filled with button-labels. Arguments for the variables we're interested
// are passed in to this constructor. We do not show the box, but do show everything inside.

// Create a series of buttons with the appropriate settings
Gtk::Button *button = new Gtk::Button("hbox->pack_start");
pack_start(*button, expand, fill, padding);
button->show();

button = new Gtk::Button("(*button,");
pack_start(*button, expand, fill, padding);
button->show();

// Create a button with the label depending on the value of expand
if (expand)
button = new Gtk::Button("true,");
else
button = new Gtk::Button("false,");

pack_start(*button, expand, fill, padding);
button->show();

// This is the same as the button creation for "expand" above, but uses the shorthand form.
button = new Gtk::Button(fill ? "true," : "false,");
pack_start(*button, expand, fill, padding);
button->show();

String s = String::format("%d);", padding);

button = new Gtk::Button(s);
pack_start(*button, expand, fill, padding);
button->show();
}

PackBox::~PackBox()
{
}

// PackBoxWindow

PackBoxWindow::PackBoxWindow(int which)
{
set_border_width(10);

// We create a vertical box (vbox) to pack the horizontal boxes into. This allows us to stack
// the horizontal boxes filled with buttons one on top of the other in this vbox.
Gtk::VBox *vbox = new Gtk::VBox;

// Which example to show.
switch (which)
{
case 1:
example_1(vbox);
break;

case 2:
example_2(vbox);
break;

case 3:
example_3(vbox);
}

// Create another new hbox; remember we can use as many as we need!
Gtk::HBox *quitbox = new Gtk::HBox;

// Our quit button.
Gtk::Button *button = new Gtk::Button("Quit");

// Connect the signal to terminate the program when the button is clicked.
button->signal_clicked().connect(slot(this, &PackBoxWindow::dispose));

// Pack the button into the quitbox. The last 3 arguments to Gtk::Box::pack_start are: expand, fill, padding.
quitbox->pack_start(*button, true, false);

// pack the quitbox into the vbox (box1)
vbox->pack_start(*quitbox, false, false);

// Pack the vbox (box1) which now contains all our widgets, into the main window.
add(*vbox);

// And show everything left
button->show();
quitbox->show();
vbox->show();

// Showing the window last so everything pops up at once.
show();

}

PackBoxWindow::~PackBoxWindow()
{
}

void
PackBoxWindow::example_1(Gtk::VBox *vbox)
{
// Create a new label.
Gtk::Label *label = new Gtk::Label("Gtk::HBox *hbox = new Gtk::HBox(false, 0);");

// Align the label to the left side.
label->set_alignment(0, 0);

// Pack the label into the vertical box (vbox). Remember that widgets added to a vbox
// will be packed one on top of the other in order.
vbox->pack_start(*label, false, false);

// Show the label
label->show();

// Create a box - homogeneous = false, spacing = 0, expand = false, fill = false, padding = 0
PackBox *box = new PackBox(false, 0, false, false, 0);
vbox->pack_start(*box, false, false);
box->show();

// Call our PackBox constructor - homogeneous = false, spacing = 0, expand = true, fill = false, padding = 0
box = new PackBox(false, 0, true, false, 0);
vbox->pack_start(*box, false, false);
box->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(false, 0, true, true, 0);
vbox->pack_start(*box, false, false);
box->show();

// Creates a separator, we'll learn more about these later, but they are quite simple.
Gtk::HSeparator *separator = new Gtk::HSeparator;

// Pack the separator into the vbox. Remember each of these widgets is being packed into a vbox,
// so they'll be stacked vertically.
vbox->pack_start(*separator, false, true, 5);
separator->show();

// Create another new label, and show it.
label = new Gtk::Label("Gtk::HBox *hbox = new Gtk::HBox(true, 0);");
label->set_alignment(0, 0);
vbox->pack_start(*label, false, false);
label->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(true, 0, true, false, 0);
vbox->pack_start(*box, false, false);
box->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(true, 0, true, true, 0);
vbox->pack_start(*box, false, false);
box->show();

// Another new separator.
separator = new Gtk::HSeparator;

// The last 3 arguments to Gtk::Box::pack_start are: expand, fill, padding.
vbox->pack_start(*separator, false, true, 5);
separator->show();
}

void
PackBoxWindow::example_2(Gtk::VBox *vbox)
{
// Create a new label.
Gtk::Label *label = new Gtk::Label("Gtk::HBox *hbox = new Gtk::HBox(false, 10);");
label->set_alignment(0, 0);
vbox->pack_start(*label, false, false);
label->show();

// Args are: homogeneous, spacing, expand, fill, padding
PackBox *box = new PackBox(false, 10, true, false, 0);
vbox->pack_start(*box, false, false);
box->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(false, 10, true, true, 0);
vbox->pack_start(* box, false, false);
box->show();

Gtk::HSeparator *separator = new Gtk::HSeparator;
// The last 3 arguments to Gtk::Box::pack_start are: expand, fill, padding.
vbox->pack_start(*separator, false, true, 5);
separator->show();

label = new Gtk::Label("Gtk::HBox *hbox = new Gtk::HBox(false, 0);");
label->set_alignment(0, 0);
vbox->pack_start(*label, false, false);
label->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(false, 0, true, false, 10);
vbox->pack_start(*box, false, false);
box->show();

// Args are: homogeneous, spacing, expand, fill, padding
box = new PackBox(false, 0, true, true, 10);
vbox->pack_start(*box, false, false);
box->show();

separator = new Gtk::HSeparator;
// The last 3 arguments to Gtk::Box::pack_start are: expand, fill, padding.
vbox->pack_start(*separator,false, true, 5);
separator->show();
}

void
PackBoxWindow::example_3(Gtk::VBox *vbox)
{
// This demonstrates the ability to use Gtk::Box::pack_end() to right justify widgets.
// First, we create a label as before.
Gtk::Label *label = new Gtk::Label("Gtk::HBox *hbox = new Gtk::HBox(false, 0);");
label->set_alignment(0, 0);
vbox->pack_start(*label, false, false);
label->show();

// Then we create a new box.
PackBox *box = new PackBox(false, 0, false, false, 0);

// Create the label that will be put at the end.
label = new Gtk::Label("end");

// Pack it using Gtk::Box::pack_end(), so it is put on the right
// side of the hbox created in the make_box() call.
box->pack_end(*label, false, false);

// Show the label.
label->show();

// Pack box into vbox
vbox->pack_start(*box, false, false);
box->show();

// A separator for the bottom.
Gtk::HSeparator *separator = new Gtk::HSeparator;

// This explicitly sets the separator to 400 pixels wide by 5 pixels high. This is so the hbox we created
// will also be 400 pixels wide, and the "end" label will be separated from the other labels in the hbox.
// Otherwise, all the widgets in the hbox would be packed as close together as possible.
separator->set_size_request(400, 5);

// pack the separator into the vbox (box1) created near the start of the constructor
vbox->pack_start(*separator, false, true, 5);
separator->show();
}

int main (int argc, char *argv[])
{
using namespace Main;

init(&argc, &argv);

if (argc != 2)
{
fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");
// This just does cleanup in GTK and exits with an exit status of 1.
exit (1);
}

int which = atoi(argv[1]);

PackBoxWindow window(which);
window.signal_destroy().connect(sigc::ptr_fun(&Xfc::Main::quit));

run();
return 0;
}

Compiling Packbox

If you compiled and installed XFC yourself, you will find the source code for Packbox in the <examples/packbox> source directory along with a Makefile. If XFC came pre-installed, or you installed it from an RPM package, you will find the source code in the </usr/share/doc/xfcui-X.X/examples/packbox> subdirectory. In this case you will have to create the Makefile yourself (replace X.X with the version number of the libXFCui library you have installed).

To create a Makefile for Packbox, add the following lines to a new text file and save it using the name "Makefile":

CC = g++

CFLAGS = -Wall -O2

packbox: packbox.cc packbox.hh
    $(CC) packbox.cc -o packbox $(CFLAGS) `pkg-config xfcui-X.X --cflags --libs`

clean:
    rm -f *.o packbox


If you cut and paste these lines make sure the whitespace before $(CC) and rm is a tab character. Packbox can display three different main windows, depending on the command line option passed: 1, 2 or 3. Using different flags, each window displays several rows of buttons, each row packed into a separate box. The button text on each button in a row corresponds to the value the button sets in the call to the pack_start(). When you compile and run this program with the value 1, you will see the following window appear:


The source code is commented it fairly well so you shouldn't have any problems following it. Try running the program with the value 2 and then 3, and resize each window to see how the button widgets in a row get resized.


Copyright © 2004-2005 The XFC Development Team Top
XFC 4.4