diff options
author | Christian Hergert <chergert@redhat.com> | 2023-01-30 12:36:22 -0800 |
---|---|---|
committer | Christian Hergert <chergert@redhat.com> | 2023-01-30 12:36:22 -0800 |
commit | b962f2b2f87fc456fc49f3e94212326c0577283e (patch) | |
tree | b2beebc70b68e8585dcf434a14810cc132b6e55f | |
parent | cab5f2bd8d234347aec76177411e1b99adce44b7 (diff) | |
download | gtk+-b962f2b2f87fc456fc49f3e94212326c0577283e.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.c | 57 |
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 |