]> Pileus Git - ~andy/gtk/blob - gtk/gtkcssshadowvalue.c
shadow: Add equal and transition support
[~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 "gtkcssnumbervalueprivate.h"
25 #include "gtkcssrgbavalueprivate.h"
26 #include "gtkstylecontextprivate.h"
27 #include "gtksymboliccolorprivate.h"
28 #include "gtkthemingengineprivate.h"
29 #include "gtkpango.h"
30
31 struct _GtkCssValue {
32   GTK_CSS_VALUE_BASE
33   guint inset :1;
34
35   GtkCssValue *hoffset;
36   GtkCssValue *voffset;
37   GtkCssValue *radius;
38   GtkCssValue *spread;
39
40   GtkCssValue *color;
41 };
42
43 static GtkCssValue *    gtk_css_shadow_value_new (GtkCssValue *hoffset,
44                                                   GtkCssValue *voffset,
45                                                   GtkCssValue *radius,
46                                                   GtkCssValue *spread,
47                                                   gboolean     inset,
48                                                   GtkCssValue *color);
49
50 static void
51 gtk_css_value_shadow_free (GtkCssValue *shadow)
52 {
53   _gtk_css_value_unref (shadow->hoffset);
54   _gtk_css_value_unref (shadow->voffset);
55   _gtk_css_value_unref (shadow->radius);
56   _gtk_css_value_unref (shadow->spread);
57   _gtk_css_value_unref (shadow->color);
58
59   g_slice_free (GtkCssValue, shadow);
60 }
61
62 static gboolean
63 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
64                             const GtkCssValue *shadow2)
65 {
66   return shadow1->inset == shadow2->inset
67       && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset)
68       && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset)
69       && _gtk_css_value_equal (shadow1->radius, shadow2->radius)
70       && _gtk_css_value_equal (shadow1->spread, shadow2->spread)
71       && _gtk_css_value_equal (shadow1->color, shadow2->color);
72 }
73
74 static GtkCssValue *
75 gtk_css_value_shadow_transition (GtkCssValue *start,
76                                  GtkCssValue *end,
77                                  double       progress)
78 {
79   if (start->inset != end->inset)
80     return NULL;
81
82   return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, progress),
83                                    _gtk_css_value_transition (start->voffset, end->voffset, progress),
84                                    _gtk_css_value_transition (start->radius, end->radius, progress),
85                                    _gtk_css_value_transition (start->spread, end->spread, progress),
86                                    start->inset,
87                                    _gtk_css_value_transition (start->color, end->color, progress));
88 }
89
90 static void
91 gtk_css_value_shadow_print (const GtkCssValue *shadow,
92                             GString           *string)
93 {
94   _gtk_css_value_print (shadow->hoffset, string);
95   g_string_append_c (string, ' ');
96   _gtk_css_value_print (shadow->voffset, string);
97   g_string_append_c (string, ' ');
98   if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
99       _gtk_css_number_value_get (shadow->spread, 100) != 0)
100     {
101       _gtk_css_value_print (shadow->radius, string);
102       g_string_append_c (string, ' ');
103     }
104
105   if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
106     {
107       _gtk_css_value_print (shadow->spread, string);
108       g_string_append_c (string, ' ');
109     }
110
111   _gtk_css_value_print (shadow->color, string);
112
113   if (shadow->inset)
114     g_string_append (string, " inset");
115
116 }
117
118 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
119   gtk_css_value_shadow_free,
120   gtk_css_value_shadow_equal,
121   gtk_css_value_shadow_transition,
122   gtk_css_value_shadow_print
123 };
124
125 static GtkCssValue *
126 gtk_css_shadow_value_new (GtkCssValue *hoffset,
127                           GtkCssValue *voffset,
128                           GtkCssValue *radius,
129                           GtkCssValue *spread,
130                           gboolean     inset,
131                           GtkCssValue *color)
132 {
133   GtkCssValue *retval;
134
135   retval = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
136
137   retval->hoffset = hoffset;
138   retval->voffset = voffset;
139   retval->radius = radius;
140   retval->spread = spread;
141   retval->inset = inset;
142   retval->color = color;
143
144   return retval;
145 }                  
146
147 GtkCssValue *
148 _gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
149 {
150   GdkRGBA transparent = { 0, 0, 0, 0 };
151
152   g_return_val_if_fail (target->class == &GTK_CSS_VALUE_SHADOW, NULL);
153
154   return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX),
155                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
156                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
157                                    _gtk_css_number_value_new (0, GTK_CSS_PX),
158                                    target->inset,
159                                    _gtk_css_rgba_value_new_from_rgba (&transparent));
160 }
161
162 static gboolean
163 value_is_done_parsing (GtkCssParser *parser)
164 {
165   return _gtk_css_parser_is_eof (parser) ||
166          _gtk_css_parser_begins_with (parser, ',') ||
167          _gtk_css_parser_begins_with (parser, ';') ||
168          _gtk_css_parser_begins_with (parser, '}');
169 }
170
171 GtkCssValue *
172 _gtk_css_shadow_value_parse (GtkCssParser *parser)
173 {
174   enum {
175     HOFFSET,
176     VOFFSET,
177     RADIUS,
178     SPREAD,
179     COLOR,
180     N_VALUES
181   };
182   GtkCssValue *values[N_VALUES] = { NULL, };
183   gboolean inset;
184   guint i;
185
186   inset = _gtk_css_parser_try (parser, "inset", TRUE);
187
188   do
189   {
190     if (values[HOFFSET] == NULL &&
191          _gtk_css_parser_has_number (parser))
192       {
193         values[HOFFSET] = _gtk_css_number_value_parse (parser,
194                                                        GTK_CSS_PARSE_LENGTH
195                                                        | GTK_CSS_NUMBER_AS_PIXELS);
196         if (values[HOFFSET] == NULL)
197           goto fail;
198
199         values[VOFFSET] = _gtk_css_number_value_parse (parser,
200                                                        GTK_CSS_PARSE_LENGTH
201                                                        | GTK_CSS_NUMBER_AS_PIXELS);
202         if (values[VOFFSET] == NULL)
203           goto fail;
204
205         if (_gtk_css_parser_has_number (parser))
206           {
207             values[RADIUS] = _gtk_css_number_value_parse (parser,
208                                                           GTK_CSS_PARSE_LENGTH
209                                                           | GTK_CSS_POSITIVE_ONLY
210                                                           | GTK_CSS_NUMBER_AS_PIXELS);
211             if (values[RADIUS] == NULL)
212               goto fail;
213           }
214         else
215           values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
216                                                         
217         if (_gtk_css_parser_has_number (parser))
218           {
219             values[SPREAD] = _gtk_css_number_value_parse (parser,
220                                                           GTK_CSS_PARSE_LENGTH
221                                                           | GTK_CSS_NUMBER_AS_PIXELS);
222             if (values[SPREAD] == NULL)
223               goto fail;
224           }
225         else
226           values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
227       }
228     else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE))
229       {
230         if (values[HOFFSET] == NULL)
231           goto fail;
232         inset = TRUE;
233         break;
234       }
235     else if (values[COLOR] == NULL)
236       {
237         GtkSymbolicColor *symbolic;
238
239         if (_gtk_css_parser_try (parser, "currentcolor", TRUE))
240           symbolic = gtk_symbolic_color_ref (_gtk_symbolic_color_get_current_color ());
241         else
242           symbolic = _gtk_css_parser_read_symbolic_color (parser);
243         if (symbolic == NULL)
244           goto fail;
245
246         values[COLOR] = _gtk_css_value_new_take_symbolic_color (symbolic);
247       }
248     else
249       {
250         /* We parsed everything and there's still stuff left?
251          * Pretend we didn't notice and let the normal code produce
252          * a 'junk at end of value' error */
253         goto fail;
254       }
255   }
256   while (values[HOFFSET] == NULL || !value_is_done_parsing (parser));
257
258   if (values[COLOR] == NULL)
259     values[COLOR] = _gtk_css_value_new_take_symbolic_color (
260                       gtk_symbolic_color_ref (
261                         _gtk_symbolic_color_get_current_color ()));
262
263   return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET],
264                                    values[RADIUS], values[SPREAD],
265                                    inset, values[COLOR]);
266
267 fail:
268   for (i = 0; i < N_VALUES; i++)
269     {
270       if (values[i])
271         _gtk_css_value_unref (values[i]);
272     }
273
274   return NULL;
275 }
276
277 GtkCssValue *
278 _gtk_css_shadow_value_compute (GtkCssValue     *shadow,
279                                GtkStyleContext *context)
280 {
281   GtkCssValue *color;
282
283   color = _gtk_css_rgba_value_compute_from_symbolic (shadow->color,
284                                                      _gtk_css_value_new_take_symbolic_color (
285                                                        gtk_symbolic_color_ref (
286                                                          _gtk_symbolic_color_get_current_color ())),
287                                                      context,
288                                                      FALSE);
289
290   return gtk_css_shadow_value_new (_gtk_css_number_value_compute (shadow->hoffset, context),
291                                    _gtk_css_number_value_compute (shadow->voffset, context),
292                                    _gtk_css_number_value_compute (shadow->radius, context),
293                                    _gtk_css_number_value_compute (shadow->spread, context),
294                                    shadow->inset,
295                                    color);
296 }
297
298 void
299 _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
300                                     cairo_t           *cr,
301                                     PangoLayout       *layout)
302 {
303   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
304
305   if (!cairo_has_current_point (cr))
306     cairo_move_to (cr, 0, 0);
307
308   cairo_save (cr);
309
310   cairo_rel_move_to (cr, 
311                      _gtk_css_number_value_get (shadow->hoffset, 0),
312                      _gtk_css_number_value_get (shadow->voffset, 0));
313   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
314   _gtk_pango_fill_layout (cr, layout);
315
316   cairo_rel_move_to (cr,
317                      - _gtk_css_number_value_get (shadow->hoffset, 0),
318                      - _gtk_css_number_value_get (shadow->voffset, 0));
319   cairo_restore (cr);
320 }
321
322 void
323 _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
324                                   cairo_t           *cr)
325 {
326   cairo_pattern_t *pattern;
327
328   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
329
330   cairo_save (cr);
331   pattern = cairo_pattern_reference (cairo_get_source (cr));
332   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
333
334   cairo_translate (cr,
335                    _gtk_css_number_value_get (shadow->hoffset, 0),
336                    _gtk_css_number_value_get (shadow->voffset, 0));
337   cairo_mask (cr, pattern);
338
339   cairo_restore (cr);
340   cairo_pattern_destroy (pattern);
341 }
342
343 void
344 _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
345                                      cairo_t           *cr,
346                                      gdouble            radius,
347                                      gdouble            progress)
348 {
349   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
350
351   cairo_save (cr);
352
353   cairo_translate (cr,
354                    _gtk_css_number_value_get (shadow->hoffset, 0),
355                    _gtk_css_number_value_get (shadow->voffset, 0));
356   _gtk_theming_engine_paint_spinner (cr,
357                                      radius, progress,
358                                      _gtk_css_rgba_value_get_rgba (shadow->color));
359
360   cairo_restore (cr);
361 }
362
363 void
364 _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
365                                  cairo_t             *cr,
366                                  const GtkRoundedBox *padding_box)
367 {
368   GtkRoundedBox box;
369   double spread;
370
371   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
372
373   cairo_save (cr);
374   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
375
376   _gtk_rounded_box_path (padding_box, cr);
377   cairo_clip (cr);
378
379   box = *padding_box;
380   _gtk_rounded_box_move (&box,
381                          _gtk_css_number_value_get (shadow->hoffset, 0),
382                          _gtk_css_number_value_get (shadow->voffset, 0));
383   spread = _gtk_css_number_value_get (shadow->spread, 0);
384   _gtk_rounded_box_shrink (&box, spread, spread, spread, spread);
385
386   _gtk_rounded_box_path (&box, cr);
387   _gtk_rounded_box_clip_path (padding_box, cr);
388
389   gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
390   cairo_fill (cr);
391
392   cairo_restore (cr);
393 }