[gtk/wip/exalm/nested-scrolling] scrolledwindow: Support nested scrolling




commit 41dcb144274da0b78df4539783862872d581c23c
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Mon Dec 14 19:57:38 2020 +0500

    scrolledwindow: Support nested scrolling
    
    While it's possible to scroll GtkScrolledWindow with touchscreen gestures,
    currently it doesn't work for touchpad: GtkScrolledWindow never propagates
    scroll events and if multiple scrolled windows are nested, only the inner
    one will receive any events.
    
    Since e9fe270e94d6dbae4655f8a31910f9fb7288d457, GtkScrolledWindow handles
    only full scroll sequences, but still accepts any scroll sequence, causing
    issues with nested scrolling.
    
    Take another step and keep track of the sequence state, checking the
    direction when a smooth scroll starts and only claiming the sequence if
    the scrolled window can scroll in the direction of the current scrolling,
    and otherwise denying it and ignoring any further events until it ends.
    
    The check must be done on the bubble phase because if there are nested
    scrolled windows that can scroll in the same direction, we want to pick
    the inner one and not the outer one. The check also has to be done in the
    scroll signal handler, since we don't have scroll deltas available in
    scroll-begin. After we determine the direction and claim the sequence, we
    can switch to the capture phase and proceed with scrolling as usual.
    
    This also means that there are 3 states of the sequence state: a sequence
    can be claimed, denied, or during the first invocation of the scroll signal
    handler it's undecided. This neatly maps to GtkEventSequenceState, so we
    can reuse it.

 gtk/gtkscrolledwindow.c | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)
---
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index d27d05c432..84eb64abef 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -271,6 +271,8 @@ typedef struct
   guint    propagate_natural_height : 1;
   guint    smooth_scroll            : 1;
 
+  GtkEventSequenceState scroll_state;
+
   int      min_content_width;
   int      min_content_height;
   int      max_content_width;
@@ -408,10 +410,10 @@ static void     indicator_set_over   (Indicator *indicator,
 static void     install_scroll_cursor (GtkScrolledWindow *scrolled_window);
 static void     uninstall_scroll_cursor (GtkScrolledWindow *scrolled_window);
 
-static void scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
-                                    double                    delta_x,
-                                    double                    delta_y,
-                                    GtkEventControllerScroll *scroll);
+static gboolean scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
+                                        double                    delta_x,
+                                        double                    delta_y,
+                                        GtkEventControllerScroll *scroll);
 
 static guint signals[LAST_SIGNAL] = {0};
 static GParamSpec *properties[NUM_PROPERTIES];
@@ -1230,11 +1232,8 @@ captured_scroll_cb (GtkEventControllerScroll *scroll,
 
   gtk_scrolled_window_cancel_deceleration (scrolled_window);
 
-  if (priv->smooth_scroll)
-    {
-      scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
-      return GDK_EVENT_STOP;
-    }
+  if (priv->smooth_scroll && priv->scroll_state != GTK_EVENT_SEQUENCE_DENIED)
+    return scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
 
   return GDK_EVENT_PROPAGATE;
 }
@@ -1319,7 +1318,7 @@ scroll_controller_scroll_begin (GtkEventControllerScroll *scroll,
   priv->smooth_scroll = TRUE;
 }
 
-static void
+static gboolean
 scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
                         double                    delta_x,
                         double                    delta_y,
@@ -1333,6 +1332,18 @@ scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
   state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
   shifted = (state & GDK_SHIFT_MASK) != 0;
 
+  if (priv->smooth_scroll && priv->scroll_state == GTK_EVENT_SEQUENCE_NONE)
+    {
+      if ((may_hscroll (scrolled_window) && ABS (delta_x) > ABS (delta_y)) ||
+          (may_vscroll (scrolled_window) && ABS (delta_y) >= ABS (delta_x)))
+        priv->scroll_state = GTK_EVENT_SEQUENCE_CLAIMED;
+      else
+        priv->scroll_state = GTK_EVENT_SEQUENCE_DENIED;
+    }
+
+  if (priv->smooth_scroll && priv->scroll_state == GTK_EVENT_SEQUENCE_DENIED)
+    return GDK_EVENT_PROPAGATE;
+
   gtk_scrolled_window_invalidate_overshoot (scrolled_window);
 
   if (shifted)
@@ -1388,6 +1399,8 @@ scrolled_window_scroll (GtkScrolledWindow        *scrolled_window,
       g_source_set_name_by_id (priv->scroll_events_overshoot_id,
                                "[gtk] start_scroll_deceleration_cb");
     }
+
+  return GDK_EVENT_STOP;
 }
 
 static gboolean
@@ -1399,8 +1412,8 @@ scroll_controller_scroll (GtkEventControllerScroll *scroll,
   GtkScrolledWindowPrivate *priv =
     gtk_scrolled_window_get_instance_private (scrolled_window);
 
-  if (!priv->smooth_scroll)
-    scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
+  if (!priv->smooth_scroll || priv->scroll_state == GTK_EVENT_SEQUENCE_NONE)
+    return scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
 
   return GDK_EVENT_STOP;
 }
@@ -1412,6 +1425,7 @@ scroll_controller_scroll_end (GtkEventControllerScroll *scroll,
   GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
 
   priv->smooth_scroll = FALSE;
+  priv->scroll_state = GTK_EVENT_SEQUENCE_NONE;
   uninstall_scroll_cursor (scrolled_window);
 }
 
@@ -1421,10 +1435,13 @@ scroll_controller_decelerate (GtkEventControllerScroll *scroll,
                               double                    initial_vel_y,
                               GtkScrolledWindow        *scrolled_window)
 {
+  GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
   double unit_x, unit_y;
   gboolean shifted;
   GdkModifierType state;
 
+  if (priv->scroll_state != GTK_EVENT_SEQUENCE_CLAIMED)
+    return;
 
   state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
 
@@ -2003,6 +2020,8 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
   priv->max_content_width = -1;
   priv->max_content_height = -1;
 
+  priv->scroll_state = GTK_EVENT_SEQUENCE_NONE;
+
   priv->overlay_scrolling = TRUE;
 
   priv->drag_gesture = gtk_gesture_drag_new ();


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