How To

Introduction

If you find spelling errors, keep them ;)! (Or as an alternative, you can correct them.)

If you have any suggestions or questions, please mail me!

Note

First an overview of how the engine works

When the engine is supposed to draw a widget, it first searches for the first matching group. When a group is found that matches to the request made by gtk then it will be drawn.

Matching

If your are familiar with the pixmap engine, you will see that this is very similar (it is basically the same). You should already know what drawing functions gtk has, and the options they have (shadow, state, detail string, etc.).

For the matching process each group:'s match statements are checked. If all are the same, the group will be drawn. If not the next group will be checked. If there happens to be no group that matches, then the default engine will be used. Each match statement is a comma separated list (logical OR).

[Table not converted]

The available functions

First of all most of the functions from gtk are available (except text related). The default functions that are available:

The LINE function is not, as in gnome, split into horizontal and vertical. Instead there is only the LINE function, and the orientation will be set accordingly.

Notice that the above list does not contain gtk_paint_box_gap and gtk_paint_shadow_gap. These two functions are drawn as a normal shadow (with the gap_side set of course). In addition to this the functions BOX_GAP_START, BOX_GAP, BOX_GAP_END and SHADOW_GAP_START, SHADOW_GAP, SHADOW_GAP_END exist. Now what happens is that the engine first renders a BOX or SHADOW. After the *_GAP functions will be rendered.
  • *_GAP_START on the left, top
  • *_GAP this is the gap
  • *_GAP_END on the right, bottom

Because with shadows it makes sense to clear the area underneath (make the gap transparent), it is possible to clear the area underneath any of theses areas with clear_area = TRUE. Please note the gap_size. This sets the height/width of the gap area. If you don't set this variable yourself the x-/ythickness of the widget will be used.

Properties

Now the properties are a cool new feature (I like it ;)). They are quite powerful, but maybe not useful for everything. Lets just have an example:

group "button_panel_focus_out" = "button_panel_prelight_out"
{
        state                                   = NORMAL
        property "has-focus"                    = TRUE
        image 20 {      recolor "#ff0000"       = bg[SELECTED]}
}

The above example checks the has-focus property of an GtkWidget. With this it is possible to draw the focus inside the BOX function instead of the FOCUS function[1]_.

Another interesting application of this are progress bars. For example in the eXperience GTK-Theme progress bars have to be aligned to one side (So that the blocks don't jump around). The direction of this can be changed by setting an property. You can easily check it with property.
Look at this:
group "progress_bar_rtl" = "progress_bar_ltr"
{
        property "orientation"                  = "GTK_PROGRESS_RIGHT_TO_LEFT" #I don't like this really ... would be nice if those quotes were not needed.
        mirror                                  = HORIZONTAL
}

If the orientation is GTK_PROGRESS_RIGHT_TO_LEFT, then the engine will draw the normal left to right progress bar, but mirrored horizontaly.

Note

Note: GType converts the enum to a string. The "GTK_PROGRESS_RIGHT_TO_LEFT" is a constant in gtktypebuiltins.c. It should be thereticaly possible to use the nick and not the name for the string conversion. But using the name is the default behavior of the GType system, and if you want to look up the name you need to load the definition of the enum. (I am not sure how that works, and I think it is overkill, but it would be nicer.)

This should work for most properties that widgets have. So have a look at the properties list in the gtk documentation of the widget you want to theme. Here is the Object Hierarchy.

I bet there are more properties that might be useful for themeing, so here is a page to list useful properties: Properties. If you find any additional properties that might be useful, just add them!

Groups

Example (Not tested, and this probably does not make any sense at all ;)):

group "button_default" = "button_normal" {
        #function       = BOX not needed, because it is inherited
        #note that this group can inherit from a group which is defined later.

        property "has-default" = TRUE

        #lets make the bg a little lighter
        fill 10 { #This changes the fill that was inherited from button_normal
                brightness              = 0.2
        }
}
group "button_normal" {
        function        = BOX

        fill 10 { #fill the background
                color                   = bg[NORMAL]
        }

        image 20 {
                file                    = "button_border.png" #red image
                recolor "#ff0000"       = bg[NORMAL]
                brightness              = -0.5
                border                  = { 2, 2, 2, 2 }
                draw_components         = BORDER
        }
}
This small example, shows the possibility of inheriting properties from another group. button_default is derived from button_normal since it is essentially the same. Only the background is a bit lighter in the example.
As you can see the order of groups does not matter for inheritance. This is essential, because it does matter for the matching. OK. Now the format of a group in general:
group ["name" [ = "inherits-from"]] {
        matching conditions
        group filter
        drawing stuff
}

The name of groups are optional (if not given a dummy name is assigned to them of the form RESERVED_XXXXXXX). I suggest you always enter a name for groups, so that you can track down errors more easily.

The order of the matching conditions, drawing stuff and filter does not matter. They may be mixed.

Some special group options:

[Table not converted]

Drawing

The engine currently can only draw images, and fill areas.

General options

Some options for drawing exist for both images and fill:

[Table not converted]

Fill

The fill is pretty simple.
Options for fill:s:

[Table not converted]

Image

Lets go ahead first, here the list:

[Table not converted]

Icons

There is an extra section for icons. This is pretty simple. Here an example (from the eXperience GTK-Theme):

style "XYZ" {
        engine "experience"
        {
                icons {
                        normal {        opacity                 = 0.8 }
                        prelight {      saturation              = 1.2
                                        brightness              = 0.05}
                        active {        brightness              = -0.06 }
                        insensitive {   opacity                 = 0.2
                                        saturation              = 0.3 }
                }
        }
}

Possible options are:

  • opacity
  • saturation
  • brightness
  • pixelate

Note: There is a bug in gdk which results in random pixels if the opacity is changed. For example the statusbar in evolution has this problem for insensitive icons. See Bugs.

(I thought mirroring and rotating icons is stupid, so it is not supported :). If you need it, and have a good reason, then I will include it.)

Misc

dynamic colors

It is possible to read colors from the GtkStyle. You can use the same statements, which you can also set in the gtkrc (eg. bg[NORMAL], fg[PRELIGHT], etc.).

[1] The eXperience theme uses this. If you do this you also need to remove the focus by just drawing nothing. Or you get that dotted default focus.