[gtk-list] Re: Repeat: Clicking on widgets - consistency question



/*
>On Wed, 2 Jun 1999 andrew@cogent.ca wrote:
>> 
>> Two problems here:
>> 
>> 1) Signals and events are orthogonal.  Signals happen to be created by
>>    events, but this is implementational.  The events should happen
>>    before the signals, logically, and are associated with any
>>    GtkObject that has an event-trapping interface (in X-Windows, a
>>    Window).  The behaviour of a GtkWidget in the creation of signals
>>    should be irrelevant to its event behaviour.  Therefore:
>
>I think you don't fully understand how it works. Events are only
>delievered to the Gtk layer in signal form; widgets never deal with an
>event except in signal handlers. Widgets have no "event behavior" separate
>from signals.

I'm the first to admit that I don't fully understand how the internals
of the GTK+ library are implemented.  I was looking in from the
outside and trying to make sense of the behaviour as it relates to the
documentation.

>Only GtkWidget and subclasses have an "event-trapping" interface; no
>X window is a GtkObject.

This is where I got lost apparently.  In GTK I can issue the command

    gtk_signal_connect (GTK_OBJECT(b1), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkButton");

In the documentation, there is some implication that a "signal" is a
synthesized high level thing that is particular to a widget's
behavior.  An "event" is a low-level thing that is particular to the
input mechanism and underlying window system.  GTK works in "signal"
space, and GDK works in "event" space.

This button press event, to the casual observer, appears to be
entirely independent of any particular behaviour of the button.  Its
handler function is event-specific (defferentiable from a signal
handler).  The button does things with the event to generate signals,
but the event occurs due to an outside influence from GDK.

>From your response, I begin to suspect that the implementation of
"events" in GTK space is really up to the widgets.  They get some
internal GDK call that they may, if they wish, turn into a
"button_press_event".  If the widget does not do this, then no
"button_press_event" occurs.  Is this wrong?  It certainly sounds
inefficient.

Alternately (and I think more likely), if a "button_press_event" is
injected into GTK space by GDK, then it should exhibit consistent
behavior across all GTK widgets, subject to the rules for halting
propagation.  It should, at the very least, be deliverable to all
widgets.  Ideally, all widgets would perform the same kind of
propagation as well, so that programs that wanted to deal with widgets
as GtkWidgets could actually do so.

>>      > GtkButton "handles" the event by returning TRUE from its
>>      > handler so the event isn't propagated to the parent.
>>    breaks the model, and even it if it was defined to *be* the model,
>>      > GtkEntry returns FALSE, indicating that its parent should also
>>      > get the event.
>>    is inconsistent.  Worse,
>
>It isn't inconsistent; the rule is that an event from the Gdk layer causes
>a signal emission on the widget owning the event window; if that emission
>returns FALSE then a signal is emitted on the widget's parent; if that
>returns FALSE then things continue, etc.

It's only not inconsistent with the rules because the rules say there
are no rules regarding *whether* a widget should propagate the event.
You are telling me implementation, and I am trying to get a handle on
intent.  As it currently stands, the intent *is* "there are no rules",
but this does not represent good long-term design.

>>      > GtkCheckButton should emit button press, I can't think of
>>      > what's going on.
>>    suggests that widget writers have no clear idea what they ought to
>>    be doing with the events they receive.
>
>It simply means that I'm not familiar with how that particular widget
>behaves. I'm sure the designers of said widget can tell you exactly what's
>going on.

I could too if I read the code, but that would not really address the
philosophical issues.  I said "what they ought to be doing".  I am
looking for the policy statement here.  The absence of such a
statement is my main concern.

>> 2) GTK+ is a pseudo-OO system, and IMHO that implies that it should
>>    exhibit consistent behaviour across all GtkObjects or subsets
>>    thereof.  The absence in consistent treatment of events, which
>>    widgets should *never* handle (meaning halt delivery), makes it
>
>You never actually explain why this is the only sensible behavior...

No, looking at it, it probably is not.  But to have random behaviour
makes treating widgets at the GtkWidget level essentially impossible.

>Also, widgets only prevent propagation; the signal corresponding to the
>event is always emitted on at least one widget.

I'll try to find out why I am not seeing this.  As you said, it may be
a bug in my test code.  The code at the bottom of this message bears
out your statement.

>>    essentially impossible to take advantage of the otherwise carefully
>>    considered OO implementation in any but the most trivial ways.  As
>>    a simple example: I want to write a program where the widgets
>>    behave normally to press/release etc.  Now I want to make ALL
>>    widgets respond to ALT-press by popping up a type-dependent
>>    configuration dialog.  This is quite simply impossible without
>>    consistent, defined, enforced event behaviour.
>
>This is nonsense. OO design does not mean that all objects behave the same
>way. Different objects are, well, different. They do share some interface;
>but you have to justify why eaach interface should be shared. There is no
>a priori reason why this one should be.

OO design does not enforce similar behaviour of all widgets at any
point in the class hierarchy, but good design suggests it.  If I want
to write a bit of code that treats all widgets at the GtkWidget level
in the hierarchy, so I can generically place, size, or configure them
without having to write specific code by type, then there needs to be
consistent behaviour *at that level*.  These are not "shared"
interfaces, they are inherited, and that difference is crucial.
Subclasses behave like their superclasses for activities defined in
the superclass.  Over-riding these behaviours should be to modify them
or hook into them, not to destroy them.  I agree that a GtkList should
not indent like a GtkButton when I click on it, but I do not agree
that they should be allowed to fundamentally differ in behaviours that
are common through GtkWidget.

>However, the interface *is* shared - they all do behave the same way *if
>the interface is invoked*. The "inconsistent" behavior you are seeing is
>the same "inconsistency" caused by clicking on one widget and not another
>- i.e. the interface is invoked in one case and not another. This has
>nothing whatsoever to do with the object hierarchy; in fact it's
>implemented by the main loop, in gtkmain.c.

The presence or absence of the invocation is currently up to the
individual widget writer.  I can see no reason why this is not made
consistent.  It would make generic programming possible.  The absence
of such consistency means that when my program encounters a new widget
through a dynamically loaded module, it cannot possibly offer a
reasonable default behaviour for it, because the widget itself does
not offer a knowable interface.

>It so happpens that it is possible to get the effect you want, because you
>can connect to signals and override the default behavior. You can also
>stop signal emissions, or install emission hooks to monitor all emissions
>of a given signal.

On a widget-by-widget basis, determined empirically.  My whole point
is that this kind of kluging should not be necessary.  The GTK
implementation is not wrong, it just appears to be under-specified.  I
am hoping that we can either extract the implicit specification from
those who know it, or if it does not exist implicitly, to create such
a specification explicitly and gain concensus on its use.

>In fact the feature you're griping about makes it possible to do what you
>want - if you just connected to all button press signals and threw up a
>dialog, you'd throw up ten dialogs, some describing invisible widgets, for
>every button press. Which would be brain damaged. What you want to do is
>respond to leaf widgets only.

Ok, and if I knew how a widget would behave, I could write code for
it.  The fact is, they all behave differently and I cannot know
without emperical testing which way it is.  Should I ask the user who
adds a widget to my program to go through a few tests and try to infer
how the widget is implemented?  That would be pretty lame.

>If you do want the 10 dialogs, just walk up the widget tree from the leaf
>- yet another way to implement what you're describing.

Again, if I only knew how it would react I could plan for it.  Walking
the tree won't work because sometimes an event hits more than one
widget in the tree, and sometimes it doesn't.  No matter how I program
the behaviour, it will fail for some widgets.  Perhaps we should put
some flags in GtkWidget that define how it will respond to and
propagate the various GDK events. (joke)

>> I propose that before this situation degenerates further that the GTK+
>> core team come up with a statement of intent with regard to event
>> handling, and then ask widget creators to stick to it.  The goal
>> should be to create a 100% consistent framework for widget creation
>> that allows the easy extension of the widget set without breaking some
>> of the fundamental assumptions on which extensible programs are based.
>> I am perfectly willing to put corporate staff on fixing the currently
>> broken widgets, but I cannot do it without a policy statement from the
>> GTK+ maintainers.
>> 
>> I would also be more than happy to assist in the development of the
>> policy statement.
>
>I think you should take the time to understand Gtk's current design
>before you recommend sweeping backward-incompatible changes. Otherwise
>no one is going to listen to you. A good way to become more familiar
>with the design is to try actually implementing some of the things
>you're talking about (either the event model changes or the
>ALT-button-press behavior).

Re-read the previous paragraph.  I proposed a "statement of intent".
You can read "design" or "guideline" if you prefer.  This would not
induce sweeping changes, and backward incompatibility is not uncommon
in the GTK evolution.  Besides, those programs that are depending on a
core behaviour that is purely at the (random?) discretion of the
widget implementor should probably be fixed anyway.

As an aside, you say I should "understand Gtk's curent design".  I
would dearly love to do just that.  Unfortunately it is not documented
at this level, and I suspect that it is not in fact specified at this
level.  From observation, there is no clear pattern of behaviour for
widgets relative to GDK events.  This implies that the design does not
specify this behaviour.  If you know otherwise, you can simply point
me at the relevant passage in the GTK Widget Designer's Guideline and
short-circuit this entire conversation.

As an interesting demonstration of incomplete specification, look at
this excerpt from the GTK mailing list:

:> For example: suppose I have a pair of radio button which control whether
:> data is saved to an external file or not. The user can select "on" with the
:> mouse OR type "save on" in the command line. Either one of these actions
:> should result in the "On" button being depressed, the "Off" button being
:> undepressed and the 'save' variable being set to 1.
:> 
:> Unfortunately, when the screen is redrawn, and the button is toggled,
:> another signal is emitted, (toggled) and I don't want this to happen.
:
:You need to save some state stating which way the callback is being called.
:One of way of doing this is to make a single function that does the work,
:but that is not called directly by the callbacks.  Instead, there are two 
:callback routines, each of which calls the same routine but passing in a flag 
:so that one routine knows who's calling it.  Then you block the other 
:callback from happening with gtk_signal_handler_block().

This is an interesting case where the widget creator decided that a
signal would be invoked if the user presses a button or the if the
toggle state is changed programmatically.  It was an arbitrary choice,
and an argument can be made for or against, depending on the
circumstances of each user.  Another implementor of say, GtkEntry,
might implement this so that a change from the keyboard triggers a
signal, but a programmatic change does not.  Perfectly reasonable.
The trouble is, this is inconsistent behaviour within the widget set.
In fact, I would vote here that the behaviour be based on a flag on
all widgets (signals_occur_on_program_changes = TRUE or FALSE), and
that widget coders be asked to explicitly treat the two cases
separately.  Other widget sets do this, because there is no default
"right" answer.  In any case, if there is no policy on how a widget
should behave to questions like this, we are reduced to
experimentation and kluge code on a widget-by-widget basis as
suggested above in order to work around arbitrariness in the widget
implementation.

Generally, if we do not have a set of guidelines, expectations, design
standards, or whatever you want to call them, then we cannot
accurately identify bugs in GTK.  How can I know whether a particular
behaviour is a bug or a "quirk" of the implementation?  If it is
defined to be a bug, I can fix it and everybody will benefit.  If it
is unspecified, then it becomes a quirk, and everybody using the
widget must simply code for the quirk.  In the end, the resulting
application will be brittle, possibly breaking from one GTK release to
the next, and will exhibit these quirks at run-time.  I will install a
system for a customer, and it will have "quirks".  If we all as a
group generate code that is intended to stand public scrutiny, I would
not like to see a review in PC magazine or Dr. Dobbs that says, "cute
system, good potential, but quirky".  The sooner we address this as a
group, the easier it will be to swallow for all of us with existing
code.

>That said there are many rough edges in the toolkit, and lots of them
>would be very worthwhile to work on. I hope you will choose to do that. 

And we are.  We are preparing patches for all of the widgets to
complete their argument implementations.  I don't want to sound like I
am idly bitching here.  We are willing to work to improve GTK - it is
a tool with huge potential.  It has just been my experience that
without development guidelines, large groups of people generate
divergent code that is difficult to maintain, and eventually all
programming with it is reduced to empirically determining the
ideosyncratic behaviours of each module in order to get something that
just works.  In that environment, the lack of a guideline makes it
impossible to identify whether a behavior is a bug or an act of
artistic expression, and nobody can write a program that performs any
kind of meta-processing or has any kind of expandability.

>Perhaps event handling can be improved, even - but you will need more
>concrete reasons, tied to the actual code and its behavior.

Are you saying that the "actual code and its behavior" define the
specification?  I hope not.

The issue with unpredictable event handling is just one example of
where a guideline seems to be warranted.  There may be others.  I
expect that the GTK maintainers debate issues like this regularly.
This is just the first one that has come to my attention.  As they
come up, I think that we should all be prepared to address them.

Andrew

As a footnote: I found the problem with GtkCheckButton not getting the
    button_press_event.  The issue was that GtkCheckButton falsely
    sets its flag GTK_NO_WINDOW, so that it must theoretically be
    placed inside a GtkEventBox in order to get its events.  In fact,
    GtkCheckButton *does* have a window, so the event never reaches
    the GtkEventBox, so no code gets run.  In the code below, the
    GtkCheckBox gets events, even though it reports not having a
    window.  This appears to be a bug in GtkCheckBox, but looking at
    the implementation it is clear that the writer was playing an
    interesting game with the GTK_NO_WINDOW flag, and I think it
    serves to reinforce my previous rant about widget coding
    guidelines.

*/

/* cc -Wall -g -o gtkbug gtkbug.c `gtk-config --libs --cflags` */

/*
 * Run with $ gtkbug 0
 *    Press the GtkButton and GtkCheckButton to see the event stop at
 *    the button.  Press GtkEntry and GtkFixed to see the event
 *    propagate to the window.  These behaviours are inconsistent.
 *
 * Run with $ gtkbug 1
 *    Press the GtkButton, GtkCheckButton, GtkFixed to see the event
 *    stop at the first widget.
 *    Press the GtkEntry to see the event propagate to the GtkFixed,
 *    even though the GtkEntry attempted to stop the event.  This is
 *    apparently a bug in the GtkEntry.
 */

#include    <stdio.h>
#include    <stdlib.h>
#include    <gtk/gtk.h>

int RetState = FALSE;

static int button_handler (GtkObject* obj, GdkEvent* ev, void* data)
{
    printf ("Button press on %s\n", (char*)data);
    return (RetState);
}

static void exit_handler (GtkObject* obj, void* data)
{
    exit (0);
}

int main (int argc, char** argv)
{
    GtkWidget       *win, *fix, *b1, *b2, *b3;

    if (argc > 1)
        RetState = (atoi (argv[1]) > 0 ? TRUE : FALSE);

    gtk_init (0, NULL);
    win = gtk_window_new (0);
    fix = gtk_fixed_new ();
    gtk_container_add (GTK_CONTAINER(win), fix);
    b1 = gtk_button_new_with_label ("gtk_button");
    b2 = gtk_check_button_new_with_label ("gtk_check_button");
    b3 = gtk_entry_new_with_max_length (32);

    gtk_fixed_put (GTK_FIXED(fix), GTK_WIDGET(b1), 0, 0);
    gtk_fixed_put (GTK_FIXED(fix), GTK_WIDGET(b2), 0, 40);
    gtk_fixed_put (GTK_FIXED(fix), GTK_WIDGET(b3), 0, 80);

    gtk_signal_connect (GTK_OBJECT(b1), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkButton");
    gtk_signal_connect (GTK_OBJECT(b2), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkCheckButton");
    gtk_signal_connect (GTK_OBJECT(b3), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkEntry");
    gtk_signal_connect (GTK_OBJECT(win), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkWindow");
    gtk_signal_connect (GTK_OBJECT(fix), "button_press_event",
                        (GtkSignalFunc)button_handler, "GtkFixed");
    
    gtk_signal_connect (GTK_OBJECT(win), "destroy",
                        (GtkSignalFunc)exit_handler, NULL);

    gtk_widget_show_all (win);
    gtk_main ();
    exit (0);
}



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