1 #include <stdlib.h>
2 #include <clutter/clutter.h>
3 
4 static gboolean
on_enter(ClutterActor * actor,ClutterEvent * event)5 on_enter (ClutterActor *actor,
6           ClutterEvent *event)
7 {
8   ClutterTransition *t;
9 
10   t = clutter_actor_get_transition (actor, "curl");
11   if (t == NULL)
12     {
13       t = clutter_property_transition_new ("@effects.curl.period");
14       clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
15       clutter_actor_add_transition (actor, "curl", t);
16       g_object_unref (t);
17     }
18 
19   clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.0);
20   clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.25);
21   clutter_timeline_rewind (CLUTTER_TIMELINE (t));
22   clutter_timeline_start (CLUTTER_TIMELINE (t));
23 
24   return CLUTTER_EVENT_STOP;
25 }
26 
27 static gboolean
on_leave(ClutterActor * actor,ClutterEvent * event)28 on_leave (ClutterActor *actor,
29           ClutterEvent *event)
30 {
31   ClutterTransition *t;
32 
33   t = clutter_actor_get_transition (actor, "curl");
34   if (t == NULL)
35     {
36       t = clutter_property_transition_new ("@effects.curl.period");
37       clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
38       clutter_actor_add_transition (actor, "curl", t);
39       g_object_unref (t);
40     }
41 
42   clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.25);
43   clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.0);
44   clutter_timeline_rewind (CLUTTER_TIMELINE (t));
45   clutter_timeline_start (CLUTTER_TIMELINE (t));
46 
47   return CLUTTER_EVENT_STOP;
48 }
49 
50 static void
on_drag_begin(ClutterDragAction * action,ClutterActor * actor,gfloat event_x,gfloat event_y,ClutterModifierType modifiers)51 on_drag_begin (ClutterDragAction   *action,
52                ClutterActor        *actor,
53                gfloat               event_x,
54                gfloat               event_y,
55                ClutterModifierType  modifiers)
56 {
57   gboolean is_copy = (modifiers & CLUTTER_SHIFT_MASK) ? TRUE : FALSE;
58   ClutterActor *drag_handle = NULL;
59   ClutterTransition *t;
60 
61   if (is_copy)
62     {
63       ClutterActor *stage = clutter_actor_get_stage (actor);
64 
65       drag_handle = clutter_actor_new ();
66       clutter_actor_set_size (drag_handle, 48, 48);
67 
68       clutter_actor_set_background_color (drag_handle, CLUTTER_COLOR_DarkSkyBlue);
69 
70       clutter_actor_add_child (stage, drag_handle);
71       clutter_actor_set_position (drag_handle, event_x, event_y);
72     }
73   else
74     drag_handle = actor;
75 
76   clutter_drag_action_set_drag_handle (action, drag_handle);
77 
78   /* fully desaturate the actor */
79   t = clutter_actor_get_transition (actor, "disable");
80   if (t == NULL)
81     {
82       t = clutter_property_transition_new ("@effects.disable.factor");
83       clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
84       clutter_actor_add_transition (actor, "disable", t);
85       g_object_unref (t);
86     }
87 
88   clutter_transition_set_from (t, G_TYPE_DOUBLE, 0.0);
89   clutter_transition_set_to (t, G_TYPE_DOUBLE, 1.0);
90   clutter_timeline_rewind (CLUTTER_TIMELINE (t));
91   clutter_timeline_start (CLUTTER_TIMELINE (t));
92 }
93 
94 static void
on_drag_end(ClutterDragAction * action,ClutterActor * actor,gfloat event_x,gfloat event_y,ClutterModifierType modifiers)95 on_drag_end (ClutterDragAction   *action,
96              ClutterActor        *actor,
97              gfloat               event_x,
98              gfloat               event_y,
99              ClutterModifierType  modifiers)
100 {
101   ClutterActor *drag_handle;
102   ClutterTransition *t;
103 
104   drag_handle = clutter_drag_action_get_drag_handle (action);
105   if (actor != drag_handle)
106     {
107       gfloat real_x, real_y;
108       ClutterActor *parent;
109 
110       /* if we are dragging a copy we can destroy the copy now
111        * and animate the real actor to the drop coordinates,
112        * transformed in the parent's coordinate space
113        */
114       clutter_actor_save_easing_state (drag_handle);
115       clutter_actor_set_easing_mode (drag_handle, CLUTTER_LINEAR);
116       clutter_actor_set_opacity (drag_handle, 0);
117       clutter_actor_restore_easing_state (drag_handle);
118       g_signal_connect (drag_handle, "transitions-completed",
119                         G_CALLBACK (clutter_actor_destroy),
120                         NULL);
121 
122       parent = clutter_actor_get_parent (actor);
123       clutter_actor_transform_stage_point (parent, event_x, event_y,
124                                            &real_x,
125                                            &real_y);
126 
127       clutter_actor_save_easing_state (actor);
128       clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC);
129       clutter_actor_set_position (actor, real_x, real_y);
130       clutter_actor_restore_easing_state (actor);
131     }
132 
133   t = clutter_actor_get_transition (actor, "disable");
134   if (t == NULL)
135     {
136       t = clutter_property_transition_new ("@effects.disable.factor");
137       clutter_timeline_set_duration (CLUTTER_TIMELINE (t), 250);
138       clutter_actor_add_transition (actor, "disable", t);
139       g_object_unref (t);
140     }
141 
142   clutter_transition_set_from (t, G_TYPE_DOUBLE, 1.0);
143   clutter_transition_set_to (t, G_TYPE_DOUBLE, 0.0);
144   clutter_timeline_rewind (CLUTTER_TIMELINE (t));
145   clutter_timeline_start (CLUTTER_TIMELINE (t));
146 }
147 
148 static ClutterDragAxis
get_drag_axis(const gchar * str)149 get_drag_axis (const gchar *str)
150 {
151   if (str == NULL || *str == '\0')
152     return CLUTTER_DRAG_AXIS_NONE;
153 
154   if (*str == 'x' || *str == 'X')
155     return CLUTTER_DRAG_X_AXIS;
156 
157   if (*str == 'y' || *str == 'Y')
158     return CLUTTER_DRAG_Y_AXIS;
159 
160   g_warn_if_reached ();
161 
162   return CLUTTER_DRAG_AXIS_NONE;
163 }
164 
165 static gchar *drag_axis = NULL;
166 static gint x_drag_threshold = 0;
167 static gint y_drag_threshold = 0;
168 
169 static GOptionEntry entries[] = {
170   {
171     "x-threshold", 'x',
172     0,
173     G_OPTION_ARG_INT,
174     &x_drag_threshold,
175     "Set the horizontal drag threshold", "PIXELS"
176   },
177   {
178     "y-threshold", 'y',
179     0,
180     G_OPTION_ARG_INT,
181     &y_drag_threshold,
182     "Set the vertical drag threshold", "PIXELS"
183   },
184   {
185     "axis", 'a',
186     0,
187     G_OPTION_ARG_STRING,
188     &drag_axis,
189     "Set the drag axis", "AXIS"
190   },
191 
192   { NULL }
193 };
194 
195 int
main(int argc,char * argv[])196 main (int argc, char *argv[])
197 {
198   ClutterActor *stage, *handle;
199   ClutterAction *action;
200   GError *error;
201 
202   error = NULL;
203   if (clutter_init_with_args (&argc, &argv,
204                               "test-drag",
205                               entries,
206                               NULL,
207                               &error) != CLUTTER_INIT_SUCCESS)
208     {
209       g_print ("Unable to run drag-action: %s\n", error->message);
210       g_error_free (error);
211 
212       return EXIT_FAILURE;
213     }
214 
215   stage = clutter_stage_new ();
216   clutter_stage_set_title (CLUTTER_STAGE (stage), "Drag Test");
217   clutter_actor_set_size (stage, 800, 600);
218   g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
219 
220   handle = clutter_actor_new ();
221   clutter_actor_set_background_color (handle, CLUTTER_COLOR_SkyBlue);
222   clutter_actor_set_size (handle, 128, 128);
223   clutter_actor_set_position (handle, (800 - 128) / 2, (600 - 128) / 2);
224   clutter_actor_set_reactive (handle, TRUE);
225   clutter_actor_add_child (stage, handle);
226   g_signal_connect (handle, "enter-event", G_CALLBACK (on_enter), NULL);
227   g_signal_connect (handle, "leave-event", G_CALLBACK (on_leave), NULL);
228 
229   action = clutter_drag_action_new ();
230   clutter_drag_action_set_drag_threshold (CLUTTER_DRAG_ACTION (action),
231                                           x_drag_threshold,
232                                           y_drag_threshold);
233   clutter_drag_action_set_drag_axis (CLUTTER_DRAG_ACTION (action),
234                                      get_drag_axis (drag_axis));
235 
236   g_signal_connect (action, "drag-begin", G_CALLBACK (on_drag_begin), NULL);
237   g_signal_connect (action, "drag-end", G_CALLBACK (on_drag_end), NULL);
238 
239   clutter_actor_add_action (handle, action);
240 
241   clutter_actor_add_effect_with_name (handle, "disable", clutter_desaturate_effect_new (0.0));
242   clutter_actor_add_effect_with_name (handle, "curl", clutter_page_turn_effect_new (0.0, 45.0, 12.0));
243 
244   clutter_actor_show (stage);
245 
246   clutter_main ();
247 
248   return EXIT_SUCCESS;
249 }
250