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