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 "gtkcssanimationprivate.h"
23 
24 #include "gtkcsseasevalueprivate.h"
25 #include "gtkprogresstrackerprivate.h"
26 
27 #include <math.h>
28 
G_DEFINE_TYPE(GtkCssAnimation,_gtk_css_animation,GTK_TYPE_STYLE_ANIMATION)29 G_DEFINE_TYPE (GtkCssAnimation, _gtk_css_animation, GTK_TYPE_STYLE_ANIMATION)
30 
31 static gboolean
32 gtk_css_animation_is_executing (GtkCssAnimation *animation)
33 {
34   GtkProgressState state = gtk_progress_tracker_get_state (&animation->tracker);
35 
36   switch (animation->fill_mode)
37     {
38     case GTK_CSS_FILL_NONE:
39       return state == GTK_PROGRESS_STATE_DURING;
40     case GTK_CSS_FILL_FORWARDS:
41       return state != GTK_PROGRESS_STATE_BEFORE;
42     case GTK_CSS_FILL_BACKWARDS:
43       return state != GTK_PROGRESS_STATE_AFTER;
44     case GTK_CSS_FILL_BOTH:
45       return TRUE;
46     default:
47       g_return_val_if_reached (FALSE);
48     }
49 }
50 
51 static double
gtk_css_animation_get_progress(GtkCssAnimation * animation)52 gtk_css_animation_get_progress (GtkCssAnimation *animation)
53 {
54   gboolean reverse, odd_iteration;
55   gint cycle = gtk_progress_tracker_get_iteration_cycle (&animation->tracker);
56   odd_iteration = cycle % 2 > 0;
57 
58   switch (animation->direction)
59     {
60     case GTK_CSS_DIRECTION_NORMAL:
61       reverse = FALSE;
62       break;
63     case GTK_CSS_DIRECTION_REVERSE:
64       reverse = TRUE;
65       break;
66     case GTK_CSS_DIRECTION_ALTERNATE:
67       reverse = odd_iteration;
68       break;
69     case GTK_CSS_DIRECTION_ALTERNATE_REVERSE:
70       reverse = !odd_iteration;
71       break;
72     default:
73       g_return_val_if_reached (0.0);
74     }
75 
76   return gtk_progress_tracker_get_progress (&animation->tracker, reverse);
77 }
78 
79 GtkStyleAnimation *
gtk_css_animation_advance(GtkStyleAnimation * style_animation,gint64 timestamp)80 gtk_css_animation_advance (GtkStyleAnimation    *style_animation,
81                            gint64                timestamp)
82 {
83   GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
84 
85   return _gtk_css_animation_advance_with_play_state (animation,
86                                                      timestamp,
87                                                      animation->play_state);
88 }
89 
90 static void
gtk_css_animation_apply_values(GtkStyleAnimation * style_animation,GtkCssAnimatedStyle * style)91 gtk_css_animation_apply_values (GtkStyleAnimation    *style_animation,
92                                 GtkCssAnimatedStyle  *style)
93 {
94   GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
95   double progress;
96   guint i;
97 
98   if (!gtk_css_animation_is_executing (animation))
99     return;
100 
101   progress = gtk_css_animation_get_progress (animation);
102   progress = _gtk_css_ease_value_transform (animation->ease, progress);
103 
104   for (i = 0; i < _gtk_css_keyframes_get_n_properties (animation->keyframes); i++)
105     {
106       GtkCssValue *value;
107       guint property_id;
108 
109       property_id = _gtk_css_keyframes_get_property_id (animation->keyframes, i);
110 
111       value = _gtk_css_keyframes_get_value (animation->keyframes,
112                                             i,
113                                             progress,
114                                             gtk_css_animated_style_get_intrinsic_value (style, property_id));
115       gtk_css_animated_style_set_animated_value (style, property_id, value);
116       _gtk_css_value_unref (value);
117     }
118 }
119 
120 static gboolean
gtk_css_animation_is_finished(GtkStyleAnimation * style_animation)121 gtk_css_animation_is_finished (GtkStyleAnimation *style_animation)
122 {
123   return FALSE;
124 }
125 
126 static gboolean
gtk_css_animation_is_static(GtkStyleAnimation * style_animation)127 gtk_css_animation_is_static (GtkStyleAnimation *style_animation)
128 {
129   GtkCssAnimation *animation = GTK_CSS_ANIMATION (style_animation);
130 
131   if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
132     return TRUE;
133 
134   return gtk_progress_tracker_get_state (&animation->tracker) == GTK_PROGRESS_STATE_AFTER;
135 }
136 
137 static void
gtk_css_animation_finalize(GObject * object)138 gtk_css_animation_finalize (GObject *object)
139 {
140   GtkCssAnimation *animation = GTK_CSS_ANIMATION (object);
141 
142   g_free (animation->name);
143   _gtk_css_keyframes_unref (animation->keyframes);
144   _gtk_css_value_unref (animation->ease);
145 
146   G_OBJECT_CLASS (_gtk_css_animation_parent_class)->finalize (object);
147 }
148 
149 static void
_gtk_css_animation_class_init(GtkCssAnimationClass * klass)150 _gtk_css_animation_class_init (GtkCssAnimationClass *klass)
151 {
152   GObjectClass *object_class = G_OBJECT_CLASS (klass);
153   GtkStyleAnimationClass *animation_class = GTK_STYLE_ANIMATION_CLASS (klass);
154 
155   object_class->finalize = gtk_css_animation_finalize;
156 
157   animation_class->advance = gtk_css_animation_advance;
158   animation_class->apply_values = gtk_css_animation_apply_values;
159   animation_class->is_finished = gtk_css_animation_is_finished;
160   animation_class->is_static = gtk_css_animation_is_static;
161 }
162 
163 static void
_gtk_css_animation_init(GtkCssAnimation * animation)164 _gtk_css_animation_init (GtkCssAnimation *animation)
165 {
166 }
167 
168 GtkStyleAnimation *
_gtk_css_animation_new(const char * name,GtkCssKeyframes * keyframes,gint64 timestamp,gint64 delay_us,gint64 duration_us,GtkCssValue * ease,GtkCssDirection direction,GtkCssPlayState play_state,GtkCssFillMode fill_mode,double iteration_count)169 _gtk_css_animation_new (const char      *name,
170                         GtkCssKeyframes *keyframes,
171                         gint64           timestamp,
172                         gint64           delay_us,
173                         gint64           duration_us,
174                         GtkCssValue     *ease,
175                         GtkCssDirection  direction,
176                         GtkCssPlayState  play_state,
177                         GtkCssFillMode   fill_mode,
178                         double           iteration_count)
179 {
180   GtkCssAnimation *animation;
181 
182   g_return_val_if_fail (name != NULL, NULL);
183   g_return_val_if_fail (keyframes != NULL, NULL);
184   g_return_val_if_fail (ease != NULL, NULL);
185   g_return_val_if_fail (iteration_count >= 0, NULL);
186 
187   animation = g_object_new (GTK_TYPE_CSS_ANIMATION, NULL);
188 
189   animation->name = g_strdup (name);
190   animation->keyframes = _gtk_css_keyframes_ref (keyframes);
191   animation->ease = _gtk_css_value_ref (ease);
192   animation->direction = direction;
193   animation->play_state = play_state;
194   animation->fill_mode = fill_mode;
195 
196   gtk_progress_tracker_start (&animation->tracker, duration_us, delay_us, iteration_count);
197   if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
198     gtk_progress_tracker_skip_frame (&animation->tracker, timestamp);
199   else
200     gtk_progress_tracker_advance_frame (&animation->tracker, timestamp);
201 
202   return GTK_STYLE_ANIMATION (animation);
203 }
204 
205 const char *
_gtk_css_animation_get_name(GtkCssAnimation * animation)206 _gtk_css_animation_get_name (GtkCssAnimation *animation)
207 {
208   g_return_val_if_fail (GTK_IS_CSS_ANIMATION (animation), NULL);
209 
210   return animation->name;
211 }
212 
213 GtkStyleAnimation *
_gtk_css_animation_advance_with_play_state(GtkCssAnimation * source,gint64 timestamp,GtkCssPlayState play_state)214 _gtk_css_animation_advance_with_play_state (GtkCssAnimation *source,
215                                             gint64           timestamp,
216                                             GtkCssPlayState  play_state)
217 {
218   GtkCssAnimation *animation;
219 
220   g_return_val_if_fail (GTK_IS_CSS_ANIMATION (source), NULL);
221 
222   animation = g_object_new (GTK_TYPE_CSS_ANIMATION, NULL);
223 
224   animation->name = g_strdup (source->name);
225   animation->keyframes = _gtk_css_keyframes_ref (source->keyframes);
226   animation->ease = _gtk_css_value_ref (source->ease);
227   animation->direction = source->direction;
228   animation->play_state = play_state;
229   animation->fill_mode = source->fill_mode;
230 
231   gtk_progress_tracker_init_copy (&source->tracker, &animation->tracker);
232   if (animation->play_state == GTK_CSS_PLAY_STATE_PAUSED)
233     gtk_progress_tracker_skip_frame (&animation->tracker, timestamp);
234   else
235     gtk_progress_tracker_advance_frame (&animation->tracker, timestamp);
236 
237   return GTK_STYLE_ANIMATION (animation);
238 }
239