[gtk/scrollable_parents_have_precedence] scrolling a container should not scroll child widgets




commit 44ce9515195d60a92898e321dfdcec459b8f8044
Author: Nelson Benítez León <nbenitezl gmail com>
Date:   Thu Jan 20 20:02:38 2022 -0400

    scrolling a container should not scroll child widgets
    
    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      |  5 +++++
 gtk/gtkrange.c         |  4 ++++
 gtk/gtkspinbutton.c    |  4 ++++
 gtk/gtkwidget.c        | 30 ++++++++++++++++++++++++++++++
 gtk/gtkwidgetprivate.h |  2 ++
 5 files changed, 45 insertions(+)
---
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index c29896319e..cd8b0d1bd7 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -1714,6 +1714,11 @@ gtk_combo_box_scroll_controller_scroll (GtkEventControllerScroll *scroll,
   GtkTreeIter iter;
   GtkTreeIter new_iter;
 
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (GTK_WIDGET (combo_box)))
+    return GDK_EVENT_PROPAGATE;
+
+
   if (!gtk_combo_box_get_active_iter (combo_box, &iter))
     return GDK_EVENT_PROPAGATE;
 
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index c3c270e6c3..9f3e684d20 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -2206,6 +2206,10 @@ gtk_range_scroll_controller_scroll (GtkEventControllerScroll *scroll,
   gboolean handled;
   GtkOrientation move_orientation;
 
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (GTK_WIDGET (range)))
+    return GDK_EVENT_PROPAGATE;
+
 #ifdef GDK_WINDOWING_MACOS
   scroll_unit = 1;
 #else
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index fb5ffbf310..6a448db2b3 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -863,6 +863,10 @@ scroll_controller_scroll (GtkEventControllerScroll *Scroll,
 {
   GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
 
+  /* Scrolling the parent window/container takes precedence - Issue #3092 */
+  if (gtk_widget_inside_scrollable_container (widget))
+    return GDK_EVENT_PROPAGATE;
+
   if (!gtk_widget_has_focus (widget))
     gtk_widget_grab_focus (widget);
   gtk_spin_button_real_spin (spin, -dy * gtk_adjustment_get_step_increment (spin->adjustment));
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index afe7e9224e..3fc20c3168 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -7165,6 +7165,36 @@ gtk_widget_get_ancestor (GtkWidget *widget,
   return widget;
 }
 
+/*< private >
+ * 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)
+    {
+      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_get_settings:
  * @widget: a `GtkWidget`
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index e1e336e6e6..b1d3c84271 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -276,6 +276,8 @@ void              _gtk_widget_update_parent_muxer          (GtkWidget    *widget
 GtkActionMuxer *  _gtk_widget_get_action_muxer             (GtkWidget    *widget,
                                                             gboolean      create);
 
+gboolean          gtk_widget_inside_scrollable_container   (GtkWidget *widget);
+
 gboolean          gtk_widget_has_tick_callback             (GtkWidget *widget);
 
 gboolean          gtk_widget_has_size_request              (GtkWidget *widget);


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