summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2012-02-21 11:29:13 +0100
committerMatthias Clasen <mclasen@redhat.com>2012-03-01 15:18:28 -0500
commit79adbda38f9ca25d351929bb46b9bc3796a7afc6 (patch)
tree54417833b9884774e346cba0e88a07fcaf7c137c
parentd9af0a5f75f5ed14cb85df32117800d5baa63647 (diff)
downloadgtk+-touch-for-3.4-2.tar.gz
paned: Use 2-finger interaction to resize the panestouch-for-3.4-2
If 2 touches happen to be both at left and right of the handle, enter into resize mode. Else, the event(s) will be propagated further to the children.
-rw-r--r--gtk/gtkpaned.c358
1 files changed, 331 insertions, 27 deletions
diff --git a/gtk/gtkpaned.c b/gtk/gtkpaned.c
index fabbc25d7f..0141710bc6 100644
--- a/gtk/gtkpaned.c
+++ b/gtk/gtkpaned.c
@@ -34,6 +34,8 @@
#include "gtktypebuiltins.h"
#include "gtkprivate.h"
#include "gtkintl.h"
+#include "gtkdnd.h"
+#include "gtkwidgetprivate.h"
#include "a11y/gtkpanedaccessible.h"
/**
@@ -96,6 +98,17 @@ enum {
CHILD2
};
+typedef struct
+{
+ GdkWindow *pane_window;
+ GdkDevice *device;
+ GdkEventSequence *sequence;
+ gdouble x;
+ gdouble y;
+ GdkEvent *button_press_event;
+ guint press_consumed : 1;
+} TouchInfo;
+
struct _GtkPanedPrivate
{
GtkPaned *first_paned;
@@ -122,6 +135,8 @@ struct _GtkPanedPrivate
guint32 grab_time;
+ GArray *touches;
+
guint handle_prelit : 1;
guint in_drag : 1;
guint in_recursion : 1;
@@ -253,6 +268,8 @@ static gboolean gtk_paned_toggle_handle_focus (GtkPaned *paned);
static GType gtk_paned_child_type (GtkContainer *container);
static void gtk_paned_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
+static gboolean gtk_paned_captured_event (GtkWidget *widget,
+ GdkEvent *event);
G_DEFINE_TYPE_WITH_CODE (GtkPaned, gtk_paned, GTK_TYPE_CONTAINER,
@@ -706,6 +723,11 @@ gtk_paned_init (GtkPaned *paned)
priv->handle_pos.y = -1;
priv->drag_pos = -1;
+
+ priv->touches = g_array_sized_new (FALSE, FALSE,
+ sizeof (TouchInfo), 2);
+
+ _gtk_widget_set_captured_event_handler (GTK_WIDGET (paned), gtk_paned_captured_event);
}
static void
@@ -861,9 +883,11 @@ static void
gtk_paned_finalize (GObject *object)
{
GtkPaned *paned = GTK_PANED (object);
-
+ GtkPanedPrivate *priv = paned->priv;
+
gtk_paned_set_saved_focus (paned, NULL);
gtk_paned_set_first_paned (paned, NULL);
+ g_array_free (priv->touches, TRUE);
G_OBJECT_CLASS (gtk_paned_parent_class)->finalize (object);
}
@@ -1279,7 +1303,9 @@ gtk_paned_create_child_window (GtkPaned *paned,
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
- attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
+ attributes.event_mask = gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK | GDK_TOUCH_MASK;
+
if (child)
{
GtkAllocation allocation;
@@ -1569,7 +1595,8 @@ gtk_paned_enter (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
- if (priv->in_drag)
+ if (priv->in_drag &&
+ priv->touches->len == 0)
update_drag (paned, event->x, event->y);
else
{
@@ -1591,7 +1618,8 @@ gtk_paned_leave (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
- if (priv->in_drag)
+ if (priv->in_drag &&
+ priv->touches->len == 0)
update_drag (paned, event->x, event->y);
else
{
@@ -1625,6 +1653,38 @@ gtk_paned_focus (GtkWidget *widget,
}
static gboolean
+start_drag (GtkPaned *paned,
+ GdkDevice *device,
+ int xpos,
+ int ypos,
+ guint time)
+{
+ GtkPanedPrivate *priv = paned->priv;
+
+ if (gdk_device_grab (device,
+ priv->handle,
+ GDK_OWNERSHIP_WINDOW, FALSE,
+ GDK_BUTTON1_MOTION_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_TOUCH_MASK,
+ NULL, time) != GDK_GRAB_SUCCESS)
+ return FALSE;
+
+ priv->in_drag = TRUE;
+ priv->grab_time = time;
+ priv->grab_device = device;
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ priv->drag_pos = xpos;
+ else
+ priv->drag_pos = ypos;
+
+ return TRUE;
+}
+
+static gboolean
gtk_paned_button_press (GtkWidget *widget,
GdkEventButton *event)
{
@@ -1634,27 +1694,10 @@ gtk_paned_button_press (GtkWidget *widget,
if (!priv->in_drag &&
(event->window == priv->handle) && (event->button == GDK_BUTTON_PRIMARY))
{
- /* We need a server grab here, not gtk_grab_add(), since
- * we don't want to pass events on to the widget's children */
- if (gdk_device_grab (event->device,
- priv->handle,
- GDK_OWNERSHIP_WINDOW, FALSE,
- GDK_BUTTON1_MOTION_MASK
- | GDK_BUTTON_RELEASE_MASK
- | GDK_ENTER_NOTIFY_MASK
- | GDK_LEAVE_NOTIFY_MASK,
- NULL, event->time) != GDK_GRAB_SUCCESS)
+ if (!start_drag (paned, event->device,
+ event->x, event->y, event->time))
return FALSE;
- priv->in_drag = TRUE;
- priv->grab_time = event->time;
- priv->grab_device = event->device;
-
- if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
- priv->drag_pos = event->x;
- else
- priv->drag_pos = event->y;
-
return TRUE;
}
@@ -1668,9 +1711,13 @@ gtk_paned_grab_broken (GtkWidget *widget,
GtkPaned *paned = GTK_PANED (widget);
GtkPanedPrivate *priv = paned->priv;
- priv->in_drag = FALSE;
- priv->drag_pos = -1;
- priv->position_set = TRUE;
+ if (event->grab_window != priv->handle &&
+ gdk_event_get_device ((GdkEvent *) event) == priv->grab_device)
+ {
+ priv->in_drag = FALSE;
+ priv->drag_pos = -1;
+ priv->position_set = TRUE;
+ }
return TRUE;
}
@@ -1685,7 +1732,7 @@ stop_drag (GtkPaned *paned)
priv->position_set = TRUE;
gdk_device_ungrab (priv->grab_device,
- priv->grab_time);
+ gtk_get_current_event_time ());
priv->grab_device = NULL;
}
@@ -1760,6 +1807,263 @@ gtk_paned_motion (GtkWidget *widget,
return FALSE;
}
+static GdkWindow *
+_gtk_paned_find_pane_window (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GtkPanedPrivate *priv = GTK_PANED (widget)->priv;
+ GtkWidget *event_widget;
+
+ event_widget = gtk_get_event_widget (event);
+
+ if (event_widget == widget)
+ {
+ if (event->any.window == priv->child1_window)
+ return priv->child1_window;
+ else if (event->any.window == priv->child2_window)
+ return priv->child2_window;
+
+ return NULL;
+ }
+ else if (event_widget == priv->child1 ||
+ gtk_widget_is_ancestor (event_widget, priv->child1))
+ return priv->child1_window;
+ else if (event_widget == priv->child2 ||
+ gtk_widget_is_ancestor (event_widget, priv->child2))
+ return priv->child2_window;
+
+ return NULL;
+}
+
+static TouchInfo *
+_gtk_paned_find_touch (GtkPaned *paned,
+ GdkDevice *device,
+ GdkEventSequence *sequence,
+ guint *index)
+{
+ GtkPanedPrivate *priv = paned->priv;
+ TouchInfo *info;
+ guint i;
+
+ for (i = 0; i < priv->touches->len; i++)
+ {
+ info = &g_array_index (priv->touches, TouchInfo, i);
+
+ if (info->device == device && info->sequence == sequence)
+ {
+ if (index)
+ *index = i;
+
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+gtk_paned_release_captured_event (GtkPaned *paned,
+ TouchInfo *touch,
+ gboolean emit)
+{
+ GtkPanedPrivate *priv = paned->priv;
+ GtkWidget *event_widget, *child;
+
+ if (!touch->button_press_event)
+ return;
+
+ event_widget = gtk_get_event_widget (touch->button_press_event);
+
+ if (touch->pane_window == priv->child1_window)
+ child = priv->child1;
+ else if (touch->pane_window == priv->child2_window)
+ child = priv->child2;
+ else
+ return;
+
+ if (emit &&
+ !_gtk_propagate_captured_event (event_widget,
+ touch->button_press_event,
+ child))
+ gtk_propagate_event (event_widget, touch->button_press_event);
+
+ gdk_event_free (touch->button_press_event);
+ touch->button_press_event = NULL;
+}
+
+static gboolean
+gtk_paned_captured_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GtkPaned *paned = GTK_PANED (widget);
+ GtkPanedPrivate *priv = paned->priv;
+ GdkDevice *device, *source_device;
+ GdkWindow *pane_window;
+ TouchInfo new = { 0 }, *info;
+ GdkEventSequence *sequence;
+ guint index;
+ gdouble event_x, event_y;
+ gint x, y;
+
+ device = gdk_event_get_device (event);
+ source_device = gdk_event_get_source_device (event);
+
+ /* We possibly deal with both pointer and touch events,
+ * depending on the target window event mask, so assume
+ * touch ID = 0 for pointer events to ease handling.
+ */
+ sequence = gdk_event_get_event_sequence (event);
+
+ gdk_event_get_coords (event, &event_x, &event_y);
+
+ if (!source_device ||
+ gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
+ return FALSE;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ case GDK_TOUCH_BEGIN:
+ if (priv->touches->len == 2)
+ return FALSE;
+
+ pane_window = _gtk_paned_find_pane_window (widget, event);
+ gtk_widget_translate_coordinates (gtk_get_event_widget (event),
+ widget,
+ (gint)event_x, (gint)event_y,
+ &x, &y);
+
+ for (index = 0; index < priv->touches->len; index++)
+ {
+ info = &g_array_index (priv->touches, TouchInfo, index);
+
+ /* There was already a touch in this pane */
+ if (info->pane_window == pane_window)
+ return FALSE;
+ }
+
+ new.device = device;
+ new.sequence = sequence;
+ new.pane_window = pane_window;
+ new.x = x;
+ new.y = y;
+ g_array_append_val (priv->touches, new);
+
+ if (priv->touches->len == 1)
+ {
+ /* It's the first touch, store the event and set
+ * a timeout in order to release the event if no
+ * second touch happens timely.
+ */
+ info = &g_array_index (priv->touches, TouchInfo, 0);
+ info->button_press_event = gdk_event_copy (event);
+ return TRUE;
+ }
+ else if (priv->touches->len == 2)
+ {
+ GtkWidget *event_widget;
+
+ /* It's the second touch, release (don't emit) the
+ * held button/touch presses.
+ */
+ for (index = 0; index < priv->touches->len; index++)
+ {
+ info = &g_array_index (priv->touches, TouchInfo, index);
+ gtk_paned_release_captured_event (paned, info, FALSE);
+ info->press_consumed = TRUE;
+ }
+
+ event_widget = gtk_get_event_widget (event);
+
+ if (event_widget == widget)
+ {
+ if (pane_window == priv->child2_window)
+ {
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ event_x += priv->handle_pos.x + priv->handle_pos.width;
+ else
+ event_y += priv->handle_pos.y + priv->handle_pos.height;
+ }
+ }
+ else
+ gtk_widget_translate_coordinates (event_widget, widget,
+ (gint)event_x, (gint)event_y,
+ &x, &y);
+
+ start_drag (paned, device, x, y,
+ event->button.time);
+
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ priv->drag_pos = x - priv->handle_pos.x;
+ else
+ priv->drag_pos = y - priv->handle_pos.y;
+
+ return TRUE;
+ }
+
+ break;
+ case GDK_BUTTON_RELEASE:
+ case GDK_TOUCH_END:
+ info = _gtk_paned_find_touch (GTK_PANED (widget), device, sequence, &index);
+
+ if (info)
+ {
+ gboolean press_consumed;
+
+ if (priv->touches->len == 2)
+ stop_drag (paned);
+
+ press_consumed = info->press_consumed;
+
+ /* Release the held button/touch press, if still queued */
+ gtk_paned_release_captured_event (paned, info, TRUE);
+ g_array_remove_index_fast (priv->touches, index);
+
+ if (press_consumed)
+ return TRUE;
+ }
+ break;
+ case GDK_MOTION_NOTIFY:
+ case GDK_TOUCH_UPDATE:
+ info = _gtk_paned_find_touch (GTK_PANED (widget), device, sequence, &index);
+
+ if (info)
+ {
+ gtk_widget_translate_coordinates (gtk_get_event_widget (event),
+ widget,
+ event_x, event_y,
+ &x, &y);
+
+ /* If there is a single touch and this isn't a continuation
+ * from a previous successful 2-touch operation, check
+ * the threshold to let the child handle events.
+ */
+ if (priv->touches->len == 1 &&
+ gtk_drag_check_threshold (widget, info->x, info->y, x, y))
+ {
+ gtk_paned_release_captured_event (paned, info, TRUE);
+ g_array_remove_index_fast (priv->touches, index);
+ return FALSE;
+ }
+ else if (priv->touches->len == 2 && index == 1)
+ {
+ /* The device grab on priv->handle is in effect now,
+ * so the event coordinates are already relative to
+ * that window.
+ */
+ update_drag (paned, event_x, event_y);
+ }
+
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
/**
* gtk_paned_new:
* @orientation: the paned's orientation.