summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Hergert <chergert@redhat.com>2023-01-30 12:36:22 -0800
committerChristian Hergert <chergert@redhat.com>2023-01-30 12:36:22 -0800
commitb962f2b2f87fc456fc49f3e94212326c0577283e (patch)
treeb2beebc70b68e8585dcf434a14810cc132b6e55f
parentcab5f2bd8d234347aec76177411e1b99adce44b7 (diff)
downloadgtk+-wip/chergert/fix-gizmo-warnings.tar.gz
range: handle GtkAdjustment::changed at next framewip/chergert/fix-gizmo-warnings
GtkAdjustment have values that are often tied to the "virtual" allocation size of a widget, such as a GtkScrollable. Thusly, when size_allocate() is called on GtkScrollable, the adjustment will emit ::changed. But a size_allocate() indicates we're in the GDK_FRAME_CLOCK_PHASE_LAYOUT and that is too late to perform changes that will affect allocation. Therefore, this defers handling of the changed signal to the next frame so that we do not perform operations that can cause widgets within the hierarchy to loose their allocation. This fixes a very common issue, where we see something like: Trying to snapshot GtkGizmo 0x... without a current allocation which was caused by GtkViewport changing it's adjustment in size_allocate() and cascading to a GtkScrolledWindow->GtkScrollbar->GtkRange->GtkGizmo. # Conflicts: # gtk/gtkrange.c
-rw-r--r--gtk/gtkrange.c57
1 files changed, 52 insertions, 5 deletions
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index ebe4cb706b..c3f613f2da 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -101,6 +101,8 @@ struct _GtkRangePrivate
int slide_initial_slider_position;
int slide_initial_coordinate_delta;
+ guint changed_tick_id;
+
guint flippable : 1;
guint inverted : 1;
guint slider_size_fixed : 1;
@@ -683,6 +685,12 @@ gtk_range_set_adjustment (GtkRange *range,
g_signal_handlers_disconnect_by_func (priv->adjustment,
gtk_range_adjustment_value_changed,
range);
+ if (priv->changed_tick_id)
+ {
+ gtk_widget_remove_tick_callback (GTK_WIDGET (range),
+ priv->changed_tick_id);
+ priv->changed_tick_id = 0;
+ }
g_object_unref (priv->adjustment);
}
@@ -1308,6 +1316,13 @@ gtk_range_dispose (GObject *object)
gtk_range_remove_step_timer (range);
+ if (priv->changed_tick_id)
+ {
+ gtk_widget_remove_tick_callback (GTK_WIDGET (range),
+ priv->changed_tick_id);
+ priv->changed_tick_id = 0;
+ }
+
if (priv->adjustment)
{
g_signal_handlers_disconnect_by_func (priv->adjustment,
@@ -2339,16 +2354,22 @@ gtk_range_drag_gesture_end (GtkGestureDrag *gesture,
stop_scrolling (range);
}
-static void
-gtk_range_adjustment_changed (GtkAdjustment *adjustment,
- gpointer data)
+static gboolean
+gtk_range_adjustment_changed_cb (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer unused)
{
- GtkRange *range = GTK_RANGE (data);
+ GtkRange *range = GTK_RANGE (widget);
GtkRangePrivate *priv = gtk_range_get_instance_private (range);
double upper = gtk_adjustment_get_upper (priv->adjustment);
double lower = gtk_adjustment_get_lower (priv->adjustment);
- gtk_widget_set_visible (priv->slider_widget, upper != lower || !GTK_IS_SCALE (range));
+ priv->changed_tick_id = 0;
+
+ if (upper == lower && GTK_IS_SCALE (range))
+ gtk_widget_hide (priv->slider_widget);
+ else
+ gtk_widget_show (priv->slider_widget);
gtk_widget_queue_allocate (priv->trough_widget);
@@ -2364,6 +2385,32 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment,
* can input into the adjustment, not a filter that the GtkRange
* will enforce on the adjustment.
*/
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gtk_range_adjustment_changed (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ GtkRange *range = GTK_RANGE (data);
+ GtkRangePrivate *priv = gtk_range_get_instance_private (range);
+
+ /* There is a good chance that we will get this notification
+ * from size_allocate() of another widget. That means we are
+ * already in the LAYOUT phase of the frame clock.
+ *
+ * It's not allowed to call gtk_widget_show()/hide() from that
+ * point because it will result in a loss of allocations which
+ * are needed to complete the current frame.
+ *
+ * Instead, defer until the next frame to perform adjustments.
+ */
+ if (priv->changed_tick_id == 0)
+ priv->changed_tick_id =
+ gtk_widget_add_tick_callback (GTK_WIDGET (range),
+ gtk_range_adjustment_changed_cb,
+ NULL, NULL);
}
static void