]> Pileus Git - ~andy/gtk/blob - docs/reference/gtk/migrating-GtkStyleContext.xml
Merge branch 'master' into broadway
[~andy/gtk] / docs / reference / gtk / migrating-GtkStyleContext.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
3                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
4 ]>
5 <chapter id="gtk-migrating-GtkStyleContext">
6   <title>Migrating from GtkStyle to GtkStyleContext</title>
7
8   <para>
9     In GTK+ 3.0, #GtkStyleContext was added to replace #GtkStyle and
10     the theming infrastructure available in 2.x. GtkStyleContext is an
11     object similar in spirit to GtkStyle, as it contains theming information,
12     although in a more complete and tokenized fashion. There are two aspects
13     to switching to GtkStyleContext: porting themes and theme engines, and
14     porting applications, libraries and widgets.
15   </para>
16
17   <refsect2 id="gtk-migrating-GtkStyleContext-themes">
18     <title>Migrating themes</title>
19
20     <para>
21       From GTK+ 3.0 on, theme engines must implement #GtkThemingEngine and be
22       installed in <filename>$libdir/gtk+-3.0/$GTK_VERSION/theming-engines</filename>,
23       and the files containing style information must be written in the CSS-like
24       format that is understood by #GtkCssProvider. For a theme named
25       "Clearlooks", the CSS file parsed by default is
26       <filename>$datadir/themes/Clearlooks/gtk-3.0/gtk.css</filename>,
27       with possible variants such as the dark theme being named
28       <filename>gtk-dark.css</filename> in the same directory.
29     </para>
30   </refsect2>
31
32   <refsect2 id="gtk-migrating-theme-GtkStyleContext-engines">
33     <title>Migrating theme engines</title>
34
35     <para>
36       Migrating a #GtkStyle based engine to a #GtkThemingEngine based one
37       should be straightforward for most of the vfuncs. Besides a cleanup
38       in the available paint methods and a simplification in the passed
39       arguments (in favor of #GtkStyleContext containing all the information),
40       the available render methods resemble those of #GtkStyle quite
41       evidently. Notable differences include:
42     </para>
43
44     <orderedlist>
45       <listitem>
46         All variations of gtk_paint_box(), gtk_paint_flat_box(),
47         gtk_paint_shadow(), gtk_paint_box_gap() and gtk_paint_shadow_gap()
48         are replaced by gtk_render_background(), gtk_render_frame() and
49         gtk_render_frame_gap(). The first function renders frameless
50         backgrounds and the last two render frames in various forms.
51       </listitem>
52       <listitem>
53         gtk_paint_resize_grip() has been subsumed by gtk_render_handle()
54         with a #GTK_STYLE_CLASS_GRIP class set in the style context.
55       </listitem>
56       <listitem>
57         gtk_paint_spinner() disappears in favor of gtk_render_activity()
58         with a #GTK_STYLE_CLASS_SPINNER class set in the style context.
59       </listitem>
60     </orderedlist>
61
62     <para>
63       The list of available render methods is:
64     </para>
65
66     <simplelist>
67       <member>
68         gtk_render_background(): Renders a widget/area background.
69       </member>
70       <member>
71         gtk_render_frame(): Renders a frame border around the given rectangle.
72         Usually the detail of the border depends on the theme information,
73         plus the current widget state.
74       </member>
75       <member>
76         gtk_render_frame_gap(): Renders a frame border with a gap on one side.
77       </member>
78       <member>
79         gtk_render_layout(): Renders a #PangoLayout.
80       </member>
81       <member>
82         gtk_render_handle(): Renders all kind of handles and resize grips,
83         depending on the style class.
84       </member>
85       <member>
86         gtk_render_check(): Render checkboxes.
87       </member>
88       <member>
89         gtk_render_option(): Render radiobuttons.
90       </member>
91       <member>
92         gtk_render_arrow(): Renders an arrow pointing to a direction.
93       </member>
94       <member>
95         gtk_render_expander(): Renders an expander indicator, such as in
96         #GtkExpander.
97       </member>
98       <member>
99         gtk_render_focus(): Renders the indication that a widget has the
100         keyboard focus.
101       </member>
102       <member>
103         gtk_render_line(): Renders a line from one coordinate to another.
104       </member>
105       <member>
106         gtk_render_slider(): Renders a slider, such as in #GtkScale.
107       </member>
108       <member>
109         gtk_render_extension(): Renders an extension that protrudes from
110         a UI element, such as a notebook tab.
111       </member>
112       <member>
113         gtk_render_activity(): Renders an area displaying activity, be it
114         a progressbar or a spinner.
115       </member>
116       <member>
117         gtk_render_icon_pixbuf(): Renders an icon into a #GdkPixbuf.
118       </member>
119     </simplelist>
120
121     <para>
122       One of the main differences to #GtkStyle-based engines is that the
123       rendered widget is totally isolated from the theme engine, all style
124       information is meant to be retrieved from the #GtkThemingEngine API,
125       or from the #GtkWidgetPath obtained from gtk_theming_engine_get_path(),
126       which fully represents the rendered widget's hierarchy from a styling
127       point of view.
128     </para>
129
130     <para>
131       The detail string available in #GtkStyle-based engines has been
132       replaced by widget regions and style classes. Regions are a way for
133       complex widgets to associate different styles with different areas,
134       such as even and odd rows in a treeview. Style classes allow sharing
135       of style information between widgets, regardless of their type.
136       Regions and style classes can be used in style sheets to associate
137       styles, and them engines can also access them. There are several
138       predefined classes and regions such as %GTK_STYLE_CLASS_BUTTON or
139       %GTK_STYLE_REGION_TAB in <filename>gtkstylecontext.h</filename>,
140       although custom widgets may define their own, which themes may
141       attempt to handle.
142     </para>
143
144   </refsect2>
145
146   <refsect2 id="gtk-migrating-GtkStyleContext-parser-extensions">
147     <title>Extending the CSS parser</title>
148
149     <para>
150       In #GtkStyle-based engines, #GtkRCStyle provided ways to extend the
151       gtkrc parser with engine-specific extensions. This has been replaced
152       by gtk_theming_engine_register_property(), which lets a theme engine
153       register new properties with an arbitrary type. While there is built-in
154       support for most basic types, it is possible to use a custom parser
155       for the property.
156     </para>
157
158     <para>
159       The installed properties depend on the #GtkThemeEngine::name property,
160       so they should be added in the <literal>constructed()</literal> vfunc.
161       For example, if an engine with the name "Clearlooks" installs a
162       "focus-color" property with the type #GdkRGBA, the property
163       <literal>-Clearlooks-focus-color</literal> will be registered and
164       accepted in CSS like this:
165       <informalexample><programlisting>
166       GtkEntry {
167         -Clearlooks-focus-color: rgba(255, 0, 0, 1.0);
168       }
169       </programlisting></informalexample>
170     </para>
171
172     <para>
173       Widget style properties also follow a similar syntax, with the widget
174       type name used as a prefix. For example, the #GtkWidget:focus-line-width
175       style property can be modified in CSS as
176       <literal>-GtkWidget-focus-line-width</literal>.
177     </para>
178   </refsect2>
179
180   <refsect2 id="gtk-migrating-GtkStyleContext-css">
181     <title>Using the CSS file format</title>
182
183     <para>
184       The syntax of RC and CSS files formats is obviously different.
185       The CSS-like syntax will hopefully be much more familiar to many
186       people, lowering the barrier for custom theming.
187     </para>
188     <para>
189       Instead of going through the syntax differences one-by-one, we
190       will present a more or less comprehensive example and discuss
191       how it can be translated into CSS:
192     </para>
193
194     <example>
195       <title>Sample RC code</title>
196       <programlisting>
197         style "default" {
198                 xthickness = 1
199                 ythickness = 1
200
201                 GtkButton::child-displacement-x = 1
202                 GtkButton::child-displacement-y = 1
203                 GtkCheckButton::indicator-size = 14
204
205                 bg[NORMAL]        = @bg_color
206                 bg[PRELIGHT]      = shade (1.02, @bg_color)
207                 bg[SELECTED]      = @selected_bg_color
208                 bg[INSENSITIVE]   = @bg_color
209                 bg[ACTIVE]        = shade (0.9, @bg_color)
210
211                 fg[NORMAL]        = @fg_color
212                 fg[PRELIGHT]      = @fg_color
213                 fg[SELECTED]      = @selected_fg_color
214                 fg[INSENSITIVE]   = darker (@bg_color)
215                 fg[ACTIVE]        = @fg_color
216
217                 text[NORMAL]      = @text_color
218                 text[PRELIGHT]    = @text_color
219                 text[SELECTED]    = @selected_fg_color
220                 text[INSENSITIVE] = darker (@bg_color)
221                 text[ACTIVE]      = @selected_fg_color
222
223                 base[NORMAL]      = @base_color
224                 base[PRELIGHT]    = shade (0.95, @bg_color)
225                 base[SELECTED]    = @selected_bg_color
226                 base[INSENSITIVE] = @bg_color
227                 base[ACTIVE]      = shade (0.9, @selected_bg_color)
228
229                 engine "clearlooks" {
230                         colorize_scrollbar = TRUE
231                         style = CLASSIC
232                 }
233         }
234
235         style "tooltips" {
236                 xthickness = 4
237                 ythickness = 4
238
239                 bg[NORMAL]        = @tooltip_bg_color
240                 fg[NORMAL]        = @tooltip_fg_color
241         }
242
243         style "button" {
244                 xthickness = 3
245                 ythickness = 3
246
247                 bg[NORMAL]        = shade (1.04, @bg_color)
248                 bg[PRELIGHT]      = shade (1.06, @bg_color)
249                 bg[ACTIVE]        = shade (0.85, @bg_color)
250         }
251
252         style "entry" {
253                 xthickness = 3
254                 ythickness = 3
255
256                 bg[SELECTED] = mix (0.4, @selected_bg_color, @base_color)
257                 fg[SELECTED] = @text_color
258
259                 engine "clearlooks" {
260                         focus_color = shade (0.65, @selected_bg_color)
261                 }
262         }
263
264         style "other" {
265                 bg[NORMAL] = &num;fff;
266         }
267
268         class "GtkWidget" style "default"
269         class "GtkEntry" style "entry"
270         widget_class "*&lt;GtkButton&gt;" style "button"
271         widget "gtk-tooltip*" style "tooltips"
272         widget_class "window-name.*.GtkButton" style "other"
273       </programlisting>
274     </example>
275
276     <para>
277       would roughly translate to this CSS:
278     </para>
279
280     <example>
281       <title>CSS translation</title>
282       <programlisting>
283         * {
284           padding: 1;
285           -GtkButton-child-displacement-x: 1;
286           -GtkButton-child-displacement-y: 1;
287           -GtkCheckButton-indicator-size: 14;
288
289           background-color: @bg_color;
290           color: @fg_color;
291
292           -Clearlooks-colorize-scrollbar: true;
293           -Clearlooks-style: classic;
294         }
295
296         *:hover {
297           background-color: shade (@bg_color, 1.02);
298         }
299
300         *:selected {
301           background-color: @selected_bg_color;
302           color: @selected_fg_color;
303         }
304
305         *:insensitive {
306           color: shade (@bg_color, 0.7);
307         }
308
309         *:active {
310           background-color: shade (@bg_color, 0.9);
311         }
312
313         .tooltip {
314           padding: 4;
315
316           background-color: @tooltip_bg_color;
317           color: @tooltip_fg_color;
318         }
319
320         .button {
321           padding: 3;
322           background-color: shade (@bg_color, 1.04);
323         }
324
325         .button:hover {
326           background-color: shade (@bg_color, 1.06);
327         }
328
329         .button:active {
330           background-color: shade (@bg_color, 0.85);
331         }
332
333         .entry {
334           padding: 3;
335
336           background-color: @base_color;
337           color: @text_color;
338         }
339
340         .entry:selected {
341           background-color: mix (@selected_bg_color, @base_color, 0.4);
342           -Clearlooks-focus-color: shade (0.65, @selected_bg_color)
343         }
344
345         /* The latter selector is an specification of the first,
346            since any widget may use the same classes or names */
347         &num;window-name .button,
348         GtkWindow&num;window-name GtkButton.button {
349           background-color: &num;fff;
350         }
351       </programlisting>
352     </example>
353
354     <para>
355       One notable difference is the reduction from fg/bg/text/base colors
356       to only foreground/background, in exchange the widget is able to render
357       its various elements with different CSS classes, which can be themed
358       independently.
359     </para>
360
361     <para>
362       Access to colors has also changed a bit. With #GtkStyle, the common
363       way to access colors is:
364       <informalexample><programlisting>
365       GdkColor *color1;
366       GdkColor color2;
367
368       color1 = &amp;style->bg[GTK_STATE_PRELIGHT];
369       gtk_style_lookup_color (style, "focus_color", &amp;color2);
370       </programlisting></informalexample>
371       With #GtkStyleContext, you generally use #GdkRGBA instead of #GdkColor
372       and the code looks like this:
373       <informalexample><programlisting>
374       GdkRGBA *color1;
375       GdkRGBA  color2;
376
377       gtk_style_context_get (context, GTK_STATE_FLAG_PRELIGHT,
378                              "background-color", &amp;color1,
379                              NULL);
380       gtk_style_context_lookup_color (context, "focus_color", &amp;color2);
381
382       ...
383
384       gdk_rgba_free (color1);
385       </programlisting></informalexample>
386       Note that the memory handling here is different: gtk_style_context_get()
387       expects the address of a GdkRGBA* and returns a newly allocated struct,
388       gtk_style_context_lookup_color() expects the address of an existing
389       struct, and fills it.
390     </para>
391
392     <para>
393       It is worth mentioning that the new file format does not support
394       custom keybindings nor stock icon mappings as the RC format did.
395     </para>
396   </refsect2>
397
398   <refsect2 id="gtk-migrating-GtkStyleContext-checklist">
399     <title>A checklist for widgets</title>
400
401     <para>
402       When porting your widgets to use #GtkStyleContext, this checklist
403       might be useful.
404     </para>
405
406     <orderedlist>
407       <listitem>
408         Replace <literal>style_set()</literal> calls with
409         <literal>style_updated()</literal>.
410       </listitem>
411
412       <listitem>
413         <para>
414           Try to identify the role of what you're rendering with any number
415           of classes. This will replace the detail string. There is a predefined
416           set of CSS classes which you can reuse where appropriate. Doing so
417           will give you theming 'for free', whereas custom classes will require
418           extra work in the theme. Note that complex widgets are likely to
419           need different styles when rendering different parts, and style
420           classes are one way to achieve this. This could result in code like
421           the following (simplified) examples:
422         </para>
423
424         <example>
425           <title>Setting a permanent CSS class</title>
426           <programlisting>
427             static void
428             gtk_button_init (GtkButton *button)
429             {
430               GtkStyleContext *context;
431
432               ...
433
434               context = gtk_widget_get_style_context (GTK_WIDGET (button));
435
436               /* Set the "button" class */
437               gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
438             }
439           </programlisting>
440         </example>
441
442         <para>
443           Or
444         </para>
445
446         <example>
447           <title>Using dynamic CSS classes for different elements</title>
448           <programlisting>
449             static gboolean
450             gtk_spin_button_draw (GtkSpinButton *spin,
451                                   cairo_t       *cr)
452             {
453               GtkStyleContext *context;
454
455               ...
456
457               context = gtk_widget_get_style_context (GTK_WIDGET (spin));
458
459               gtk_style_context_save (context);
460               gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
461
462               /* Call to entry draw impl with "entry" class */
463               parent_class->draw (spin, cr);
464
465               gtk_style_context_restore (context);
466               gtk_style_context_save (context);
467
468               /* Render up/down buttons with the "button" class */
469               gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
470               draw_up_button (spin, cr);
471               draw_down_button (spin, cr);
472
473               gtk_style_context_restore (context);
474
475               ...
476             }
477           </programlisting>
478         </example>
479
480         <para>
481           Note that #GtkStyleContext only provides fg/bg colors, so text/base
482           is done through distinctive theming of the different classes. For
483           example, an entry would usually be black on white while a button
484           would usually be black on light grey.
485         </para>
486       </listitem>
487
488       <listitem>
489         Replace all <literal>gtk_paint_*()</literal> calls with corresponding
490         <literal>gtk_render_*()</literal> calls. The most distinctive changes
491         are the use of #GtkStateFlags to represent the widget state and the
492         lack of #GtkShadowType. For gtk_render_check() and gtk_render_option(),
493         the @shadow_type parameter is replaced by the #GTK_STATE_FLAG_ACTIVE
494         and #GTK_STATE_FLAG_INCONSISTENT state flags. For things such as
495         pressed/unpressed button states, #GTK_STATE_FLAG_ACTIVE is used, and
496         the CSS may style normal/active states differently to render
497         outset/inset borders, respectively.
498       </listitem>
499
500       <listitem>
501         The various <literal>gtk_widget_modify_*()</literal> functions to
502         override colors or fonts for individual widgets have been replaced
503         by similar <literal>gtk_widget_override_*()</literal> functions.
504       </listitem>
505
506       <listitem>
507         It is no longer necessary to call gtk_widget_style_attach(),
508         gtk_style_attach(), gtk_style_detach() or gtk_widget_ensure_style().
509       </listitem>
510
511       <listitem>
512         Replace all uses of xthickness/ythickness. #GtkStyleContext uses the
513         CSS box model, and there are border-width/padding/margin properties to
514         replace the different applications of X and Y thickness. Note that all
515         of this is merely a guideline. Widgets may choose to follow it or not.
516       </listitem>
517     </orderedlist>
518   </refsect2>
519
520   <refsect2 id="gtk-migrating-GtkStyleContext-parsing">
521     <title>Parsing of custom resources</title>
522     <para>
523       As a consequence of the RC format going away, calling gtk_rc_parse() or
524       gtk_rc_parse_string() won't have any effect on a widgets appearance.
525       The way to replace these calls is using a custom #GtkStyleProvider,
526       either for an individual widget through gtk_style_context_add_provider()
527       or for all widgets on a screen through gtk_style_context_add_provider_for_screen().
528       Typically, the provider will be a #GtkCssProvider, which parse CSS
529       information from a file or from a string.
530     </para>
531
532     <para>
533       Notice that you can also get style information from custom resources
534       by implementing the #GtkStyleProvider interface yourself. This is
535       an advanced feature that should be rarely used.
536     </para>
537   </refsect2>
538
539   <refsect2 id="gtk-migrating-GtkStyleContext-bonus-points">
540     <title>Bonus points</title>
541
542     <para>
543       There are some features in #GtkStyleContext that were not available in
544       #GtkStyle, or were made available over time for certain widgets through
545       extending the detail string in obscure ways. There is a lot more
546       information available when rendering UI elements, and it is accessible
547       in more uniform, less hacky ways. By going through this list you'll
548       ensure your widget is a good citizen in a fully themable user interface.
549     </para>
550
551     <orderedlist>
552       <listitem>
553         If your widget renders a series of similar elements, such as tabs
554         in a #GtkNotebook or rows/column in a #GtkTreeView, consider adding
555         regions through gtk_style_context_add_region(). These regions can be
556         referenced in CSS and the :nth-child pseudo-class may be used to match
557         the elements depending on the flags passed.
558
559         <example>
560           <title>Theming widget regions</title>
561           <programlisting>
562             GtkNotebook tab {
563               background-color: &num;f3329d;
564             }
565
566             GtkTreeView row::nth-child (even) {
567               background-color: &num;dddddd;
568             }
569           </programlisting>
570         </example>
571       </listitem>
572
573       <listitem>
574         <para>
575           If your container renders child widgets within different regions,
576           make it implement GtkContainer::get_path_for_child(). This function
577           lets containers assign a special #GtkWidgetPath to child widgets
578           depending on their role/region. This is necessary to extend the
579           concept above throughout the widget hierarchy.
580         </para>
581
582         <para>
583           For example, a #GtkNotebook modifies the tab labels' #GtkWidgetPath
584           so the "tab" region is added. This makes it possible to theme tab
585           labels through:
586         </para>
587
588         <example>
589           <title>Theming a widget within a parent container region</title>
590           <programlisting>
591             GtkNotebook tab GtkLabel {
592               font: Sans 8;
593             }
594           </programlisting>
595         </example>
596
597       </listitem>
598
599       <listitem>
600         If you intend several visual elements to look interconnected,
601         make sure you specify rendered elements' connection areas with
602         gtk_style_context_set_junction_sides(). It is of course up to the
603         theme to make use of this information or not.
604       </listitem>
605
606       <listitem>
607         <para>
608           #GtkStyleContext supports implicit animations on state changes for
609           the most simple case out-of-the-box: widgets with a single animatable
610           area, whose state is changed with gtk_widget_set_state_flags() or
611           gtk_widget_unset_state_flags(). These functions trigger animated
612           transitions for the affected state flags. Examples of widgets for
613           which this kind of animation may be sufficient are #GtkButton or
614           #GtkEntry.
615         </para>
616         <para>
617           If your widget consists of more than a simple area, and these areas
618           may be rendered with different states, make sure to mark the rendered
619           areas with gtk_style_context_push_animatable_region() and
620           gtk_style_context_pop_animatable_region().
621         </para>
622
623         <para>
624           gtk_style_context_notify_state_change() may be used to trigger a
625           transition for a given state. The region ID will determine the
626           animatable region that is affected by this transition.
627         </para>
628       </listitem>
629     </orderedlist>
630   </refsect2>
631 </chapter>