Re: gtk_widget_queue_resize() forgetting allocation



On Mon, 27 Sep 2004, Owen Taylor wrote:

I'm not *completely* sure this should be considered a GTK+ bug, since
what you are doing is fairly marginal. But there is a fairly easy
fix, which would be, in gtk_widget_size_allocate(), do:

  alloc_needed = GTK_WIDGET_ALLOC_NEEDED (widget);
-  GTK_PRIVATE_UNSET_FLAG (widget, GTK_ALLOC_NEEDED);
+  /* Preserve request/allocate ordering */
+  if (!GTK_WIDGET_REQUEST_NEEDED (widget))
+    GTK_PRIVATE_UNSET_FLAG (widget, GTK_ALLOC_NEEDED);

So, I'd actually appreciate it if you could file a bug in bugzilla
with your test case as an attachment and a link to this analysis
in the mail.gnome.org archives.

that is: #153896.

i've run into REQUEST_NEEDED still being set upon ::size-allocate while
debugging another scenario. unfortunately, the exact setup to reproduce
the bug i'm talking about is fairly complicated, it involves a lot of
widgets, a bunch of signal connections, size groups and a scrolled window.
here, siblings of a scrolled window (grouped together with the scrolled
window in a GtkSizeGroup) end up size requested but not properly size
allocated, with flags !REQUEST_NEEDED && ALLOC_NEEDED or with both
flags unset, but the last size-allocate left out.

the actual problem is that gtk_widget_size_request() may be called during size-allocation. with scrolled windows, this is actually not
uncommon, since size-allocate on a scrolled window changes the scroll
adjustments, and that in turn may change scrollbar visibility (which
queues a resize).

but if queue_resize is called during the size-allocation phase, for
some selected widgets inside a hirachy, the following state changes can
occour (widget and sibling don't need to share the same immediate parent,
but they have a common container in their ancestry and share a size-group):

queue_resize:
		REQUEST_NEEDED	ALLOC_NEEDED
ancestry	*		*
widget		*		*
sibling		*		*

size_request:
		REQUEST_NEEDED	ALLOC_NEEDED
ancestry	 		*
widget		 		*
sibling		 		*

start of size_allocate phase:
		REQUEST_NEEDED	ALLOC_NEEDED
ancestry	 		 	(got allocated)
widget		 		 	(currently being allocated)
sibling		 		*

widget calls queue_resize from size_allocate, which causes
queue_resize(sibling) (due to the GtkSizeGroup):
		REQUEST_NEEDED	ALLOC_NEEDED
ancestry	*		*
widget		*		*
sibling		* 		*

end of first size_allocate phase:
		REQUEST_NEEDED	ALLOC_NEEDED
ancestry * * widget * *
sibling		* 		 	(got allocated)

after another size_request and size_allocate round, this will lead to
ancestry and widget being allocated new sizes, but will leave sibling
unallocated (though it was requested a new size).

calling gtk_widget_size_request() on a widget basically means:
1) call size_request on this widget (REQUEST_NEEDED set)
2) call size-allocate on this widget (ALLOC_NEEDED set)
and having REQUEST_NEEDED and/or ALLOC_NEEDED set on a widget requires
3) all its ancestry up to its resize-container have those flags set as well,
4) its resize-container must be in the idle-sizer queue.

so, doing just:
-  GTK_PRIVATE_UNSET_FLAG (widget, GTK_ALLOC_NEEDED);
+  /* Preserve request/allocate ordering */
+  if (!GTK_WIDGET_REQUEST_NEEDED (widget))
+    GTK_PRIVATE_UNSET_FLAG (widget, GTK_ALLOC_NEEDED);
in gtk_widget_size_allocate() isn't good enough.
only calling gtk_widget_size_allocate() will cause proper size
requisition on GtkSizeGroup siblings and care of the ancestry
invariants (3) and (4).

so CVS head now contains:

+Tue Oct  5 17:06:26 2004  Tim Janik  <timj gtk org>
+
+       * gtk/gtkwidget.c (gtk_widget_size_allocate): if REQUEST_NEEDED is still
+       set on ::size-allocate, another size-request has been queued since
+       ::size-request and needs to be requeued.
+
--- gtk/gtkwidget.c     16 Aug 2004 18:38:55 -0000      1.380
+++ gtk/gtkwidget.c     5 Oct 2004 15:08:14 -0000
@@ -2705,7 +2705,13 @@ gtk_widget_size_allocate (GtkWidget      *wid
                      old_allocation.y != real_allocation.y);

   if (!alloc_needed && !size_changed && !position_changed)
-    return;
+    {
+      if (GTK_WIDGET_REQUEST_NEEDED (widget))
+        { /* another resize has been queued */
+          gtk_widget_queue_resize (widget);
+        }
+      return;
+    }

   g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0, &real_allocation);

@@ -2743,6 +2749,11 @@ gtk_widget_size_allocate (GtkWidget      *wid
       GdkRegion *invalidate = gdk_region_rectangle (&widget->parent->allocation);
       gtk_widget_invalidate_widget_windows (widget->parent, invalidate);
       gdk_region_destroy (invalidate);
+    }
+ + if (GTK_WIDGET_REQUEST_NEEDED (widget))
+    { /* another resize has been queued */
+      gtk_widget_queue_resize (widget);
     }
 }


and i want to merge this into 2.4, but thought i'd ask for review first.
(this should also fix the issue reported in #153896)

---
ciaoTJ



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