gesture-action: Let subclasses override the GestureTriggerEdge handling

Let gesture subclasses override how the drag threshold should
be handled:

• CLUTTER_GESTURE_TRIGGER_NONE tells GestureAction that the gesture
  must begin immediately and there's no drag limit that will cause
  its cancellation;
• CLUTTER_GESTURE_TRIGGER_AFTER is the default GestureAction behaviour,
  where it needs to wait until the drag threshold has been exceeded
  before considering the gesture valid;
• CLUTTER_GESTURE_TRIGGER_BEFORE will make GestureAction cancel
  the gesture once the drag exceed the configured threshold.

For example, ZoomAction and RotateAction could set
CLUTTER_GESTURE_TRIGGER_NONE since the use of two fingers makes the
begin of the action more self-evident, while an hypothetical Tap
gesture may use CLUTTER_GESTURE_TRIGGER_BEFORE to cancel the tap if
the pointer moves too much.

https://bugzilla.gnome.org/show_bug.cgi?id=685028
This commit is contained in:
Emanuele Aina 2012-10-15 23:37:38 +02:00
parent cbab0a62ad
commit abcf1d589f
3 changed files with 153 additions and 48 deletions

View File

@ -226,6 +226,7 @@ source_h_priv = \
$(srcdir)/clutter-event-translator.h \ $(srcdir)/clutter-event-translator.h \
$(srcdir)/clutter-event-private.h \ $(srcdir)/clutter-event-private.h \
$(srcdir)/clutter-flatten-effect.h \ $(srcdir)/clutter-flatten-effect.h \
$(srcdir)/clutter-gesture-action-private.h \
$(srcdir)/clutter-id-pool.h \ $(srcdir)/clutter-id-pool.h \
$(srcdir)/clutter-master-clock.h \ $(srcdir)/clutter-master-clock.h \
$(srcdir)/clutter-model-private.h \ $(srcdir)/clutter-model-private.h \

View File

@ -0,0 +1,59 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2012 Collabora Ltd..
*
* 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/>.
*/
#ifndef __CLUTTER_GESTURE_ACTION_PRIVATE_H__
#define __CLUTTER_GESTURE_ACTION_PRIVATE_H__
#include <clutter/clutter-gesture-action.h>
G_BEGIN_DECLS
/*< private >
* ClutterGestureTriggerEdge:
* @CLUTTER_GESTURE_TRIGGER_NONE: Tell #ClutterGestureAction that
* the gesture must begin immediately and there's no drag limit that
* will cause its cancellation;
* @CLUTTER_GESTURE_TRIGGER_AFTER: Tell #ClutterGestureAction that
* it needs to wait until the drag threshold has been exceeded before
* considering that the gesture has begun;
* @CLUTTER_GESTURE_TRIGGER_BEFORE: Tell #ClutterGestureAction that
* the gesture must begin immegiately and that it must be cancelled
* once the drag exceed the configured threshold.
*
* Enum passed to the _clutter_gesture_action_set_threshold_trigger_edge()
* function.
*
* Since: 1.14
*/
typedef enum
{
CLUTTER_GESTURE_TRIGGER_EDGE_NONE = 0,
CLUTTER_GESTURE_TRIGGER_EDGE_AFTER,
CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE
} ClutterGestureTriggerEdge;
void _clutter_gesture_action_set_threshold_trigger_edge (ClutterGestureAction *action,
ClutterGestureTriggerEdge edge);
G_END_DECLS
#endif /* __CLUTTER_GESTURE_ACTION_PRIVATE_H__ */

View File

@ -87,7 +87,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "clutter-gesture-action.h" #include "clutter-gesture-action-private.h"
#include "clutter-debug.h" #include "clutter-debug.h"
#include "clutter-enum-types.h" #include "clutter-enum-types.h"
@ -122,6 +122,8 @@ struct _ClutterGestureActionPrivate
guint actor_capture_id; guint actor_capture_id;
gulong stage_capture_id; gulong stage_capture_id;
ClutterGestureTriggerEdge edge;
guint in_gesture : 1; guint in_gesture : 1;
}; };
@ -209,6 +211,15 @@ gesture_unregister_point (ClutterGestureAction *action, gint position)
g_array_remove_index (priv->points, position); g_array_remove_index (priv->points, position);
} }
static gint
gesture_get_threshold (ClutterGestureAction *action)
{
gint threshold;
ClutterSettings *settings = clutter_settings_get_default ();
g_object_get (settings, "dnd-drag-threshold", &threshold, NULL);
return threshold;
}
static void static void
cancel_gesture (ClutterGestureAction *action) cancel_gesture (ClutterGestureAction *action)
{ {
@ -227,6 +238,38 @@ cancel_gesture (ClutterGestureAction *action)
g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor); g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor);
} }
static gboolean
begin_gesture (ClutterGestureAction *action,
ClutterActor *actor)
{
ClutterGestureActionPrivate *priv = action->priv;
gboolean return_value;
priv->in_gesture = TRUE;
if (!CLUTTER_GESTURE_ACTION_GET_CLASS (action)->gesture_prepare (action, actor))
{
cancel_gesture (action);
return FALSE;
}
/* clutter_gesture_action_cancel() may have been called during
* gesture_prepare(), check that the gesture is still active. */
if (!priv->in_gesture)
return FALSE;
g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor,
&return_value);
if (!return_value)
{
cancel_gesture (action);
return FALSE;
}
return TRUE;
}
static gboolean static gboolean
stage_captured_event_cb (ClutterActor *stage, stage_captured_event_cb (ClutterActor *stage,
ClutterEvent *event, ClutterEvent *event,
@ -234,7 +277,7 @@ stage_captured_event_cb (ClutterActor *stage,
{ {
ClutterGestureActionPrivate *priv = action->priv; ClutterGestureActionPrivate *priv = action->priv;
ClutterActor *actor; ClutterActor *actor;
gint position; gint position, drag_threshold;
gboolean return_value; gboolean return_value;
GesturePoint *point; GesturePoint *point;
gfloat motion_x, motion_y; gfloat motion_x, motion_y;
@ -272,40 +315,18 @@ stage_captured_event_cb (ClutterActor *stage,
if (priv->points->len < priv->requested_nb_points) if (priv->points->len < priv->requested_nb_points)
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
drag_threshold = gesture_get_threshold (action);
if (!priv->in_gesture) if (!priv->in_gesture)
{ {
gint drag_threshold; /* Wait until the drag threshold has been exceeded
ClutterSettings *settings = clutter_settings_get_default (); * before starting _TRIGGER_EDGE_AFTER gestures. */
if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER &&
g_object_get (settings, (fabsf (point->press_y - motion_y) < drag_threshold) &&
"dnd-drag-threshold", &drag_threshold, (fabsf (point->press_x - motion_x) < drag_threshold))
NULL);
if ((ABS (point->press_y - motion_y) >= drag_threshold) ||
(ABS (point->press_x - motion_x) >= drag_threshold))
{
priv->in_gesture = TRUE;
if (!CLUTTER_GESTURE_ACTION_GET_CLASS (action)->gesture_prepare (action, actor))
{
cancel_gesture (action);
return CLUTTER_EVENT_PROPAGATE;
}
/* clutter_gesture_action_cancel() may have been called during
* gesture_prepare(), check that the gesture is still active. */
if (!priv->in_gesture)
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, if (!begin_gesture(action, actor))
&return_value);
if (!return_value)
{
cancel_gesture (action);
return CLUTTER_EVENT_PROPAGATE;
}
}
else
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
} }
@ -325,6 +346,16 @@ stage_captured_event_cb (ClutterActor *stage,
cancel_gesture (action); cancel_gesture (action);
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
} }
/* Check if a _TRIGGER_EDGE_BEFORE gesture needs to be cancelled because
* the drag threshold has been exceeded. */
if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE &&
((fabsf (point->press_y - motion_y) > drag_threshold) ||
(fabsf (point->press_x - motion_x) > drag_threshold)))
{
cancel_gesture (action);
return CLUTTER_EVENT_PROPAGATE;
}
break; break;
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
@ -400,6 +431,11 @@ actor_captured_event_cb (ClutterActor *actor,
G_CALLBACK (stage_captured_event_cb), G_CALLBACK (stage_captured_event_cb),
action); action);
/* Start the gesture immediately if the gesture has no
* _TRIGGER_EDGE_AFTER drag threshold. */
if (priv->edge != CLUTTER_GESTURE_TRIGGER_EDGE_AFTER)
begin_gesture (action, actor);
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
} }
@ -448,6 +484,26 @@ default_event_handler (ClutterGestureAction *action,
return TRUE; return TRUE;
} }
/*< private >
* _clutter_gesture_action_set_threshold_trigger_edge:
* @action: a #ClutterGestureAction
* @edge: the %ClutterGestureTriggerEdge
*
* Sets the edge trigger for the gesture drag threshold, if any.
*
* This function can be called by #ClutterGestureAction subclasses that needs
* to change the %CLUTTER_GESTURE_TRIGGER_EDGE_AFTER default.
*
* Since: 1.14
*/
void
_clutter_gesture_action_set_threshold_trigger_edge (ClutterGestureAction *action,
ClutterGestureTriggerEdge edge)
{
action->priv->edge = edge;
}
static void static void
clutter_gesture_action_class_init (ClutterGestureActionClass *klass) clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
{ {
@ -562,6 +618,7 @@ clutter_gesture_action_init (ClutterGestureAction *self)
self->priv->points = g_array_sized_new (FALSE, TRUE, sizeof (GesturePoint), 3); self->priv->points = g_array_sized_new (FALSE, TRUE, sizeof (GesturePoint), 3);
self->priv->requested_nb_points = 1; self->priv->requested_nb_points = 1;
self->priv->edge = CLUTTER_GESTURE_TRIGGER_EDGE_AFTER;
} }
/** /**
@ -812,18 +869,15 @@ clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action,
if (priv->points->len < priv->requested_nb_points) if (priv->points->len < priv->requested_nb_points)
cancel_gesture (action); cancel_gesture (action);
} }
else else if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER)
{ {
if (priv->points->len >= priv->requested_nb_points) if (priv->points->len >= priv->requested_nb_points)
{ {
ClutterActor *actor = ClutterActor *actor =
clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
ClutterSettings *settings = clutter_settings_get_default ();
gint i, drag_threshold; gint i, drag_threshold;
g_object_get (settings, drag_threshold = gesture_get_threshold (action);
"dnd-drag-threshold", &drag_threshold,
NULL);
for (i = 0; i < priv->points->len; i++) for (i = 0; i < priv->points->len; i++)
{ {
@ -832,16 +886,7 @@ clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action,
if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) || if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) ||
(ABS (point->press_x - point->last_motion_x) >= drag_threshold)) (ABS (point->press_x - point->last_motion_x) >= drag_threshold))
{ {
gboolean return_value; begin_gesture (action, actor);
priv->in_gesture = TRUE;
g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor,
&return_value);
if (!return_value)
cancel_gesture (action);
break; break;
} }
} }