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
Contents
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.
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"
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.