[clutter] Add PanAxis mode that automatically pins scroll based on initial movement



commit 0c75e178145c3296f05deefed34be3691b1ffb3b
Author: Gustavo Noronha Silva <gustavo noronha collabora com>
Date:   Wed Sep 11 12:33:57 2013 -0300

    Add PanAxis mode that automatically pins scroll based on initial movement
    
    This code is inspired by the implementation of the same feature for the
    Mx toolkit's MxKineticScrollView. See commit 4d08771.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=707982

 clutter/clutter-enums.h            |    6 ++-
 clutter/clutter-pan-action.c       |  120 ++++++++++++++++++++++++++++++------
 clutter/clutter-pan-action.h       |    5 ++
 doc/reference/clutter-sections.txt |    1 +
 examples/pan-action.c              |   58 ++++++++++++++++-
 5 files changed, 167 insertions(+), 23 deletions(-)
---
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index 9dd1b66..859579d 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -1018,6 +1018,8 @@ typedef enum { /*< prefix=CLUTTER_SWIPE_DIRECTION >*/
  * @CLUTTER_PAN_AXIS_NONE: No constraint
  * @CLUTTER_PAN_X_AXIS: Set a constraint on the X axis
  * @CLUTTER_PAN_Y_AXIS: Set a constraint on the Y axis
+ * @CLUTTER_PAN_AXIS_AUTO: Constrain panning automatically based on initial
+ *   movement (available since 1.24)
  *
  * The axis of the constraint that should be applied on the
  * panning action
@@ -1028,7 +1030,9 @@ typedef enum { /*< prefix=CLUTTER_PAN >*/
   CLUTTER_PAN_AXIS_NONE = 0,
 
   CLUTTER_PAN_X_AXIS,
-  CLUTTER_PAN_Y_AXIS
+  CLUTTER_PAN_Y_AXIS,
+
+  CLUTTER_PAN_AXIS_AUTO
 } ClutterPanAxis;
 
 
diff --git a/clutter/clutter-pan-action.c b/clutter/clutter-pan-action.c
index a1f6124..f2d1e18 100644
--- a/clutter/clutter-pan-action.c
+++ b/clutter/clutter-pan-action.c
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2010  Intel Corporation.
  * Copyright (C) 2011  Robert Bosch Car Multimedia GmbH.
- * Copyright (C) 2012  Collabora Ltd.
+ * Copyright (C) 2012, 2014  Collabora Ltd.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -80,6 +80,14 @@ typedef enum
   PAN_STATE_INTERPOLATING
 } PanState;
 
+typedef enum
+{
+  SCROLL_PINNED_UNKNOWN,
+  SCROLL_PINNED_NONE,
+  SCROLL_PINNED_HORIZONTAL,
+  SCROLL_PINNED_VERTICAL
+} PinState;
+
 struct _ClutterPanActionPrivate
 {
   ClutterPanAxis pan_axis;
@@ -102,6 +110,8 @@ struct _ClutterPanActionPrivate
   gfloat release_y;
 
   guint should_interpolate : 1;
+
+  PinState pin_state;
 };
 
 enum
@@ -135,7 +145,34 @@ emit_pan (ClutterPanAction *self,
           ClutterActor     *actor,
           gboolean          is_interpolated)
 {
+  ClutterPanActionPrivate *priv = self->priv;
   gboolean retval;
+
+  if (priv->pin_state == SCROLL_PINNED_UNKNOWN)
+    {
+      priv->pin_state = SCROLL_PINNED_NONE;
+      if (priv->pan_axis == CLUTTER_PAN_AXIS_AUTO)
+        {
+          gfloat delta_x;
+          gfloat delta_y;
+          gfloat scroll_threshold = G_PI_4/2;
+          gfloat drag_angle;
+
+          clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self), 0, &delta_x, &delta_y);
+
+          if (delta_x != 0.0f)
+            drag_angle = atanf (delta_y / delta_x);
+          else
+            drag_angle = G_PI_2;
+
+          if ((drag_angle > -scroll_threshold) && (drag_angle < scroll_threshold))
+            priv->pin_state = SCROLL_PINNED_HORIZONTAL;
+          else if ((drag_angle > (G_PI_2 - scroll_threshold)) ||
+                    (drag_angle < -(G_PI_2 - scroll_threshold)))
+            priv->pin_state = SCROLL_PINNED_VERTICAL;
+        }
+    }
+
   g_signal_emit (self, pan_signals[PAN], 0, actor, is_interpolated, &retval);
 }
 
@@ -207,6 +244,7 @@ gesture_begin (ClutterGestureAction  *gesture,
   ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture);
   ClutterPanActionPrivate *priv = self->priv;
 
+  priv->pin_state = SCROLL_PINNED_UNKNOWN;
   priv->state = PAN_STATE_PANNING;
   priv->interpolated_x = priv->interpolated_y = 0.0f;
   priv->dx = priv->dy = 0.0f;
@@ -297,25 +335,10 @@ clutter_pan_action_real_pan (ClutterPanAction *self,
                              ClutterActor     *actor,
                              gboolean          is_interpolated)
 {
-  ClutterPanActionPrivate *priv = self->priv;
   gfloat dx, dy;
   ClutterMatrix transform;
 
-  clutter_pan_action_get_motion_delta (self, 0, &dx, &dy);
-
-  switch (priv->pan_axis)
-    {
-    case CLUTTER_PAN_AXIS_NONE:
-      break;
-
-    case CLUTTER_PAN_X_AXIS:
-      dy = 0.0f;
-      break;
-
-    case CLUTTER_PAN_Y_AXIS:
-      dx = 0.0f;
-      break;
-    }
+  clutter_pan_action_get_constrained_motion_delta (self, 0, &dx, &dy);
 
   clutter_actor_get_child_transform (actor, &transform);
   cogl_matrix_translate (&transform, dx, dy, 0.0f);
@@ -605,7 +628,7 @@ clutter_pan_action_set_pan_axis (ClutterPanAction *self,
 
   g_return_if_fail (CLUTTER_IS_PAN_ACTION (self));
   g_return_if_fail (axis >= CLUTTER_PAN_AXIS_NONE &&
-                    axis <= CLUTTER_PAN_Y_AXIS);
+                    axis <= CLUTTER_PAN_AXIS_AUTO);
 
   priv = self->priv;
 
@@ -832,6 +855,67 @@ clutter_pan_action_get_interpolated_delta (ClutterPanAction *self,
 }
 
 /**
+ * clutter_pan_action_get_constrained_motion_delta:
+ * @self: A #ClutterPanAction
+ * @point: the touch point index, with 0 being the first touch
+  *   point received by the action
+ * @delta_x: (out) (optional): return location for the X delta
+ * @delta_y: (out) (optional): return location for the Y delta
+ *
+ * Retrieves the delta, in stage space, dependent on the current state
+ * of the #ClutterPanAction, and respecting the constraint specified by the
+ * #ClutterPanAction:pan-axis property.
+ *
+ * Return value: the distance since last motion event
+ *
+ * Since: 1.24
+ */
+gfloat
+clutter_pan_action_get_constrained_motion_delta (ClutterPanAction *self,
+                                                 guint             point,
+                                                 gfloat           *out_delta_x,
+                                                 gfloat           *out_delta_y)
+{
+  ClutterPanActionPrivate *priv;
+  gfloat delta_x = 0.f, delta_y = 0.f, distance;
+
+  g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.0f);
+
+  priv = self->priv;
+
+  distance = clutter_pan_action_get_motion_delta (self, point, &delta_x, &delta_y);
+
+  switch (priv->pan_axis)
+    {
+    case CLUTTER_PAN_AXIS_NONE:
+      break;
+
+    case CLUTTER_PAN_AXIS_AUTO:
+      if (priv->pin_state == SCROLL_PINNED_VERTICAL)
+        delta_x = 0.0f;
+      else if (priv->pin_state == SCROLL_PINNED_HORIZONTAL)
+        delta_y = 0.0f;
+      break;
+
+    case CLUTTER_PAN_X_AXIS:
+      delta_y = 0.0f;
+      break;
+
+    case CLUTTER_PAN_Y_AXIS:
+      delta_x = 0.0f;
+      break;
+    }
+
+  if (out_delta_x)
+    *out_delta_x = delta_x;
+
+  if (out_delta_y)
+    *out_delta_y = delta_y;
+
+  return distance;
+}
+
+/**
  * clutter_pan_action_get_motion_delta:
  * @self: A #ClutterPanAction
  * @point: the touch point index, with 0 being the first touch
diff --git a/clutter/clutter-pan-action.h b/clutter/clutter-pan-action.h
index 8d90b7a..37f0c99 100644
--- a/clutter/clutter-pan-action.h
+++ b/clutter/clutter-pan-action.h
@@ -142,6 +142,11 @@ void            clutter_pan_action_get_motion_coords        (ClutterPanAction *s
                                                              guint             point,
                                                              gfloat           *motion_x,
                                                              gfloat           *motion_y);
+CLUTTER_AVAILABLE_IN_1_24
+gfloat          clutter_pan_action_get_constrained_motion_delta (ClutterPanAction *self,
+                                                                 guint             point,
+                                                                 gfloat           *delta_x,
+                                                                 gfloat           *delta_y);
 G_END_DECLS
 
 #endif /* __CLUTTER_PAN_ACTION_H__ */
diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt
index 56389db..0f807df 100644
--- a/doc/reference/clutter-sections.txt
+++ b/doc/reference/clutter-sections.txt
@@ -3566,6 +3566,7 @@ clutter_pan_action_get_interpolated_coords
 clutter_pan_action_get_interpolated_delta
 clutter_pan_action_get_motion_coords
 clutter_pan_action_get_motion_delta
+clutter_pan_action_get_constrained_motion_delta
 <SUBSECTION Standard>
 CLUTTER_IS_PAN_ACTION
 CLUTTER_IS_PAN_ACTION_CLASS
diff --git a/examples/pan-action.c b/examples/pan-action.c
index e873305..4e3f73f 100644
--- a/examples/pan-action.c
+++ b/examples/pan-action.c
@@ -83,7 +83,7 @@ create_scroll_actor (ClutterActor *stage)
   pan_action = clutter_pan_action_new ();
   clutter_pan_action_set_interpolate (CLUTTER_PAN_ACTION (pan_action), TRUE);
   g_signal_connect (pan_action, "pan", G_CALLBACK (on_pan), NULL);
-  clutter_actor_add_action (scroll, pan_action);
+  clutter_actor_add_action_with_name (scroll, "pan", pan_action);
 
   clutter_actor_set_reactive (scroll, TRUE);
 
@@ -113,10 +113,45 @@ on_key_press (ClutterActor *stage,
   return CLUTTER_EVENT_STOP;
 }
 
+static gboolean
+label_clicked_cb (ClutterText *label, ClutterEvent *event, ClutterActor *scroll)
+{
+  ClutterPanAction *action = CLUTTER_PAN_ACTION (clutter_actor_get_action (scroll, "pan"));
+  const gchar *label_text = clutter_text_get_text (label);
+
+  if (g_str_equal (label_text, "X AXIS"))
+    clutter_pan_action_set_pan_axis (action, CLUTTER_PAN_X_AXIS);
+  else if (g_str_equal (label_text, "Y AXIS"))
+    clutter_pan_action_set_pan_axis (action, CLUTTER_PAN_Y_AXIS);
+  else if (g_str_equal (label_text, "AUTO"))
+    clutter_pan_action_set_pan_axis (action, CLUTTER_PAN_AXIS_AUTO);
+  else
+    clutter_pan_action_set_pan_axis (action, CLUTTER_PAN_AXIS_NONE);
+
+  return TRUE;
+}
+
+static void
+add_label (const gchar *text, ClutterActor *box, ClutterActor *scroll)
+{
+  ClutterActor *label;
+
+  label = clutter_text_new_with_text (NULL, text);
+  clutter_actor_set_reactive (label, TRUE);
+  clutter_actor_set_x_align (label, CLUTTER_ACTOR_ALIGN_START);
+  clutter_actor_set_x_expand (label, TRUE);
+
+  clutter_actor_add_child (box, label);
+
+  g_signal_connect (label, "button-release-event",
+                    G_CALLBACK (label_clicked_cb), scroll);
+}
+
 int
 main (int argc, char *argv[])
 {
-  ClutterActor *stage, *scroll, *info;
+  ClutterActor *stage, *scroll, *box, *info;
+  ClutterLayoutManager *layout;
 
   if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
     return EXIT_FAILURE;
@@ -129,9 +164,24 @@ main (int argc, char *argv[])
   scroll = create_scroll_actor (stage);
   clutter_actor_add_child (stage, scroll);
 
+  box = clutter_actor_new ();
+  clutter_actor_add_child (stage, box);
+  clutter_actor_set_position (box, 12, 12);
+
+  layout = clutter_box_layout_new ();
+  clutter_box_layout_set_orientation (CLUTTER_BOX_LAYOUT (layout), CLUTTER_ORIENTATION_VERTICAL);
+  clutter_actor_set_layout_manager (box, layout);
+
   info = clutter_text_new_with_text (NULL, "Press <space> to reset the image position.");
-  clutter_actor_add_child (stage, info);
-  clutter_actor_set_position (info, 12, 12);
+  clutter_actor_add_child (box, info);
+
+  info = clutter_text_new_with_text (NULL, "Click labels below to change AXIS pinning.");
+  clutter_actor_add_child (box, info);
+
+  add_label ("NONE", box, scroll);
+  add_label ("X AXIS", box, scroll);
+  add_label ("Y AXIS", box, scroll);
+  add_label ("AUTO", box, scroll);
 
   g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
   g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press), scroll);


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