]> Pileus Git - ~andy/gtk/blob - gtk/gtkcsscomputedvalues.c
f9ec618c97674f4dbaebebf9b20e96fe6c94bc04
[~andy/gtk] / gtk / gtkcsscomputedvalues.c
1 /*
2  * Copyright © 2012 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19
20 #include "config.h"
21
22 #include "gtkcsscomputedvaluesprivate.h"
23
24 #include "gtkcssanimationprivate.h"
25 #include "gtkcssarrayvalueprivate.h"
26 #include "gtkcssenumvalueprivate.h"
27 #include "gtkcssinheritvalueprivate.h"
28 #include "gtkcssinitialvalueprivate.h"
29 #include "gtkcssnumbervalueprivate.h"
30 #include "gtkcssshorthandpropertyprivate.h"
31 #include "gtkcssstringvalueprivate.h"
32 #include "gtkcssstylepropertyprivate.h"
33 #include "gtkcsstransitionprivate.h"
34 #include "gtkstyleanimationprivate.h"
35 #include "gtkstylepropertiesprivate.h"
36 #include "gtkstylepropertyprivate.h"
37 #include "gtkstyleproviderprivate.h"
38
39 G_DEFINE_TYPE (GtkCssComputedValues, _gtk_css_computed_values, G_TYPE_OBJECT)
40
41 static void
42 gtk_css_computed_values_dispose (GObject *object)
43 {
44   GtkCssComputedValues *values = GTK_CSS_COMPUTED_VALUES (object);
45
46   if (values->values)
47     {
48       g_ptr_array_unref (values->values);
49       values->values = NULL;
50     }
51   if (values->sections)
52     {
53       g_ptr_array_unref (values->sections);
54       values->sections = NULL;
55     }
56   if (values->animated_values)
57     {
58       g_ptr_array_unref (values->animated_values);
59       values->animated_values = NULL;
60     }
61
62   g_slist_free_full (values->animations, g_object_unref);
63   values->animations = NULL;
64
65   G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->dispose (object);
66 }
67
68 static void
69 gtk_css_computed_values_finalize (GObject *object)
70 {
71   GtkCssComputedValues *values = GTK_CSS_COMPUTED_VALUES (object);
72
73   _gtk_bitmask_free (values->depends_on_parent);
74   _gtk_bitmask_free (values->equals_parent);
75   _gtk_bitmask_free (values->depends_on_color);
76   _gtk_bitmask_free (values->depends_on_font_size);
77
78   G_OBJECT_CLASS (_gtk_css_computed_values_parent_class)->finalize (object);
79 }
80
81 static void
82 _gtk_css_computed_values_class_init (GtkCssComputedValuesClass *klass)
83 {
84   GObjectClass *object_class = G_OBJECT_CLASS (klass);
85
86   object_class->dispose = gtk_css_computed_values_dispose;
87   object_class->finalize = gtk_css_computed_values_finalize;
88 }
89
90 static void
91 _gtk_css_computed_values_init (GtkCssComputedValues *values)
92 {
93   values->depends_on_parent = _gtk_bitmask_new ();
94   values->equals_parent = _gtk_bitmask_new ();
95   values->depends_on_color = _gtk_bitmask_new ();
96   values->depends_on_font_size = _gtk_bitmask_new ();
97 }
98
99 GtkCssComputedValues *
100 _gtk_css_computed_values_new (void)
101 {
102   return g_object_new (GTK_TYPE_CSS_COMPUTED_VALUES, NULL);
103 }
104
105 static void
106 maybe_unref_section (gpointer section)
107 {
108   if (section)
109     gtk_css_section_unref (section);
110 }
111
112 void
113 _gtk_css_computed_values_compute_value (GtkCssComputedValues *values,
114                                         GtkStyleContext      *context,
115                                         guint                 id,
116                                         GtkCssValue          *specified,
117                                         GtkCssSection        *section)
118 {
119   GtkCssDependencies dependencies;
120   GtkCssValue *value;
121
122   g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values));
123   g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
124
125   /* http://www.w3.org/TR/css3-cascade/#cascade
126    * Then, for every element, the value for each property can be found
127    * by following this pseudo-algorithm:
128    * 1) Identify all declarations that apply to the element
129    */
130   if (specified == NULL)
131     {
132       GtkCssStyleProperty *prop = _gtk_css_style_property_lookup_by_id (id);
133
134       if (_gtk_css_style_property_is_inherit (prop))
135         specified = _gtk_css_inherit_value_new ();
136       else
137         specified = _gtk_css_initial_value_new ();
138     }
139   else
140     _gtk_css_value_ref (specified);
141
142   value = _gtk_css_value_compute (specified, id, context, &dependencies);
143
144   _gtk_css_computed_values_set_value (values, id, value, dependencies, section);
145
146   _gtk_css_value_unref (value);
147   _gtk_css_value_unref (specified);
148 }
149
150 void
151 _gtk_css_computed_values_set_animated_value (GtkCssComputedValues *values,
152                                              guint                 id,
153                                              GtkCssValue          *value)
154 {
155   g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values));
156   g_return_if_fail (value != NULL);
157
158   if (values->animated_values == NULL)
159     values->animated_values = g_ptr_array_new_with_free_func ((GDestroyNotify)_gtk_css_value_unref);
160   if (id >= values->animated_values->len)
161    g_ptr_array_set_size (values->animated_values, id + 1);
162
163   if (g_ptr_array_index (values->animated_values, id))
164     _gtk_css_value_unref (g_ptr_array_index (values->animated_values, id));
165   g_ptr_array_index (values->animated_values, id) = _gtk_css_value_ref (value);
166
167 }
168
169 void
170 _gtk_css_computed_values_set_value (GtkCssComputedValues *values,
171                                     guint                 id,
172                                     GtkCssValue          *value,
173                                     GtkCssDependencies    dependencies,
174                                     GtkCssSection        *section)
175 {
176   g_return_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values));
177
178   if (values->values == NULL)
179     values->values = g_ptr_array_new_with_free_func ((GDestroyNotify)_gtk_css_value_unref);
180   if (id >= values->values->len)
181    g_ptr_array_set_size (values->values, id + 1);
182
183   if (g_ptr_array_index (values->values, id))
184     _gtk_css_value_unref (g_ptr_array_index (values->values, id));
185   g_ptr_array_index (values->values, id) = _gtk_css_value_ref (value);
186
187   if (dependencies & (GTK_CSS_DEPENDS_ON_PARENT | GTK_CSS_EQUALS_PARENT))
188     values->depends_on_parent = _gtk_bitmask_set (values->depends_on_parent, id, TRUE);
189   if (dependencies & (GTK_CSS_EQUALS_PARENT))
190     values->equals_parent = _gtk_bitmask_set (values->equals_parent, id, TRUE);
191   if (dependencies & (GTK_CSS_DEPENDS_ON_COLOR))
192     values->depends_on_color = _gtk_bitmask_set (values->depends_on_color, id, TRUE);
193   if (dependencies & (GTK_CSS_DEPENDS_ON_FONT_SIZE))
194     values->depends_on_font_size = _gtk_bitmask_set (values->depends_on_font_size, id, TRUE);
195
196   if (section)
197     {
198       if (values->sections == NULL)
199         values->sections = g_ptr_array_new_with_free_func (maybe_unref_section);
200       if (values->sections->len <= id)
201         g_ptr_array_set_size (values->sections, id + 1);
202
203       g_ptr_array_index (values->sections, id) = gtk_css_section_ref (section);
204     }
205 }
206
207 GtkCssValue *
208 _gtk_css_computed_values_get_value (GtkCssComputedValues *values,
209                                     guint                 id)
210 {
211   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
212
213   if (values->animated_values &&
214       id < values->animated_values->len &&
215       g_ptr_array_index (values->animated_values, id))
216     return g_ptr_array_index (values->animated_values, id);
217
218   return _gtk_css_computed_values_get_intrinsic_value (values, id);
219 }
220
221 GtkCssValue *
222 _gtk_css_computed_values_get_intrinsic_value (GtkCssComputedValues *values,
223                                               guint                 id)
224 {
225   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
226
227   if (values->values == NULL ||
228       id >= values->values->len)
229     return NULL;
230
231   return g_ptr_array_index (values->values, id);
232 }
233
234 GtkCssSection *
235 _gtk_css_computed_values_get_section (GtkCssComputedValues *values,
236                                       guint                 id)
237 {
238   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
239
240   if (values->sections == NULL ||
241       id >= values->sections->len)
242     return NULL;
243
244   return g_ptr_array_index (values->sections, id);
245 }
246
247 GtkBitmask *
248 _gtk_css_computed_values_get_difference (GtkCssComputedValues *values,
249                                          GtkCssComputedValues *other)
250 {
251   GtkBitmask *result;
252   guint i, len;
253
254   len = MIN (values->values->len, other->values->len);
255   result = _gtk_bitmask_new ();
256   if (values->values->len != other->values->len)
257     result = _gtk_bitmask_invert_range (result, len, MAX (values->values->len, other->values->len));
258   
259   for (i = 0; i < len; i++)
260     {
261       if (!_gtk_css_value_equal (g_ptr_array_index (values->values, i),
262                                  g_ptr_array_index (other->values, i)))
263         result = _gtk_bitmask_set (result, i, TRUE);
264     }
265
266   return result;
267 }
268
269 /* TRANSITIONS */
270
271 typedef struct _TransitionInfo TransitionInfo;
272 struct _TransitionInfo {
273   guint index;                  /* index into value arrays */
274   gboolean pending;             /* TRUE if we still need to handle it */
275 };
276
277 static void
278 transition_info_add (TransitionInfo    infos[GTK_CSS_PROPERTY_N_PROPERTIES],
279                      GtkStyleProperty *property,
280                      guint             index)
281 {
282   if (property == NULL)
283     {
284       guint i;
285
286       for (i = 0; i < _gtk_css_style_property_get_n_properties (); i++)
287         {
288           GtkCssStyleProperty *prop = _gtk_css_style_property_lookup_by_id (i);
289
290           transition_info_add (infos, GTK_STYLE_PROPERTY (prop), index);
291         }
292     }
293   else if (GTK_IS_CSS_SHORTHAND_PROPERTY (property))
294     {
295       GtkCssShorthandProperty *shorthand = GTK_CSS_SHORTHAND_PROPERTY (property);
296       guint i;
297
298       for (i = 0; i < _gtk_css_shorthand_property_get_n_subproperties (shorthand); i++)
299         {
300           GtkCssStyleProperty *prop = _gtk_css_shorthand_property_get_subproperty (shorthand, i);
301
302           transition_info_add (infos, GTK_STYLE_PROPERTY (prop), index);
303         }
304     }
305   else if (GTK_IS_CSS_STYLE_PROPERTY (property))
306     {
307       guint id;
308       
309       if (!_gtk_css_style_property_is_animated (GTK_CSS_STYLE_PROPERTY (property)))
310         return;
311
312       id = _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (property));
313       g_assert (id < GTK_CSS_PROPERTY_N_PROPERTIES);
314       infos[id].index = index;
315       infos[id].pending = TRUE;
316     }
317   else
318     {
319       g_assert_not_reached ();
320     }
321 }
322
323 static void
324 transition_infos_set (TransitionInfo  infos[GTK_CSS_PROPERTY_N_PROPERTIES],
325                       GtkCssValue    *transitions)
326 {
327   guint i;
328
329   for (i = 0; i < _gtk_css_array_value_get_n_values (transitions); i++)
330     {
331       GtkStyleProperty *property;
332       GtkCssValue *prop_value;
333
334       prop_value = _gtk_css_array_value_get_nth (transitions, i);
335       if (g_ascii_strcasecmp (_gtk_css_ident_value_get (prop_value), "all") == 0)
336         property = NULL;
337       else
338         {
339           property = _gtk_style_property_lookup (_gtk_css_ident_value_get (prop_value));
340           if (property == NULL)
341             continue;
342         }
343       
344       transition_info_add (infos, property, i);
345     }
346 }
347
348 static GtkStyleAnimation *
349 gtk_css_computed_values_find_transition (GtkCssComputedValues *values,
350                                          guint                 property_id)
351 {
352   GSList *list;
353
354   for (list = values->animations; list; list = list->next)
355     {
356       if (!GTK_IS_CSS_TRANSITION (list->data))
357         continue;
358
359       if (_gtk_css_transition_get_property (list->data) == property_id)
360         return list->data;
361     }
362
363   return NULL;
364 }
365
366 static void
367 gtk_css_computed_values_create_css_transitions (GtkCssComputedValues *values,
368                                                 gint64                timestamp,
369                                                 GtkCssComputedValues *source)
370 {
371   TransitionInfo transitions[GTK_CSS_PROPERTY_N_PROPERTIES] = { { 0, } };
372   GtkCssValue *durations, *delays, *timing_functions;
373   guint i;
374
375   transition_infos_set (transitions, _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_TRANSITION_PROPERTY));
376
377   durations = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_TRANSITION_DURATION);
378   delays = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_TRANSITION_DELAY);
379   timing_functions = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_TRANSITION_TIMING_FUNCTION);
380
381   for (i = 0; i < GTK_CSS_PROPERTY_N_PROPERTIES; i++)
382     {
383       GtkStyleAnimation *animation;
384       GtkCssValue *start, *end;
385       double duration, delay;
386
387       if (!transitions[i].pending)
388         continue;
389
390       duration = _gtk_css_number_value_get (_gtk_css_array_value_get_nth (durations, transitions[i].index), 100);
391       delay = _gtk_css_number_value_get (_gtk_css_array_value_get_nth (delays, transitions[i].index), 100);
392       if (duration + delay == 0.0)
393         continue;
394
395       start = _gtk_css_computed_values_get_intrinsic_value (source, i);
396       end = _gtk_css_computed_values_get_intrinsic_value (values, i);
397       if (_gtk_css_value_equal (start, end))
398         {
399           animation = gtk_css_computed_values_find_transition (GTK_CSS_COMPUTED_VALUES (source), i);
400           if (animation)
401             values->animations = g_slist_prepend (values->animations, g_object_ref (animation));
402         }
403       else
404         {
405           animation = _gtk_css_transition_new (i,
406                                                start,
407                                                end,
408                                                _gtk_css_array_value_get_nth (timing_functions, i),
409                                                timestamp + delay * G_USEC_PER_SEC,
410                                                timestamp + (delay + duration) * G_USEC_PER_SEC);
411           values->animations = g_slist_prepend (values->animations, animation);
412         }
413     }
414 }
415
416 static GtkStyleAnimation *
417 gtk_css_computed_values_find_animation (GtkCssComputedValues *values,
418                                         const char           *name)
419 {
420   GSList *list;
421
422   for (list = values->animations; list; list = list->next)
423     {
424       if (!GTK_IS_CSS_ANIMATION (list->data))
425         continue;
426
427       if (g_str_equal (_gtk_css_animation_get_name (list->data), name))
428         return list->data;
429     }
430
431   return NULL;
432 }
433
434 static void
435 gtk_css_computed_values_create_css_animations (GtkCssComputedValues *values,
436                                                gint64                timestamp,
437                                                GtkStyleContext      *context)
438 {
439   GtkStyleProviderPrivate *provider;
440   GtkCssValue *durations, *delays, *timing_functions, *animations;
441   GtkCssValue *iteration_counts, *directions, *play_states, *fill_modes;
442   guint i;
443
444   provider = _gtk_style_context_get_style_provider (context);
445   animations = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_NAME);
446   durations = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_DURATION);
447   delays = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_DELAY);
448   timing_functions = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_TIMING_FUNCTION);
449   iteration_counts = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_ITERATION_COUNT);
450   directions = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_DIRECTION);
451   play_states = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_PLAY_STATE);
452   fill_modes = _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_ANIMATION_FILL_MODE);
453
454   for (i = 0; i < _gtk_css_array_value_get_n_values (animations); i++)
455     {
456       GtkStyleAnimation *animation;
457       GtkCssKeyframes *keyframes;
458       const char *name;
459       
460       name = _gtk_css_ident_value_get (_gtk_css_array_value_get_nth (animations, i));
461       if (g_ascii_strcasecmp (name, "none") == 0)
462         continue;
463
464       animation = gtk_css_computed_values_find_animation (values, name);
465       if (animation)
466         continue;
467
468       keyframes = _gtk_style_provider_private_get_keyframes (provider, name);
469       if (keyframes == NULL)
470         continue;
471
472       keyframes = _gtk_css_keyframes_compute (keyframes, context);
473
474       animation = _gtk_css_animation_new (name,
475                                           keyframes,
476                                           timestamp + _gtk_css_number_value_get (_gtk_css_array_value_get_nth (delays, i), 100) * G_USEC_PER_SEC,
477                                           _gtk_css_number_value_get (_gtk_css_array_value_get_nth (durations, i), 100) * G_USEC_PER_SEC,
478                                           _gtk_css_array_value_get_nth (timing_functions, i),
479                                           _gtk_css_direction_value_get (_gtk_css_array_value_get_nth (directions, i)),
480                                           _gtk_css_play_state_value_get (_gtk_css_array_value_get_nth (play_states, i)),
481                                           _gtk_css_fill_mode_value_get (_gtk_css_array_value_get_nth (fill_modes, i)),
482                                           _gtk_css_number_value_get (_gtk_css_array_value_get_nth (iteration_counts, i), 100));
483       values->animations = g_slist_prepend (values->animations, animation);
484     }
485 }
486
487 /* PUBLIC API */
488
489 void
490 _gtk_css_computed_values_create_animations (GtkCssComputedValues *values,
491                                             gint64                timestamp,
492                                             GtkCssComputedValues *source,
493                                             GtkStyleContext      *context)
494 {
495   gtk_css_computed_values_create_css_transitions (values, timestamp, source);
496   gtk_css_computed_values_create_css_animations (values, timestamp, context);
497 }
498
499 GtkBitmask *
500 _gtk_css_computed_values_advance (GtkCssComputedValues *values,
501                                   gint64                timestamp)
502 {
503   GtkBitmask *changed;
504   GPtrArray *old_computed_values;
505   GSList *list;
506   guint i;
507
508   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
509   g_return_val_if_fail (timestamp >= values->current_time, NULL);
510
511   values->current_time = timestamp;
512   old_computed_values = values->animated_values;
513   values->animated_values = NULL;
514
515   list = values->animations;
516   while (list)
517     {
518       GtkStyleAnimation *animation = list->data;
519       
520       list = list->next;
521
522       _gtk_style_animation_set_values (animation,
523                                        timestamp,
524                                        GTK_CSS_COMPUTED_VALUES (values));
525       
526       if (_gtk_style_animation_is_finished (animation, timestamp))
527         {
528           values->animations = g_slist_remove (values->animations, animation);
529           g_object_unref (animation);
530         }
531     }
532
533   /* figure out changes */
534   changed = _gtk_bitmask_new ();
535
536   for (i = 0; i < GTK_CSS_PROPERTY_N_PROPERTIES; i++)
537     {
538       GtkCssValue *old_animated, *new_animated;
539
540       old_animated = old_computed_values && i < old_computed_values->len ? g_ptr_array_index (old_computed_values, i) : NULL;
541       new_animated = values->animated_values && i < values->animated_values->len ? g_ptr_array_index (values->animated_values, i) : NULL;
542
543       if (!_gtk_css_value_equal0 (old_animated, new_animated))
544         changed = _gtk_bitmask_set (changed, i, TRUE);
545     }
546
547   if (old_computed_values)
548     g_ptr_array_unref (old_computed_values);
549
550   return changed;
551 }
552
553 gboolean
554 _gtk_css_computed_values_is_static (GtkCssComputedValues *values)
555 {
556   GSList *list;
557
558   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), TRUE);
559
560   for (list = values->animations; list; list = list->next)
561     {
562       if (!_gtk_style_animation_is_static (list->data, values->current_time))
563         return FALSE;
564     }
565
566   return TRUE;
567 }