GtkWidget In Detail

This section catalogs the functions in GtkWidgetClass more rigorously, and describes the default implementation of each.

Destruction

Widget destruction is not significantly different from object destruction in general, as described in the section called Object Finalization in the chapter called The GTK+ Object and Type System. As always, there are three phases: shutdown, destroy, and finalize. Only the destroy method is a signal; the others are class functions only. If you override any of the three, you must "chain up" to the parent class implementation.

GtkWidget has default implementations of all three; you should know what they do:

Showing, Hiding, and Mapping

Four methods are provided to show and hide widgets. show and hide are rarely overridden; the default implementations almost always suffice. show_all and hide_all are overridden by container widgets; they show or hide the container and all its children. Their default implementation simply shows the widget itself: no non-container should need to change this. "show" and "hide" are registered as signals, but the _all variants are not.

The default implementation of show sets the GTK_VISIBLE flag, and maps the widget if its parent is mapped. When containers are mapped, they map any children with the GTK_VISIBLE flag set. Thus, the show implementation ensures that a widget will be mapped eventually. (When a widget is finally mapped, the GTK_MAPPED flag is set in addition to GTK_VISIBLE.)

The default hide implementation does the opposite: it unsets the GTK_VISIBLE flag, and unmaps the widget if GTK_MAPPED is set.

The default map and unmap implementations are much more frequently overridden. The default implementations should suffice for windowless widgets and widgets with a single window (widget->window). Widgets with additional subwindows or other special needs may need to override the defaults.

The map method is responsible for putting a widget on the screen. The default implementation sets the GTK_MAPPED flag, and calls gdk_window_show() on widget->window for widgets that have a window. If a widget has subwindows, or needs to take any special action when it appears on the screen, it must override the map method. (It may optionally chain up to the default method, however.) Container widgets are required to override the map method, because they must iterate over their children and map each child widget with the GTK_VISIBLE flag set (i.e., each child widget that's been shown).

The unmap method is simply the reverse of the map method; it undoes anything the map method did. By default, it unsets the GTK_MAPPED flag and calls gdk_window_hide() to hide widget->window. Container widgets must override the method to unmap their child windows.

Keep in mind that none of these methods are invoked directly; they are invoked by calling gtk_widget_show(), gtk_widget_map(), and so on. These functions may perform special actions before and after invocation. Here is a summary:

Realization

The realize and unrealize class functions are registered as signals. Realization is the process of creating GDK resources associated with the widget; including but not limited to widget->window and widget->style.

A realize method should do the following:

The default implementation of realize is only appropriate for windowless widgets; it sets the GTK_REALIZED flag, sets widget->window to the parent widget's window, increases the reference count on the parent's window, and sets widget->style. Widgets with their own GdkWindow must override the realize method.

The "realize" signal invokes the realize method as its default handler. This signal should never be emitted directly, because there are substantial pre- and post-conditions to be enforced. gtk_widget_realize() takes care of the details. Among other things, it ensures that the widget's parent is realized; GTK+ maintains the invariant that widgets cannot be realized unless their parents are also realized.

As you might expect, the unrealize method reverses a widget's realization, freeing the resources created in realize. The default unrealize method is appropriate for all widgets; it does the following:

Widgets are required to chain up if they override their base class's unrealize method. This ensures that all resources are freed. Overriding the default method may be necessary if a widget has more than one GdkWindow or other special needs. All windows should be destroyed just as GtkEv destroys its event_window, that is, the window's user data field should be set to NULL before destruction:


      gdk_window_set_user_data(ev->event_window, NULL);
      gdk_window_destroy(ev->event_window);
      ev->event_window = NULL;

      

This keeps GTK+ from sending a useless GDK_DESTROY event for the window.

The "unrealize" signal is emitted via gtk_widget_unrealize(). gtk_widget_unrealize() does some internal bookkeeping which is important but not very interesting; just be careful to use this function rather than emitting the signal directly.

Drawing

The relationship between draw, draw_focus, draw_default, and expose_event was discussed in the section called Drawing. All four class functions are registered as default handlers for a signal of the same name. The "draw" signal redraws a portion of the widget; it is emitted by GTK+ or by widget implementations.

Widget implementations should emit the "draw_focus" signal themselves, generally in response to focus events; the signal draws or undraws a frame indicating that the widget is focused. gtk_window_set_default() emits the "draw_default" signal for both the widget losing default status and the widget gaining it. (gtk_widget_grab_default() calls gtk_window_set_default().) Only widgets with the GTK_CAN_FOCUS or GTK_CAN_DEFAULT flag set need to worry about the draw_focus and draw_default methods. These flags should be set in a widget's instance initialization function.

Only the draw method has a default implementation in GtkWidget; the default implementation synthesizes an expose event covering the widget's entire allocation. This allows you to write an expose event handler that also handles the draw signal.

GTK+ normally emits the "draw" signal in an idle function. That is, it keeps a list of widgets (and regions of them) that need to be redrawn; when no events are pending, the GTK+ main loop runs an idle function that traverses the list and emits the "draw" signal for each widget. Redraw areas are merged as much as possible to avoid multiple redraws, and the idle function is removed after it runs once. This arrangement minimizes the number of redraws and reduces flicker.

Size Negotiation

The size negotiation process has already been described in the section called Size Allocation in the chapter called GTK+ Basics and the section called Size Negotiation. The two signals/methods involved are size_request and size_allocate. GtkWidget provides default implementations of each.

Here is the default size_request method:


static void
gtk_widget_real_size_request (GtkWidget         *widget,
                              GtkRequisition    *requisition)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));

  requisition->width = widget->requisition.width;
  requisition->height = widget->requisition.height;
}

      

This implementation is appropriate for widgets that always request the same size; GtkArrow and GtkDrawingArea, for example, use this default. Widgets using the default must initialize widget->requisition with their fixed request; GtkArrow does this in gtk_arrow_init(). GtkDrawingArea begins with a default size, but allows users to change the request with gtk_drawing_area_set_size().

Widgets whose request depends on their children, or the amount of text they contain, or some other factor, should override the default size request method with a method that calculates the size they want.

Size allocation is mildly more complicated; here is its default implementation:


static void
gtk_widget_real_size_allocate (GtkWidget     *widget,
                               GtkAllocation *allocation)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_WIDGET (widget));

  widget->allocation = *allocation;
  
  if (GTK_WIDGET_REALIZED (widget) &&
      !GTK_WIDGET_NO_WINDOW (widget))
     {
        gdk_window_move_resize (widget->window,
                                allocation->x, allocation->y,
                                allocation->width, allocation->height);
     }
}
      

This should suffice for most simple widgets. Widgets like GtkEv, or any container, need to update internal data structures or distribute the allocation they receive among child widgets; these widgets will override size_allocate. It is possible but not required to chain up to the default implementation.

The wrapper function which emits the "size_allocate" signal is significantly more involved than the signal handlers. gtk_widget_size_allocate() takes into account gtk_widget_set_usize() and ensures widgets are redrawn if their size changes. (Unsurprisingly, gtk_widget_size_request() also exists and should be used instead of emitting "size_request" directly.)

GtkContainer

GtkContainer is the base class for all widgets that contain one or more other widgets. GtkBin is a subclass of GtkContainer, and is the base class for widgets with a single child.

The GtkContainer Instance Struct

Here is GtkContainer:


typedef struct _GtkContainer       GtkContainer;

struct _GtkContainer
{
  GtkWidget widget;
  
  GtkWidget *focus_child;
  
  guint border_width : 16;
  guint need_resize : 1;
  guint resize_mode : 2;
  
  GSList *resize_widgets;
};
    

The focus_child member is the child in the container with the keyboard focus; it can be NULL if no child has the focus. The GtkContainer implementation handles setting and unsetting this field. The border_width member is a width in pixels to add to the container's size request on all sides; the container will also subtract this value from its allocation. (In other words, the border_width is a blank space around the container.) Library users set the border_width field with gtk_container_set_border_width(); GtkContainer subclasses must honor its value in their size_request and size_allocate implementations.

The need_resize, resize_mode, and resize_widgets fields are implementation details that subclasses should not have to read or modify. These fields are used to implement gtk_widget_queue_resize(); when a resize is queued for a widget, the GtkContainer implementation ensures that the size negotiation process (requisition/allocation) will take place a in a one-shot idle handler. Subclasses of GtkContainer simply implement size_request and size_allocate, and everything works.

The GtkContainer Class Struct


typedef struct _GtkContainerClass  GtkContainerClass;

struct _GtkContainerClass
{
  GtkWidgetClass parent_class;
  
  guint   n_child_args;

  void (* add)                  (GtkContainer    *container,
                                 GtkWidget       *widget);
  void (* remove)               (GtkContainer    *container,
                                 GtkWidget       *widget);
  void (* check_resize)         (GtkContainer    *container);
  void (* forall)               (GtkContainer    *container,
                                 gboolean         include_internals,
                                 GtkCallback      callback,
                                 gpointer         callbabck_data);
  gint (* focus)                (GtkContainer    *container,
                                 GtkDirectionType  direction);
  void (* set_focus_child)      (GtkContainer    *container,
                                 GtkWidget       *widget);
  GtkType (*child_type)         (GtkContainer   *container);
  void    (*set_child_arg)      (GtkContainer   *container,
                                 GtkWidget      *child,
                                 GtkArg         *arg,
                                 guint           arg_id);
  void    (*get_child_arg)      (GtkContainer   *container,
                                 GtkWidget      *child,
                                 GtkArg         *arg,
                                 guint           arg_id);
  gchar*  (*composite_name)     (GtkContainer   *container,
                                 GtkWidget      *child);

  /* Padding for future expansion */
  GtkFunction pad1;
  GtkFunction pad2;
};
    

Many of these class functions do not have corresponding signals. add, remove, check_resize, focus, and set_focus_child methods are default handlers for signals with the same name. The others are just methods.

The check_resize method recalculates layout if necessary; it is invoked by the idle handler installed by gtk_widget_queue_resize(). As the previous section mentioned, subclasses should not have to concern themselves with this process.

The focus and set_focus_child methods handle moving focus around a GtkWindow. Users can move the focus with the arrow keys and the tab key. This results in emissions of the "focus" signal, with the direction argument indicating which way the focus should move. The possible directions are: GTK_DIR_TAB_FORWARD, GTK_DIR_TAB_BACKWARD, GTK_DIR_UP, GTK_DIR_DOWN, GTK_DIR_LEFT, and GTK_DIR_RIGHT. GtkContainer provides a default implementation based on the geometric location of child widgets; it works fine for most standard layout containers, such as GtkBox and GtkTable. It should also work for GtkBin subclasses. More elaborate or unusual containers, such as the tree and list widgets, or GtkNotebook, override this method. The focus method should return TRUE if an appropriate child was found and the focus was moved (using gtk_widget_grab_focus()).

The set_focus_child method is used by gtk_widget_grab_focus() to set the focus child of a container. gtk_widget_grab_focus() can be invoked by the focus method of a container, or by a widget implementation (for example, GtkEntry grabs the focus if the user clicks on it). The default implementation simply sets the focus_child field of the container, and increments the child's reference count.

composite_name returns a special name for a child when it's a part of a particular container. For example, the composite names of the two GtkScrollbar widgets in a GtkScrolledWindow are "hscrollbar" and "vscrollbar." These names allow themes to specify widget attributes precisely. The default implementation of this method should always work fine; it returns the name set with gtk_widget_set_composite_name().

n_child_args, set_child_arg, and get_child_arg are exactly analagous to the n_args, get_arg, and set_arg fields of GtkObjectClass. the section called Using Object Arguments in Your Own GtkObject Subclass in the chapter called The GTK+ Object and Type System mentions this briefly. Child arguments are used to get and set attributes of the container-child unit, such as the packing parameters for GtkBox or the attachment parameters for GtkTable. In contrast, normal object arguments set the characteristics of a single object in isolation. Though the implementation differs, child arguments work almost exactly like the object arguments described in the chapter called The GTK+ Object and Type System. The only visible difference is that the get and set functions take a container and a widget as arguments, instead of a single object.

The following section introduces the remaining functions in GtkContainer by describing their implementation in GtkBin.

GtkBin: Containers With One Child

GtkBin doesn't introduce any new interfaces; it simply adds a data member to each container instance for storing a single child, and provides default implementations for the container methods that operate on this one child. GtkBin implements the add, remove, forall, and child_type methods from GtkContainer; combined with GtkContainer's default implementations, simple subclasses of GtkBin (such as GtkEventBox, GtkAlignment, and GtkFrame) do not need to override any GtkContainer methods. Here's the instance struct:


typedef struct _GtkBin       GtkBin;

struct _GtkBin
{
  GtkContainer container;

  GtkWidget *child;
};

      

And the class struct:


typedef struct _GtkBinClass  GtkBinClass;

struct _GtkBinClass
{
  GtkContainerClass parent_class;
};

      

No rocket science here.

Adding and Removing Children

The add and remove functions do not have a default implementation (well, technically they do: the default implementation prints a warning that they aren't implemented). All containers should override these functions; their corresponding signals are emitted when library users call gtk_container_add() and gtk_container_remove(). For containers that normally require parameters when adding children (such as GtkBox and GtkTable), the add method should simply use reasonable defaults.

GtkBin implements an add method as follows:


static void
gtk_bin_add (GtkContainer *container,
             GtkWidget    *child)
{
  GtkBin *bin;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_BIN (container));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  bin = GTK_BIN (container);
  g_return_if_fail (bin->child == NULL);

  gtk_widget_set_parent (child, GTK_WIDGET (bin));
  bin->child = child;

  if (GTK_WIDGET_VISIBLE (child->parent))
    {
      if (GTK_WIDGET_REALIZED (child->parent) &&
          !GTK_WIDGET_REALIZED (child))
        gtk_widget_realize (child);
      
      if (GTK_WIDGET_MAPPED (child->parent) &&
          !GTK_WIDGET_MAPPED (child))
        gtk_widget_map (child);
    }
  
  if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (container))
    gtk_widget_queue_resize (child);
}
    

Notice the required parts of the add method:

  • gtk_widget_set_parent() is called to set the widget's parent; this fills in widget->parent and handles reference counting and some other internal details.

  • The container saves a pointer to the child; this is easy for GtkBin, since there is only one child and it goes in bin->child.

  • If the container has been shown (i.e., if its GTK_VISIBLE flag is set), parent and child's GTK_REALIZED and GTK_MAPPED states are synchronized. That is, the child is realized and mapped if the parent is realized and mapped.

  • If both parent and child have been shown, a resize is queued. That is, layout will be recalculated in light of the new child.

The remove method reverses the process; here is its implementation:


static void
gtk_bin_remove (GtkContainer *container,
                GtkWidget    *child)
{
  GtkBin *bin;
  gboolean widget_was_visible;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_BIN (container));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  bin = GTK_BIN (container);
  g_return_if_fail (bin->child == child);

  widget_was_visible = GTK_WIDGET_VISIBLE (child);
  
  gtk_widget_unparent (child);
  bin->child = NULL;
  
  if (widget_was_visible)
    gtk_widget_queue_resize (GTK_WIDGET (container));
}
    

A remove method is little more than a wrapper for gtk_widget_unparent that queues a resize if necessary. Most remove methods would check the container's GTK_VISIBLE flag before queueing a resize, just as gtk_bin_add() does; GtkBin does not because toplevel widgets like GtkWindow derive from it, and those widgets always queue a resize, regardless of visibility.

Iterating Over Children

Containers have a forall method for iterating over their children; GtkContainer's default methods use forall, since they know nothing about the data members in subclasses' instance structs. The forall method invokes a callback on each child, with the provided callback data as the second argument. Obviously it's going to be trivial for GtkBin:


static void
gtk_bin_forall (GtkContainer *container,
                gboolean      include_internals,
                GtkCallback   callback,
                gpointer      callback_data)
{
  GtkBin *bin;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_BIN (container));
  g_return_if_fail (callback != NULL);

  bin = GTK_BIN (container);

  if (bin->child)
    (* callback) (bin->child, callback_data);
}
    

You may notice that GtkBin ignores the include_internals argument. Some containers have "incidental" child widgets in addition to the user-provided children they are primarily designed to hold. For example, GtkNotebook has a widget labelling each of its tabs; GtkCList uses buttons to title each column in the list. These internal widgets must be included in the iteration in many cases; for example, when drawing each child in a container, or destroying each child in a container. However, some operations only operate on the "primary" children, such as the pages in GtkNotebook. The include_internals flag indicates whether to invoke the callback on incidental widgets.

Convenience functions are provided that invoke the forall method; these are used by application authors as well as GtkContainer internals. gtk_container_foreach() iterates over only the primary children of a container, while gtk_container_forall() iterates over all the children.

Child Type

The child_type method returns the type of children a container can hold. For example, a GtkMenuShell (parent class of GtkMenu and GtkMenuBar) can only hold children of type GTK_TYPE_MENU_ITEM. The child_type method allows GUI builders and scripting languages to determine at runtime what sort of children a container will accept. GTK_TYPE_NONE indicates that a container will not accept children at this time, for whatever reason.

The GtkBin implementation accepts GTK_TYPE_WIDGET if the bin is empty, and GTK_TYPE_NONE if the bin already contains a child:


static GtkType
gtk_bin_child_type (GtkContainer *container)
{
  if (!GTK_BIN (container)->child)
    return GTK_TYPE_WIDGET;
  else
    return GTK_TYPE_NONE;
}
    

Other GtkBin Functionality

GtkBin also provides default implementations of expose, map, unmap, and draw from GtkWidgetClass. Most GtkBin subclasses will override these methods to handle the unique features of the subclass, but chain up to the GtkBin method to deal with their child widget.

The GtkBin map and unmap implementations are mostly the usual boilerplate (set GTK_MAPPED, show widget->window) but they add an important step unique to containers: mapping the child if it has been shown (remember that GTK_VISIBLE signals a widget has been shown), and unmapping the child when the bin is unmapped. Here's the code:


static void
gtk_bin_map (GtkWidget *widget)
{
  GtkBin *bin;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_BIN (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
  bin = GTK_BIN (widget);

  if (bin->child &&
      GTK_WIDGET_VISIBLE (bin->child) &&
      !GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_map (bin->child);

  if (!GTK_WIDGET_NO_WINDOW (widget))
    gdk_window_show (widget->window);
}

static void
gtk_bin_unmap (GtkWidget *widget)
{
  GtkBin *bin;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_BIN (widget));

  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
  bin = GTK_BIN (widget);

  if (GTK_WIDGET_NO_WINDOW (widget))
     gtk_widget_queue_clear (widget);
  else
    gdk_window_hide (widget->window);

  if (bin->child && GTK_WIDGET_MAPPED (bin->child))
    gtk_widget_unmap (bin->child);
}
    

GtkBin's draw and expose implementations simply redraw the child widget; most subclasses will need to override these methods in order to draw themselves, then chain up to draw the child. The code:


static void
gtk_bin_draw (GtkWidget    *widget,
              GdkRectangle *area)
{
  GtkBin *bin;
  GdkRectangle child_area;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_BIN (widget));

  bin = GTK_BIN (widget);

  if (GTK_WIDGET_DRAWABLE (bin))
    {
      if (bin->child && GTK_WIDGET_DRAWABLE (bin->child) &&
          gtk_widget_intersect (bin->child, area, &child_area))
        gtk_widget_draw (bin->child, &child_area);
    }
}

static gint
gtk_bin_expose (GtkWidget      *widget,
                GdkEventExpose *event)
{
  GtkBin *bin;
  GdkEventExpose child_event;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_BIN (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      bin = GTK_BIN (widget);

      child_event = *event;
      if (bin->child && GTK_WIDGET_DRAWABLE (bin->child) &&
          GTK_WIDGET_NO_WINDOW (bin->child) &&
          gtk_widget_intersect (bin->child, &event->area, &child_event.area))
        gtk_widget_event (bin->child, (GdkEvent*) &child_event);
    }

  return FALSE;
}
    

Notice that expose events are only generated for windowless widgets; widgets with windows will receive their own expose events. Containers are required to generate expose events for windowless children.

You can probably guess that gtk_widget_intersect() determines the intersection of a rectangle and a child widget.