Re: new accelerator system



On 22 May 1998, Owen Taylor wrote:

> > an object can have a certain keyvalue/modifier combination associated with the
> > emission of a certain signal. those associations are called "accelerator
> > bindings" and are handled by a GtkAccelGroup.
> > an object to be subject of an accelerator binding has to meat certain
> > requirements:
> > it needs to introduce the "add-accelerator" and "remove-accelerator" signals
> > in its class branch, fitting the following signatures:
> > 
> > void	(*add_accelerator)	(GtkObject	*object,
> > 				 GtkAccelGroup	*ac_group,
> > 				 guint		 activation_signal_id,
> > 				 guint		 keyval,
> > 				 guint		 modifiers,
> > 				 GtkAccelFlags	 accel_flags);
> > void	(*remove_accelerator)	(GtkObject	*object,
> > 				 GtkAccelGroup	*ac_group,
> > 				 guint		 activation_signal_id,
> > 				 guint		 keyval,
> > 				 guint		 modifiers);
> > 
> > the marshaller functions for these signals are provided in gtkaccelgroup.c.
> 
> So - these signals are not in GtkWidget and have to be separately
> added to each widget that wants to be able to handle an accelerator???

nope, these signals will substitute the current GtkWidget::install_accelerator
and GtkWIdget::remove_accelerator signals.

> (This seems like unecessary complexity ;-) Also, it is quite possible
> that someone would want to add some accelerators that are not
> associated with a "typical" accelerator widget.
> 
>  gtk_accel_group_add (accel_group, cancel_button, "clicked",
>                       GDK_ESCAPE, 0, 0);

i don't see what would be "untypical" in this example.

> IMO, these signals should be provided by GtkWidget with default
> handlers. If you want to make the default handlers public to

yep, it's just that gtkaccelgroup.c implements those handlers.

> allow other GtkObjects to also handle adding accelerators - well
> that would be OK...

yep, that's the intend

> > the `binding_flags' argument specifies certain "behaviours" of an accelerator
> > binding. possible values can be composed by or'ing several of the following
> > values:
> > 
> > typedef enum
> > {
> >   GTK_ACCEL_VISIBLE		= 1 << 0	/* should the binding appear in
> >   						 * the widget's display?
> >   						 */,
> >   GTK_ACCEL_SIGNAL_VISIBLE	= 1 << 1	/* should the signal associated
> >   						 * with this binding be also
> >   						 * visible?
> >   						 */,
> >   GTK_ACCEL_LOCKED		= 1 << 2	/* may this binding be removed
> >   						 * again?
> >   						 */,
> >   GTK_ACCEL_MASK		= 0x07
> > } GtkAccelFlags;
> 
> Could you provide examples for GTK_ACCEL_VISIBLE and GTK_ACCEL_SIGNAL_VISIBLE?

that are flags meant as hints for an upcoming GtkAccelLabel (yet to be written)
imagine you have certain accelerator bindings for a container e.g. button or
menuitem, that has a GtkAccelLabel as child. you then tell that very label to
snoop at the accelerator signals of the container widget and the accelerator
flags determine what will actually be displayed by the label.

> > the corresponding functions for adding/removing an accelerator binding for
> > an object are:
> > 
> > void            gtk_accel_group_add            (GtkAccelGroup  *accel_group,
> >                                                 GtkObject      *object,
> >                                                 const gchar  *activation_signal,
> >                                                 guint           accel_key,
> >                                                 guint           accel_mods,
> >                                                 GtkAccelFlags   accel_flags);
> > void            gtk_accel_group_remove         (GtkAccelGroup  *accel_group,
> >                                                 GtkObject      *object,
> >                                                 guint           accel_key,
> >                                                 guint           accel_mods);
> 
> Could you provide examples for GTK_ACCEL_VISIBLE and GTK_ACCEL_SIGNAL_VISIBLE?

i've done that above already ;)

> > these two functions do in fact not really remove or add the binding, but emit
> > the above mentioned signals on the specified object. the objects default
> > handler is then responsible for the actual addition/removal of the accelerator
> > and should do so just by calling either of
> > 
> > void            gtk_accel_group_handle_add     (GtkObject      *object,
> >                                                 GtkAccelGroup  *accel_group,
> >                                                 guint      activation_signal_id,
> >                                                 guint           accel_key,
> >                                                 guint           accel_mods,
> >                                                 GtkAccelFlags   accel_flags);
> > or
> > void            gtk_accel_group_remove         (GtkAccelGroup  *accel_group,
> >                                                 GtkObject      *object,
> >                                                 guint           accel_key,
> >                                                 guint           accel_mods);
> > 
> > this might appear as unneccessary complexity but isn't. an object actually
> > introducing the add_accelerator signal, e.g.
> > 
> >   widget_signals[ADD_ACCELERATOR] =
> >     gtk_signal_new ("add-accelerator",
> >                     GTK_RUN_LAST,
> >                     object_class->type,
> >                     GTK_SIGNAL_OFFSET (GtkWidgetClass, add_accelerator),
> > 		    gtk_accel_group_marshal_add,
> >                     GTK_TYPE_NONE, 3,
> >                     GTK_TYPE_BOXED,
> >                     GTK_TYPE_UINT,
> >                     GTK_TYPE_UINT,
> >                     GTK_TYPE_UINT,
> >                     GTK_TYPE_ACCEL_FLAGS);
> > 
> > can just do
> > 
> >   widget_class->add_accelerator = gtk_accel_group_handle_add;
> 
> Are you then saying that the Widget class does have these signals?
> Or was this an hypothetical example...

yes and no. ;)

> > and is all set about adding accelerator bindings. this step has been made to
> > keep the accelerator system fully independant from the widget concept, and
> > thus even the signal emission functions are provided by gtkaccelgroup.c.
> 
> Do you have an example in mind where you would want to have an
> accelerator on a GtkObject other than a widget? 

yep i have one in mind, but it'll not apply to native gtk-applications.
imagine you build a text mode toolkit using the libgtkobj abstraction, you can
reuse the accelerator code for that as well.

> > GtkAccelGroup
> > -------------
> > an accelerator group consists of a set of accelerator bindings for several
> > widgets, whereas any widget is allowed to have as many accelerator bindings as
> > desired. an accelerator binding specifically consists of a keyvalue, its
> > modifiers, a widget pointer and a signal introduced on the widgets class
> > branch. the signal will be emitted for this widget if the accelerator group is
> > "activated" for a specific keyvalue/modifier pair. within accelerator groups,
> > a specific keyvalue/modifier combination is unique. however, installation
> > of accelerator bindings for a GtkAccelGroup will either remove an already
> > existing binding with the same keyval/modifiers or be ignored if such an
> > existing binding is locked. 
> 
> > furthermore all GtkAccelGroups that are joined
> > by any object that joined the GtkAccelGroup subject for the accelerator
> > installation, will be requested to remove this certain keyval/modifier
> > combination.
> > objects that wish to activate certain accelerator groups (usually toplevel
> > widgets e.g. a GtkWindow) are expected to announce their interest in an
> > accelerator group prior to any activation of such. this announcement
> > is perfomed via
> > 
> > void gtk_accel_group_join (GtkAccelGroup	*ac_group,
> >                            GtkObject         	*object);
> > 
> > the counterpart function is
> > 
> > void gtk_accel_group_leave (GtkAccelGroup	*ac_group,
> > 			    GtkObject		*object);
> > 
> > after an object has joined a GtkAccelGroup, it can "ativate" that group by
> > calling
> 
> I guess this points out a problem with the AccelGroup name. 
> It is a group of what? Not accelerators. The above implies
> that it is a group of toplevel widgets that share common bindings
> - which though true, is not really getting at what it does.

hm, i kind of see your point, but not really ;)
it really is a group of objects sharing the same binding set...

> And is not obvious from the name.
> 
> Alternate names that come to mind:
> 
>  GtkAccel[erator]Table  (GtkAcceleratorTable is the best name, but
>                          we probably want to pick something new)
>  GtkAccel[erator]Binder

hm, maybe GtkAccelBinder would present an acceptable alternative...

>  GtkBinder
>  GtkKeyBinder

> After the above quibbles - a more sweeping ideas:
> 
> Could this mechanism be used to do the customizable bindings
> for Entries and Text widgets? You would just attach an
> AcceleratorGroup with the appropriate bindings to the widget,
> and add a whole bunch of "move_backword_character" type signals.

hm, you would actually need to have a new signal per each action you'd
want to bind to the widget. i don't think that this would be the right approach
for text/entry widget bindings.

> (Even vi bindings can be handled - you keep a mode flag in the
>  Text/Entry widget, add accelerators for "h", "j", "k", "l", etc
>  but if the mode is "insertion" you handle insertion keypresses 
>  before activating the bindings from the accelerator table)
> 
> The difficulty is one of efficiency. You would need to create
> a separate accelerator table (with dozens of bindings) for
> each Entry in the application. Which might or might not be
> a problem. I don't have a feel yet as for how "lightweight"
> an Accelerator group is.

an accelerator group is very lightweight, as of now i have:
struct _GtkAccelGroup
{
  guint   ref_count;
  guint   lock_count;
  guint   modifier_mask;
  GSList *members               /* of type GtkObject* */;
};
but a binding is not as lightweight:
struct _GtkAccelEntry
{
  /* key portion
   */
  GtkAccelGroup *accel_group;
  guint          accelerator_key;
  guint          accelerator_mods;

  /* other fields
   */
  GtkAccelFlags  accel_flags;
  GtkObject     *object;
  guint          signal_id;
};


> (but there would be a lot of
> signal emission going on to set up the bindings)

i have had the same problem with the pattern-editor in BEAST, and i could
solve it in a moderate manner using a hash-table on pattern editor class
basis. i'll mail you a code example to glance at so you get the idea.

> One possibility -
> 
> gboolean gtk_accel_group_activate (GtkAccelGroup	*ac_group,
> 			           guint		 keyval,
> 			           guint		 modifiers,
> +  			           gpointer	         accelerator_data);
>  
> gtk_entry_move_forward (GtkWidget *dummy_entry, gpointer accelerator_data)
> {
>   GtkEntry entry = GTK_ENTRY (accelerator_data);
>   ...
> }
> 
> And there is one per-application singleton Entry that does nothing
> but forward key presses to the real entries. (it is never added to a
> parent realized or shown.)

this wouldn't work, we would need to change all existing candidates for
accelerator signals to take the above extra argument. keep in mind that
the AccelGroup activation code does a simple:

 gtk_signal_emit (entry->object, entry->signal_id);

so signal_id can identify any kind of signal whose signature fits
gtk_signal_default_marshaller.
 
> (Or you could just keep track of the currently focused entry on
> a global basis, and dispatch events to there without the
> accelerator_data argument
> 
> gtk_entry_move_forward (GtkWidget *dummy_entry, gpointer accelerator_data)
> {
>   GtkEntry entry = focused_entry;
>   ...
> }
> )

hm, i'd want to be able to do GTK_WIDGET_UNSET_FLAGS(entry, GTK_CAN_FOCUS)
and still get my accelerators proccessed. as mentioned earlier, those bindings
should really be handled on GtkEntryClass and/or GtkTextClass basis.

> Anyways, there is a lot of power in your proposal. A fair bit of
> complexity too, though most of this stuff will be hidden from the
> application programmer. The CANCEL example probably does need
> to work...

sure, i actually introduced no limitations compared to the old accelerator
table code, and the "clicked" example was a very valid one for that.

> 
> Regards,
>                                         Owen
> 

---
ciaoTJ



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