From 32b3d27bb91fd305e0d095910a75295e455edd8c Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 12 Feb 2014 17:41:03 +0100 Subject: [PATCH] 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 --- clutter/clutter-gesture-action.c | 163 ++++++++++++++++++++++++++++--- clutter/clutter-gesture-action.h | 10 ++ clutter/clutter-swipe-action.c | 24 ++--- 3 files changed, 172 insertions(+), 25 deletions(-) diff --git a/clutter/clutter-gesture-action.c b/clutter/clutter-gesture-action.c index 3e8b50cf9..35019a791 100644 --- a/clutter/clutter-gesture-action.c +++ b/clutter/clutter-gesture-action.c @@ -124,6 +124,7 @@ struct _ClutterGestureActionPrivate gulong stage_capture_id; ClutterGestureTriggerEdge edge; + float distance_x, distance_y; guint in_gesture : 1; }; @@ -134,6 +135,8 @@ enum PROP_N_TOUCH_POINTS, PROP_THRESHOLD_TRIGGER_EDGE, + PROP_THRESHOLD_TRIGGER_DISTANCE_X, + PROP_THRESHOLD_TRIGGER_DISTANCE_Y, PROP_LAST }; @@ -265,7 +268,7 @@ gesture_update_release_point (GesturePoint *point, } static gint -gesture_get_threshold (void) +gesture_get_default_threshold (void) { gint threshold; ClutterSettings *settings = clutter_settings_get_default (); @@ -274,15 +277,18 @@ gesture_get_threshold (void) } 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; 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) && - (fabsf (point->press_x - motion_x) < drag_threshold)) + if ((fabsf (point->press_y - motion_y) < threshold_y) && + (fabsf (point->press_x - motion_x) < threshold_x)) return TRUE; return FALSE; } @@ -352,7 +358,8 @@ stage_captured_event_cb (ClutterActor *stage, { ClutterGestureActionPrivate *priv = action->priv; ClutterActor *actor; - gint position, drag_threshold; + gint position; + float threshold_x, threshold_y; gboolean return_value; GesturePoint *point; @@ -391,7 +398,7 @@ stage_captured_event_cb (ClutterActor *stage, /* Wait until the drag threshold has been exceeded * before starting _TRIGGER_EDGE_AFTER gestures. */ 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); 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 * 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 && - ((fabsf (point->press_y - point->last_motion_y) > drag_threshold) || - (fabsf (point->press_x - point->last_motion_x) > drag_threshold))) + ((fabsf (point->press_y - point->last_motion_y) > threshold_y) || + (fabsf (point->press_x - point->last_motion_x) > threshold_x))) { cancel_gesture (action); 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)); 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: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -595,6 +610,20 @@ clutter_gesture_action_get_property (GObject *gobject, g_value_set_enum (value, self->priv->edge); 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: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; @@ -659,6 +688,44 @@ clutter_gesture_action_class_init (ClutterGestureActionClass *klass) CLUTTER_PARAM_READWRITE | 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, PROP_LAST, gesture_props); @@ -1030,16 +1097,17 @@ clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action, { ClutterActor *actor = 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++) { GesturePoint *point = &g_array_index (priv->points, GesturePoint, i); - if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) || - (ABS (point->press_x - point->last_motion_x) >= drag_threshold)) + if ((fabsf (point->press_y - point->last_motion_y) >= threshold_y) || + (fabsf (point->press_x - point->last_motion_x) >= threshold_x)) { begin_gesture (action, actor); break; @@ -1200,3 +1268,70 @@ clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action) 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 (); + } +} diff --git a/clutter/clutter-gesture-action.h b/clutter/clutter-gesture-action.h index 9660c3e17..3f4691e07 100644 --- a/clutter/clutter-gesture-action.h +++ b/clutter/clutter-gesture-action.h @@ -155,6 +155,16 @@ void clutter_gesture_action_set_threshold_trigger_edg CLUTTER_AVAILABLE_IN_1_18 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 #endif /* __CLUTTER_GESTURE_ACTION_H__ */ diff --git a/clutter/clutter-swipe-action.c b/clutter/clutter-swipe-action.c index cac619929..452a5a020 100644 --- a/clutter/clutter-swipe-action.c +++ b/clutter/clutter-swipe-action.c @@ -54,7 +54,7 @@ struct _ClutterSwipeActionPrivate ClutterSwipeDirection h_direction; ClutterSwipeDirection v_direction; - int threshold; + float distance_x, distance_y; }; enum @@ -74,13 +74,15 @@ gesture_begin (ClutterGestureAction *action, ClutterActor *actor) { ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; - ClutterSettings *settings = clutter_settings_get_default (); /* reset the state at the beginning of a new gesture */ priv->h_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; } @@ -108,14 +110,14 @@ gesture_progress (ClutterGestureAction *action, delta_x = press_x - motion_x; delta_y = press_y - motion_y; - if (delta_x >= priv->threshold) + if (delta_x >= priv->distance_x) 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; - if (delta_y >= priv->threshold) + if (delta_y >= priv->distance_y) 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; /* cancel gesture on direction reversal */ @@ -152,14 +154,14 @@ gesture_end (ClutterGestureAction *action, 0, &release_x, &release_y); - if (release_x - press_x > priv->threshold) + if (release_x - press_x > priv->distance_y) 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; - if (release_y - press_y > priv->threshold) + if (release_y - press_y > priv->distance_y) 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; /* XXX:2.0 remove */