GestureActions: Add per-action thresholds

Instead of relying on the dnd drag threshold, add per-action
horizontal and vertical thresholds. Use them in the swipe action
as well.

https://bugzilla.gnome.org/show_bug.cgi?id=724242
This commit is contained in:
Bastien Nocera 2014-02-12 17:41:03 +01:00
parent 54e2657cb0
commit 32b3d27bb9
3 changed files with 172 additions and 25 deletions

View File

@ -124,6 +124,7 @@ struct _ClutterGestureActionPrivate
gulong stage_capture_id; gulong stage_capture_id;
ClutterGestureTriggerEdge edge; ClutterGestureTriggerEdge edge;
float distance_x, distance_y;
guint in_gesture : 1; guint in_gesture : 1;
}; };
@ -134,6 +135,8 @@ enum
PROP_N_TOUCH_POINTS, PROP_N_TOUCH_POINTS,
PROP_THRESHOLD_TRIGGER_EDGE, PROP_THRESHOLD_TRIGGER_EDGE,
PROP_THRESHOLD_TRIGGER_DISTANCE_X,
PROP_THRESHOLD_TRIGGER_DISTANCE_Y,
PROP_LAST PROP_LAST
}; };
@ -265,7 +268,7 @@ gesture_update_release_point (GesturePoint *point,
} }
static gint static gint
gesture_get_threshold (void) gesture_get_default_threshold (void)
{ {
gint threshold; gint threshold;
ClutterSettings *settings = clutter_settings_get_default (); ClutterSettings *settings = clutter_settings_get_default ();
@ -274,15 +277,18 @@ gesture_get_threshold (void)
} }
static gboolean static gboolean
gesture_point_pass_threshold (GesturePoint *point, ClutterEvent *event) gesture_point_pass_threshold (ClutterGestureAction *action,
GesturePoint *point,
ClutterEvent *event)
{ {
gint drag_threshold = gesture_get_threshold (); float threshold_x, threshold_y;
gfloat motion_x, motion_y; gfloat motion_x, motion_y;
clutter_event_get_coords (event, &motion_x, &motion_y); clutter_event_get_coords (event, &motion_x, &motion_y);
clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y);
if ((fabsf (point->press_y - motion_y) < drag_threshold) && if ((fabsf (point->press_y - motion_y) < threshold_y) &&
(fabsf (point->press_x - motion_x) < drag_threshold)) (fabsf (point->press_x - motion_x) < threshold_x))
return TRUE; return TRUE;
return FALSE; return FALSE;
} }
@ -352,7 +358,8 @@ stage_captured_event_cb (ClutterActor *stage,
{ {
ClutterGestureActionPrivate *priv = action->priv; ClutterGestureActionPrivate *priv = action->priv;
ClutterActor *actor; ClutterActor *actor;
gint position, drag_threshold; gint position;
float threshold_x, threshold_y;
gboolean return_value; gboolean return_value;
GesturePoint *point; GesturePoint *point;
@ -391,7 +398,7 @@ stage_captured_event_cb (ClutterActor *stage,
/* Wait until the drag threshold has been exceeded /* Wait until the drag threshold has been exceeded
* before starting _TRIGGER_EDGE_AFTER gestures. */ * before starting _TRIGGER_EDGE_AFTER gestures. */
if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER && if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER &&
gesture_point_pass_threshold (point, event)) gesture_point_pass_threshold (action, point, event))
{ {
gesture_update_motion_point (point, event); gesture_update_motion_point (point, event);
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
@ -420,10 +427,10 @@ stage_captured_event_cb (ClutterActor *stage,
/* Check if a _TRIGGER_EDGE_BEFORE gesture needs to be cancelled because /* Check if a _TRIGGER_EDGE_BEFORE gesture needs to be cancelled because
* the drag threshold has been exceeded. */ * the drag threshold has been exceeded. */
drag_threshold = gesture_get_threshold (); clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y);
if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE && if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE &&
((fabsf (point->press_y - point->last_motion_y) > drag_threshold) || ((fabsf (point->press_y - point->last_motion_y) > threshold_y) ||
(fabsf (point->press_x - point->last_motion_x) > drag_threshold))) (fabsf (point->press_x - point->last_motion_x) > threshold_x)))
{ {
cancel_gesture (action); cancel_gesture (action);
return CLUTTER_EVENT_PROPAGATE; return CLUTTER_EVENT_PROPAGATE;
@ -571,6 +578,14 @@ clutter_gesture_action_set_property (GObject *gobject,
clutter_gesture_action_set_threshold_trigger_edge (self, g_value_get_enum (value)); clutter_gesture_action_set_threshold_trigger_edge (self, g_value_get_enum (value));
break; break;
case PROP_THRESHOLD_TRIGGER_DISTANCE_X:
clutter_gesture_action_set_threshold_trigger_distance (self, g_value_get_float (value), self->priv->distance_y);
break;
case PROP_THRESHOLD_TRIGGER_DISTANCE_Y:
clutter_gesture_action_set_threshold_trigger_distance (self, self->priv->distance_x, g_value_get_float (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break; break;
@ -595,6 +610,20 @@ clutter_gesture_action_get_property (GObject *gobject,
g_value_set_enum (value, self->priv->edge); g_value_set_enum (value, self->priv->edge);
break; break;
case PROP_THRESHOLD_TRIGGER_DISTANCE_X:
if (self->priv->distance_x > 0.0)
g_value_set_float (value, self->priv->distance_x);
else
g_value_set_float (value, gesture_get_default_threshold ());
break;
case PROP_THRESHOLD_TRIGGER_DISTANCE_Y:
if (self->priv->distance_y > 0.0)
g_value_set_float (value, self->priv->distance_y);
else
g_value_set_float (value, gesture_get_default_threshold ());
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break; break;
@ -659,6 +688,44 @@ clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
CLUTTER_PARAM_READWRITE | CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY); G_PARAM_CONSTRUCT_ONLY);
/**
* ClutterGestureAction:threshold-trigger-distance-x:
*
* The horizontal trigger distance to be used by the action to either
* emit the #ClutterGestureAction::gesture-begin signal or to emit
* the #ClutterGestureAction::gesture-cancel signal.
*
* A negative value will be interpreted as the default drag threshold.
*
* Since: 1.18
*/
gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_X] =
g_param_spec_float ("threshold-trigger-distance-x",
P_("Threshold Trigger Horizontal Distance"),
P_("The horizontal trigger distance used by the action"),
-1.0, G_MAXFLOAT, -1.0,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
/**
* ClutterGestureAction:threshold-trigger-distance-y:
*
* The vertical trigger distance to be used by the action to either
* emit the #ClutterGestureAction::gesture-begin signal or to emit
* the #ClutterGestureAction::gesture-cancel signal.
*
* A negative value will be interpreted as the default drag threshold.
*
* Since: 1.18
*/
gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_Y] =
g_param_spec_float ("threshold-trigger-distance-y",
P_("Threshold Trigger Vertical Distance"),
P_("The vertical trigger distance used by the action"),
-1.0, G_MAXFLOAT, -1.0,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, g_object_class_install_properties (gobject_class,
PROP_LAST, PROP_LAST,
gesture_props); gesture_props);
@ -1030,16 +1097,17 @@ clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action,
{ {
ClutterActor *actor = ClutterActor *actor =
clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
gint i, drag_threshold; gint i;
float threshold_x, threshold_y;
drag_threshold = gesture_get_threshold (); clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y);
for (i = 0; i < priv->points->len; i++) for (i = 0; i < priv->points->len; i++)
{ {
GesturePoint *point = &g_array_index (priv->points, GesturePoint, i); GesturePoint *point = &g_array_index (priv->points, GesturePoint, i);
if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) || if ((fabsf (point->press_y - point->last_motion_y) >= threshold_y) ||
(ABS (point->press_x - point->last_motion_x) >= drag_threshold)) (fabsf (point->press_x - point->last_motion_x) >= threshold_x))
{ {
begin_gesture (action, actor); begin_gesture (action, actor);
break; break;
@ -1200,3 +1268,70 @@ clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action)
return action->priv->edge; return action->priv->edge;
} }
/**
* clutter_gesture_action_set_threshold_trigger_distance:
* @action: a #ClutterGestureAction
* @x: the distance on the horizontal axis
* @y: the distance on the vertical axis
*
* Sets the threshold trigger distance for the gesture drag threshold, if any.
*
* This function should only be called by sub-classes of
* #ClutterGestureAction during their construction phase.
*
* Since: 1.18
*/
void
clutter_gesture_action_set_threshold_trigger_distance (ClutterGestureAction *action,
float x,
float y)
{
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
if (fabsf (x - action->priv->distance_x) > FLOAT_EPSILON)
{
action->priv->distance_x = x;
g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_X]);
}
if (fabsf (y - action->priv->distance_y) > FLOAT_EPSILON)
{
action->priv->distance_y = y;
g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_Y]);
}
}
/**
* clutter_gesture_action_get_threshold_trigger_distance:
* @action: a #ClutterGestureAction
* @x: (out) (allow-none): The return location for the horizontal distance, or %NULL
* @y: (out) (allow-none): The return location for the vertical distance, or %NULL
*
* Retrieves the threshold trigger distance of the gesture @action,
* as set using clutter_gesture_action_set_threshold_trigger_distance().
*
* Since: 1.18
*/
void
clutter_gesture_action_get_threshold_trigger_distance (ClutterGestureAction *action,
float *x,
float *y)
{
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
if (x != NULL)
{
if (action->priv->distance_x > 0.0)
*x = action->priv->distance_x;
else
*x = gesture_get_default_threshold ();
}
if (y != NULL)
{
if (action->priv->distance_y > 0.0)
*y = action->priv->distance_y;
else
*y = gesture_get_default_threshold ();
}
}

View File

@ -155,6 +155,16 @@ void clutter_gesture_action_set_threshold_trigger_edg
CLUTTER_AVAILABLE_IN_1_18 CLUTTER_AVAILABLE_IN_1_18
ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action); ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action);
CLUTTER_AVAILABLE_IN_1_18
void clutter_gesture_action_set_threshold_trigger_distance (ClutterGestureAction *action,
float x,
float y);
CLUTTER_AVAILABLE_IN_1_18
void clutter_gesture_action_get_threshold_trigger_distance (ClutterGestureAction *action,
float *x,
float *y);
G_END_DECLS G_END_DECLS
#endif /* __CLUTTER_GESTURE_ACTION_H__ */ #endif /* __CLUTTER_GESTURE_ACTION_H__ */

View File

@ -54,7 +54,7 @@ struct _ClutterSwipeActionPrivate
ClutterSwipeDirection h_direction; ClutterSwipeDirection h_direction;
ClutterSwipeDirection v_direction; ClutterSwipeDirection v_direction;
int threshold; float distance_x, distance_y;
}; };
enum enum
@ -74,13 +74,15 @@ gesture_begin (ClutterGestureAction *action,
ClutterActor *actor) ClutterActor *actor)
{ {
ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv;
ClutterSettings *settings = clutter_settings_get_default ();
/* reset the state at the beginning of a new gesture */ /* reset the state at the beginning of a new gesture */
priv->h_direction = 0; priv->h_direction = 0;
priv->v_direction = 0; priv->v_direction = 0;
g_object_get (settings, "dnd-drag-threshold", &priv->threshold, NULL); g_object_get (action,
"threshold-trigger-distance-x", &priv->distance_x,
"threshold-trigger-distance-y", &priv->distance_y,
NULL);
return TRUE; return TRUE;
} }
@ -108,14 +110,14 @@ gesture_progress (ClutterGestureAction *action,
delta_x = press_x - motion_x; delta_x = press_x - motion_x;
delta_y = press_y - motion_y; delta_y = press_y - motion_y;
if (delta_x >= priv->threshold) if (delta_x >= priv->distance_x)
h_direction = CLUTTER_SWIPE_DIRECTION_RIGHT; h_direction = CLUTTER_SWIPE_DIRECTION_RIGHT;
else if (delta_x < -priv->threshold) else if (delta_x < -priv->distance_x)
h_direction = CLUTTER_SWIPE_DIRECTION_LEFT; h_direction = CLUTTER_SWIPE_DIRECTION_LEFT;
if (delta_y >= priv->threshold) if (delta_y >= priv->distance_y)
v_direction = CLUTTER_SWIPE_DIRECTION_DOWN; v_direction = CLUTTER_SWIPE_DIRECTION_DOWN;
else if (delta_y < -priv->threshold) else if (delta_y < -priv->distance_y)
v_direction = CLUTTER_SWIPE_DIRECTION_UP; v_direction = CLUTTER_SWIPE_DIRECTION_UP;
/* cancel gesture on direction reversal */ /* cancel gesture on direction reversal */
@ -152,14 +154,14 @@ gesture_end (ClutterGestureAction *action,
0, 0,
&release_x, &release_y); &release_x, &release_y);
if (release_x - press_x > priv->threshold) if (release_x - press_x > priv->distance_y)
direction |= CLUTTER_SWIPE_DIRECTION_RIGHT; direction |= CLUTTER_SWIPE_DIRECTION_RIGHT;
else if (press_x - release_x > priv->threshold) else if (press_x - release_x > priv->distance_x)
direction |= CLUTTER_SWIPE_DIRECTION_LEFT; direction |= CLUTTER_SWIPE_DIRECTION_LEFT;
if (release_y - press_y > priv->threshold) if (release_y - press_y > priv->distance_y)
direction |= CLUTTER_SWIPE_DIRECTION_DOWN; direction |= CLUTTER_SWIPE_DIRECTION_DOWN;
else if (press_y - release_y > priv->threshold) else if (press_y - release_y > priv->distance_y)
direction |= CLUTTER_SWIPE_DIRECTION_UP; direction |= CLUTTER_SWIPE_DIRECTION_UP;
/* XXX:2.0 remove */ /* XXX:2.0 remove */