Re: GtkButtons and RUN_FIRST/RUN_LAST



Joel Becker <jlbec evilplan org> writes:

> On Sat, Oct 27, 2001 at 12:58:06AM +0200, Tim Janik wrote:
> > pretty much yes.
> > 
> > we don't have an emission model of:
> > a) run user-before handlers
> > b) run default handler
> > c) run normal user handlers
> > 
> > because on occasions we really need to do stuff before any
> > user handler is being run. so we ended up with:
> > 
> > 1) RUN_FIRST default handler (accumulate return value)
> > 2) run normal user handlers (accumulate return values)
> > 3) RUN_LAST default handler (accumulate return value)
> > 4) run user-after handlers (accumulate return values)
> > 5) RUN_CLEANUP default handler (return value is ignored,
> >    can't be intercepted)
> > (stages (1)-(4) are interceptable with emit_stop)
> > 
> > this model is, as a superset, at least functionally equivalent to the
> > above a)b)c) thingy, just the connect functions are named a bit differently.
> 
> 	It isn't a superset, though.  In the a)b)c) model, the user
> handler connected with default semantics can expect the widget to be
> done with its state.  The latter model expects that all user handlers
> will know the inconsistent state and behave accordingly.  This is an
> unecessary burden (IMHO) on library user.
> 	I understand the logic of "Hey, we really need this to run
> first".  So yes, under that logic I can see why a button signal isn't
> necessarily a candidate.  But I still think that application developers
> have a right to expect things to be in order when their handlers run,
> unless they explicitly ask for something different.  I'd want to see:
> 
> 1) RUN_FIRST default handler (things we *have* to do first)
> 2) connect_before() user handlers (they know what they asked for)
> 3) RUN_LAST default handler (better known as RUN_DEFAULT.  Normal stuff)
> 4) run normal user handlers (user needs no assumptions here)
> 5) connect_after() user handlers (again, they know what they want here)
> 6) RUN_CLEANUP default handler
> 
> 	I really just dislike forcing the user to understand the
> limitations, inconsistencies, and other artifacts of the fact that
> things are half-done when their handler is run.  But I can work with the
> current system for what I have to do, and I know the signal system's
> ordering, so I will be ok. :-)

As a blanket statement:

 If you need to connect to a signal _after_ the default signal runs,
 then your code is either wrong, or a bad hack to work around a
 deficiency in GTK+.

The reason why this is the case, is that if know you must connect
after the default handler, then you are guessing what the default
handler will do and acting based on that guess. This is extrordinarily
fragile.

A signal that says "do something" like ::clicked, must be handled by a
single entity. If I was designing GTK+ from scratch, I'd make
::clicked have a boolean return value with true-stops-emit, just like
event signals do now.

If a subclass of GtkButton has a default handler for clicked, then
that default handler is the entity handling the signal.  You should be
able (though you can't with the old RUN_FIRST clicked) to replace that
handler with a different one. Connecting after (all you can do with
the old RUN_FIRST clicked) is guessing what is going to happen
and is wrong.

Basically, there is very little excuse for RUN_FIRST signals - if
there is some invariant that GTK+ must enforce, then it should enforce
it _before_ emitting the signal. Otherwise, subclasses of the widget
that want to supply a default handler will be exposed to the broken
invariant.

You can basically divide signals into two groups

 - "do something" signals
 - "notification of state change" signals.

As described above, "do something" signals should only be received by
one entity, and RUN_FIRST is essentially broken for them ... it's
saying that the subclass always wins, which reduces flexibility.

RUN_FIRST is a little more justified for notification of state change
signals ... in this situation, order is essentially irrelevant, so by
letting widgets get notification first, we can make it a little more
likely that the application will see a consistent state. (::state_set
would be a good example of this.)

Regards,
                                        Owen

P.S. - A better design for GTK+ signals would have been if there was
_only_ RUN_LAST, connect() meant connect_after(), and there was
a special connect_before() you could use if you meant it. 
In conjuction with making all "do something" signals TRUE-stops-emit,
this would give us a safe, robust, and _simple_ way of doing
things. Too late now, of course.

After all, if RUN_FIRST behavior was truly necessary in some
situation, then you could always use a separate virtual function and
signal.



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