widget: Improve button press emulation on sequence denied

Ensure that state being set on pointer emulating touches actually
gets propagated properly on widgets with gestures that only handle
pointer events.
This commit is contained in:
Carlos Garnacho 2014-03-26 10:25:09 +01:00
parent c49ac32393
commit 8e1d5f98de
3 changed files with 151 additions and 28 deletions

View File

@ -1161,3 +1161,40 @@ _gtk_gesture_handled_sequence_press (GtkGesture *gesture,
return data->press_handled;
}
gboolean
_gtk_gesture_get_pointer_emulating_sequence (GtkGesture *gesture,
GdkEventSequence **sequence)
{
GtkGesturePrivate *priv;
GdkEventSequence *seq;
GHashTableIter iter;
PointData *data;
g_return_val_if_fail (GTK_IS_GESTURE (gesture), FALSE);
priv = gtk_gesture_get_instance_private (gesture);
g_hash_table_iter_init (&iter, priv->points);
while (g_hash_table_iter_next (&iter, (gpointer*) &seq, (gpointer*) &data))
{
switch (data->event->type)
{
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
if (!data->event->touch.emulating_pointer)
continue;
/* Fall through */
case GDK_BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
case GDK_MOTION_NOTIFY:
*sequence = seq;
return TRUE;
default:
break;
}
}
return FALSE;
}

View File

@ -27,6 +27,10 @@ G_BEGIN_DECLS
gboolean _gtk_gesture_handled_sequence_press (GtkGesture *gesture,
GdkEventSequence *sequence);
gboolean _gtk_gesture_get_pointer_emulating_sequence
(GtkGesture *gesture,
GdkEventSequence **sequence);
G_END_DECLS
#endif /* __GTK_GESTURE_PRIVATE_H__ */

View File

@ -4121,26 +4121,60 @@ _gtk_widget_emulate_press (GtkWidget *widget,
gdk_event_free (press);
}
static gboolean
_gtk_widget_set_sequence_state_internal (GtkWidget *widget,
GdkEventSequence *sequence,
GtkEventSequenceState state)
static const GdkEvent *
_gtk_widget_get_last_event (GtkWidget *widget,
GdkEventSequence *sequence)
{
GtkWidgetPrivate *priv = widget->priv;
const GdkEvent *mimic_event = NULL;
gboolean retval, handled = FALSE;
EventControllerData *data;
const GdkEvent *event;
GList *l;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
event = gtk_gesture_get_last_event (data->controller, sequence);
if (event)
return event;
}
if (!GTK_IS_GESTURE (data->controller))
return NULL;
}
static gboolean
_gtk_widget_set_sequence_state_internal (GtkWidget *widget,
GdkEventSequence *sequence,
GtkEventSequenceState state,
gboolean emulates_pointer)
{
gboolean retval, sequence_handled, handled = FALSE;
GtkWidgetPrivate *priv = widget->priv;
const GdkEvent *mimic_event;
gboolean send_event = FALSE;
EventControllerData *data;
GdkEventSequence *seq;
GList *l;
if (!priv->event_controllers && state != GTK_EVENT_SEQUENCE_CLAIMED)
return TRUE;
mimic_event = _gtk_widget_get_last_event (widget, sequence);
for (l = priv->event_controllers; l; l = l->next)
{
seq = sequence;
data = l->data;
if (seq && emulates_pointer &&
!gtk_gesture_handles_sequence (data->controller, seq))
seq = NULL;
if (!gtk_gesture_handles_sequence (data->controller, seq))
continue;
retval = gtk_gesture_set_sequence_state (GTK_GESTURE (data->controller),
sequence, state);
sequence_handled =
_gtk_gesture_handled_sequence_press (data->controller, seq);
retval = gtk_gesture_set_sequence_state (data->controller, seq, state);
handled |= retval;
/* If the sequence goes denied, check whether this is a controller attached
@ -4148,42 +4182,66 @@ _gtk_widget_set_sequence_state_internal (GtkWidget *widget,
* it was consumed), the corresponding press will be emulated for widgets
* beneath, so the widgets beneath get a coherent stream of events from now on.
*/
if (retval && !mimic_event &&
if (retval && sequence_handled &&
data->propagation_phase == GTK_PHASE_CAPTURE &&
state == GTK_EVENT_SEQUENCE_DENIED &&
_gtk_gesture_handled_sequence_press (GTK_GESTURE (data->controller),
sequence))
mimic_event = gtk_gesture_get_last_event (GTK_GESTURE (data->controller),
sequence);
state == GTK_EVENT_SEQUENCE_DENIED)
send_event = TRUE;
}
if (mimic_event)
if (send_event && mimic_event)
_gtk_widget_emulate_press (widget, mimic_event);
return handled;
}
static gboolean
_gtk_widget_cancel_sequence (GtkWidget *widget,
GdkEventSequence *sequence)
_gtk_widget_cancel_sequence (GtkWidget *widget,
GdkEventSequence *sequence,
gboolean emulates_pointer)
{
GtkWidgetPrivate *priv = widget->priv;
EventControllerData *data;
gboolean handled = FALSE;
GdkEventSequence *seq;
GList *l;
for (l = priv->event_controllers; l; l = l->next)
{
seq = sequence;
data = l->data;
if (seq && emulates_pointer &&
!gtk_gesture_handles_sequence (data->controller, seq))
seq = NULL;
if (!gtk_gesture_handles_sequence (data->controller, seq))
continue;
handled |=
gtk_gesture_cancel_sequence (data->controller, seq);
}
return handled;
}
static gboolean
_gtk_widget_get_emulating_sequence (GtkWidget *widget,
GdkEventSequence **sequence)
{
GtkWidgetPrivate *priv = widget->priv;
EventControllerData *data;
GList *l;
for (l = priv->event_controllers; l; l = l->next)
{
data = l->data;
if (!GTK_IS_GESTURE (data->controller))
continue;
handled |= gtk_gesture_cancel_sequence (GTK_GESTURE (data->controller),
sequence);
if (_gtk_gesture_get_pointer_emulating_sequence (data->controller,
sequence))
return TRUE;
}
return handled;
return FALSE;
}
static gboolean
@ -4193,11 +4251,34 @@ gtk_widget_real_sequence_state_changed (GtkWidget *widget,
GtkEventSequenceState state)
{
GtkEventSequenceState changed_state;
gboolean emulates_pointer = FALSE;
const GdkEvent *last_event;
if (!sequence)
{
if (!_gtk_widget_get_emulating_sequence (widget, &sequence))
return FALSE;
}
else
{
last_event = _gtk_widget_get_last_event (changed_widget, sequence);
if (!last_event)
return FALSE;
if ((last_event->type == GDK_TOUCH_BEGIN ||
last_event->type == GDK_TOUCH_UPDATE ||
last_event->type == GDK_TOUCH_END) &&
last_event->touch.emulating_pointer)
emulates_pointer = TRUE;
}
if (widget == changed_widget)
return _gtk_widget_set_sequence_state_internal (widget, sequence, state);
else if (gtk_widget_is_ancestor (widget, changed_widget))
return _gtk_widget_cancel_sequence (widget, sequence);
return _gtk_widget_set_sequence_state_internal (widget, sequence,
state, emulates_pointer);
else if (state == GTK_EVENT_SEQUENCE_CLAIMED &&
gtk_widget_is_ancestor (widget, changed_widget))
return _gtk_widget_cancel_sequence (widget, sequence, emulates_pointer);
else
{
changed_state = gtk_widget_get_sequence_state (changed_widget, sequence);
@ -4205,7 +4286,8 @@ gtk_widget_real_sequence_state_changed (GtkWidget *widget,
if (state == GTK_EVENT_SEQUENCE_CLAIMED &&
changed_state == GTK_EVENT_SEQUENCE_CLAIMED)
return _gtk_widget_set_sequence_state_internal (widget, sequence,
GTK_EVENT_SEQUENCE_DENIED);
GTK_EVENT_SEQUENCE_DENIED,
emulates_pointer);
}
return FALSE;