The Official GNOME 2 Developers Guide

3.4 Container and Layout Widgets

It would be practically impossible to organize normal widgets without container widgets. Each container has a specific way of ordering its widgets based on parameters and packing order.

The principal container types are as follows :

You already saw some examples of HBoxes and VBoxes in earlier examples. The following program demonstrates button boxes, panes, notebooks, and tables.

Notice that the widget declarations in the main program are grouped by container.

/* -*-coding: utf-8;-*- */ /* container.c -- container demo */ #include <gtk/gtk.h> /* standard handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) { return FALSE; } void end_program(GtkWidget *widget, gpointer data) { gtk_main_quit(); } int main(int argc, char **argv) { GtkWindow *window; GtkHPaned *h_pane; GtkVPaned *v_pane; GtkVButtonBox *button_column; /* button box elements */ GtkButton *button[3]; GtkTable *table; /* table elements */ GtkButton *tictac[3][3]; gint i, j; GtkNotebook *notebook; /* notebook elements */ GtkLabel *page_1_content; GtkImage *page_2_apple; GtkButton *page_3_button; GtkLabel *page_1_title, *page_2_title, *page_3_title; /* initialize GTK+, create a window, attach handlers */ gtk_init(&argc, &argv); window = g_object_new(GTK_TYPE_WINDOW, "title", "Container Madness", "default_height", 200, "default_width", 300, "border-width", 12, NULL); /* attach standard event handlers */ g_signal_connect(window, "delete_event", G_CALLBACK(delete_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(end_program), NULL);

The program initially divides the main window into two panes with a horizontal pane widget that the user can slide left and right.

/* Divide window horizontally with a pane */ h_pane = g_object_new(GTK_TYPE_HPANED, NULL); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(h_pane));

So far, there's been nothing terribly new about this application. Now let's put another widget (a vertical paned container, to be specific) in the left pane created by the preceding code:

/* create a vertical paned container and put it in the left side of the horizontal pane above */ v_pane = g_object_new(GTK_TYPE_VPANED, NULL); gtk_paned_add1(GTK_PANED(h_pane), GTK_WIDGET(v_pane));

Notice that the packing function for paned containers here is gtk_paned_add1() , to put the widget into the left pane. If this were a vertical paned container, it would put the widget into the top pane. Refer to Section 3.4.3 for more information on paned containers.

The next three statements create three buttons; there is nothing unusual about them.

/* create three buttons */ button[0] = g_object_new(GTK_TYPE_BUTTON, "label", "Foo", NULL); button[1] = g_object_new(GTK_TYPE_BUTTON, "label", "Bar", NULL); button[2] = g_object_new(GTK_TYPE_BUTTON, "label", "Baz", NULL);

A vertical button box holds the buttons. Because a button box is a form of GtkBox , the packing functions are the same as for a regular box container (see Section 3.4.1).

/* put the buttons in a vertical button box */ button_column = g_object_new(GTK_TYPE_VBUTTON_BOX, NULL); for (i=0; i<3; i++) { gtk_box_pack_start_defaults(GTK_BOX(button_column), GTK_WIDGET(button[i])); }

As the following comment indicates, this button box will go into the top pane of the vertically paned container (recall that this container is on the left side of the window).

/* put the vertical button box into the top pane of v_pane, from earlier */ gtk_paned_add1(GTK_PANED(v_pane), GTK_WIDGET(button_column));

The following code shows how to create a table container. Because the work of creating all of the widgets for the table can be mundane, a short loop will create a button for each cell in the table. See Section 3.4.2 for a description of a table container's properties and methods .

/* create a 3x3 table container */ table = g_object_new(GTK_TYPE_TABLE, "n-rows", 3, "n-columns", 3, "homogeneous", TRUE, NULL); /* fill the table with some buttons */ for (i=0; i<3; i++) { for (j=0; j<3; j++) { tictac[i][j] = g_object_new(GTK_TYPE_BUTTON, NULL); gtk_table_attach_defaults(table, GTK_WIDGET(tictac[i][j]), i, i+1, j, j+1); } } /* label the buttons in the table's diagonal */ g_object_set(tictac[0][0], "label", "Tic", NULL); g_object_set(tictac[1][1], "label", "Tac", NULL); g_object_set(tictac[2][2], "label", "Toe", NULL); /* put the table in the lower pane of v_pane, from above */ gtk_paned_add2(GTK_PANED(v_pane), GTK_WIDGET(table));

The last container widget in this program is a notebook that occupies the right pane in the main window. The first of three pages in the notebook contains a "Page 1!" label. The page needs a title to put on its tab; the first page's title is "This."

/* create a notebook */ notebook = g_object_new(GTK_TYPE_NOTEBOOK, NULL); /* put the notebook in the window's right pane */ gtk_paned_add2(GTK_PANED(h_pane), GTK_WIDGET(notebook)); /* create notebook's page 1, containing only a label */ page_1_content = g_object_new(GTK_TYPE_LABEL, "label", "Page 1!", NULL); /* create page 1's title ("This") */ page_1_title = g_object_new(GTK_TYPE_LABEL, "label", "This", NULL); /* add the page to the notebook */ gtk_notebook_append_page_menu(notebook, GTK_WIDGET(page_1_content), GTK_WIDGET(page_1_title), NULL);

Section 3.4.4 describes notebook packing methods such as gtk_notebook_append_page_menu() , as well as the many properties that you can assign to notebooks.

The following fragment creates two more notebook pages: one with an image of an apple, and the other containing a single button. After all of the notebook pages are in place, we're also ready to display the window and start the main event loop. Figure 3.7 shows the final application.

Figure 3.7: Container demonstration.

/* add another page containing an apple image */ page_2_apple = g_object_new(GTK_TYPE_IMAGE, "file", "apple-green.png", NULL); page_2_title = g_object_new(GTK_TYPE_LABEL, "label", "That", NULL); gtk_notebook_append_page_menu(notebook, GTK_WIDGET(page_2_apple), GTK_WIDGET(page_2_title), NULL); /* page 3 contains a button */ page_3_button = g_object_new(GTK_TYPE_BUTTON, "label", "Click me", NULL); page_3_title = g_object_new(GTK_TYPE_LABEL, "label", "The Other", NULL); gtk_notebook_append_page_menu(notebook, GTK_WIDGET(page_3_button), GTK_WIDGET(page_3_title), NULL); /* show the whole thing and start GTK+ main loop */ gtk_widget_show_all(GTK_WIDGET(window)); gtk_main(); return 0; }

All container classes have GtkContainer as a superclass (class identifier: TK_TYPE_CONTAINER ). You have seen

gtk_container_add( container , widget )

from this class; all of the examples so far use it to pack a widget into the ain window.

Note  

Use gtk_container_add() only with simple containers such as GtkWindow and GtkFrame . The more complex containers need more parameters and therefore have their own packing functions.

To remove a widget from a container, use

gtk_container_remove(container, widget)

Warning  

Taking a widget out of its container usually leads to the widget's destruction, because the container held the only (previously floating) reference to the widget object. Obtain a new reference for the widget object if you want to save it.

The gtk_container_add_with_properties() function is a convenience function that allows you to place a widget in a property like gtk_container_add() , but allows a NULL - terminated list of property and value pairs for the widget (the syntax for the list is the same as for g_object_set() ).

If you need to run the same function on many widgets at once, one particularly useful utility is

gtk_container_foreach( container , callback_function , data )

This runs the GtkCallback function callback_function on all of the widgets inside container . The callback takes a widget and data as its parameters; the type definition is as follows:

typedef void (*GtkCallback) (GtkWidget *widget, gpointer data);

All objects derived from the GtkContainer class have these two properties:

g_object_set( container , "child", widget , NULL)

is identical to

gtk_container_add( container , widget )

3.4.1 Boxes

Whether a horizontal box ( GtkHBox , GTK_TYPE_HBOX ) or a vertical box ( GtkVBox , GTK_TYPE_VBOX ), all boxes has the same purpose: to arrange widgets in a line.

These two functions pack a widget into a box:

gtk_box_pack_start( box, widget, expand, fill, padding ) gtk_box_pack_end( box, widget, expand, fill, padding )

The parameters for both functions are

The gtk_box_pack_start() function packs a widget at the front of the box. All widgets previously packed into the box with this function still appear in front of any new widget. gtk_box_pack_end() is a similar function for packing at the end of acontainer.

Note  

In a VBox, the start of the container is the top. In an HBox, it's usually the left side, but don't always assume this. If a user's locale sets a language where the writing goes from right to left, the horizontal widgets are likely to be reversed .

If you don't feel like typing all of the parameters for these two packing functions all of the time, use these two functions:

gtk_box_pack_start_defaults( box , widget ) gtk_box_pack_end_defaults( box , widget )

These are like their counterparts described earlier, but set fill and expand to TRUE , and padding to 0.

The GtkBox class has two properties:

The button box classes ( GtkHButtonBox and GtkVButtonBox , class type identifiers GTK_TYPE_HBUTTON_BOX and GTK_TYPE_VBUTTON_BOX ) have the same methods and properties as normal boxes. There is one additional GtkButtonBox property that gives you finer control of how the box arranges the buttons: layout-style . Its value is one of the following:

Container Child Properties

When you pack a widget into a GtkBox container, the widget obtains some child properties that control how it appears in the box. However, these are not regular GObject properties; you need to use special functions to retrieve and set the values.

The child property access functions rely on the GValue system (see Section 2.4.2):

void gtk_container_child_get_property (GtkContainer * container , GtkWidget * child , const gchar * property_name , GValue * value ); void gtk_container_child_set_property (GtkContainer * container , GtkWidget * child , const gchar * property_name , const GValue * value );

The preceding child property functions require that value be an allocated and initialized GValue . This example with the padding child property should give you the idea:

GValue *value; /* initialize value */ gv = g_new0(GValue, 1); g_value_init(gv, G_TYPE_INT); /* get current child property padding value */ gtk_container_child_get_property( container , widget , "padding", gv); /* set padding value to a ludicrous value */ g_value_set_int(gv, 100); gtk_container_child_set_property( container , widget , "padding", gv); g_free(value);

Here are the GtkBox child properties:

A child of a GtkButtonBox widget receives an additional secondary child property (type: gboolean ). When TRUE , the widget appears at the other side of the button box. Help buttons and the like often get this treatment.

3.4.2 Tables

An object of the GtkTable class is essentially a bounded two-dimensional box. The example in Section 3.4 showed that tables have n-rows and n-columns properties for the number of rows and columns in the table.

After you create a table, you can use it as a container. Naturally, it's not a good idea to arbitrarily pack widgets into the table. You can assign a widget to a specific table cell, and you can also tell it to span rows and columns. The function for inserting a widget into a table is

gtk_table_attach( table , widget , left_attach , right_attach , top_attach , bottom_attach , xoptions , yoptions , xpadding , ypadding )

The arguments are as follows:

After you insert a widget into a table, the widget gets most of the parameters just described as child properties. The property names are left-attach , right-attach , top-attach , bottom-attach , x-options , y-options , x-padding , and y-padding .

GtkTable containers also have these properties:

3.4.3 Paned Widgets

A paned widget is a container with two sides; each side holds a child widget. A user-adjustable slider bar divides the panes. On a GtkHPaned ( GTK_TYPE_HPANED ) widget, a vertical slider divides child widgets in the left and right panes, and a GtkVPaned ( GTK_TYPE_VPANED ) widget has a horizontal slider with child widgets above and below. If you have trouble associating the names with the correct orientation, forget about the sliders and think of them as boxes.

To place widgets into a paned container, use these functions:

gtk_paned_add1( paned , widget ) gtk_paned_add2( paned , widget )

gtk_paned_add1() places a widget into the left or top of the container, and gtk_paned_add2() packs widgets into the right or bottom. The only two arguments to these functions are the container and its new child widget, as you saw in Section 3.4's example program.

If you need some control over the size of the child widgets, use these two instead:

gtk_paned_pack1( paned , widget , resize , shrink ) gtk_paned_pack2( paned , widget , resize , shrink )

Here, resize and shrink are Boolean values. If resize is TRUE , the paned container may resize the widget. If you set shrink to TRUE , the user may shrink the widget as desired with the slider bar, but if it is FALSE , you cannot make the widget smaller than its requested minimum size.

The parent class of the two paned container types ( GtkPaned ) defines two properties:

3.4.4 Notebooks

A notebook container ( GtkNotebook , GTK_TYPE_NOTEBOOK ) consists of several pages with tabs. When you click a page's tab, the page comes to the foreground, and you can work with any widgets on the page. You can also assign a context menu to the tabs; when you right-click a tab, you get a menu of the tabs.

After you create a notebook, you can add pages with several functions. Among the most verbose of these is

gtk_notebook_append_page_menu( notebook , widget , tab_label , menu_label )

The parameters are

Note  

You can put just about any kind of widget in a page's tab, but the only one that really makes any sense is a label.

If you don't care about the menu, use

gtk_notebook_append_page( notebook , widget , tab_label )

Here are some other ways to add pages:

To remove a page from a notebook, use

gtk_notebook_remove_page( notebook , page_number )

Notebook properties include

A widget packed into a notebook takes on these child properties:

3.4.5 Alignment Containers

If you want finer control of the placement and proportions of a widget, place it in a GtkAlignment ( GTK_TYPE_ALIGNMENT ) container. The container's properties are all gfloat values:

Note  

You will frequently encounter properties with names like xalign and yalign in other widget classes; they control alignment in the same manner as a GtkWidget container.

3.4.6 Sensible Widget Arrangement

At this point, you should have a pretty good idea of how you can use containers to lay out your widgets. When it comes to how you should arrange them, the GNOME guidelines have some tips on how to achieve consistency, clarity, and legibility:

Категории