mirror of
https://github.com/brl/mutter.git
synced 2024-11-24 09:00:42 -05:00
clutter/gesture: Cancel other gestures when moving to RECOGNIZING
Quite often there are situations where multiple gestures try to recognize, keeping track of the same set of points (for example an edge drag gesture on the stage and a click gesture somewhere in the scenegraph). Usually what's wanted here is that the first gesture to move to RECOGNIZING wins over all other active gestures and "claims" the point for itself. We implement this by introducing a concept called "influencing". It works by making all gestures operating on a shared set of points aware of each other using ClutterAction->register_sequence(). ClutterGesture uses this vfunc to keep track of all other ClutterGestures that are potentially conflicting, and keeps a list (priv->cancel_on_recognizing) of those. As soon as the move to RECOGNIZING happens, all gestures inside this list get moved to CANCELLED. To allow fine-grained control over this behavior, two APIs are introduced: 1) on the implementation level (should_influence() and should_be_influenced_by()): This is a vfunc that gets called as soon as a potential conflict is detected. It's helpful when a specific gesture always behaves the same towards another gesture, for example to make sure a LongPress gesture never cancels a DragGesture. 2) on the gesture user level, clutter_gesture_can_not_cancel() is introduced: This allows control for the user of a gesture to specify that a specific instance of a gesture won't cancel another gesture. Calling this twice so that both gestures can't cancel each other allows for things like simultaneous recognition of a pinch-to-zoom and rotate gesture. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2389>
This commit is contained in:
parent
7dd37558b1
commit
63cc4da4f9
@ -42,4 +42,12 @@ void clutter_action_sequence_cancelled (ClutterAction *action,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence);
|
||||
|
||||
gboolean clutter_action_register_sequence (ClutterAction *self,
|
||||
const ClutterEvent *event);
|
||||
|
||||
int clutter_action_setup_sequence_relationship (ClutterAction *action_1,
|
||||
ClutterAction *action_2,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -118,3 +118,29 @@ clutter_action_sequence_cancelled (ClutterAction *action,
|
||||
if (action_class->sequence_cancelled)
|
||||
action_class->sequence_cancelled (action, device, sequence);
|
||||
}
|
||||
|
||||
gboolean
|
||||
clutter_action_register_sequence (ClutterAction *self,
|
||||
const ClutterEvent *event)
|
||||
{
|
||||
ClutterActionClass *action_class = CLUTTER_ACTION_GET_CLASS (self);
|
||||
|
||||
if (action_class->register_sequence)
|
||||
return action_class->register_sequence (self, event);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
clutter_action_setup_sequence_relationship (ClutterAction *action_1,
|
||||
ClutterAction *action_2,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence)
|
||||
{
|
||||
ClutterActionClass *action_class = CLUTTER_ACTION_GET_CLASS (action_1);
|
||||
|
||||
if (action_class->setup_sequence_relationship)
|
||||
return action_class->setup_sequence_relationship (action_1, action_2, device, sequence);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,6 +54,14 @@ struct _ClutterActionClass
|
||||
void (* sequence_cancelled) (ClutterAction *action,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence);
|
||||
|
||||
gboolean (* register_sequence) (ClutterAction *self,
|
||||
const ClutterEvent *event);
|
||||
|
||||
int (* setup_sequence_relationship) (ClutterAction *action_1,
|
||||
ClutterAction *action_2,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence);
|
||||
};
|
||||
|
||||
/* ClutterActor API */
|
||||
|
@ -70,6 +70,15 @@
|
||||
* (and immediately) enter the requested state. To deal with this, never
|
||||
* assume the state has changed after calling clutter_gesture_set_state(),
|
||||
* and react to state changes by implementing the state_changed() vfunc.
|
||||
*
|
||||
* ## Relationships of gestures
|
||||
*
|
||||
* By default, when multiple gestures try to recognize while sharing one or
|
||||
* more points, the first gesture to move to RECOGNIZING wins, and implicitly
|
||||
* moves all conflicting gestures to state CANCELLED. This behavior can be
|
||||
* prohibited by using the clutter_gesture_can_not_cancel() API or by
|
||||
* implementing the should_influence() or should_be_influenced_by() vfuncs
|
||||
* in your #ClutterGesture subclass.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@ -116,6 +125,12 @@ struct _ClutterGesturePrivate
|
||||
unsigned int latest_index;
|
||||
|
||||
ClutterGestureState state;
|
||||
|
||||
GHashTable *in_relationship_with;
|
||||
|
||||
GPtrArray *cancel_on_recognizing;
|
||||
|
||||
GHashTable *can_not_cancel;
|
||||
};
|
||||
|
||||
enum
|
||||
@ -477,7 +492,27 @@ set_state (ClutterGesture *self,
|
||||
}
|
||||
|
||||
if (new_state == CLUTTER_GESTURE_STATE_WAITING)
|
||||
g_array_set_size (priv->sequences, 0);
|
||||
{
|
||||
GHashTableIter iter;
|
||||
ClutterGesture *other_gesture;
|
||||
|
||||
g_array_set_size (priv->sequences, 0);
|
||||
|
||||
g_hash_table_iter_init (&iter, priv->in_relationship_with);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &other_gesture, NULL))
|
||||
{
|
||||
ClutterGesturePrivate *other_priv =
|
||||
clutter_gesture_get_instance_private (other_gesture);
|
||||
gboolean removed;
|
||||
|
||||
removed = g_hash_table_remove (other_priv->in_relationship_with, self);
|
||||
g_assert (removed);
|
||||
|
||||
g_hash_table_iter_remove (&iter);
|
||||
}
|
||||
|
||||
g_ptr_array_set_size (priv->cancel_on_recognizing, 0);
|
||||
}
|
||||
|
||||
priv->state = new_state;
|
||||
|
||||
@ -524,11 +559,50 @@ maybe_move_to_waiting (ClutterGesture *self)
|
||||
set_state (self, CLUTTER_GESTURE_STATE_WAITING);
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_influence_other_gestures (ClutterGesture *self)
|
||||
{
|
||||
ClutterGesturePrivate *priv = clutter_gesture_get_instance_private (self);
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_RECOGNIZING ||
|
||||
priv->state == CLUTTER_GESTURE_STATE_COMPLETED)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->cancel_on_recognizing->len; i++)
|
||||
{
|
||||
ClutterGesture *other_gesture = priv->cancel_on_recognizing->pdata[i];
|
||||
ClutterGesturePrivate *other_priv =
|
||||
clutter_gesture_get_instance_private (other_gesture);
|
||||
|
||||
if (!g_hash_table_contains (priv->in_relationship_with, other_gesture))
|
||||
continue;
|
||||
|
||||
g_assert (other_priv->state != CLUTTER_GESTURE_STATE_WAITING);
|
||||
|
||||
if (other_priv->state == CLUTTER_GESTURE_STATE_CANCELLED ||
|
||||
other_priv->state == CLUTTER_GESTURE_STATE_COMPLETED)
|
||||
continue;
|
||||
|
||||
set_state (other_gesture, CLUTTER_GESTURE_STATE_CANCELLED);
|
||||
maybe_move_to_waiting (other_gesture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_state_authoritative (ClutterGesture *self,
|
||||
ClutterGestureState new_state)
|
||||
{
|
||||
ClutterGesturePrivate *priv = clutter_gesture_get_instance_private (self);
|
||||
ClutterGestureState old_state = priv->state;
|
||||
|
||||
set_state (self, new_state);
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_RECOGNIZING ||
|
||||
(old_state != CLUTTER_GESTURE_STATE_RECOGNIZING &&
|
||||
priv->state == CLUTTER_GESTURE_STATE_COMPLETED))
|
||||
maybe_influence_other_gestures (self);
|
||||
maybe_move_to_waiting (self);
|
||||
}
|
||||
|
||||
@ -656,52 +730,6 @@ clutter_gesture_handle_event (ClutterAction *action,
|
||||
if (clutter_event_get_flags (event) & CLUTTER_EVENT_FLAG_SYNTHETIC)
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_CANCELLED ||
|
||||
priv->state == CLUTTER_GESTURE_STATE_COMPLETED)
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
|
||||
if (event_type == CLUTTER_BUTTON_PRESS ||
|
||||
event_type == CLUTTER_TOUCH_BEGIN)
|
||||
{
|
||||
ClutterInputDevice *source_device =
|
||||
clutter_event_get_source_device (event);
|
||||
gboolean retval;
|
||||
|
||||
if (priv->sequences->len > 0)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->sequences->len; i++)
|
||||
{
|
||||
GestureSequenceData *iter = &g_array_index (priv->sequences, GestureSequenceData, i);
|
||||
ClutterInputDevice *iter_source_device;
|
||||
|
||||
if (iter->ended)
|
||||
continue;
|
||||
|
||||
iter_source_device = clutter_event_get_source_device (iter->begin_event);
|
||||
|
||||
if (iter_source_device != source_device)
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_signal_emit (self, obj_signals[SHOULD_HANDLE_SEQUENCE], 0,
|
||||
event, &retval);
|
||||
if (!retval)
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_WAITING)
|
||||
{
|
||||
set_state_authoritative (self, CLUTTER_GESTURE_STATE_POSSIBLE);
|
||||
g_assert (priv->state == CLUTTER_GESTURE_STATE_POSSIBLE);
|
||||
}
|
||||
|
||||
register_sequence (self, event);
|
||||
}
|
||||
|
||||
if ((seq_data = get_sequence_data (self, device, sequence, &seq_index)) == NULL)
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
|
||||
@ -799,6 +827,11 @@ clutter_gesture_handle_event (ClutterAction *action,
|
||||
if (stage)
|
||||
clutter_stage_notify_action_implicit_grab (stage, device, sequence);
|
||||
}
|
||||
|
||||
debug_message (self,
|
||||
"Cancelling other gestures on newly added point automatically");
|
||||
|
||||
maybe_influence_other_gestures (self);
|
||||
}
|
||||
|
||||
return CLUTTER_EVENT_PROPAGATE;
|
||||
@ -812,6 +845,156 @@ clutter_gesture_sequence_cancelled (ClutterAction *action,
|
||||
cancel_point (CLUTTER_GESTURE (action), device, sequence);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clutter_gesture_register_sequence (ClutterAction *action,
|
||||
const ClutterEvent *sequence_begin_event)
|
||||
{
|
||||
ClutterGesture *self = CLUTTER_GESTURE (action);
|
||||
ClutterGesturePrivate *priv = clutter_gesture_get_instance_private (self);
|
||||
ClutterInputDevice *source_device = clutter_event_get_source_device (sequence_begin_event);
|
||||
gboolean retval;
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_CANCELLED ||
|
||||
priv->state == CLUTTER_GESTURE_STATE_COMPLETED)
|
||||
return FALSE;
|
||||
|
||||
if (priv->sequences->len > 0)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->sequences->len; i++)
|
||||
{
|
||||
GestureSequenceData *iter = &g_array_index (priv->sequences, GestureSequenceData, i);
|
||||
ClutterInputDevice *iter_source_device;
|
||||
|
||||
if (iter->ended)
|
||||
continue;
|
||||
|
||||
iter_source_device = clutter_event_get_source_device (iter->begin_event);
|
||||
|
||||
if (iter_source_device != source_device)
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_signal_emit (self, obj_signals[SHOULD_HANDLE_SEQUENCE], 0,
|
||||
sequence_begin_event, &retval);
|
||||
if (!retval)
|
||||
return FALSE;
|
||||
|
||||
if (priv->state == CLUTTER_GESTURE_STATE_WAITING)
|
||||
{
|
||||
set_state_authoritative (self, CLUTTER_GESTURE_STATE_POSSIBLE);
|
||||
g_assert (priv->state == CLUTTER_GESTURE_STATE_POSSIBLE);
|
||||
}
|
||||
|
||||
register_sequence (self, sequence_begin_event);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_influence_on_other_gesture (ClutterGesture *self,
|
||||
ClutterGesture *other_gesture,
|
||||
gboolean *cancel_other_gesture_on_recognizing)
|
||||
{
|
||||
ClutterGesturePrivate *priv = clutter_gesture_get_instance_private (self);
|
||||
ClutterGestureClass *gesture_class = CLUTTER_GESTURE_GET_CLASS (self);
|
||||
ClutterGestureClass *other_gesture_class = CLUTTER_GESTURE_GET_CLASS (other_gesture);
|
||||
|
||||
/* The default: We cancel other gestures when we recognize */
|
||||
gboolean cancel = TRUE;
|
||||
|
||||
/* First check with the implementation specific APIs */
|
||||
if (gesture_class->should_influence)
|
||||
gesture_class->should_influence (self, other_gesture, &cancel);
|
||||
|
||||
if (other_gesture_class->should_be_influenced_by)
|
||||
other_gesture_class->should_be_influenced_by (other_gesture, self, &cancel);
|
||||
|
||||
/* Then apply overrides made using the public methods */
|
||||
if (priv->can_not_cancel &&
|
||||
g_hash_table_contains (priv->can_not_cancel, other_gesture))
|
||||
cancel = FALSE;
|
||||
|
||||
*cancel_other_gesture_on_recognizing = cancel;
|
||||
}
|
||||
|
||||
static int
|
||||
clutter_gesture_setup_sequence_relationship (ClutterAction *action_1,
|
||||
ClutterAction *action_2,
|
||||
ClutterInputDevice *device,
|
||||
ClutterEventSequence *sequence)
|
||||
{
|
||||
if (!CLUTTER_IS_GESTURE (action_1) || !CLUTTER_IS_GESTURE (action_2))
|
||||
return 0;
|
||||
|
||||
ClutterGesture *gesture_1 = CLUTTER_GESTURE (action_1);
|
||||
ClutterGesture *gesture_2 = CLUTTER_GESTURE (action_2);
|
||||
ClutterGesturePrivate *priv_1 = clutter_gesture_get_instance_private (gesture_1);
|
||||
ClutterGesturePrivate *priv_2 = clutter_gesture_get_instance_private (gesture_2);
|
||||
gboolean cancel_1_on_recognizing;
|
||||
gboolean cancel_2_on_recognizing;
|
||||
|
||||
/* When CANCELLED or COMPLETED, we refuse to accept new points in
|
||||
* register_sequence(). Also when WAITING it's impossible to have points,
|
||||
* that leaves only two states, POSSIBLE and RECOGNIZING.
|
||||
*/
|
||||
g_assert (priv_1->state == CLUTTER_GESTURE_STATE_POSSIBLE ||
|
||||
priv_1->state == CLUTTER_GESTURE_STATE_RECOGNIZING);
|
||||
g_assert (priv_2->state == CLUTTER_GESTURE_STATE_POSSIBLE ||
|
||||
priv_2->state == CLUTTER_GESTURE_STATE_RECOGNIZING);
|
||||
|
||||
g_assert (get_sequence_data (gesture_1, device, sequence, NULL) != NULL &&
|
||||
get_sequence_data (gesture_2, device, sequence, NULL) != NULL);
|
||||
|
||||
/* If gesture 1 knows gesture 2 (this implies vice-versa), everything's
|
||||
* figured out already, we won't negotiate again for any new shared sequences!
|
||||
*/
|
||||
if (g_hash_table_contains (priv_1->in_relationship_with, gesture_2))
|
||||
{
|
||||
cancel_1_on_recognizing = g_ptr_array_find (priv_2->cancel_on_recognizing, gesture_1, NULL);
|
||||
cancel_2_on_recognizing = g_ptr_array_find (priv_1->cancel_on_recognizing, gesture_2, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
setup_influence_on_other_gesture (gesture_1, gesture_2,
|
||||
&cancel_2_on_recognizing);
|
||||
|
||||
setup_influence_on_other_gesture (gesture_2, gesture_1,
|
||||
&cancel_1_on_recognizing);
|
||||
|
||||
CLUTTER_NOTE (GESTURES,
|
||||
"Setting up relation between \"<%s> [<%s>:%p]\" (cancel: %d) "
|
||||
"and \"<%s> [<%s>:%p]\" (cancel: %d)",
|
||||
clutter_actor_meta_get_name (CLUTTER_ACTOR_META (gesture_1)),
|
||||
G_OBJECT_TYPE_NAME (gesture_1), gesture_1,
|
||||
cancel_1_on_recognizing,
|
||||
clutter_actor_meta_get_name (CLUTTER_ACTOR_META (gesture_2)),
|
||||
G_OBJECT_TYPE_NAME (gesture_2), gesture_2,
|
||||
cancel_2_on_recognizing);
|
||||
|
||||
g_hash_table_add (priv_1->in_relationship_with, g_object_ref (gesture_2));
|
||||
g_hash_table_add (priv_2->in_relationship_with, g_object_ref (gesture_1));
|
||||
|
||||
if (cancel_2_on_recognizing)
|
||||
g_ptr_array_add (priv_1->cancel_on_recognizing, gesture_2);
|
||||
|
||||
if (cancel_1_on_recognizing)
|
||||
g_ptr_array_add (priv_2->cancel_on_recognizing, gesture_1);
|
||||
}
|
||||
|
||||
if (cancel_2_on_recognizing && !cancel_1_on_recognizing)
|
||||
return -1;
|
||||
|
||||
if (!cancel_2_on_recognizing && cancel_1_on_recognizing)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_gesture_set_actor (ClutterActorMeta *meta,
|
||||
ClutterActor *actor)
|
||||
@ -869,6 +1052,28 @@ clutter_gesture_get_property (GObject *gobject,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
other_gesture_disposed (gpointer user_data,
|
||||
GObject *finalized_gesture)
|
||||
{
|
||||
GHashTable *hashtable = user_data;
|
||||
|
||||
g_hash_table_remove (hashtable, finalized_gesture);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_weak_ref_hashtable (GHashTable *hashtable)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
GObject *key;
|
||||
|
||||
g_hash_table_iter_init (&iter, hashtable);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
|
||||
g_object_weak_unref (key, other_gesture_disposed, hashtable);
|
||||
|
||||
g_hash_table_destroy (hashtable);
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_gesture_finalize (GObject *gobject)
|
||||
{
|
||||
@ -889,6 +1094,15 @@ clutter_gesture_finalize (GObject *gobject)
|
||||
|
||||
g_array_unref (priv->sequences);
|
||||
|
||||
g_assert (g_hash_table_size (priv->in_relationship_with) == 0);
|
||||
g_hash_table_destroy (priv->in_relationship_with);
|
||||
|
||||
g_assert (priv->cancel_on_recognizing->len == 0);
|
||||
g_ptr_array_free (priv->cancel_on_recognizing, TRUE);
|
||||
|
||||
if (priv->can_not_cancel)
|
||||
destroy_weak_ref_hashtable (priv->can_not_cancel);
|
||||
|
||||
G_OBJECT_CLASS (clutter_gesture_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
@ -906,6 +1120,8 @@ clutter_gesture_class_init (ClutterGestureClass *klass)
|
||||
|
||||
action_class->handle_event = clutter_gesture_handle_event;
|
||||
action_class->sequence_cancelled = clutter_gesture_sequence_cancelled;
|
||||
action_class->register_sequence = clutter_gesture_register_sequence;
|
||||
action_class->setup_sequence_relationship = clutter_gesture_setup_sequence_relationship;
|
||||
|
||||
meta_class->set_actor = clutter_gesture_set_actor;
|
||||
meta_class->set_enabled = clutter_gesture_set_enabled;
|
||||
@ -1054,6 +1270,11 @@ clutter_gesture_init (ClutterGesture *self)
|
||||
|
||||
priv->state = CLUTTER_GESTURE_STATE_WAITING;
|
||||
|
||||
priv->in_relationship_with = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) g_object_unref, NULL);
|
||||
|
||||
priv->cancel_on_recognizing = g_ptr_array_new ();
|
||||
|
||||
priv->can_not_cancel = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1420,3 +1641,39 @@ clutter_gesture_get_point_event (ClutterGesture *self,
|
||||
|
||||
return seq_data->latest_event;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_gesture_can_not_cancel:
|
||||
* @self: a #ClutterGesture
|
||||
* @other_gesture: the other #ClutterGesture
|
||||
*
|
||||
* In case @self and @other_gesture are operating on the same points, calling
|
||||
* this function will make sure that @self does not cancel @other_gesture
|
||||
* when @self moves to state RECOGNIZING.
|
||||
*
|
||||
* To allow two gestures to recognize simultaneously using the same set of
|
||||
* points (for example a zoom and a rotate gesture on the same actor), call
|
||||
* clutter_gesture_can_not_cancel() twice, so that both gestures can not
|
||||
* cancel each other.
|
||||
*/
|
||||
void
|
||||
clutter_gesture_can_not_cancel (ClutterGesture *self,
|
||||
ClutterGesture *other_gesture)
|
||||
{
|
||||
ClutterGesturePrivate *priv;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_GESTURE (self));
|
||||
g_return_if_fail (CLUTTER_IS_GESTURE (other_gesture));
|
||||
|
||||
priv = clutter_gesture_get_instance_private (self);
|
||||
|
||||
if (!priv->can_not_cancel)
|
||||
priv->can_not_cancel = g_hash_table_new (NULL, NULL);
|
||||
|
||||
if (!g_hash_table_add (priv->can_not_cancel, other_gesture))
|
||||
return;
|
||||
|
||||
g_object_weak_ref (G_OBJECT (other_gesture),
|
||||
(GWeakNotify) other_gesture_disposed,
|
||||
priv->can_not_cancel);
|
||||
}
|
||||
|
@ -88,6 +88,20 @@ struct _ClutterGestureClass
|
||||
* ClutterGestureClass::may_recognize: (skip)
|
||||
*/
|
||||
gboolean (* may_recognize) (ClutterGesture *self);
|
||||
|
||||
/**
|
||||
* ClutterGestureClass::should_influence: (skip)
|
||||
*/
|
||||
void (* should_influence) (ClutterGesture *self,
|
||||
ClutterGesture *other_gesture,
|
||||
gboolean *cancel_on_recognizing);
|
||||
|
||||
/**
|
||||
* ClutterGestureClass::should_be_influenced_by: (skip)
|
||||
*/
|
||||
void (* should_be_influenced_by) (ClutterGesture *self,
|
||||
ClutterGesture *other_gesture,
|
||||
gboolean *cancelled_on_recognizing);
|
||||
};
|
||||
|
||||
CLUTTER_EXPORT
|
||||
@ -144,4 +158,8 @@ CLUTTER_EXPORT
|
||||
const ClutterEvent * clutter_gesture_get_point_event (ClutterGesture *self,
|
||||
int point_index);
|
||||
|
||||
CLUTTER_EXPORT
|
||||
void clutter_gesture_can_not_cancel (ClutterGesture *self,
|
||||
ClutterGesture *other_gesture);
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -4292,6 +4292,47 @@ clutter_stage_maybe_lost_implicit_grab (ClutterStage *self,
|
||||
cleanup_implicit_grab (entry);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_sequence_actions (GArray *emission_chain,
|
||||
const ClutterEvent *sequence_begin_event)
|
||||
{
|
||||
ClutterInputDevice *device = clutter_event_get_device (sequence_begin_event);
|
||||
ClutterEventSequence *sequence = clutter_event_get_event_sequence (sequence_begin_event);
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0; i < emission_chain->len; i++)
|
||||
{
|
||||
EventReceiver *receiver = &g_array_index (emission_chain, EventReceiver, i);
|
||||
|
||||
if (!receiver->action)
|
||||
continue;
|
||||
|
||||
if (!clutter_action_register_sequence (receiver->action, sequence_begin_event))
|
||||
g_clear_object (&receiver->action);
|
||||
}
|
||||
|
||||
for (i = 0; i < emission_chain->len; i++)
|
||||
{
|
||||
EventReceiver *receiver_1 = &g_array_index (emission_chain, EventReceiver, i);
|
||||
|
||||
if (!receiver_1->action)
|
||||
continue;
|
||||
|
||||
for (j = i + 1; j < emission_chain->len; j++)
|
||||
{
|
||||
EventReceiver *receiver_2 = &g_array_index (emission_chain, EventReceiver, j);
|
||||
|
||||
if (!receiver_2->action)
|
||||
continue;
|
||||
|
||||
clutter_action_setup_sequence_relationship (receiver_1->action,
|
||||
receiver_2->action,
|
||||
device,
|
||||
sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
clutter_stage_emit_event (ClutterStage *self,
|
||||
const ClutterEvent *event)
|
||||
@ -4389,6 +4430,7 @@ clutter_stage_emit_event (ClutterStage *self,
|
||||
clutter_actor_set_implicitly_grabbed (entry->implicit_grab_actor, TRUE);
|
||||
|
||||
create_event_emission_chain (self, entry->event_emission_chain, seat_grab_actor, target_actor);
|
||||
setup_sequence_actions (entry->event_emission_chain, event);
|
||||
}
|
||||
|
||||
if (entry && entry->press_count)
|
||||
|
Loading…
Reference in New Issue
Block a user