GtkStyle and Themes

GtkStyle is not part of GDK, but it is an important abstraction layer between GTK+ and GDK that allows users to customize how widgets are rendered. Instead of drawing with GDK directly, widgets should prefer GDK resources from a GtkStyle, and special drawing functions provided in gtk/gtkstyle.h. Often there is no appropriate function, but when there is it should be used.

A GtkStyle stores GDK resources to be used when drawing widgets. Styles allow widgets to share these resources, reducing overhead; they also permit users to customize GTK+'s appearance. Here is the GtkStyle struct:


typedef struct _GtkStyle GtkStyle;

struct _GtkStyle
{
  GtkStyleClass *klass;

  GdkColor fg[5];
  GdkColor bg[5];
  GdkColor light[5];
  GdkColor dark[5];
  GdkColor mid[5];
  GdkColor text[5];
  GdkColor base[5];
  
  GdkColor black;
  GdkColor white;
  GdkFont *font;
  
  GdkGC *fg_gc[5];
  GdkGC *bg_gc[5];
  GdkGC *light_gc[5];
  GdkGC *dark_gc[5];
  GdkGC *mid_gc[5];
  GdkGC *text_gc[5];
  GdkGC *base_gc[5];
  GdkGC *black_gc;
  GdkGC *white_gc;
  
  GdkPixmap *bg_pixmap[5];
  
  /* private */
  
  gint ref_count;
  gint attach_count;
  
  gint depth;
  GdkColormap *colormap;
  
  GtkThemeEngine *engine;
  
  gpointer        engine_data;
  
  GtkRcStyle     *rc_style;

  GSList         *styles;
};

The private fields should be ignored. The public fields contain GDK resources for widget rendering. The first group of fields contains arrays of colors; these arrays are indexed by the widget state enumeration (GTK_STATE_ACTIVE, etc.). A widget might use widget->style->fg[GTK_STATE_NORMAL] to render text, for example. Each widget has an associated style, stored in the style field of GtkWidget.

Widgets should use the font stored in their associated GtkStyle; they should use the style's graphics contexts when drawing in the style's colors.

GtkStyle also contains a virtual table, GtkStyleClass, which can be implemented by a dynamically-loaded theme engine. The virtual table is quite large, so it isn't reproduced here. Have a look at gtk/gtkstyle.h.

gtk/gtkstyle.h contains drawing functions that use a style's virtual table to draw various GUI elements. There are two variants of each drawing function. One variant, prefixed with gtk_draw_, renders to any drawable; the other variant, prefixed with gtk_paint_, renders part of a widget. For example, gtk_draw_shadow() looks like this:


void gtk_draw_shadow  (GtkStyle      *style,
                       GdkWindow     *window,
                       GtkStateType   state_type,
                       GtkShadowType  shadow_type,
                       gint           x,
                       gint           y,
                       gint           width,
                       gint           height);

While gtk_paint_shadow() adds area, widget, and detail arguments:


void gtk_paint_shadow  (GtkStyle     *style,
                        GdkWindow    *window,
                        GtkStateType  state_type,
                        GtkShadowType shadow_type,
                        GdkRectangle  *area,
                        GtkWidget     *widget,
                        gchar         *detail,
                        gint           x,
                        gint           y,
                        gint           width,
                        gint           height);

Each of these corresponds to the draw_shadow member in GtkStyleClass.

All gtk_paint_ functions add the same three arguments to their gtk_draw_ counterparts; the area argument is a clipping rectangle, the widget argument is the widget being drawn to, and the detail argument is a hint used by theme engines. Here's a call to gtk_paint_shadow() from the GtkEntry source code, for example:


  gtk_paint_shadow (widget->style, widget->window,
                    GTK_STATE_NORMAL, GTK_SHADOW_IN,
                    NULL, widget, "entry",
                    x, y, width, height);

Here the area argument is NULL, specifying that no clipping should be used.

Because there are a couple dozen functions in GtkStyleClass, and there are numerous examples in the GTK+ source code, this book won't describe them in detail. When writing your own widgets, simply locate a GTK+ widget that draws a similar graphical element, and use the same gtk_paint_ function it uses.