[gtk/BUG_gtk3_containers_scroll_precedence] containers that can be scrolled should have precedence




commit a1628cb80bf542a375a384270c3619952e78baeb
Author: Nelson Benítez León <nbenitezl gmail com>
Date:   Wed Jan 19 15:33:03 2022 -0400

    containers that can be scrolled should have precedence
    
    over child widgets that also react to scrolling
    like GtkComboBox, GtkScale and GtkSpinButton,
    because otherwise when you're in the middle of
    scrolling the view/window you can involuntarily
    scroll over the widgets and change its values.
    
    This problem can be seen in applications like
    pavucontrol, gnome-tweaks prefs, devhelp prefs,
    and so on.
    
    Fixes issue #3092

 gtk/gtkcombobox.c      |  8 ++++++--
 gtk/gtkrange.c         |  5 +++++
 gtk/gtkspinbutton.c    |  8 ++++++--
 gtk/gtkwidget.c        | 30 ++++++++++++++++++++++++++++++
 gtk/gtkwidgetprivate.h |  1 +
 5 files changed, 48 insertions(+), 4 deletions(-)
---
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index ef4996c027..57fbbd6e0c 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -2707,8 +2707,12 @@ gtk_combo_box_scroll_event (GtkWidget          *widget,
   GtkTreeIter iter;
   GtkTreeIter new_iter;
 
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (widget))
+    return GDK_EVENT_PROPAGATE;
+
   if (!gtk_combo_box_get_active_iter (combo_box, &iter))
-    return TRUE;
+    return GDK_EVENT_STOP;
 
   if (event->direction == GDK_SCROLL_UP)
     found = tree_prev (combo_box, priv->model,
@@ -2720,7 +2724,7 @@ gtk_combo_box_scroll_event (GtkWidget          *widget,
   if (found)
     gtk_combo_box_set_active_iter (combo_box, &new_iter);
 
-  return TRUE;
+  return GDK_EVENT_STOP;
 }
 
 /*
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 774174df52..826e6c4aac 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -3118,6 +3118,11 @@ gtk_range_scroll_event (GtkWidget      *widget,
   GtkRange *range = GTK_RANGE (widget);
   GtkRangePrivate *priv = range->priv;
   double delta = _gtk_range_get_wheel_delta (range, event);
+
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (widget))
+    return GDK_EVENT_PROPAGATE;
+
   gboolean handled;
 
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index 8e65d8a272..878e8c2354 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -1335,6 +1335,10 @@ gtk_spin_button_scroll (GtkWidget      *widget,
   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
   GtkSpinButtonPrivate *priv = spin->priv;
 
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (widget))
+    return GDK_EVENT_PROPAGATE;
+
   if (event->direction == GDK_SCROLL_UP)
     {
       if (!gtk_widget_has_focus (widget))
@@ -1348,9 +1352,9 @@ gtk_spin_button_scroll (GtkWidget      *widget,
       gtk_spin_button_real_spin (spin, -gtk_adjustment_get_step_increment (priv->adjustment));
     }
   else
-    return FALSE;
+    return GDK_EVENT_PROPAGATE;
 
-  return TRUE;
+  return GDK_EVENT_STOP;
 }
 
 static gboolean
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index b6e00115bb..ed85c47d1d 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -11661,6 +11661,36 @@ gtk_widget_get_ancestor (GtkWidget *widget,
   return widget;
 }
 
+/**
+ * gtk_widget_inside_scrollable_container:
+ * @widget: a #GtkWidget
+ *
+ * Private function used by GtkComboBox, GtkRange, GtkSpinButton - See issue #3092
+ *
+ * Returns: whether @widget is inside a scrollable container (like eg.
+ * GtkScrolledWindow, GtkViewPort) and the view can currently be scrolled
+ * i.e. the scrollbars can move because the content excedes the page_size
+ **/
+gboolean
+gtk_widget_inside_scrollable_container (GtkWidget *widget)
+{
+  GtkWidget *ancestor;
+  GtkAdjustment *vadj;
+  gdouble upper, page_size;
+
+  ancestor = gtk_widget_get_ancestor (gtk_widget_get_parent (widget), GTK_TYPE_SCROLLABLE);
+  if (ancestor && GTK_IS_SCROLLABLE (ancestor))
+    {
+      vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (ancestor));
+      g_object_get (vadj, "upper", &upper, "page_size", &page_size, NULL);
+
+      if (!G_APPROX_VALUE ((upper - page_size), 0.0, DBL_EPSILON))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 /**
  * gtk_widget_set_visual:
  * @widget: a #GtkWidget
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 1b7ddf2ff0..722d8f7e1c 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -282,6 +282,7 @@ GList *           _gtk_widget_list_controllers             (GtkWidget
                                                             GtkPropagationPhase  phase);
 gboolean          _gtk_widget_consumes_motion              (GtkWidget           *widget,
                                                             GdkEventSequence    *sequence);
+gboolean          gtk_widget_inside_scrollable_container   (GtkWidget *widget);
 
 gboolean          gtk_widget_has_tick_callback             (GtkWidget *widget);
 


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