Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-scroll-actor.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2012  Intel Corporation
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * SECTION:clutter-scroll-actor
24  * @Title: ClutterScrollActor
25  * @Short_Description: An actor for displaying a portion of its children
26  *
27  * #ClutterScrollActor is an actor that can be used to display a portion
28  * of the contents of its children.
29  *
30  * The extent of the area of a #ClutterScrollActor is defined by the size
31  * of its children; the visible region of the children of a #ClutterScrollActor
32  * is set by using clutter_scroll_actor_scroll_to_point() or by using
33  * clutter_scroll_actor_scroll_to_rect() to define a point or a rectangle
34  * acting as the origin, respectively.
35  *
36  * #ClutterScrollActor does not provide pointer or keyboard event handling,
37  * nor does it provide visible scroll handles.
38  *
39  * <informalexample>
40  *  <programlisting>
41  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/scroll-actor.c">
42  *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
43  * </xi:include>
44  *  </programlisting>
45  * </informalexample>
46  *
47  * #ClutterScrollActor is available since Clutter 1.12.
48  */
49
50 #ifdef HAVE_CONFIG_H
51 #include "config.h"
52 #endif
53
54 #include "clutter-scroll-actor.h"
55
56 #include "clutter-actor-private.h"
57 #include "clutter-animatable.h"
58 #include "clutter-debug.h"
59 #include "clutter-enum-types.h"
60 #include "clutter-private.h"
61 #include "clutter-property-transition.h"
62 #include "clutter-transition.h"
63
64 struct _ClutterScrollActorPrivate
65 {
66   ClutterPoint scroll_to;
67
68   ClutterScrollMode scroll_mode;
69
70   ClutterTransition *transition;
71 };
72
73 enum
74 {
75   PROP_0,
76
77   PROP_SCROLL_MODE,
78
79   PROP_LAST
80 };
81
82 enum
83 {
84   ANIM_PROP_0,
85
86   ANIM_PROP_SCROLL_TO,
87
88   ANIM_PROP_LAST
89 };
90
91 static GParamSpec *obj_props[PROP_LAST] = { NULL, };
92 static GParamSpec *animatable_props[ANIM_PROP_LAST] = { NULL, };
93
94 static ClutterAnimatableIface *parent_animatable_iface = NULL;
95
96 static void     clutter_animatable_iface_init   (ClutterAnimatableIface *iface);
97
98 G_DEFINE_TYPE_WITH_CODE (ClutterScrollActor, clutter_scroll_actor, CLUTTER_TYPE_ACTOR,
99                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
100                                                 clutter_animatable_iface_init))
101
102 static void
103 clutter_scroll_actor_apply_transform (ClutterActor *actor,
104                                       CoglMatrix   *transform)
105 {
106   ClutterScrollActorPrivate *priv = CLUTTER_SCROLL_ACTOR (actor)->priv;
107   float x_factor, y_factor;
108
109   CLUTTER_ACTOR_CLASS (clutter_scroll_actor_parent_class)->apply_transform (actor, transform);
110
111   if (priv->scroll_mode & CLUTTER_SCROLL_HORIZONTALLY)
112     x_factor = -priv->scroll_to.x;
113   else
114     x_factor = 0.f;
115
116   if (priv->scroll_mode & CLUTTER_SCROLL_VERTICALLY)
117     y_factor = -priv->scroll_to.y;
118   else
119     y_factor = 0.f;
120
121   cogl_matrix_translate (transform, x_factor, y_factor, 0.0f);
122 }
123
124 static inline void
125 clutter_scroll_actor_push_clip (ClutterActor *actor)
126 {
127   ClutterScrollActorPrivate *priv = CLUTTER_SCROLL_ACTOR (actor)->priv;
128   ClutterActorBox allocation;
129   float width, height;
130   float x, y;
131
132   clutter_actor_get_allocation_box (actor, &allocation);
133   clutter_actor_box_get_size (&allocation, &width, &height);
134
135   if (priv->scroll_mode & CLUTTER_SCROLL_HORIZONTALLY)
136     x = priv->scroll_to.x;
137   else
138     x = 0.f;
139
140   if (priv->scroll_mode & CLUTTER_SCROLL_VERTICALLY)
141     y = priv->scroll_to.y;
142   else
143     y = 0.f;
144
145   /* offset the clip so that we keep it at the right place */
146   cogl_clip_push_rectangle (x,
147                             y,
148                             x + width,
149                             y + height);
150 }
151
152 static void
153 clutter_scroll_actor_paint (ClutterActor *actor)
154 {
155   clutter_scroll_actor_push_clip (actor);
156
157   CLUTTER_ACTOR_CLASS (clutter_scroll_actor_parent_class)->paint (actor);
158
159   cogl_clip_pop ();
160 }
161
162 static void
163 clutter_scroll_actor_pick (ClutterActor       *actor,
164                            const ClutterColor *pick_color)
165 {
166   ClutterActorIter iter;
167   ClutterActor *child;
168
169   clutter_scroll_actor_push_clip (actor);
170
171   CLUTTER_ACTOR_CLASS (clutter_scroll_actor_parent_class)->pick (actor, pick_color);
172
173   /* FIXME - this has to go away when we remove the vfunc check inside
174    * the ClutterActor::pick default implementation
175    */
176   clutter_actor_iter_init (&iter, actor);
177   while (clutter_actor_iter_next (&iter, &child))
178     clutter_actor_paint (child);
179
180   cogl_clip_pop ();
181 }
182
183 static void
184 clutter_scroll_actor_set_scroll_to_internal (ClutterScrollActor *self,
185                                              const ClutterPoint *point)
186 {
187   ClutterScrollActorPrivate *priv = self->priv;
188   ClutterActor *actor = CLUTTER_ACTOR (self);
189
190   if (clutter_point_equals (&priv->scroll_to, point))
191     return;
192
193   if (point == NULL)
194     clutter_point_init (&priv->scroll_to, 0.f, 0.f);
195   else
196     priv->scroll_to = *point;
197
198   clutter_actor_queue_redraw (actor);
199 }
200
201 static void
202 clutter_scroll_actor_set_property (GObject      *gobject,
203                                    guint         prop_id,
204                                    const GValue *value,
205                                    GParamSpec   *pspec)
206 {
207   ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject);
208
209   switch (prop_id)
210     {
211     case PROP_SCROLL_MODE:
212       clutter_scroll_actor_set_scroll_mode (actor, g_value_get_flags (value));
213       break;
214
215     default:
216       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
217     }
218 }
219
220 static void
221 clutter_scroll_actor_get_property (GObject    *gobject,
222                                    guint       prop_id,
223                                    GValue     *value,
224                                    GParamSpec *pspec)
225 {
226   ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject);
227
228   switch (prop_id)
229     {
230     case PROP_SCROLL_MODE:
231       g_value_set_flags (value, actor->priv->scroll_mode);
232       break;
233
234     default:
235       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
236     }
237 }
238
239 static void
240 clutter_scroll_actor_class_init (ClutterScrollActorClass *klass)
241 {
242   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
243   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
244
245   g_type_class_add_private (klass, sizeof (ClutterScrollActorPrivate));
246
247   gobject_class->set_property = clutter_scroll_actor_set_property;
248   gobject_class->get_property = clutter_scroll_actor_get_property;
249
250   actor_class->apply_transform = clutter_scroll_actor_apply_transform;
251   actor_class->paint = clutter_scroll_actor_paint;
252   actor_class->pick = clutter_scroll_actor_pick;
253
254   /**
255    * ClutterScrollActor:scroll-mode:
256    *
257    * The scrollin direction.
258    *
259    * Since: 1.12
260    */
261   obj_props[PROP_SCROLL_MODE] =
262     g_param_spec_flags ("scroll-mode",
263                         P_("Scroll Mode"),
264                         P_("The scrolling direction"),
265                         CLUTTER_TYPE_SCROLL_MODE,
266                         CLUTTER_SCROLL_BOTH,
267                         G_PARAM_READWRITE |
268                         G_PARAM_STATIC_STRINGS);
269
270   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
271 }
272
273 static void
274 clutter_scroll_actor_init (ClutterScrollActor *self)
275 {
276   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_SCROLL_ACTOR,
277                                             ClutterScrollActorPrivate);
278
279   self->priv->scroll_mode = CLUTTER_SCROLL_BOTH;
280 }
281
282 static GParamSpec *
283 clutter_scroll_actor_find_property (ClutterAnimatable *animatable,
284                                     const char        *property_name)
285 {
286   if (strcmp (property_name, "scroll-to") == 0)
287     return animatable_props[ANIM_PROP_SCROLL_TO];
288
289   return parent_animatable_iface->find_property (animatable, property_name);
290 }
291
292 static void
293 clutter_scroll_actor_set_final_state (ClutterAnimatable *animatable,
294                                       const char        *property_name,
295                                       const GValue      *value)
296 {
297   if (strcmp (property_name, "scroll-to") == 0)
298     {
299       ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable);
300       const ClutterPoint *point = g_value_get_boxed (value);
301
302       clutter_scroll_actor_set_scroll_to_internal (self, point);
303     }
304   else
305     parent_animatable_iface->set_final_state (animatable, property_name, value);
306 }
307
308 static void
309 clutter_scroll_actor_get_initial_state (ClutterAnimatable *animatable,
310                                         const char        *property_name,
311                                         GValue            *value)
312 {
313   if (strcmp (property_name, "scroll-to") == 0)
314     {
315       ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable);
316
317       g_value_set_boxed (value, &self->priv->scroll_to);
318     }
319   else
320     parent_animatable_iface->get_initial_state (animatable, property_name, value);
321 }
322
323 static void
324 clutter_animatable_iface_init (ClutterAnimatableIface *iface)
325 {
326   parent_animatable_iface = g_type_interface_peek_parent (iface);
327
328   animatable_props[ANIM_PROP_SCROLL_TO] =
329     g_param_spec_boxed ("scroll-to",
330                         "Scroll To",
331                         "The point to scroll the actor to",
332                         CLUTTER_TYPE_POINT,
333                         G_PARAM_READWRITE |
334                         G_PARAM_STATIC_STRINGS |
335                         CLUTTER_PARAM_ANIMATABLE);
336
337   iface->find_property = clutter_scroll_actor_find_property;
338   iface->get_initial_state = clutter_scroll_actor_get_initial_state;
339   iface->set_final_state = clutter_scroll_actor_set_final_state;
340 }
341
342 /**
343  * clutter_scroll_actor_new:
344  *
345  * Creates a new #ClutterScrollActor.
346  *
347  * Return value: (transfer full): The newly created #ClutterScrollActor
348  *   instance.
349  *
350  * Since: 1.12
351  */
352 ClutterActor *
353 clutter_scroll_actor_new (void)
354 {
355   return g_object_new (CLUTTER_TYPE_SCROLL_ACTOR, NULL);
356 }
357
358 /**
359  * clutter_scroll_actor_set_scroll_mode:
360  * @actor: a #ClutterScrollActor
361  * @mode: a #ClutterScrollMode
362  *
363  * Sets the #ClutterScrollActor:scroll-mode property.
364  *
365  * Since: 1.12
366  */
367 void
368 clutter_scroll_actor_set_scroll_mode (ClutterScrollActor *actor,
369                                       ClutterScrollMode   mode)
370 {
371   ClutterScrollActorPrivate *priv;
372
373   g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
374
375   priv = actor->priv;
376
377   if (priv->scroll_mode == mode)
378     return;
379
380   priv->scroll_mode = mode;
381
382   g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_SCROLL_MODE]);
383 }
384
385 /**
386  * clutter_scroll_actor_get_scroll_mode:
387  * @actor: a #ClutterScrollActor
388  *
389  * Retrieves the #ClutterScrollActor:scroll-mode property
390  *
391  * Return value: the scrolling mode
392  *
393  * Since: 1.12
394  */
395 ClutterScrollMode
396 clutter_scroll_actor_get_scroll_mode (ClutterScrollActor *actor)
397 {
398   g_return_val_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor), CLUTTER_SCROLL_NONE);
399
400   return actor->priv->scroll_mode;
401 }
402
403 /**
404  * clutter_scroll_actor_scroll_to_point:
405  * @actor: a #ClutterScrollActor
406  * @point: a #ClutterPoint
407  *
408  * Scrolls the contents of @actor so that @point is the new origin
409  * of the visible area.
410  *
411  * The coordinates of @point must be relative to the @actor.
412  *
413  * This function will use the currently set easing state of the @actor
414  * to transition from the current scroll origin to the new one.
415  *
416  * Since: 1.12
417  */
418 void
419 clutter_scroll_actor_scroll_to_point (ClutterScrollActor *actor,
420                                       const ClutterPoint *point)
421 {
422   ClutterScrollActorPrivate *priv;
423   const ClutterAnimationInfo *info;
424
425   g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
426   g_return_if_fail (point != NULL);
427
428   priv = actor->priv;
429
430   info = _clutter_actor_get_animation_info (CLUTTER_ACTOR (actor));
431
432   /* jump to the end if there is no easing state, or if the easing
433    * state has a duration of 0 msecs
434    */
435   if (info->cur_state == NULL ||
436       info->cur_state->easing_duration == 0)
437     {
438       /* ensure that we remove any currently running transition */
439       if (priv->transition != NULL)
440         {
441           clutter_actor_remove_transition (CLUTTER_ACTOR (actor),
442                                            "scroll-to");
443           priv->transition = NULL;
444         }
445
446       clutter_scroll_actor_set_scroll_to_internal (actor, point);
447
448       return;
449     }
450
451   if (priv->transition == NULL)
452     {
453       priv->transition = clutter_property_transition_new ("scroll-to");
454       clutter_transition_set_animatable (priv->transition,
455                                          CLUTTER_ANIMATABLE (actor));
456       clutter_transition_set_remove_on_complete (priv->transition, TRUE);
457
458       /* delay only makes sense if the transition has just been created */
459       clutter_timeline_set_delay (CLUTTER_TIMELINE (priv->transition),
460                                   info->cur_state->easing_delay);
461       /* we need this to clear the priv->transition pointer */
462       g_object_add_weak_pointer (G_OBJECT (priv->transition), (gpointer *) &priv->transition);
463
464       clutter_actor_add_transition (CLUTTER_ACTOR (actor),
465                                     "scroll-to",
466                                     priv->transition);
467
468       /* the actor now owns the transition */
469       g_object_unref (priv->transition);
470     }
471
472   /* if a transition already exist, update its bounds */
473   clutter_transition_set_from (priv->transition,
474                                CLUTTER_TYPE_POINT,
475                                &priv->scroll_to);
476   clutter_transition_set_to (priv->transition,
477                              CLUTTER_TYPE_POINT,
478                              point);
479
480   /* always use the current easing state */
481   clutter_timeline_set_duration (CLUTTER_TIMELINE (priv->transition),
482                                  info->cur_state->easing_duration);
483   clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (priv->transition),
484                                       info->cur_state->easing_mode);
485
486   /* ensure that we start from the beginning */
487   clutter_timeline_rewind (CLUTTER_TIMELINE (priv->transition));
488   clutter_timeline_start (CLUTTER_TIMELINE (priv->transition));
489 }
490
491 /**
492  * clutter_scroll_actor_scroll_to_rect:
493  * @actor: a #ClutterScrollActor
494  * @rect: a #ClutterRect
495  *
496  * Scrolls @actor so that @rect is in the visible portion.
497  *
498  * Since: 1.12
499  */
500 void
501 clutter_scroll_actor_scroll_to_rect (ClutterScrollActor *actor,
502                                      const ClutterRect  *rect)
503 {
504   ClutterRect n_rect;
505
506   g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
507   g_return_if_fail (rect != NULL);
508
509   n_rect = *rect;
510
511   /* normalize, so that we have a valid origin */
512   clutter_rect_normalize (&n_rect);
513
514   clutter_scroll_actor_scroll_to_point (actor, &n_rect.origin);
515 }