Re: [gtk-list] Re: [ repeat message] toggle button: clicked == toggled, and vice versa




Paul Barton-Davis <pbd@op.net> writes:

> >> All I really want is a way to set a button to be "active" without
> >> making it appear that there was user interaction.
> >> 
> >> The error, I think, is that toggle buttons have 2 signals, but anytime
> >> you generate 1, you also generate the other. It seemed to me from
> >> reading the docs that one could connect to "toggled" instead of
> >> "clicked", and would then *every* change to the button
> >> state. Alternatively, one could connect to "clicked", and see only
> >> those changes caused by a mouse click.
> >
> >You'll need to explain a bit more about what you are trying to
> >do - to justify _why_ you want this change, instead of explaining
> >_what_ you want.
> 
> Claim 1:
> The buttons in GTK cannot currently be set programmatically without
> invoking the callbacks attached to the signals sent when user
> interaction occurs.
> 
> Claim 2:
> Many other GUI toolkits have buttons whose state can be set by a
> program in a way that does not make it appear that there was any user
> interaction (i.e. callbacks associated with user interaction will not
> be called).
> 
> Claim 3:
> This is a useful feature.

I'd admit that you have a point here; we actually preserve
this distinction for GtkEditable widgets, where ::changed
means "changed by the user" (more or less). 

I probably could be convinced that we should change this for
1.4, since connecting to ::clicked on a toggle button I'd
consider that everybody _should_ be connecting to ::toggled
instead of ::clicked currently, and the behavior of ::clicked
is undefined.

(This is the great thing about poor documentation, you can 
just declare some random piece of the API "undefined" and
some other piece "defined by the implementation" ;-)

But see below for an approach that, although I didn't mention
it in my last message, has generally proved to be cleaner
and more convenient for me than the other approach I described.
 
> >Several common approaches have been suggested as to how people
> >typically deal with the problem - blocking handlers, using a
> 
> blocking handlers can only be done (currently) if you know the handler
> ID or data pointer. this is a violation of the encapsulation that GTK
> signals offer. the idea that a button knows who has connected to it
> seems totally wrong to me.
> 
> >global boolean, 
> 
> this means that every callback for the signal has to know that this
> button can sometimes be set programmatically, and that it (the
> callback) must always check the boolean. 
> 
> in short, it has to know that its really a different kind of button.
> i am all for deriving a new class, but what to call it, when it has
> the kind of behaviour i would have expected from a ToggleButton anyway?
>  
> >		 connecting your signal handlers after you set
> >up the initial state.
> 
> not much use. the point here is to deal with situations where some
> event source other than the X input devices causes a change in the
> button state; these can happen at any time.

Well, other than the above mechanisms, one way that has worked
generally well for me in a lot of cases, which could be said
to be cleaner is to have:

 void
 set_state_variable (gboolean new_value)
 {
   if (new_value != state_variable)
     {
       state_variable = new_value;
       gtk_toggle_button_set_state (toggle_button, new_state);
     }
 }

And, then, make your listeners look like:

 void
 state_variable_toggled (GtkWidget *widget, gpointer data)
 {
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) != state_variable)  
     {
       state_variable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
       /* Do whatever you need to do on user toggle */
     } 
 }

The idea that the ::toggled signal is a _possible_ notification
of new state can also be used to address Bernhard's problem
with MVC (with V=C)

 - When the value changes in the model, you send a notification
   to the all views that the value has changed, then retrieve
   the value from the controller and if it doesn't match their
   current value, then they change their current value.

 - When you get a "toggled" signal in the controller/view, then you
   retrieve the current value in the widget, and if that
   doesn't match the value in the model, then you change the
   model.

(Which is, of course, is just a generalization of the above code.)

The key point here is that you can't just make your ::toggled
callback just toggle the value in the model. What it needs to
do is sync the value in the model with the value in the view.

> >Any change to make "clicked" not be emitted via
> >gtk_toggle_button_set_active() could potentially break existing
> >code, so would have to be approached with caution.
> 
> I understand this. That's why I was really asking if we could write
> and merge gtk_signal_block_handlers(). That way, I could write code
> that did:
> 
>      gtk_signal_block_handlers (GTK_WIDGET(toggleButton), "clicked");
>      gtk_toggle_button_set_active (GTK_BUTTON(toggleButton)
>      gtk_signal_unblock_handlers (GTK_WIDGET(toggleButton), "clicked");
> 
> and everything would be just fine (albeit a gross kludge :)

I consider this a bad violation of the signal model, because it allows
you to change the state of an object and hide that change from
listeners that have nothing to do with you.

While "clicked" doesn't necessarily indicate a change in state,
"toggled" does, and adding a gtk_signal_blocK_handlers invites:

      gtk_signal_block_handlers (GTK_WIDGET(toggleButton), "toggled");
      gtk_toggle_button_set_active (GTK_BUTTON(toggleButton)
      gtk_signal_unblock_handlers (GTK_WIDGET(toggleButton), "toggled");
 
So, I'd rather, if anything, change the semantics of "clicked"
for 1.4 and document that change.

Regards,
                                        Owen



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