]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshadowvalue.c
cssvalue: Add _gtk_css_value_transition()
[~andy/gtk] / gtk / gtkcssshadowvalue.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 Red Hat, Inc.
3  *
4  * Author: Cosimo Cecchi <cosimoc@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "config.h"
21
22 #include "gtkcssshadowvalueprivate.h"
23
24 #include "gtkstylecontextprivate.h"
25 #include "gtkthemingengineprivate.h"
26 #include "gtkpango.h"
27
28 typedef struct _GtkShadowElement GtkShadowElement;
29
30 struct _GtkShadowElement {
31   gint16 hoffset;
32   gint16 voffset;
33   gint16 radius;
34   gint16 spread;
35
36   gboolean inset;
37
38   GdkRGBA color;
39   GtkSymbolicColor *symbolic_color;
40 };
41
42 static void
43 shadow_element_print (GtkShadowElement *element,
44                       GString          *str)
45 {
46   gchar *color_str;
47
48   if (element->inset)
49     g_string_append (str, "inset ");
50
51   g_string_append_printf (str, "%d %d ",
52                           (gint) element->hoffset,
53                           (gint) element->voffset);
54
55   if (element->radius != 0)
56     g_string_append_printf (str, "%d ", (gint) element->radius);
57
58   if (element->spread != 0)
59     g_string_append_printf (str, "%d ", (gint) element->spread);
60
61   if (element->symbolic_color != NULL)
62     color_str = gtk_symbolic_color_to_string (element->symbolic_color);
63   else
64     color_str = gdk_rgba_to_string (&element->color);
65
66   g_string_append (str, color_str);
67   g_free (color_str);
68 }
69
70 static void
71 shadow_element_free (GtkShadowElement *element)
72 {
73   if (element->symbolic_color != NULL)
74     gtk_symbolic_color_unref (element->symbolic_color);
75
76   g_slice_free (GtkShadowElement, element);
77 }
78
79 static GtkShadowElement *
80 shadow_element_new (gdouble hoffset,
81                     gdouble voffset,
82                     gdouble radius,
83                     gdouble spread,
84                     gboolean inset,
85                     GdkRGBA *color,
86                     GtkSymbolicColor *symbolic_color)
87 {
88   GtkShadowElement *retval;
89
90   retval = g_slice_new0 (GtkShadowElement);
91
92   retval->hoffset = hoffset;
93   retval->voffset = voffset;
94   retval->radius = radius;
95   retval->spread = spread;
96   retval->inset = inset;
97
98   if (symbolic_color != NULL)
99     retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color);
100
101   if (color != NULL)
102     retval->color = *color;
103
104   return retval;
105 }                  
106
107 /****************
108  * GtkCssValue *
109  ****************/
110
111 struct _GtkCssValue {
112   GTK_CSS_VALUE_BASE
113   GList *elements;
114 };
115
116 static void
117 gtk_css_value_shadow_free (GtkCssValue *shadow)
118 {
119   g_list_free_full (shadow->elements,
120                     (GDestroyNotify) shadow_element_free);
121   g_slice_free (GtkCssValue, shadow);
122 }
123
124 static gboolean
125 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
126                             const GtkCssValue *shadow2)
127 {
128   /* FIXME */
129   return shadow1 == shadow2;
130 }
131
132 static GtkCssValue *
133 gtk_css_value_shadow_transition (GtkCssValue *start,
134                                  GtkCssValue *end,
135                                  double       progress)
136 {
137   return NULL;
138 }
139
140 static void
141 gtk_css_value_shadow_print (const GtkCssValue *shadow,
142                             GString           *string)
143 {
144   gint length;
145   GList *l;
146
147   length = g_list_length (shadow->elements);
148
149   if (length == 0)
150     {
151       g_string_append (string, "none");
152       return;
153     }
154
155   shadow_element_print (shadow->elements->data, string);
156
157   if (length == 1)
158     return;
159
160   for (l = g_list_next (shadow->elements); l != NULL; l = l->next)
161     {
162       g_string_append (string, ", ");
163       shadow_element_print (l->data, string);
164     }
165 }
166
167 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
168   gtk_css_value_shadow_free,
169   gtk_css_value_shadow_equal,
170   gtk_css_value_shadow_transition,
171   gtk_css_value_shadow_print
172 };
173
174 static GtkCssValue none_singleton = { &GTK_CSS_VALUE_SHADOW, 1, NULL };
175
176 GtkCssValue *
177 _gtk_css_shadow_value_new_none (void)
178 {
179   return _gtk_css_value_ref (&none_singleton);
180 }
181
182 GtkCssValue *
183 _gtk_css_shadow_value_parse (GtkCssParser *parser)
184 {
185   gboolean have_inset, have_color, have_lengths;
186   gdouble hoffset, voffset, blur, spread;
187   GtkSymbolicColor *color;
188   GtkShadowElement *element;
189   GtkCssValue *shadow;
190   guint i;
191
192   if (_gtk_css_parser_try (parser, "none", TRUE))
193     return _gtk_css_shadow_value_new_none ();
194
195   shadow = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
196
197   do
198     {
199       have_inset = have_lengths = have_color = FALSE;
200
201       for (i = 0; i < 3; i++)
202         {
203           if (!have_inset && 
204               _gtk_css_parser_try (parser, "inset", TRUE))
205             {
206               have_inset = TRUE;
207               continue;
208             }
209             
210           if (!have_lengths &&
211               _gtk_css_parser_try_double (parser, &hoffset))
212             {
213               have_lengths = TRUE;
214
215               if (!_gtk_css_parser_try_double (parser, &voffset))
216                 {
217                   _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
218                   _gtk_css_value_unref (shadow);
219                   return NULL;
220                 }
221
222               if (!_gtk_css_parser_try_double (parser, &blur))
223                 blur = 0;
224
225               if (!_gtk_css_parser_try_double (parser, &spread))
226                 spread = 0;
227
228               continue;
229             }
230
231           if (!have_color)
232             {
233               have_color = TRUE;
234
235               /* XXX: the color is optional and UA-defined if it's missing,
236                * but it doesn't really make sense for us...
237                */
238               color = _gtk_css_parser_read_symbolic_color (parser);
239
240               if (color == NULL)
241                 {
242                   _gtk_css_value_unref (shadow);
243                   return NULL;
244                 }
245             }
246         }
247
248       if (!have_color || !have_lengths)
249         {
250           _gtk_css_parser_error (parser, "Must specify at least color and offsets");
251           _gtk_css_value_unref (shadow);
252           return NULL;
253         }
254
255       element = shadow_element_new (hoffset, voffset,
256                                     blur, spread, have_inset,
257                                     NULL, color);
258
259       shadow->elements = g_list_append (shadow->elements, element);
260
261       gtk_symbolic_color_unref (color);
262
263     }
264   while (_gtk_css_parser_try (parser, ",", TRUE));
265
266   return shadow;
267 }
268
269 GtkCssValue *
270 _gtk_css_shadow_value_compute (GtkCssValue     *shadow,
271                                GtkStyleContext *context)
272 {
273   GtkCssValue *resolved_shadow;
274   GtkShadowElement *element, *resolved_element;
275   GdkRGBA color;
276   GList *l;
277
278   resolved_shadow = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
279
280   for (l = shadow->elements; l != NULL; l = l->next)
281     {
282       element = l->data;
283
284       if (!_gtk_style_context_resolve_color (context,
285                                              element->symbolic_color,
286                                              &color))
287         {
288           _gtk_css_value_unref (resolved_shadow);
289           return NULL;
290         }
291
292       resolved_element =
293         shadow_element_new (element->hoffset, element->voffset,
294                             element->radius, element->spread, element->inset,
295                             &color, NULL);
296
297       resolved_shadow->elements =
298         g_list_append (resolved_shadow->elements, resolved_element);
299     }
300
301   return resolved_shadow;
302 }
303
304 void
305 _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
306                                     cairo_t           *cr,
307                                     PangoLayout       *layout)
308 {
309   GList *l;
310   GtkShadowElement *element;
311
312   if (!cairo_has_current_point (cr))
313     cairo_move_to (cr, 0, 0);
314
315   /* render shadows starting from the last one,
316    * and the others on top.
317    */
318   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
319     {
320       element = l->data;
321
322       cairo_save (cr);
323
324       cairo_rel_move_to (cr, element->hoffset, element->voffset);
325       gdk_cairo_set_source_rgba (cr, &element->color);
326       _gtk_pango_fill_layout (cr, layout);
327
328       cairo_rel_move_to (cr, -element->hoffset, -element->voffset);
329       cairo_restore (cr);
330   }
331 }
332
333 void
334 _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
335                                   cairo_t           *cr)
336 {
337   GList *l;
338   GtkShadowElement *element;
339   cairo_pattern_t *pattern;
340
341   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
342     {
343       element = l->data;
344
345       cairo_save (cr);
346       pattern = cairo_pattern_reference (cairo_get_source (cr));
347       gdk_cairo_set_source_rgba (cr, &element->color);
348
349       cairo_translate (cr, element->hoffset, element->voffset);
350       cairo_mask (cr, pattern);
351
352       cairo_restore (cr);
353       cairo_pattern_destroy (pattern);
354     }
355 }
356
357 void
358 _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
359                                      cairo_t           *cr,
360                                      gdouble            radius,
361                                      gdouble            progress)
362 {
363   GtkShadowElement *element;
364   GList *l;
365
366   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
367     {
368       element = l->data;
369
370       cairo_save (cr);
371
372       cairo_translate (cr, element->hoffset, element->voffset);
373       _gtk_theming_engine_paint_spinner (cr,
374                                          radius, progress,
375                                          &element->color);
376
377       cairo_restore (cr);
378     }
379 }
380
381 void
382 _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
383                                  cairo_t             *cr,
384                                  const GtkRoundedBox *padding_box)
385 {
386   GtkShadowElement *element;
387   GtkRoundedBox box;
388   GList *l;
389
390   cairo_save (cr);
391   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
392
393   _gtk_rounded_box_path (padding_box, cr);
394   cairo_clip (cr);
395
396   /* render shadows starting from the last one,
397    * and the others on top.
398    */
399   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
400     {
401       element = l->data;
402
403       if (!element->inset)
404         continue;
405
406       box = *padding_box;
407       _gtk_rounded_box_move (&box, element->hoffset, element->voffset);
408       _gtk_rounded_box_shrink (&box,
409                                element->spread, element->spread,
410                                element->spread, element->spread);
411
412       _gtk_rounded_box_path (&box, cr);
413       _gtk_rounded_box_clip_path (padding_box, cr);
414
415       gdk_cairo_set_source_rgba (cr, &element->color);
416       cairo_fill (cr);
417   }
418
419   cairo_restore (cr);
420 }