Re: new accelerator system
- From: Tim Janik <timj gtk org>
- To: Owen Taylor <otaylor gtk org>
- cc: Gtk+ MList <gtk-list redhat com>
- Subject: Re: new accelerator system
- Date: Fri, 22 May 1998 20:46:58 +0200 (CEST)
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]