[clutter/master-next: 46/51] Add ClutterScrollActor



commit be2f25eb92692a37e93cfd3d906c7c629a2384c5
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Thu Apr 12 15:56:53 2012 +0100

    Add ClutterScrollActor
    
    ClutterScrollActor is an actor that allows showing a portion of its
    contents.

 clutter/Makefile.am                   |    2 +
 clutter/clutter-enums.h               |   20 ++
 clutter/clutter-scroll-actor.c        |  478 +++++++++++++++++++++++++++++++++
 clutter/clutter-scroll-actor.h        |   97 +++++++
 clutter/clutter-types.h               |    1 +
 clutter/clutter.h                     |    1 +
 clutter/clutter.symbols               |    7 +
 tests/interactive/Makefile.am         |    3 +-
 tests/interactive/test-scroll-actor.c |  179 ++++++++++++
 9 files changed, 787 insertions(+), 1 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index d59afd1..e52edb2 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -108,6 +108,7 @@ source_h =					\
 	$(srcdir)/clutter-property-transition.h	\
 	$(srcdir)/clutter-script.h		\
 	$(srcdir)/clutter-scriptable.h		\
+	$(srcdir)/clutter-scroll-actor.h	\
 	$(srcdir)/clutter-settings.h		\
 	$(srcdir)/clutter-shader-effect.h	\
 	$(srcdir)/clutter-shader-types.h	\
@@ -186,6 +187,7 @@ source_c = \
 	$(srcdir)/clutter-script.c		\
 	$(srcdir)/clutter-script-parser.c	\
 	$(srcdir)/clutter-scriptable.c		\
+	$(srcdir)/clutter-scroll-actor.c	\
 	$(srcdir)/clutter-settings.c		\
 	$(srcdir)/clutter-shader-effect.c	\
 	$(srcdir)/clutter-shader-types.c	\
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index 2c3d61e..8f5bd0d 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -1213,6 +1213,26 @@ typedef enum {
   CLUTTER_ORIENTATION_VERTICAL
 } ClutterOrientation;
 
+/**
+ * ClutterScrollMode:
+ * @CLUTTER_SCROLL_NONE: Ignore scrolling
+ * @CLUTTER_SCROLL_HORIZONTALLY: Scroll only horizontally
+ * @CLUTTER_SCROLL_VERTICALLY: Scroll only vertically
+ * @CLUTTER_SCROLL_BOTH: Scroll in both directions
+ *
+ * Scroll modes.
+ *
+ * Since: 1.12
+ */
+typedef enum { /*< prefix=CLUTTER_SCROLL >*/
+  CLUTTER_SCROLL_NONE         = 0,
+
+  CLUTTER_SCROLL_HORIZONTALLY = 1 << 0,
+  CLUTTER_SCROLL_VERTICALLY   = 1 << 1,
+
+  CLUTTER_SCROLL_BOTH         = CLUTTER_SCROLL_HORIZONTALLY | CLUTTER_SCROLL_VERTICALLY
+} ClutterScrollMode;
+
 G_END_DECLS
 
 #endif /* __CLUTTER_ENUMS_H__ */
diff --git a/clutter/clutter-scroll-actor.c b/clutter/clutter-scroll-actor.c
new file mode 100644
index 0000000..4d78da1
--- /dev/null
+++ b/clutter/clutter-scroll-actor.c
@@ -0,0 +1,478 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2012  Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:clutter-scroll-actor
+ * @Title: ClutterScrollActor
+ * @Short_Description: An actor for displaying a portion of its children
+ *
+ * #ClutterScrollActor is an actor that can be used to display a portion
+ * of the contents of its children.
+ *
+ * The extent of the area of a #ClutterScrollActor is defined by the size
+ * of its children; the visible region of the children of a #ClutterScrollActor
+ * is set by using clutter_scroll_actor_scroll_to_point() or by using
+ * clutter_scroll_actor_scroll_to_rect() to define a point or a rectangle
+ * acting as the origin, respectively.
+ *
+ * #ClutterScrollActor does not provide pointer or keyboard event handling,
+ * nor does it provide visible scroll handles.
+ *
+ * #ClutterScrollActor is available since Clutter 1.12.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-scroll-actor.h"
+
+#include "clutter-actor-private.h"
+#include "clutter-animatable.h"
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-private.h"
+#include "clutter-property-transition.h"
+#include "clutter-transition.h"
+
+struct _ClutterScrollActorPrivate
+{
+  ClutterPoint scroll_to;
+
+  ClutterScrollMode scroll_mode;
+
+  ClutterTransition *transition;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_SCROLL_MODE,
+
+  PROP_LAST
+};
+
+enum
+{
+  ANIM_PROP_0,
+
+  ANIM_PROP_SCROLL_TO,
+
+  ANIM_PROP_LAST
+};
+
+static GParamSpec *obj_props[PROP_LAST] = { NULL, };
+static GParamSpec *animatable_props[ANIM_PROP_LAST] = { NULL, };
+
+static ClutterAnimatableIface *parent_animatable_iface = NULL;
+
+static void     clutter_animatable_iface_init   (ClutterAnimatableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterScrollActor, clutter_scroll_actor, CLUTTER_TYPE_ACTOR,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE,
+                                                clutter_animatable_iface_init))
+
+static void
+clutter_scroll_actor_apply_transform (ClutterActor *actor,
+                                      CoglMatrix   *transform)
+{
+  ClutterScrollActorPrivate *priv = CLUTTER_SCROLL_ACTOR (actor)->priv;
+  float x_factor, y_factor;
+
+  CLUTTER_ACTOR_CLASS (clutter_scroll_actor_parent_class)->apply_transform (actor, transform);
+
+  if (priv->scroll_mode & CLUTTER_SCROLL_HORIZONTALLY)
+    x_factor = -priv->scroll_to.x;
+  else
+    x_factor = 0.f;
+
+  if (priv->scroll_mode & CLUTTER_SCROLL_VERTICALLY)
+    y_factor = -priv->scroll_to.y;
+  else
+    y_factor = 0.f;
+
+  cogl_matrix_translate (transform, x_factor, y_factor, 0.0f);
+}
+
+static void
+clutter_scroll_actor_paint (ClutterActor *actor)
+{
+  ClutterScrollActorPrivate *priv = CLUTTER_SCROLL_ACTOR (actor)->priv;
+  ClutterActorBox allocation;
+  float width, height;
+  float x, y;
+
+  clutter_actor_get_allocation_box (actor, &allocation);
+  clutter_actor_box_get_size (&allocation, &width, &height);
+
+  if (priv->scroll_mode & CLUTTER_SCROLL_HORIZONTALLY)
+    x = priv->scroll_to.x;
+  else
+    x = 0.f;
+
+  if (priv->scroll_mode & CLUTTER_SCROLL_VERTICALLY)
+    y = priv->scroll_to.y;
+  else
+    y = 0.f;
+
+  /* offset the clip so that we keep it at the right place */
+  cogl_clip_push_rectangle (x,
+                            y,
+                            x + width,
+                            y + height);
+
+  CLUTTER_ACTOR_CLASS (clutter_scroll_actor_parent_class)->paint (actor);
+
+  cogl_clip_pop ();
+}
+
+static void
+clutter_scroll_actor_set_property (GObject      *gobject,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_SCROLL_MODE:
+      clutter_scroll_actor_set_scroll_mode (actor, g_value_get_flags (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+clutter_scroll_actor_get_property (GObject    *gobject,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_SCROLL_MODE:
+      g_value_set_flags (value, actor->priv->scroll_mode);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+clutter_scroll_actor_class_init (ClutterScrollActorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ClutterScrollActorPrivate));
+
+  gobject_class->set_property = clutter_scroll_actor_set_property;
+  gobject_class->get_property = clutter_scroll_actor_get_property;
+
+  actor_class->apply_transform = clutter_scroll_actor_apply_transform;
+  actor_class->paint = clutter_scroll_actor_paint;
+
+  /**
+   * ClutterScrollActor:scroll-mode:
+   *
+   * The scrollin direction.
+   *
+   * Since: 1.12
+   */
+  obj_props[PROP_SCROLL_MODE] =
+    g_param_spec_flags ("scroll-mode",
+                        P_("Scroll Mode"),
+                        P_("The scrolling direction"),
+                        CLUTTER_TYPE_SCROLL_MODE,
+                        CLUTTER_SCROLL_BOTH,
+                        G_PARAM_READWRITE |
+                        G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
+}
+
+static void
+clutter_scroll_actor_init (ClutterScrollActor *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_SCROLL_ACTOR,
+                                            ClutterScrollActorPrivate);
+
+  self->priv->scroll_mode = CLUTTER_SCROLL_BOTH;
+}
+
+static GParamSpec *
+clutter_scroll_actor_find_property (ClutterAnimatable *animatable,
+                                    const char        *property_name)
+{
+  if (strcmp (property_name, "scroll-to") == 0)
+    return animatable_props[ANIM_PROP_SCROLL_TO];
+
+  return parent_animatable_iface->find_property (animatable, property_name);
+}
+
+static void
+clutter_scroll_actor_set_final_state (ClutterAnimatable *animatable,
+                                      const char        *property_name,
+                                      const GValue      *value)
+{
+  if (strcmp (property_name, "scroll-to") == 0)
+    {
+      ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable);
+      const ClutterPoint *point = g_value_get_boxed (value);
+
+      if (point == NULL)
+        clutter_point_init (&self->priv->scroll_to, 0.f, 0.f);
+      else
+        self->priv->scroll_to = *point;
+
+      clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
+    }
+  else
+    parent_animatable_iface->set_final_state (animatable, property_name, value);
+}
+
+static void
+clutter_scroll_actor_get_initial_state (ClutterAnimatable *animatable,
+                                        const char        *property_name,
+                                        GValue            *value)
+{
+  if (strcmp (property_name, "scroll-to") == 0)
+    {
+      ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable);
+
+      g_value_set_boxed (value, &self->priv->scroll_to);
+    }
+  else
+    parent_animatable_iface->get_initial_state (animatable, property_name, value);
+}
+
+static void
+clutter_animatable_iface_init (ClutterAnimatableIface *iface)
+{
+  parent_animatable_iface = g_type_interface_peek_parent (iface);
+
+  animatable_props[ANIM_PROP_SCROLL_TO] =
+    g_param_spec_boxed ("scroll-to",
+                        "Scroll To",
+                        "The point to scroll the actor to",
+                        CLUTTER_TYPE_POINT,
+                        G_PARAM_READWRITE |
+                        G_PARAM_STATIC_STRINGS |
+                        CLUTTER_PARAM_ANIMATABLE);
+
+  iface->find_property = clutter_scroll_actor_find_property;
+  iface->get_initial_state = clutter_scroll_actor_get_initial_state;
+  iface->set_final_state = clutter_scroll_actor_set_final_state;
+}
+
+/**
+ * clutter_scroll_actor_new:
+ *
+ * Creates a new #ClutterScrollActor.
+ *
+ * Return value: (transfer full): The newly created #ClutterScrollActor
+ *   instance.
+ *
+ * Since: 1.12
+ */
+ClutterActor *
+clutter_scroll_actor_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_SCROLL_ACTOR, NULL);
+}
+
+/**
+ * clutter_scroll_actor_set_scroll_mode:
+ * @actor: a #ClutterScrollActor
+ * @mode: a #ClutterScrollMode
+ *
+ * Sets the #ClutterScrollActor:scroll-mode property.
+ *
+ * Since: 1.12
+ */
+void
+clutter_scroll_actor_set_scroll_mode (ClutterScrollActor *actor,
+                                      ClutterScrollMode   mode)
+{
+  ClutterScrollActorPrivate *priv;
+
+  g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
+
+  priv = actor->priv;
+
+  if (priv->scroll_mode == mode)
+    return;
+
+  priv->scroll_mode = mode;
+
+  g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_SCROLL_MODE]);
+}
+
+/**
+ * clutter_scroll_actor_get_scroll_mode:
+ * @actor: a #ClutterScrollActor
+ *
+ * Retrieves the #ClutterScrollActor:scroll-mode property
+ *
+ * Return value: the scrolling mode
+ *
+ * Since: 1.12
+ */
+ClutterScrollMode
+clutter_scroll_actor_get_scroll_mode (ClutterScrollActor *actor)
+{
+  g_return_val_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor), CLUTTER_SCROLL_NONE);
+
+  return actor->priv->scroll_mode;
+}
+
+static void
+on_transition_completed (ClutterTimeline *timeline,
+                         ClutterScrollActor *actor)
+{
+  actor->priv->transition = NULL;
+}
+
+/**
+ * clutter_scroll_actor_scroll_to_point:
+ * @actor: a #ClutterScrollActor
+ * @point: a #ClutterPoint
+ *
+ * Scrolls the contents of @actor so that @point is the new origin
+ * of the visible area.
+ *
+ * This function will use the currently set easing state of the @actor
+ * to transition from the current scroll origin to the new one.
+ *
+ * Since: 1.12
+ */
+void
+clutter_scroll_actor_scroll_to_point (ClutterScrollActor *actor,
+                                      const ClutterPoint *point)
+{
+  ClutterScrollActorPrivate *priv;
+  const ClutterAnimationInfo *info;
+
+  g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
+  g_return_if_fail (point != NULL);
+
+  priv = actor->priv;
+
+  if (clutter_point_equals (&priv->scroll_to, point))
+    return;
+
+  info = _clutter_actor_get_animation_info (CLUTTER_ACTOR (actor));
+
+  /* jump to the end if there is no easing state, or if the easing
+   * state has a duration of 0 msecs
+   */
+  if (info->cur_state == NULL ||
+      info->cur_state->easing_duration == 0)
+    {
+      /* ensure that we remove any currently running transition */
+      if (priv->transition != NULL)
+        {
+          clutter_actor_remove_transition (CLUTTER_ACTOR (actor),
+                                           "scroll-to");
+          priv->transition = NULL;
+        }
+
+      priv->scroll_to = *point;
+
+      clutter_actor_queue_redraw (CLUTTER_ACTOR (actor));
+
+      return;
+    }
+
+  if (priv->transition == NULL)
+    {
+      priv->transition = clutter_property_transition_new ("scroll-to");
+      clutter_transition_set_animatable (priv->transition,
+                                         CLUTTER_ANIMATABLE (actor));
+      clutter_transition_set_remove_on_complete (priv->transition, TRUE);
+
+      /* delay only makes sense if the transition has just been created */
+      clutter_timeline_set_delay (CLUTTER_TIMELINE (priv->transition),
+                                  info->cur_state->easing_delay);
+      /* we need this to clear the priv->transition pointer */
+      g_signal_connect (priv->transition, "completed",
+                        G_CALLBACK (on_transition_completed),
+                        actor);
+
+      clutter_actor_add_transition (CLUTTER_ACTOR (actor),
+                                    "scroll-to",
+                                    priv->transition);
+
+      /* the actor now owns the transition */
+      g_object_unref (priv->transition);
+    }
+
+  /* if a transition already exist, update its bounds */
+  clutter_transition_set_from (priv->transition,
+                               CLUTTER_TYPE_POINT,
+                               &priv->scroll_to);
+  clutter_transition_set_to (priv->transition,
+                             CLUTTER_TYPE_POINT,
+                             point);
+
+  /* always use the current easing state */
+  clutter_timeline_set_duration (CLUTTER_TIMELINE (priv->transition),
+                                 info->cur_state->easing_duration);
+  clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (priv->transition),
+                                      info->cur_state->easing_mode);
+
+  /* ensure that we start from the beginning */
+  clutter_timeline_rewind (CLUTTER_TIMELINE (priv->transition));
+  clutter_timeline_start (CLUTTER_TIMELINE (priv->transition));
+}
+
+/**
+ * clutter_scroll_actor_scroll_to_rect:
+ * @actor: a #ClutterScrollActor
+ * @rect: a #ClutterRect
+ *
+ * Scrolls @actor so that @rect is in the visible portion.
+ *
+ * Since: 1.12
+ */
+void
+clutter_scroll_actor_scroll_to_rect (ClutterScrollActor *actor,
+                                     const ClutterRect  *rect)
+{
+  ClutterRect n_rect;
+
+  g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor));
+  g_return_if_fail (rect != NULL);
+
+  n_rect = *rect;
+
+  /* normalize, so that we have a valid origin */
+  clutter_rect_normalize (&n_rect);
+
+  clutter_scroll_actor_scroll_to_point (actor, &n_rect.origin);
+}
diff --git a/clutter/clutter-scroll-actor.h b/clutter/clutter-scroll-actor.h
new file mode 100644
index 0000000..cad5081
--- /dev/null
+++ b/clutter/clutter-scroll-actor.h
@@ -0,0 +1,97 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2012  Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_SCROLL_ACTOR_H__
+#define __CLUTTER_SCROLL_ACTOR_H__
+
+#include <clutter/clutter-types.h>
+#include <clutter/clutter-actor.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_SCROLL_ACTOR               (clutter_scroll_actor_get_type ())
+#define CLUTTER_SCROLL_ACTOR(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActor))
+#define CLUTTER_IS_SCROLL_ACTOR(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCROLL_ACTOR))
+#define CLUTTER_SCROLL_ACTOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActorClass))
+#define CLUTTER_IS_SCROLL_ACTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SCROLL_ACTOR))
+#define CLUTTER_SCROLL_ACTOR_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActorClass))
+
+typedef struct _ClutterScrollActorPrivate       ClutterScrollActorPrivate;
+typedef struct _ClutterScrollActorClass         ClutterScrollActorClass;
+
+/**
+ * ClutterScrollActor:
+ *
+ * The <structname>ClutterScrollActor</structname> structure contains only
+ * private data, and should be accessed using the provided API.
+ *
+ * Since: 1.12
+ */
+struct _ClutterScrollActor
+{
+  /*< private >*/
+  ClutterActor parent_instance;
+
+  ClutterScrollActorPrivate *priv;
+};
+
+/**
+ * ClutterScrollActorClass:
+ *
+ * The <structname>ClutterScrollActor</structname> structure contains only
+ * private data.
+ *
+ * Since: 1.12
+ */
+struct _ClutterScrollActorClass
+{
+  /*< private >*/
+  ClutterActorClass parent_instance;
+
+  gpointer _padding[8];
+};
+
+CLUTTER_AVAILABLE_IN_1_12
+GType clutter_scroll_actor_get_type (void) G_GNUC_CONST;
+
+CLUTTER_AVAILABLE_IN_1_12
+ClutterActor *          clutter_scroll_actor_new                (void);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                    clutter_scroll_actor_set_scroll_mode    (ClutterScrollActor *actor,
+                                                                 ClutterScrollMode   mode);
+CLUTTER_AVAILABLE_IN_1_12
+ClutterScrollMode       clutter_scroll_actor_get_scroll_mode    (ClutterScrollActor *actor);
+
+CLUTTER_AVAILABLE_IN_1_12
+void                    clutter_scroll_actor_scroll_to_point    (ClutterScrollActor *actor,
+                                                                 const ClutterPoint *point);
+CLUTTER_AVAILABLE_IN_1_12
+void                    clutter_scroll_actor_scroll_to_rect     (ClutterScrollActor *actor,
+                                                                 const ClutterRect  *rect);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_SCROLL_ACTOR_H__ */
diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h
index fe65671..d57d7b0 100644
--- a/clutter/clutter-types.h
+++ b/clutter/clutter-types.h
@@ -58,6 +58,7 @@ typedef struct _ClutterLayoutManager            ClutterLayoutManager;
 typedef struct _ClutterActorIter                ClutterActorIter;
 typedef struct _ClutterPaintNode                ClutterPaintNode;
 typedef struct _ClutterContent                  ClutterContent; /* dummy */
+typedef struct _ClutterScrollActor	        ClutterScrollActor;
 
 typedef struct _ClutterInterval         	ClutterInterval;
 typedef struct _ClutterAnimatable       	ClutterAnimatable; /* dummy */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index d96713f..c5da997 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -88,6 +88,7 @@
 #include "clutter-property-transition.h"
 #include "clutter-scriptable.h"
 #include "clutter-script.h"
+#include "clutter-scroll-actor.h"
 #include "clutter-settings.h"
 #include "clutter-shader-effect.h"
 #include "clutter-shader-types.h"
diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols
index 553bfc4..f2daed4 100644
--- a/clutter/clutter.symbols
+++ b/clutter/clutter.symbols
@@ -1085,7 +1085,14 @@ clutter_script_lookup_filename
 clutter_script_new
 clutter_script_set_translation_domain
 clutter_script_unmerge_objects
+clutter_scroll_actor_get_scroll_mode
+clutter_scroll_actor_get_type
+clutter_scroll_actor_new
+clutter_scroll_actor_scroll_to_point
+clutter_scroll_actor_scroll_to_rect
+clutter_scroll_actor_set_scroll_mode
 clutter_scroll_direction_get_type
+clutter_scroll_mode_get_type
 clutter_settings_get_default
 clutter_settings_get_type
 clutter_set_default_frame_rate
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 94b68e9..72b545d 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -58,7 +58,8 @@ UNIT_TESTS = \
 	test-transitions.c \
 	test-content.c \
 	test-canvas.c \
-	test-keyframe-transition.c
+	test-keyframe-transition.c \
+	test-scroll-actor.c
 
 if X11_TESTS
 UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-scroll-actor.c b/tests/interactive/test-scroll-actor.c
new file mode 100644
index 0000000..8f344ef
--- /dev/null
+++ b/tests/interactive/test-scroll-actor.c
@@ -0,0 +1,179 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <clutter/clutter.h>
+
+static const char *menu_items_name[] = {
+  "Option 1",
+  "Option 2",
+  "Option 3",
+  "Option 4",
+  "Option 5",
+  "Option 6",
+  "Option 7",
+  "Option 8",
+  "Option 9",
+  "Option 10",
+  "Option 11",
+};
+
+static const guint menu_items_len = G_N_ELEMENTS (menu_items_name);
+
+static void
+select_item_at_index (ClutterActor *scroll, 
+                      int           index_)
+{
+  ClutterPoint point;
+  ClutterActor *menu, *item;
+  gpointer old_selected;
+
+  menu = clutter_actor_get_first_child (scroll);
+
+  old_selected = g_object_get_data (G_OBJECT (scroll), "selected-item");
+  if (old_selected != NULL)
+    {
+      item = clutter_actor_get_child_at_index (menu, GPOINTER_TO_INT (old_selected));
+      clutter_text_set_color (CLUTTER_TEXT (item), CLUTTER_COLOR_White);
+    }
+
+  if (index_ < 0)
+    index_ = clutter_actor_get_n_children (menu) - 1;
+  else if (index_ >= clutter_actor_get_n_children (menu))
+    index_ = 0;
+
+  item = clutter_actor_get_child_at_index (menu, index_);
+  clutter_actor_get_position (item, &point.x, &point.y);
+
+  clutter_actor_save_easing_state (scroll);
+  clutter_scroll_actor_scroll_to_point (CLUTTER_SCROLL_ACTOR (scroll), &point);
+  clutter_actor_restore_easing_state (scroll);
+
+  clutter_text_set_color (CLUTTER_TEXT (item), CLUTTER_COLOR_LightSkyBlue);
+
+  g_object_set_data (G_OBJECT (scroll), "selected-item",
+                     GINT_TO_POINTER (index_));
+}
+
+static void
+select_next_item (ClutterActor *scroll)
+{
+  gpointer selected_ = g_object_get_data (G_OBJECT (scroll), "selected-item");
+
+  select_item_at_index (scroll, GPOINTER_TO_INT (selected_) + 1);
+}
+
+static void
+select_prev_item (ClutterActor *scroll)
+{
+  gpointer selected_ = g_object_get_data (G_OBJECT (scroll), "selected-item");
+
+  select_item_at_index (scroll, GPOINTER_TO_INT (selected_) - 1);
+}
+
+static ClutterActor *
+create_menu_item (const char *name)
+{
+  ClutterActor *text;
+
+  text = clutter_text_new ();
+  clutter_text_set_font_name (CLUTTER_TEXT (text), "Sans Bold 24");
+  clutter_text_set_text (CLUTTER_TEXT (text), name);
+  clutter_text_set_color (CLUTTER_TEXT (text), CLUTTER_COLOR_White);
+  clutter_actor_set_margin_left (text, 12.f);
+  clutter_actor_set_margin_right (text, 12.f);
+
+  return text;
+}
+
+static ClutterActor *
+create_menu_actor (ClutterActor *scroll)
+{
+  ClutterActor *menu;
+  ClutterLayoutManager *layout_manager;
+  guint i;
+
+  layout_manager = clutter_box_layout_new ();
+  clutter_box_layout_set_orientation (CLUTTER_BOX_LAYOUT (layout_manager),
+                                      CLUTTER_ORIENTATION_VERTICAL);
+  clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout_manager), 12.f);
+
+  menu = clutter_actor_new ();
+  clutter_actor_set_layout_manager (menu, layout_manager);
+  clutter_actor_set_background_color (menu, CLUTTER_COLOR_Black);
+
+  for (i = 0; i < menu_items_len; i++)
+    clutter_actor_add_child (menu, create_menu_item (menu_items_name[i]));
+
+  return menu;
+}
+
+static ClutterActor *
+create_scroll_actor (ClutterActor *stage)
+{
+  ClutterActor *scroll;
+
+  scroll = clutter_scroll_actor_new ();
+  clutter_actor_set_name (scroll, "scroll");
+
+  clutter_actor_set_position (scroll, 0.f, 18.f);
+  clutter_actor_add_constraint (scroll, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.5));
+  clutter_actor_add_constraint (scroll, clutter_bind_constraint_new (stage, CLUTTER_BIND_HEIGHT, -36.f));
+
+  clutter_scroll_actor_set_scroll_mode (CLUTTER_SCROLL_ACTOR (scroll),
+                                        CLUTTER_SCROLL_VERTICALLY);
+
+  clutter_actor_add_child (scroll, create_menu_actor (scroll));
+
+  select_item_at_index (scroll, 0);
+
+  return scroll;
+}
+
+static gboolean
+on_key_press (ClutterActor *stage,
+              ClutterEvent *event,
+              gpointer      unused)
+{
+  ClutterActor *scroll;
+  guint key_symbol;
+
+  scroll = clutter_actor_get_first_child (stage);
+
+  key_symbol = clutter_event_get_key_symbol (event);
+
+  if (key_symbol == CLUTTER_KEY_Up)
+    select_prev_item (scroll);
+  else if (key_symbol == CLUTTER_KEY_Down)
+    select_next_item (scroll);
+
+  return CLUTTER_EVENT_STOP;
+}
+
+G_MODULE_EXPORT const char *
+test_scroll_actor_describe (void)
+{
+  return "Scrolling actor example.";
+}
+
+G_MODULE_EXPORT int
+test_scroll_actor_main (int argc, char *argv[])
+{
+  ClutterActor *stage;
+
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  stage = clutter_stage_new ();
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "Scroll Actor");
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+  g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), NULL);
+
+  clutter_actor_add_child (stage, create_scroll_actor (stage));
+
+  clutter_actor_show (stage);
+
+  clutter_main ();
+
+  return EXIT_SUCCESS;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]