1 #include <stdlib.h>
2 #include <gmodule.h>
3 #include <clutter/clutter.h>
4 
5 /* all the easing modes provided by Clutter */
6 static const struct {
7   const gchar *name;
8   ClutterAnimationMode mode;
9 } easing_modes[] = {
10   { "linear", CLUTTER_LINEAR },
11   { "easeInQuad", CLUTTER_EASE_IN_QUAD },
12   { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
13   { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
14   { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
15   { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
16   { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
17   { "easeInQuart", CLUTTER_EASE_IN_QUART },
18   { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
19   { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
20   { "easeInQuint", CLUTTER_EASE_IN_QUINT },
21   { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
22   { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
23   { "easeInSine", CLUTTER_EASE_IN_SINE },
24   { "easeOutSine", CLUTTER_EASE_OUT_SINE },
25   { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
26   { "easeInExpo", CLUTTER_EASE_IN_EXPO },
27   { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
28   { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
29   { "easeInCirc", CLUTTER_EASE_IN_CIRC },
30   { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
31   { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
32   { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
33   { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
34   { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
35   { "easeInBack", CLUTTER_EASE_IN_BACK },
36   { "easeOutBack", CLUTTER_EASE_OUT_BACK },
37   { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
38   { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
39   { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
40   { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
41 };
42 
43 #define HELP_TEXT       "Easing mode: %s (%d of %d)\n" \
44                         "Left click to tween\n" \
45                         "Right click to change the easing mode"
46 
47 static const gint n_easing_modes = G_N_ELEMENTS (easing_modes);
48 static gint current_mode = 0;
49 
50 static gint duration = 1;
51 static gboolean recenter = FALSE;
52 
53 static ClutterActor *main_stage = NULL;
54 static ClutterActor *easing_mode_label = NULL;
55 
56 static ClutterAnimation *last_animation = NULL;
57 
58 /* recenter_bouncer:
59  *
60  * repositions (through an animation) the bouncer at the center of the stage
61  */
62 static void
recenter_bouncer(ClutterAnimation * animation,ClutterActor * rectangle)63 recenter_bouncer (ClutterAnimation *animation,
64                   ClutterActor     *rectangle)
65 {
66   gfloat base_x, base_y;
67   gint cur_mode;
68 
69   base_x = clutter_actor_get_width (main_stage) / 2;
70   base_y = clutter_actor_get_height (main_stage) / 2;
71 
72   cur_mode = easing_modes[current_mode].mode;
73 
74   clutter_actor_animate (rectangle, cur_mode, 250,
75                          "x", base_x,
76                          "y", base_y,
77                          NULL);
78 }
79 
80 static gboolean
on_button_press(ClutterActor * actor,ClutterButtonEvent * event,ClutterActor * rectangle)81 on_button_press (ClutterActor       *actor,
82                  ClutterButtonEvent *event,
83                  ClutterActor       *rectangle)
84 {
85   if (event->button == CLUTTER_BUTTON_SECONDARY)
86     {
87       gchar *text;
88 
89       /* cycle through the various easing modes */
90       current_mode = (current_mode + 1 < n_easing_modes)
91                    ? current_mode + 1
92                    : 0;
93 
94       /* update the text of the label */
95       text = g_strdup_printf (HELP_TEXT,
96                               easing_modes[current_mode].name,
97                               current_mode + 1,
98                               n_easing_modes);
99 
100       clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text);
101       g_free (text);
102     }
103   else if (event->button == CLUTTER_BUTTON_PRIMARY)
104     {
105       ClutterAnimation *animation;
106       ClutterAnimationMode cur_mode;
107 
108       cur_mode = easing_modes[current_mode].mode;
109 
110       /* tween the actor using the current easing mode */
111       animation =
112         clutter_actor_animate (rectangle, cur_mode, duration * 1000,
113                                "x", event->x,
114                                "y", event->y,
115                                NULL);
116 
117       /* if we were asked to, recenter the bouncer at the end of the
118        * animation. we keep track of the animation to avoid connecting
119        * the signal handler to the same Animation twice.
120        */
121       if (recenter && last_animation != animation)
122         g_signal_connect_after (animation, "completed",
123                                 G_CALLBACK (recenter_bouncer),
124                                 rectangle);
125 
126       last_animation = animation;
127     }
128 
129   return TRUE;
130 }
131 
132 static gboolean
draw_bouncer(ClutterCairoTexture * texture,cairo_t * cr)133 draw_bouncer (ClutterCairoTexture *texture,
134               cairo_t             *cr)
135 {
136   const ClutterColor *bouncer_color;
137   cairo_pattern_t *pattern;
138   guint width, height;
139   float radius;
140 
141   clutter_cairo_texture_get_surface_size (texture, &width, &height);
142 
143   radius = MAX (width, height);
144 
145   clutter_cairo_texture_clear (texture);
146 
147   cairo_arc (cr, radius / 2, radius / 2, radius / 2, 0.0, 2.0 * G_PI);
148 
149   bouncer_color = CLUTTER_COLOR_DarkScarletRed;
150 
151   pattern = cairo_pattern_create_radial (radius / 2, radius / 2, 0,
152                                          radius, radius, radius);
153   cairo_pattern_add_color_stop_rgba (pattern,
154                                      0,
155                                      bouncer_color->red / 255.0,
156                                      bouncer_color->green / 255.0,
157                                      bouncer_color->blue / 255.0,
158                                      bouncer_color->alpha / 255.0);
159   cairo_pattern_add_color_stop_rgba (pattern,
160                                      0.85,
161                                      bouncer_color->red / 255.0,
162                                      bouncer_color->green / 255.0,
163                                      bouncer_color->blue / 255.0,
164                                      0.25);
165 
166   cairo_set_source (cr, pattern);
167   cairo_fill_preserve (cr);
168 
169   cairo_pattern_destroy (pattern);
170 
171   return TRUE;
172 }
173 
174 static ClutterActor *
make_bouncer(gfloat width,gfloat height)175 make_bouncer (gfloat width,
176               gfloat height)
177 {
178   ClutterActor *retval;
179 
180   retval = clutter_cairo_texture_new (width, height);
181   g_signal_connect (retval, "draw", G_CALLBACK (draw_bouncer), NULL);
182 
183   clutter_actor_set_name (retval, "bouncer");
184   clutter_actor_set_size (retval, width, height);
185   clutter_actor_set_anchor_point (retval, width / 2, height / 2);
186   clutter_actor_set_reactive (retval, TRUE);
187 
188   /* make sure we draw the bouncer immediately */
189   clutter_cairo_texture_invalidate (CLUTTER_CAIRO_TEXTURE (retval));
190 
191   return retval;
192 }
193 
194 static GOptionEntry test_easing_entries[] = {
195   {
196     "re-center", 'r',
197     0,
198     G_OPTION_ARG_NONE, &recenter,
199     "Re-center the actor when the animation ends",
200     NULL
201   },
202   {
203     "duration", 'd',
204     0,
205     G_OPTION_ARG_INT, &duration,
206     "Duration of the animation",
207     "SECONDS"
208   },
209 
210   { NULL }
211 };
212 
213 G_MODULE_EXPORT int
test_easing_main(int argc,char * argv[])214 test_easing_main (int argc, char *argv[])
215 {
216   ClutterActor *stage, *rect, *label;
217   gchar *text;
218   gfloat stage_width, stage_height;
219   GError *error = NULL;
220 
221   if (clutter_init_with_args (&argc, &argv,
222                               NULL,
223                               test_easing_entries,
224                               NULL,
225                               &error) != CLUTTER_INIT_SUCCESS)
226     return 1;
227 
228   stage = clutter_stage_new ();
229   clutter_stage_set_title (CLUTTER_STAGE (stage), "Easing Modes");
230   clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue);
231   g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
232   main_stage = stage;
233 
234   clutter_actor_get_size (stage, &stage_width, &stage_height);
235 
236   /* create the actor that we want to tween */
237   rect = make_bouncer (50, 50);
238   clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
239   clutter_actor_set_position (rect, stage_width / 2, stage_height / 2);
240 
241   text = g_strdup_printf (HELP_TEXT,
242                           easing_modes[current_mode].name,
243                           current_mode + 1,
244                           n_easing_modes);
245 
246   label = clutter_text_new ();
247   clutter_container_add_actor (CLUTTER_CONTAINER (stage), label);
248   clutter_text_set_text (CLUTTER_TEXT (label), text);
249   clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.95));
250   clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95));
251   easing_mode_label = label;
252 
253   g_free (text);
254 
255   g_signal_connect (stage,
256                     "button-press-event", G_CALLBACK (on_button_press),
257                     rect);
258 
259   clutter_actor_show (stage);
260 
261   clutter_main ();
262 
263   return EXIT_SUCCESS;
264 }
265 
266 G_MODULE_EXPORT const char *
test_easing_describe(void)267 test_easing_describe (void)
268 {
269   return "Visualize all easing modes provided by Clutter";
270 }
271