Widget Concepts

This section discusses concepts that apply to all widgets, including memory management and certain special states widgets can be in. It's a "conceptual" section; however, the concepts are very important to practical topics covered later in the book.

Widget Life Cycle

Widget resource and memory management is mostly automatic. However, there are a couple of "gotchas" to keep in mind if you're doing more complicated things.

#include <gtk/gtkwidget.h>

void gtk_widget_destroy(GtkWidget* widget);

Figure 21. Widget Destruction

A widget can be destroyed at any time by calling gtk_widget_destroy() (shown in Figure 21); destroying a widget frees any associated memory and other resources. If the widget is inside a container, it is automatically removed from the container before it's destroyed. It's worth noting that gtk_widget_destroy() is simply another name for gtk_object_destroy(); GtkObjects have "virtual destructors" so gtk_object_destroy() will always do the right thing.

Internally, a reference count is maintained for all widgets (actually, all GtkObjects). Objects begin their life with a reference count of 1, even though they have not yet been referenced. At this stage the object is said to be floating and is flagged as such. It is possible to remove the object's initial reference; this is called sinking the floating object and will destroy the object if the floating reference was the only one.

Containers first reference and then sink any floating widgets that are added to them. By sinking a widget, a container "takes ownership" of it for resource management purposes. Thus, the reference count of the widget remains 1, but the object is no longer flagged as floating. When a widget is removed from a container---or the container is destroyed---the reference count is decremented to 0. When an object's reference count reaches 0, it is destroyed.

In practice, this means that you only have to destroy toplevel widgets; any widgets that are inside a container will be destroyed along with the container.

There's a danger here, however. Sometimes you want to remove a widget from a container; perhaps some element of your interface is optional or only appears under certain circumstances. When you remove the widget (using gtk_container_remove()), it will be unreferenced, its reference count will drop to 0, and it will be destroyed. To avoid this situation, you should add a reference to the widget before you remove it. Figure 22 lists the functions to manipulate reference counts.

#include <gtk/gtkobject.h>

void gtk_object_ref(GtkObject* object);

void gtk_object_unref(GtkObject* object);

void gtk_object_sink(GtkObject* object);

Figure 22. Reference Counting

gtk_object_ref() and gtk_object_unref() have widget-specific variants (gtk_widget_ref(), etc.) but the object and widget versions are completely synonymous. The widget-specific versions are leftovers from earlier versions of GTK+.

So to safely remove a widget from a container, you might do this:


  gtk_widget_ref(widget);
  gtk_container_remove(container, widget);

The widget now has one reference, held by your code. At some point you'll need to release the reference, destroying the widget. (It would make sense to do so after re-adding the widget to some other container, for example.)

It's worth pointing out that removing widgets from containers is uncommon; in general it's faster to simply hide the widget with gtk_widget_hide(), then gtk_widget_show() it at some later time.

gtk_object_sink() is used almost exclusively in widget implementations, when you expect to be the primary "owner" of an object. If an object is not "floating", gtk_object_sink() has no effect. To claim ownership of a widget, do this:


  gtk_widget_ref(widget);
  gtk_object_sink(GTK_OBJECT(widget));

This code adds one reference to the widget; if the widget was "floating," it also subtracts one reference. If the widget was not floating, gtk_widget_sink() has no effect.

It's important to understand the details because in some cases they can be important. But most of the time, you can get by with a few simple rules:

Realizing, Mapping, and Showing

Fully understanding GTK+ requires some minimal understanding of the X Window System. This book assumes you have a user-level understanding---you know what an X server is, that X is network transparent, what a window manager does, and so on. A few more details are needed to write programs, however.

One detail is particularly important: the X Window System maintains a tree of windows. "Window" in this sense refers to an X window, not a GtkWindow---GtkWindow is a GTK+-specific concept, a widget that corresponds to an application's toplevel X window. An X window is not the user-visible concept "window" represented by GtkWindow; rather, it's an abstraction used by the X server to partition the screen. The "background" displayed by your X server is the root window; the root window has no parent. Application windows are typically near-children of the root window; most window managers create a child of the root window to hold the window's titlebar and other decorations, and place the application window inside. Window managers have total control over application windows---they can reposition them, reparent them, and iconify them at will. Application windows can in turn contain subwindows, which are controlled by the application. Note that GTK+ uses the GDK library, rather than using X directly; in GDK, there is a thin X window wrapper called GdkWindow. Don't confuse GdkWindow and GtkWindow.

An X window, or a GdkWindow, gives the X server hints about the structure of the graphics being displayed. Since X is network transparent, this helps reduce network traffic. The X server knows how to show windows on the screen; hide them; move them around (keeping children in position relative to their parents); capture events such as mouse movements on a per-window basis; and so on. A GdkWindow is also the fundamental unit for drawing graphics---you can't draw to "the screen" as a whole, you must draw on a GdkWindow.

Most GTK+ widgets have a corresponding GdkWindow. There are exceptions, such as GtkLabel; these are referred to as "no window widgets," and are relatively lightweight. Widgets with no associated GdkWindow draw into their parent's GdkWindow. Some operations, such as capturing events, require a GdkWindow; thus they are impossible on no-window widgets.

Widgets pass through a number of states related to their GdkWindow:

In typical user code, you only need to call gtk_widget_show(); this implies realizing and mapping the widget as soon as its parent is realized and mapped. It's important to understand that gtk_widget_show() has no immediate effect, it merely schedules the widget to be shown. This means you don't have to worry about showing widgets in any particular order; it also means that you can't immediately access the GdkWindow of a widget. Sometimes you need to access the GdkWindow; in those cases you'll want to manually call gtk_widget_realize() to create it. gtk_widget_realize() will also realize a widget's parents, if appropriate. It's uncommon to need gtk_widget_realize(); if you find that you do, perhaps you are approaching the problem incorrectly.

Destroying a widget automatically reverses the entire sequence of events, recursively unrealizing the widget's children and the widget itself.

Figure 23 summarizes the functions discussed in this section.

#include <gtk/gtkwidget.h>

void gtk_widget_realize(GtkWidget* widget);

void gtk_widget_unrealize(GtkWidget* widget);

void gtk_widget_map(GtkWidget* widget);

void gtk_widget_unmap(GtkWidget* widget);

void gtk_widget_show(GtkWidget* widget);

void gtk_widget_hide(GtkWidget* widget);

Figure 23. Showing/Realizing Widgets

Figure 24 summarizes macros for querying the states discussed in this section.

#include <gtk/gtkwidget.h>

GTK_WIDGET_NO_WINDOW(widget);

GTK_WIDGET_REALIZED(widget);

GTK_WIDGET_MAPPED(widget);

GTK_WIDGET_VISIBLE(widget);

Figure 24. Widget Predicates

Other Widget Concepts

This section describes a few other concepts associated with the GtkWidget base class, including sensitivity, focus, and widget states.

Sensitivity

Widgets can be sensitive or insensitive; insensitive widgets do not respond to input. (On other platforms, this is termed "ghosted" or "inactive").

gtk_widget_set_sensitive() (Figure 25) changes a widget's sensitivity.

#include <gtk/gtkwidget.h>

void gtk_widget_set_sensitive(GtkWidget* widget, gboolean setting);

Figure 25. Changing Sensitivity

By default sensitivity is set to TRUE. A widget is only "really" sensitive if all its parents are sensitive; that is, you can make an entire container full of widgets (in)sensitive by setting the sensitivity of the container. The "real" sensitivity of a widget, including its parent's state, can be tested with the GTK_WIDGET_IS_SENSITIVE() macro. The sensitivity of the widget itself, which only matters if the widget's parent is sensitive, can be queried using GTK_WIDGET_SENSITIVE(). These are in Figure 26.

#include <gtk/gtkwidget.h>

GTK_WIDGET_IS_SENSITIVE(widget);

GTK_WIDGET_SENSITIVE(widget);

Figure 26. Sensitivity

Focus

Within each toplevel window, one widget at a time may have the keyboard focus. Any key events received by the toplevel window are forwarded to the focused widget. This is important because typing something on the keyboard should have only one effect---changing only one text entry field, for example.

Most widgets will give some visual indication that they have the current focus. Using the default GTK+ theme, the focused widget is typically surrounded by a thin black frame. The user can move the focus between widgets, using the arrow keys or the Tab key. Focus can also move to a widget if the user clicks it.

The concept of focus is important for keyboard navigation. For example, pressing Enter or the space bar "activates" many widgets if they have the focus; you can move between buttons with the Tab key, and press one with Space, for example.

Grabs

Widgets can grab the pointer and keyboard away from other widgets. This essentially means that the widget becomes "modal"; input goes only to that widget, and the focus can't be changed to another widget. A typical reason to grab input is to create a modal dialog; if a window has the grab, interaction with other windows is blocked. Note that there is another, GDK-level "grab"; a GDK keyboard or pointer grab occurs on an X-server-wide basis---that is, other applications are unable to receive keyboard or mouse events. A widget grab is a GTK+ concept; it only grabs events away from other widgets in the same application.

Default

Each window may have at most one default widget. For example, dialogs typically have a default button which is activated when the user presses the Enter key.

Widget States

Widgets have states which determine their appearance:

  • Normal: just like it sounds.

  • Active: a button is currently pressed in, or a check box is currently checked, for example.

  • Prelight: the mouse is over the widget (and typically clicking would have some effect). Buttons "highlight" when you move over them, for example.

  • Selected: the widget is in a list or other set of alternatives, and is the currently selected option.

  • Insensitive: the widget is "ghosted," inactive, or unresponsive; it will not respond to input.

The exact meaning and visual representation of a given state depends on the particular widget and the current theme. You can access the state of the widget GTK_WIDGET_STATE() (Figure 27). This macro returns one of the constants GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STATE_SELECTED, or GTK_STATE_INSENSITIVE.

#include <gtk/gtkwidget.h>

GTK_WIDGET_STATE(widget);

Figure 27. State Accessor