changing usize in ::size-request (bug #155139)



hi all.

matthias just pointed me at #155139, which in a nutshell fails to draw
notebook tabs since we applied:

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

the reason for this can be found in the bug report itself:

static void tab_label_set_size (GtkWidget *window, GtkWidget *label) { int label_width; label_width = window->allocation.width/TAB_NB_MAX; if (label_width < TAB_MIN_SIZE) label_width = TAB_MIN_SIZE; gtk_widget_set_size_request (label, label_width, -1); } static void tab_label_size_request_cb (GtkWidget *window, GtkRequisition *requisition, GtkWidget *child) { GtkWidget *hbox; GtkWidget *nb; nb = child->parent; hbox = gtk_notebook_get_tab_label (GTK_NOTEBOOK (nb), child); tab_label_set_size (window, hbox); }
[...]
 label = gtk_label_new ("new tab");
[...]
g_signal_connect (window, "size_request", G_CALLBACK (tab_label_size_request_cb), child);

that is, in the label's ::size-request handler tab_label_size_request_cb()
(at which point the label itself has already been size requested), the
testcase unconditionally sets the label's usize. that in turn queues a
new resize on the label.
however, due to the handling of the ::size-request signal inside
gtk, this request is later forgotten about (gtksizegroup.c):

      g_signal_emit_by_name (widget,
                             "size_request",
                             &widget->requisition);

      GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);

basically, gtk doesn't support queueing new size requests from
::size-request, and that's been that way for a looong time.

[owen: FYI, applying my if (GTK_WIDGET_REQUEST_NEEDED (widget))
 gtk_widget_size_request (widget); fix from last week did indeed trigger an
 endless resizing loop with the test case and without the set_usize() guards.]

the bug at hand can fairly easily be circumvented in gtk by not queueing a
resize from set_usize() if the new usize doesn't actually change anything.
since this is a reasonable efficiency improvement to make anyway, i've
comitted the appropriate guards to gtk_widget_set_usize_internal(). it
doesn't fix the more fundamental problem of people wanting to adjust widget
sizes around size requisition time though.

ideally, the ::size-request signal would be RUN_LAST. it's not,
due to historic reasons though, and i'm afraid making it RUN_LAST
will significantly affect signal connections in exiting code:

static void
example_size_request_handler (GtkWidget     *widget,
                             GtkRequisition *requisition)
{
  requisition->width += 200; /* increase the size of a stock GtkTextView or similar widget */
  requisition->height += 100;
}

such a handler would be rendered ineffective if the size requisition
default handler is executed *after* ordinary signal handlers (RUN_LAST),
unless example_size_request_handler is connected as GTK_CONNECT_AFTER.

so in order to deal with widget size adjustments around ::size-request time,
i see these possibilities:
1) make ::size-request a RUN_LAST signal as it ought to be. this should only
   be done, when people are willing to put some work into porting their
   applications to a new gtk+ version anyway, e.g. around gtk+-3.0.
2) introduce a ::custom-size or similar signal that is emitted directly
   before ::size-request, and can be used by users to setup resizing handler
   connections like the above (tab_label_size_request_cb, example_size_request_handler).
3) preserve the current state of affairs, where size adjustments that queue
   a new resize during ::size-request are simply not supported.
4) make ::size-request a NO_RECURSE signal, and introduce a new private flag for
   widgets (or similar mechanism like a private in-request widget list), which
   can be used to detect queueing resizes during emission of ::size-request.
   in that case, gtk_widget_queue_resize() could simply emit_by_name (widget,
   "size-request"); which will cause a the current emission of ::size-request
   to be restarted after the user handler queueing a resize returned.
   that is, make ::size-request deal with recursive resize requests the way we
   handle GtkAdjustment::value-changed.
   this may still be incompatible with some existing user code setups, but
   hopefully less so than simply making ::size-request RUN_LAST and as a
   consequence, the caveats from (1) about applying this around a 3.0 change
   only probably apply here as well.

for the first three cases, it might actually make sense to add some magic to the
signal code, to issue a warning once per runtime, for !AFTER signal connections
to ::size-request.

---
ciaoTJ



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