1 /*
2  * Load an image into a texture, which can then be zoomed in/out
3  * (double click on button 1, double click on button 3 respectively);
4  * also resets the texture to the stage center when a key is pressed
5  * (better would be to prevent drags taking the actor off-stage,
6  * but the implementation is much more complicated)
7  */
8 #include <stdlib.h>
9 #include <clutter/clutter.h>
10 
11 #define STAGE_SIDE 400.0
12 
13 static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
14 
15 /* on key press, center the actor on the stage;
16  * useful if you drag it off-stage accidentally
17  */
18 static gboolean
key_press_cb(ClutterActor * actor,ClutterEvent * event,gpointer user_data)19 key_press_cb (ClutterActor *actor,
20               ClutterEvent *event,
21               gpointer      user_data)
22 {
23   gfloat width, height;
24 
25   clutter_actor_get_size (actor, &width, &height);
26 
27   clutter_actor_set_anchor_point (actor, width / 2, height / 2);
28 
29   clutter_actor_set_position (actor,
30                               STAGE_SIDE / 2,
31                               STAGE_SIDE / 2);
32 
33   return TRUE;
34 }
35 
36 /* on double click, zoom in on the clicked point;
37  * also keeps scale in the range 0.1 to 20
38  */
39 static gboolean
clicked_cb(ClutterActor * actor,ClutterEvent * event,gpointer user_data)40 clicked_cb (ClutterActor *actor,
41             ClutterEvent *event,
42             gpointer      user_data)
43 {
44   gdouble scale;
45   gfloat click_x, click_y;
46   gfloat click_target_x, click_target_y;
47   guint32 button;
48 
49   /* don't do anything unless there was a double click */
50   if (clutter_event_get_click_count (event) < 2)
51     return TRUE;
52 
53   /* work out new scale */
54   button = clutter_event_get_button (event);
55 
56   clutter_actor_get_scale (actor, &scale, NULL);
57 
58   if (button == CLUTTER_BUTTON_PRIMARY)
59     scale *= 1.2;
60   else if (button == CLUTTER_BUTTON_SECONDARY)
61     scale /= 1.2;
62 
63   /* don't do anything if scale is outside bounds */
64   if (scale < 0.1 || scale > 20.0)
65     return TRUE;
66 
67   /* get the location of the click on the scaled actor */
68   clutter_event_get_coords (event, &click_x, &click_y);
69   clutter_actor_transform_stage_point (actor,
70                                        click_x, click_y,
71                                        &click_target_x, &click_target_y);
72 
73   /* anchor the actor on the clicked point on its surface */
74   clutter_actor_set_anchor_point (actor, click_target_x, click_target_y);
75 
76   /* set the actor's position to the click coords: it won't move,
77    * because the anchor point is already there; but
78    * the scale will now be centered on these coords (as the
79    * scale center defaults to the anchor point); so the anchor point
80    * on the actor won't move from under the pointer
81    */
82   clutter_actor_set_position (actor, click_x, click_y);
83 
84   clutter_actor_animate (actor, CLUTTER_LINEAR, 500,
85                          "scale-x", scale,
86                          "scale-y", scale,
87                          NULL);
88 
89   return TRUE;
90 }
91 
92 int
main(int argc,char * argv[])93 main (int   argc,
94       char *argv[])
95 {
96   ClutterActor *stage;
97   ClutterActor *texture;
98   gchar *image_path;
99   GError *error = NULL;
100 
101   if (argc < 2)
102     {
103       g_print ("Usage: %s <path to image file>\n", argv[0]);
104       exit (EXIT_FAILURE);
105     }
106 
107   image_path = argv[1];
108 
109   if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
110     return 1;
111 
112   stage = clutter_stage_new ();
113   clutter_actor_set_size (stage, STAGE_SIDE, STAGE_SIDE);
114   clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
115   g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
116 
117   texture = clutter_texture_new ();
118   clutter_actor_set_reactive (texture, TRUE);
119   clutter_actor_set_width (texture, STAGE_SIDE);
120   clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture), TRUE);
121 
122   clutter_actor_add_action (texture, clutter_drag_action_new ());
123 
124   g_object_set (G_OBJECT (texture),
125                 "scale-gravity", CLUTTER_GRAVITY_NORTH_WEST,
126                 NULL);
127 
128   clutter_texture_set_from_file (CLUTTER_TEXTURE (texture), image_path, &error);
129 
130   if (error != NULL)
131     {
132       g_warning ("Error loading %s\n%s", image_path, error->message);
133       g_error_free (error);
134       exit (EXIT_FAILURE);
135     }
136 
137   clutter_actor_set_y (texture, (STAGE_SIDE - clutter_actor_get_height (texture)) * 0.5);
138 
139   g_signal_connect (texture,
140                     "button-release-event",
141                     G_CALLBACK (clicked_cb),
142                     NULL);
143 
144   g_signal_connect_swapped (stage,
145                             "key-press-event",
146                             G_CALLBACK (key_press_cb),
147                             texture);
148 
149   clutter_container_add_actor (CLUTTER_CONTAINER (stage), texture);
150 
151   clutter_actor_show (stage);
152 
153   clutter_main ();
154 
155   return EXIT_SUCCESS;
156 }
157