[gtk+/multitouch: 11/123] scrolledwindow: Allow selections and drag-and-drop when kinetic scrolling is enabled



commit 26b8258c2e511083f6bd3a8024ba7205c8b50251
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Wed Mar 23 17:42:21 2011 +0100

    scrolledwindow: Allow selections and drag-and-drop when kinetic scrolling is enabled
    
    If the scrolling doesn't start after a long press, the scrolling is
    cancelled and events are handled by child widget normally.

 gtk/gtkscrolledwindow.c      |  126 +++++++++++++++++++++++++++++++++++++++--
 tests/testkineticscrolling.c |   64 ++++++++++++++++++++-
 2 files changed, 180 insertions(+), 10 deletions(-)
---
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 1f1a74b..03b3465 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -168,6 +168,7 @@ struct _GtkScrolledWindowPrivate
 
   /* Kinetic scrolling */
   GdkWindow             *event_window;
+  GdkEvent              *button_press_event;
   guint                  kinetic_scrolling_enabled : 1;
   guint                  in_drag                   : 1;
   guint                  hmoving                   : 1;
@@ -175,6 +176,7 @@ struct _GtkScrolledWindowPrivate
   guint                  button_press_id;
   guint                  motion_notify_id;
   guint                  button_release_id;
+  guint                  press_and_hold_id;
   MotionEventList        motion_events;
   GtkTimeline           *deceleration_timeline;
   gdouble                dx;
@@ -230,6 +232,10 @@ static gboolean gtk_scrolled_window_scroll_event       (GtkWidget         *widge
                                                         GdkEventScroll    *event);
 static gboolean gtk_scrolled_window_button_press_event (GtkWidget         *widget,
                                                         GdkEvent          *event);
+static gboolean gtk_scrolled_window_press_and_hold      (GtkWidget        *widget,
+                                                         GtkPressAndHoldAction action,
+                                                         gint              x,
+                                                         gint              y);
 static gboolean gtk_scrolled_window_focus              (GtkWidget         *widget,
                                                         GtkDirectionType   direction);
 static void     gtk_scrolled_window_add                (GtkContainer      *container,
@@ -1125,9 +1131,14 @@ gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
         gdk_window_show (priv->event_window);
       motion_event_list_init (&priv->motion_events, 3);
       priv->button_press_id =
-              g_signal_connect (scrolled_window, "captured_event",
-                                G_CALLBACK (gtk_scrolled_window_button_press_event),
-                                NULL);
+        g_signal_connect (scrolled_window, "captured-event",
+                          G_CALLBACK (gtk_scrolled_window_button_press_event),
+                          NULL);
+      priv->press_and_hold_id =
+        g_signal_connect (scrolled_window, "press-and-hold",
+                          G_CALLBACK (gtk_scrolled_window_press_and_hold),
+                          NULL);
+
       /* Hide the scrollbars */
       gtk_scrolled_window_auto_hide_scrollbars_start (scrolled_window,
                                                       AUTO_HIDE_SCROLLBARS_TIMEOUT);
@@ -1164,6 +1175,11 @@ gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
           g_signal_handler_disconnect (scrolled_window, priv->button_release_id);
           priv->button_release_id = 0;
         }
+      if (priv->press_and_hold_id > 0)
+        {
+          g_signal_handler_disconnect (scrolled_window, priv->press_and_hold_id);
+          priv->press_and_hold_id = 0;
+        }
       motion_event_list_clear (&priv->motion_events);
       if (priv->event_window)
         gdk_window_hide (priv->event_window);
@@ -1242,6 +1258,18 @@ gtk_scrolled_window_destroy (GtkWidget *widget)
       g_signal_handler_disconnect (widget, priv->button_release_id);
       priv->button_release_id = 0;
     }
+  if (priv->press_and_hold_id > 0)
+    {
+      g_signal_handler_disconnect (widget, priv->press_and_hold_id);
+      priv->press_and_hold_id = 0;
+    }
+
+  if (priv->button_press_event)
+    {
+      gdk_event_free (priv->button_press_event);
+      priv->button_press_event = NULL;
+    }
+
   motion_event_list_clear (&priv->motion_events);
   gtk_scrolled_window_auto_hide_scrollbars_stop (scrolled_window);
 
@@ -2795,6 +2823,61 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window,
 }
 
 static gboolean
+gtk_scrolled_window_press_and_hold (GtkWidget             *widget,
+                                    GtkPressAndHoldAction  action,
+                                    gint                   x,
+                                    gint                   y)
+{
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  GtkScrolledWindowPrivate *priv = scrolled_window->priv;
+
+  switch (action)
+    {
+    case GTK_PRESS_AND_HOLD_QUERY:
+      return !priv->in_drag;
+    case GTK_PRESS_AND_HOLD_TRIGGER:
+      /* Cancel the scrolling and send the button press
+       * event to the child widget
+       */
+
+      gdk_device_ungrab (gdk_event_get_device (priv->button_press_event), GDK_CURRENT_TIME);
+
+      if (priv->motion_notify_id > 0)
+        {
+          g_signal_handler_disconnect (scrolled_window, priv->motion_notify_id);
+          priv->motion_notify_id = 0;
+        }
+      if (priv->button_release_id > 0)
+        {
+          g_signal_handler_disconnect (scrolled_window, priv->button_release_id);
+          priv->button_release_id = 0;
+        }
+
+      /* We are going to synthesize the button press event so that
+       * it can be handled by child widget, but we don't want to
+       * handle it, so block both button-press and and press-and-hold
+       * during this button press
+       */
+      g_signal_handler_block (scrolled_window, priv->button_press_id);
+      g_signal_handler_block (scrolled_window, priv->press_and_hold_id);
+
+      gtk_main_do_event (priv->button_press_event);
+
+      g_signal_handler_unblock (scrolled_window, priv->button_press_id);
+      g_signal_handler_unblock (scrolled_window, priv->press_and_hold_id);
+      gdk_event_free (priv->button_press_event);
+      priv->button_press_event = NULL;
+      break;
+    case GTK_PRESS_AND_HOLD_CANCEL:
+    default:
+      break;
+    }
+
+  /* Doesn't really matter in this case */
+  return FALSE;
+}
+
+static gboolean
 gtk_scrolled_window_button_release_event (GtkWidget *widget,
                                           GdkEvent  *_event)
 {
@@ -2812,6 +2895,10 @@ gtk_scrolled_window_button_release_event (GtkWidget *widget,
   if (event->button != 1)
     return FALSE;
 
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  if (!child)
+    return FALSE;
+
   gdk_device_ungrab (gdk_event_get_device (_event), event->time);
 
   if (priv->motion_notify_id > 0)
@@ -2829,13 +2916,30 @@ gtk_scrolled_window_button_release_event (GtkWidget *widget,
     {
       gtk_scrolled_window_auto_hide_scrollbars_start (scrolled_window,
                                                       AUTO_HIDE_SCROLLBARS_TIMEOUT);
+      /* There hasn't been scrolling at all, so just let the
+       * child widget handle the events normally
+       */
+      if (priv->button_press_event)
+        {
+          g_signal_handler_block (widget, priv->button_press_id);
+          gtk_main_do_event (priv->button_press_event);
+          g_signal_handler_unblock (widget, priv->button_press_id);
+          gdk_event_free (priv->button_press_event);
+          priv->button_press_event = NULL;
+          gtk_main_do_event (_event);
+
+          return TRUE;
+      }
+
       return FALSE;
     }
   priv->in_drag = FALSE;
 
-  child = gtk_bin_get_child (GTK_BIN (widget));
-  if (!child)
-    return FALSE;
+  if (priv->button_press_event)
+    {
+      gdk_event_free (priv->button_press_event);
+      priv->button_press_event = NULL;
+    }
 
   distance = gtk_scrolled_window_get_deceleration_distance (scrolled_window, event->x_root, event->y_root);
   gtk_scrolled_window_start_deceleration (scrolled_window, distance);
@@ -2881,6 +2985,12 @@ gtk_scrolled_window_motion_notify_event (GtkWidget *widget,
         return FALSE;
     }
 
+  if (priv->button_press_event)
+    {
+      gdk_event_free (priv->button_press_event);
+      priv->button_press_event = NULL;
+    }
+
   motion = motion_event_list_last (&priv->motion_events);
 
   hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
@@ -2983,7 +3093,9 @@ gtk_scrolled_window_button_press_event (GtkWidget *widget,
   else
     priv->in_drag = FALSE;
 
-  return FALSE;
+  priv->button_press_event = gdk_event_copy (_event);
+
+  return TRUE;
 }
 
 static gboolean
diff --git a/tests/testkineticscrolling.c b/tests/testkineticscrolling.c
index 693825a..6410ce5 100644
--- a/tests/testkineticscrolling.c
+++ b/tests/testkineticscrolling.c
@@ -1,5 +1,15 @@
 #include <gtk/gtk.h>
 
+enum
+{
+  TARGET_GTK_TREE_MODEL_ROW
+};
+
+static GtkTargetEntry row_targets[] =
+{
+  { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, TARGET_GTK_TREE_MODEL_ROW }
+};
+
 static void
 on_button_clicked (GtkWidget *widget, gpointer data)
 {
@@ -12,6 +22,9 @@ kinetic_scrolling (void)
   GtkWidget *window, *swindow, *table;
   GtkWidget *label;
   GtkWidget *vbox, *button;
+  GtkWidget *treeview;
+  GtkCellRenderer *renderer;
+  GtkListStore *store;
   GtkWidget *textview;
   gint i;
 
@@ -20,18 +33,23 @@ kinetic_scrolling (void)
   g_signal_connect (window, "delete_event",
                     G_CALLBACK (gtk_main_quit), NULL);
 
-  table = gtk_table_new (2, 2, FALSE);
+  table = gtk_table_new (2, 3, FALSE);
 
   label = gtk_label_new ("Non scrollable widget using viewport");
   gtk_table_attach (GTK_TABLE (table), label,
                     0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (label);
 
-  label = gtk_label_new ("Scrollable widget");
+  label = gtk_label_new ("Scrollable widget: TreeView");
   gtk_table_attach (GTK_TABLE (table), label,
                     1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
   gtk_widget_show (label);
 
+  label = gtk_label_new ("Scrollable widget: TextView");
+  gtk_table_attach (GTK_TABLE (table), label,
+                    2, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+  gtk_widget_show (label);
+
   vbox = gtk_vbox_new (FALSE, 1);
   for (i = 0; i < 80; i++)
     {
@@ -55,6 +73,46 @@ kinetic_scrolling (void)
                              0, 1, 1, 2);
   gtk_widget_show (swindow);
 
+  treeview = gtk_tree_view_new ();
+  gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview),
+                                          GDK_BUTTON1_MASK,
+                                          row_targets,
+                                          G_N_ELEMENTS (row_targets),
+                                          GDK_ACTION_MOVE | GDK_ACTION_COPY);
+  gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview),
+                                        row_targets,
+                                        G_N_ELEMENTS (row_targets),
+                                        GDK_ACTION_MOVE | GDK_ACTION_COPY);
+
+  renderer = gtk_cell_renderer_text_new ();
+  g_object_set (renderer, "editable", TRUE, NULL);
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
+                                               0, "Title",
+                                               renderer,
+                                               "text", 0,
+                                               NULL);
+  store = gtk_list_store_new (1, G_TYPE_STRING);
+  for (i = 0; i < 80; i++)
+    {
+      GtkTreeIter iter;
+      gchar *label = g_strdup_printf ("Row number %d", i);
+
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, 0, label, -1);
+      g_free (label);
+    }
+  gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
+  g_object_unref (store);
+
+  swindow = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow), TRUE);
+  gtk_container_add (GTK_CONTAINER (swindow), treeview);
+  gtk_widget_show (treeview);
+
+  gtk_table_attach_defaults (GTK_TABLE (table), swindow,
+                             1, 2, 1, 2);
+  gtk_widget_show (swindow);
+
   textview = gtk_text_view_new ();
   swindow = gtk_scrolled_window_new (NULL, NULL);
   gtk_scrolled_window_set_kinetic_scrolling (GTK_SCROLLED_WINDOW (swindow), TRUE);
@@ -62,7 +120,7 @@ kinetic_scrolling (void)
   gtk_widget_show (textview);
 
   gtk_table_attach_defaults (GTK_TABLE (table), swindow,
-                             1, 2, 1, 2);
+                             2, 3, 1, 2);
   gtk_widget_show (swindow);
 
   gtk_container_add (GTK_CONTAINER (window), table);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]