[gtk+/gestures] switch: Use GtkGesture to handle input events
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/gestures] switch: Use GtkGesture to handle input events
- Date: Mon, 19 May 2014 19:19:14 +0000 (UTC)
commit 14f94e73945d0e42e9e8371233299ee2a4e62e9f
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon May 19 16:08:56 2014 +0200
switch: Use GtkGesture to handle input events
A pan gesture is used to handle switch dragging, which is only triggered
by horizontal panning movements. A multipress gesture handles the cases
where clicking without dragging happens, just toggling the switch.
gtk/gtkswitch.c | 255 +++++++++++++++++++++++++-----------------------------
1 files changed, 118 insertions(+), 137 deletions(-)
---
diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c
index e038192..3330ea9 100644
--- a/gtk/gtkswitch.c
+++ b/gtk/gtkswitch.c
@@ -62,6 +62,9 @@ struct _GtkSwitchPrivate
GtkAction *action;
GtkActionHelper *action_helper;
+ GtkGesture *pan_gesture;
+ GtkGesture *multipress_gesture;
+
gint handle_x;
gint offset;
gint drag_start;
@@ -69,8 +72,6 @@ struct _GtkSwitchPrivate
guint state : 1;
guint is_active : 1;
- guint is_dragging : 1;
- guint in_press : 1;
guint in_switch : 1;
guint use_action_appearance : 1;
};
@@ -110,171 +111,131 @@ G_DEFINE_TYPE_WITH_CODE (GtkSwitch, gtk_switch, GTK_TYPE_WIDGET,
gtk_switch_activatable_interface_init));
G_GNUC_END_IGNORE_DEPRECATIONS;
-static gboolean
-gtk_switch_button_press (GtkWidget *widget,
- GdkEventButton *event)
+static void
+gtk_switch_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkSwitch *sw)
{
- GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
+ GtkSwitchPrivate *priv = sw->priv;
GtkAllocation allocation;
- /* Don't handle extra mouse buttons events, let them bubble up */
- if (event->button > 5)
- return GDK_EVENT_PROPAGATE;
-
- gtk_widget_get_allocation (widget, &allocation);
+ gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
if (priv->is_active)
- {
- /* if the event occurred in the "off" area, then this
- * is a direct toggle
- */
- if (event->x <= allocation.width / 2)
- {
- priv->in_press = TRUE;
- return GDK_EVENT_STOP;
- }
-
- priv->offset = event->x - allocation.width / 2;
- }
+ priv->offset = allocation.width / 2;
else
- {
- /* if the event occurred in the "on" area, then this
- * is a direct toggle
- */
- if (event->x >= allocation.width / 2)
- {
- priv->in_press = TRUE;
- return GDK_EVENT_STOP;
- }
-
- priv->offset = event->x;
- }
-
- priv->drag_start = event->x;
-
- g_object_get (gtk_widget_get_settings (widget),
- "gtk-dnd-drag-threshold", &priv->drag_threshold,
- NULL);
+ priv->offset = 0;
- return GDK_EVENT_STOP;
+ /* If the press didn't happen in the draggable handle,
+ * cancel the pan gesture right away
+ */
+ if ((priv->is_active && x <= allocation.width / 2) ||
+ (!priv->is_active && x > allocation.width / 2))
+ gtk_gesture_set_state (priv->pan_gesture, GTK_EVENT_SEQUENCE_DENIED);
}
-static gboolean
-gtk_switch_motion (GtkWidget *widget,
- GdkEventMotion *event)
+static void
+gtk_switch_multipress_gesture_released (GtkGestureMultiPress *gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkSwitch *sw)
{
- GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
-
- /* if this is a direct toggle we don't handle motion */
- if (priv->in_press)
- return GDK_EVENT_PROPAGATE;
-
- if (ABS (event->x - priv->drag_start) < priv->drag_threshold)
- return GDK_EVENT_STOP;
-
- if (event->state & GDK_BUTTON1_MASK)
- {
- gint position = event->x - priv->offset;
- GtkAllocation allocation;
- GtkStyleContext *context;
- GtkStateFlags state;
- GtkBorder padding;
- gint width;
-
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
-
- gtk_style_context_save (context);
- gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
- gtk_style_context_get_padding (context, state, &padding);
- gtk_style_context_restore (context);
-
- gtk_widget_get_allocation (widget, &allocation);
-
- width = allocation.width;
-
- /* constrain the handle within the trough width */
- if (position > (width / 2) - padding.right)
- priv->handle_x = width / 2 - padding.right;
- else if (position < padding.left)
- priv->handle_x = 0;
- else
- priv->handle_x = position;
-
- priv->is_dragging = TRUE;
+ GtkSwitchPrivate *priv = sw->priv;
+ GdkEventSequence *sequence;
+ GtkAllocation allocation;
- /* we need to redraw the handle */
- gtk_widget_queue_draw (widget);
+ gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
- return GDK_EVENT_STOP;
- }
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
- return GDK_EVENT_PROPAGATE;
+ if (priv->in_switch &&
+ gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
+ gtk_switch_set_active (sw, !priv->is_active);
}
-static gboolean
-gtk_switch_button_release (GtkWidget *widget,
- GdkEventButton *event)
+static void
+gtk_switch_pan_gesture_pan (GtkGesturePan *gesture,
+ GtkPanDirection direction,
+ gdouble offset,
+ GtkSwitch *sw)
{
- GtkSwitchPrivate *priv = GTK_SWITCH (widget)->priv;
+ GtkWidget *widget = GTK_WIDGET (sw);
+ GtkSwitchPrivate *priv = sw->priv;
GtkAllocation allocation;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
+ gint width, position;
- /* Don't handle extra mouse buttons events, let them bubble up */
- if (event->button > 5)
- return GDK_EVENT_PROPAGATE;
+ if (direction == GTK_PAN_DIRECTION_LEFT)
+ offset = -offset;
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+ position = priv->offset + offset;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_restore (context);
gtk_widget_get_allocation (widget, &allocation);
- /* dragged toggles have a "soft" grab, so we don't care whether we
- * are in the switch or not when the button is released; we do care
- * for direct toggles, instead
- */
- if (!priv->is_dragging && !priv->in_switch)
- return GDK_EVENT_PROPAGATE;
+ width = allocation.width;
- /* direct toggle */
- if (priv->in_press)
- {
- priv->in_press = FALSE;
- gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
+ /* constrain the handle within the trough width */
+ if (position > (width / 2) - padding.right)
+ priv->handle_x = width / 2 - padding.right;
+ else if (position < padding.left)
+ priv->handle_x = 0;
+ else
+ priv->handle_x = position;
- return GDK_EVENT_STOP;
- }
+ /* we need to redraw the handle */
+ gtk_widget_queue_draw (widget);
+}
- /* toggle the switch if the handle was clicked but a drag had not been
- * initiated */
- if (!priv->is_dragging && !priv->in_press)
- {
- gtk_switch_set_active (GTK_SWITCH (widget), !priv->is_active);
+static void
+gtk_switch_pan_gesture_drag_end (GtkGestureDrag *gesture,
+ gdouble x,
+ gdouble y,
+ GtkSwitch *sw)
+{
+ GtkSwitchPrivate *priv = sw->priv;
+ GdkEventSequence *sequence;
+ GtkAllocation allocation;
+ gboolean active = FALSE;
- return GDK_EVENT_STOP;
- }
+ sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
- /* dragged toggle */
- if (priv->is_dragging)
+ if (gtk_gesture_get_sequence_state (GTK_GESTURE (gesture), sequence) == GTK_EVENT_SEQUENCE_CLAIMED)
{
- priv->is_dragging = FALSE;
+ gtk_widget_get_allocation (GTK_WIDGET (sw), &allocation);
/* if half the handle passed the middle of the switch, then we
* consider it to be on
*/
if ((priv->handle_x + (allocation.width / 4)) >= (allocation.width / 2))
- {
- gtk_switch_set_active (GTK_SWITCH (widget), TRUE);
- priv->handle_x = allocation.width / 2;
- }
- else
- {
- gtk_switch_set_active (GTK_SWITCH (widget), FALSE);
- priv->handle_x = 0;
- }
-
- gtk_widget_queue_draw (widget);
-
- return GDK_EVENT_STOP;
+ active = TRUE;
}
+ else if (!gtk_gesture_handles_sequence (priv->multipress_gesture, sequence))
+ active = priv->is_active;
+ else
+ return;
+
+ if (active)
+ priv->handle_x = allocation.width / 2;
+ else
+ priv->handle_x = 0;
- return GDK_EVENT_PROPAGATE;
+ gtk_switch_set_active (sw, active);
+ gtk_widget_queue_draw (GTK_WIDGET (sw));
}
static gboolean
@@ -593,7 +554,7 @@ gtk_switch_draw (GtkWidget *widget,
g_object_unref (layout);
- if (priv->is_dragging)
+ if (gtk_gesture_is_recognized (priv->pan_gesture))
handle.x = x + priv->handle_x;
else if (priv->is_active)
handle.x = x + width - handle.width;
@@ -869,9 +830,6 @@ gtk_switch_class_init (GtkSwitchClass *klass)
widget_class->map = gtk_switch_map;
widget_class->unmap = gtk_switch_unmap;
widget_class->draw = gtk_switch_draw;
- widget_class->button_press_event = gtk_switch_button_press;
- widget_class->button_release_event = gtk_switch_button_release;
- widget_class->motion_notify_event = gtk_switch_motion;
widget_class->enter_notify_event = gtk_switch_enter;
widget_class->leave_notify_event = gtk_switch_leave;
@@ -952,10 +910,33 @@ gtk_switch_class_init (GtkSwitchClass *klass)
static void
gtk_switch_init (GtkSwitch *self)
{
+ GtkGesture *gesture;
+
self->priv = gtk_switch_get_instance_private (self);
self->priv->use_action_appearance = TRUE;
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
+
+ gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self));
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+ gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
+ g_signal_connect (gesture, "pressed",
+ G_CALLBACK (gtk_switch_multipress_gesture_pressed), self);
+ g_signal_connect (gesture, "released",
+ G_CALLBACK (gtk_switch_multipress_gesture_released), self);
+ gtk_gesture_attach (gesture, GTK_PHASE_TARGET);
+ self->priv->multipress_gesture = gesture;
+
+ gesture = gtk_gesture_pan_new (GTK_WIDGET (self),
+ GTK_PAN_ORIENTATION_HORIZONTAL);
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE);
+ gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
+ g_signal_connect (gesture, "pan",
+ G_CALLBACK (gtk_switch_pan_gesture_pan), self);
+ g_signal_connect (gesture, "drag-end",
+ G_CALLBACK (gtk_switch_pan_gesture_drag_end), self);
+ gtk_gesture_attach (gesture, GTK_PHASE_TARGET);
+ self->priv->pan_gesture = gesture;
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]