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