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



On Tue, 2004-10-12 at 19:39 +0200, Tim Janik wrote:
> On Tue, 12 Oct 2004, Owen Taylor wrote:
> 
> > On Tue, 2004-10-12 at 18:04 +0200, Tim Janik wrote:
> >
> >> 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);
> >
> > How does this actually cause drawing to break? Is the problem that the
> > requisition of the label is changing without a new call to
> > gtk_label_size_request()? I don't see how that would matter, offhand.
> 
> i don't either, and i didn't bother debugging. basically, if queue_resize
> got called after the ::size-request default handler and we then unconditionally
> GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED); we enter an unsupported
> state (since invariants got broken) after which anything can follow, so i
> rather not continue debugging at this point.

I did go ahead and debug the rest, and what is going on is a bit
interesting; the problem comes from _gtk_container_queue_resize(); the
way it sets the flags is:

 while (!GTK_WIDGET_ALLOC_NEEDED (widget) || !GTK_WIDGET_REQUEST_NEEDED (widge\t))
    {
      GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
      GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
      if ((resize_container && widget == GTK_WIDGET (resize_container)) ||
          !widget->parent)
        break;
                                                                                
      widget = widget->parent;
    }

So, the fact that my last patch changed a stray REQUEST_NEEDED to a 
stray REQUEST_NEEDED *and* a stray ALLOC_NEEDED causes the loop above
to terminate prematurely.

So, the quick fix to eliminate this class of problems is to simply loop
all the way up to the resize container always. It's not the real fix...
for the real fix, we have to eliminate the broken invariants in the
first place.

[...]

> >>  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.
> >
> > What's the use case? Generally, I'd expect the example to use:
> >
> >    gtk_widget_set_size_request (label, label_width, -1);
> >
> > With
> >
> >    *requisition.width = label_width;
> >
> > This won't actually work because it will break GtkLabel's alignment
> > code, but you could introduce an intermediate container and get it to
> > work.
> 
> i don't understand you here. the use case is given by the bug report, the
> label size depends on the toplevel allocation. i also gave another example
> for a size-requisition patch up where i simply grew a requested
> GtkTextView size.

Making the label size depend on toplevel allocation should be in the
toplevel's  *size allocate*. Doing it in the size request is just silly.
Growing the text view size should work currently. But I agree that
it's by far the best if you can call gtk_widget_size_request() at any
point and all the invariants hold.

> >> 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.
> 
> let me add:
> 5) change gtk_widget_size_allocate() to re-queue a resize if GTK_REQUEST_NEEDED
>     remains set upon function exit. this keeps the invariant of ::size-request
>     having to follow every queue_resize (for DRAWABLE widgets).
>     this may trigger endless resizing loops, but only if users unconditionally
>     cause queue_resize from ::size-request signal handlers, in which case it
>     can be argued they get what they deserve (requested). note that the testcase
>     from #155139 is such a scenrio though. it only happens to work with
>     recent CVS because i made set_usize() queue a resize conditionally.

Silly question - doesn't it fix everything if we simply just move
unsetting the GTK_REQUEST_NEEDED flag before we emit ::size-request? 

Of the invariants I outlined earlier, the one that is broken is 
"REQUEST_NEEDED on child implies REQUEST_NEEDED on parent.

 When we emit ::size-request on the widget, one two things can happen:

 - Either no descendant calls gtk_widget_queue_resize(), in which case 
   REQUEST_NEEDED will be unset on all children

 - Or a descendant calls gtk_widget_queue_resize() in which case the 
   REQUEST_NEEDED flag will be set again on the widget.

> mundane cases can easily be constructed where such circular scenarios either:
> - loop endlessly due to ping-pong size requests (the lable<->window dependancy might
>    request alternating sizes like: 4,5,4,5,4,5,4,5), or
> - get into a feedback loop (with size requests: 8,...,256,...,65536,...)
> and due to float-round-off, font-sizes, theme settings, etc. these pathologic cases
> might not even always be triggered.
> 
> if we want to completely prevent such scenarios, i might even add:
> (6) explicitely catch queue_resize() during ::size-request emissions by a mechanism
>      similar to that suggested in (4), and issue a warning.
> 
> i think we should stop to look for more use cases for queue_resize() from ::size-request
> though. experience shows that code is usually used in unintended or not planned for
> ways, so we should rather make a general decision on how to deal with recursive resize
> requests, than to focus on getting arbitrary cases to work and ignore others.

I think we should give people the rope ... yes, calling queue_resize()
out of ::size-request or ::size-allocate can easily trigger loops, but
especially when the entire window content is known, it can be safe and
useful in cases... you can use it to emulate height-for-width for a
widget, for example.

Regards,
							Owen

Attachment: signature.asc
Description: This is a digitally signed message part



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