{i} Please feel free to add documentation or correct things. If you have any comments, please write me an e-mail (benjamin@sipsolutions.net), or come to #gnome-art on irc.gnome.org. A list of widgets:

Things missing:

  • Inheritance, i.e., style "button" = "default", but this is usually not necessary because of merging, and needs a warning, as it doesn't work correctly for engine options as far as is known.

  • The GtkSettings properties are not mentioned here. See http://library.gnome.org/devel/gtk/stable/GtkSettings.html#id3227906 for a list of properties that can be set (not all of them make sense from the gtkrc).

  • Need information about icon size handling and a list of icon sizes in GNOME (see also Redmond gtkrc that sets numerous icon sizes)
  • How theme engines are used; see comment in the section.
  • Locale-specific gtkrcs (gtkrc.de will, for example, be parsed in German locales). This will only be useful in very rare cases (maybe for keybinding stuff, which is not the topic of this page).


GTK Theming Tutorial

Introduction

This tutorial will explain the basics of creating a GTK+2 theme for use with GNOME 2.2 and above. Some knowledge of the GTK+2 API can be helpful with more complicated themes; documentation can be found at http://developer.gnome.org/doc/API/2.0/gtk/index.html. Other people's themes can be very helpful. You can find more GTK themes in the resources linked from ../UsefulLinks.

Themes generally live in either a public directory (available to all users, typically in /usr/share/themes/) or in ~/.themes/ (just for you). A convenient place to put the themes you create are in ~/.themes/YOUR_THEME_NAME/gtk-2.0/. Within this gtk-2.0 directory you will place all files that go with your theme. Note that if themes of the same name are installed in both the public and the ~/.themes directories that GNOME will use the one in ~/.themes.

A good way to learn theming is to examine other people's themes. Your GNOME installation undoubtedly came with quite a few themes. More can be found at:

Architecture

Widgets

GTK has a large set of widgets like buttons, scroll bars and edit boxes. Each of these widgets can be themed separately.

Every widget is derived from GtkWidget. This means, changes to the properties of GtkWidget will effect all widgets. Furthermore many other widgets have "parent widgets". For example, properties of GtkButton will also be applied to GtkCheckButton unless it is explicitly stated otherwise.

Hence this hierarchy — found at https://developer.gnome.org/gtk-tutorial/2.90/x477.html — is important when applying styles and we will come back to it later on.

Styles

Everything you want to change is being changed in so called "styles". Within these styles you have two kinds of properties. On the one hand there is a limited set of predefined style-properties of the GTK+ theming system, which define things like the width of the scrollbar. On the other hand, there are the theming possibilities the engines define. These engine styles are, where most of the theming options are possible.

The interesting part about the GTK theming system is that different styles are merged to create the final one. So, you will usually define a base style with all common options in it, and then change colors for a specific widget.

Engines

As said above engines provide you with many interesting styling options. These options however are engine specific, and if you learned them for one Engine, you will still have to learn the options, other engines provide. Without any engine, GTK uses a very basic theme, that offers only a very boring look (the Raleigh theme does not use an engine).

There are many different engines out there, some providing a lot of possibilities, some a very fixed look. An important part of theming GTK+ is to find one or multiple engines on which to base the theme.

The gtkrc File

The GTK theme lives inside the gtkrc file. The file format is case- sensitive; comments are marked with a # (hash). Multiline comments are also possible with the C like /* */.

A small example follows, that shows how to create very a basic theme.

style "default-style"
{
        # modify the x/ythickness, used for spacing all over the place
        xthickness = 3
        ythickness = 3

        # one can set so called "style properties"
        GtkRange::slider-width = 15

        # set the background to a light grey
        bg[NORMAL] = "#f6f6f6"
        # and the forground to black
        fg[NORMAL] = "#000000"
}

class "GtkWidget" style "default-style"

All, this snippet does, is create a style called "default-style", which then gets applied to all widgets.

Lets take a closer look at the different options inside the style.

The Style

While the above example style is very small, it gives you an idea of everything that is possible with plain GTK+.

x/ythickness

        xthickness = 3
        ythickness = 3

This option is used in many places to determine padding between text and border of widgets. For details see the geometry documentation of the specific widgets.

Style Properties

        GtkRange::slider-width = 15

This example sets the "slider-width" style property of /GtkRange to 15 pixel.

There are a lot of style properties like this one available within GTK and also some specific to an appalication. They make it possible for you to change the basic behavior of widgets. Please refer to the widget documentation and the autogenerated list of style properties available at /StyleProperties.

Style properties are always of the form

        WidgetName::style-property-name = VALUE

The format of the value depends on the type of the style properties. Usually possible values are enumerations from GTK+ like GTK_SHADOW_IN, integers and color values (same format as below). Note that TRUE and FALSE cannot be used in the theme, you can use 1 and 0 instead. (XXX is this OK?)

Some examples of style properties:

Property

Description

Example

GtkWidget::focus-line-width

Width of the focus line

GtkWidget::focus-line-width = 2

GtkWidget::link-color

Color of unvisited links, that is supposed to be used by applications

GtkWidget::link-color = "#0000ff"

...

An interesting feature is that you can easily set style properties on subclasses only. So instead of setting GtkRange::trough-side-details on a style only applied to GtkScale, you can just set it only on the scale.

style "default" {
        GtkScale::trough-side-details = 1
}

class "GtkWidget" style "default"

This will only affect /GtkScale, and not scrollbars.

Colors

bg[NORMAL] = "#f6f6f6"

bg stands for background. There are four valid categories:

Category

Usage

fg

Foreground color. Used for text on buttons. Also used for the button borders in some engines.

bg

Background color. This is the background color of windows and buttons.

text

Text color for text input widgets and lists (/GtkTreeView).

base

Background color of text widgets and lists.

NORMAL is the state the color should be used for. There are five states inside GTK.

State

Usage

NORMAL

The normal color, nothing special happening.

PRELIGHT

Prelight means mouse over effects. Usually the background will be slightly lighter.

ACTIVE

This state is used for buttons while the mouse is pressed.

INSENSITIVE

Used for widgets that are insensitive and cannot be used at that point.

SELECTED

This state is used for example for selected text.

There are five different ways to describe colors, the most popular is probably the hexadecimal notation used in the example above. The possibilities are

hexadecimal

"#RGB", "#RRGGBB", "#RRRGGGBBB" and "#RRRRGGGGBBBB"

floating point

{R, B, G} where R, B and G are floating point numbers between and including 0.0 and 1.0 (That is 1.0 not simply 1 as that will be interpreted as an integer)

integer values

{R, B, G} where R, B and G are values between and including 0 and 65535

color strings from X

eg. "Red", "Magenta", etc. These are shipped with the X server, and can often be found in /etc/X11/rgb.txt

Symbolic colors and color expressions

For example lighter(@color_name). Please see /SymbolicColors for details.

Applying the Style

At the top of the snippet we defined a style with some colors. Still GTK needs to know where this style should be used. This is done with the line

class "GtkWidget" style "default-style"

This line applies the style "default-style" to every widget that is based on GtkWidget. As every widget is based on GtkWidget, it will be used everywhere.

In GTK there are three ways to apply styles. These are the class, widget_class and widget statements. Understanding how styles are applied is a key to understanding the whole of the GTK theming system. A very detailed explanation follows.

The different matching lines

class

The class matches based on the widget class hierarchy (again see http://developer.gnome.org/doc/API/2.0/gtk/ch01.html).

When finding styles relevant to the current widget, it walks down the widget hierarchy, trying if the widgets class name matches the one in the statement. Lets use the example from above, together with a GtkToggleButton.

It will test the following class names (in this order of importance):

  • GtkWidget <- match found here

  • GtkContainer, no match

  • GtkBin, no match

  • GtkButton, no match

  • GtkToggleButton, no match

Note that you can not only use class names like GtkWidget but also expressions like "Gt?T*". A '*' stands for arbitary number of characters and a '?' for exactly one. Both of these will also match the dots in the path.

widget_class matches

Here the nesting of widgets comes into play. Lets assume a simple "Hello World!" application. In this application you have a GtkWindow that contains a GtkButton which contains a GtkLabel with the text "Hello World".

Lets look at the following example:

widget_class "*.GtkButton.*" style "button-content"

Classpath

the window

GtkWindow

the button inside the window

GtkWidget.GtkButton

the label inside the button

GtkWidget.GtkButton.GtkLabel

"*.GtkButton.*" only matches to "GtkWidget.GtkButton.GtkLabel", so only the label uses the "button-content" style. But there is a catch, if we want "button-content" to also be used for toggle buttons. They usually have look the same way as ordinary buttons. Historically one usually would do things like

widget_class "*.*Button.*" style "button-content"

which will also work for GtkToggleButton. Since GTK+ 2.10 however there is another way.

widget_class "*.<GtkButton>.*" style "button-content"

The angle brackets work similar to the class match we had earlier (however without pattern matching). So if there is a GtkToggleButton GTK will notice that it is based on GtkButton and also apply the style to the label.

If you want to theme specific widgets in applications it can be harder to figure out the different paths. One possible way to do this is to use the eXperience engine and setting EXPERIENCE_PRINT_WIDGET_PATH=TRUE in the command line. (XXX)

widget matches

widget matches work on the names of the widgets. This is very useful if there is a specific widget in an application that needs to be modified. Similar to the widget_class match, the matching is done against a string of names separated with dots. If one of the widget does not have a name the class name will be used instead.

So, lets consider the above example. The button is named "my-button" and the window is named "my-app". The label does not have any name set.

widget path

the window

my-app

the button

my-app.my-button

the label

my-app.my-button.GtkLabel

So if you want to the just the button, you can do something like:

widget "my-app.my-button" style "my-app button fix"

Order of the Merge

GTK merges the different "matches" in the order we covered them above. Styles merged in later on override the settings of the earlier ones. This makes sense, as the class matches are very broad while the widget match is very specific and often picks out single widgets. The order for widget_class and widget matches is simply whatever order is in the file, later ones overriding the previous.

For class matches this is different. GTK gives a higher priority if the matched class is higher up in the hierarchy. Lets take this broken example:

class "*" style "default"
class "GtkButton" style "button"

This will work great for normal buttons, but it does not work for toggle buttons. This is because GtkToggleButton matches against the "*" with a higher priority than it does against the "GtkButton" match. The styles are merged as following:

Test against

styles merged in

GtkWidget

"default"

GtkContainer

"default"

GtkBin

"default"

GtkButton

first "default", then "button" (order in the gtkrc)

GtkToggleButton

"default" again, which overrides the settings from "button"

As you can see the "default" style is merged in a lot of times. So using "*" to assign a default to every widget is not a good idea as GTK+ will be doing excessive work and you may not get the wanted results.

{i} In reality gtk will also match class up to GObject, but this does not make any difference in practice.

Priority

/!\ The following is here for completeness sake. It is only needed in very seldom cases to override applications settings. Examples for this are the nautilus "more" widget (ie. the thing at the top that is shown in the trash and when burning CDs/DVDs) or to override GIMP settings.

It is possible to add a priority to any of the above "matches".

class "SomeWidget" style : PRIORITY "some-style"

Where PRIORITY is one of the following, sorted by increasing priority:

Priority

lowest

gtk

Priority of the built-in theme

application

dunno what this is for, if application use this they will be overridden by the theme.

theme

Default priority of themes

rc

gtkrc files set in the GTK2_RC_FILES environment variable and the standard files ~/.gtkrc-2.0 and /etc/gtk-2.0/gtkrc

highest

Engines

/!\ Hmm, I am not entirely happy with this section. While I think that there is not much it should cover, this feels like it is too little.

As said earlier, engines are used to define the look and extend the styles. For example to use the Clearlooks engine, you can just do the following.

style "some-style" {
        engine "clearlooks" {
                # engine specific settings go here
        }
}

This means the Clearlooks engine will be used with its default settings. You can modify engine settings in the block. For a description of the possible options for different engines please refere to their documentation at ../GtkEngines.

Tips

There are some preview applications like "The Widget Factory" available. Have a look at ../UsefulLinks.

You can split the gtkrc file into multiple files by using the include option. So you could do an extra file just for button styles called button-styles and simply include that.

# include the button-styles file
include "button-styles"

Previous chapter: Icon Themes

Back to Tutorial start page

Next chapter: GDM Themes

Check List

Engine

  • Have a good GTK+ hacker give a once-over look at the theme engine's code. Is it sane? Does it do anything obviously stupid? Does it leak cairo contexts, GCs, pixmaps, regions, etc.?
  • Check the implementation of the GtkStyleClass virtual methods. Do they handle the "detail" argument correctly (null detail, unexpected string in detail)? Do they handle the "widget" argument correctly (may be null as well)? What happens if you draw very small elements (2x2 pixel boxes, 0x0 pixel boxes, etc.)?

    • gtk-engines has a "torture test" application. This program basically brute forces almost all code paths in an engine.

Theme

  • Check that all the core desktop applications look correct with the theme. "Look correct" means, for example, that there are no sharp/ugly transitions in gradient backgrounds; this may be a bug in the app's widget structure. At least checking for this will help in finding bugs somewhere.

  • Check applications that do funny stuff with widgets. Check the tool bars in Evolution and its message composer; those go through libbonoboui, not GTK+, and this has been known to cause problems with gradient-y themes.
  • Check that flipped widgets in RTL locales are correctly drawn.
  • Check that foreign tool kits using gtk engines for rendering work with your engine. The big applications to try this are OpenOffice and Firefox.

  • Does the theme have enough contrast on low grade laptop displays? Industrial has problems with this, for example.
  • Does the theme look correct if one increases and decreases the font size? (In addition to text readability, take note of Menu and SpinButton arrows, which change according to font size.)

  • Does the theme have acceptable performance on low-end machines? This is difficult to test, but have someone try it on a low-memory machine equipped with an Intel Pentium II processor.

Attic/GnomeArt/Tutorials/GtkThemes (last edited 2013-11-27 14:33:56 by WilliamJonMcCann)