Two insights about underline accelerators
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gtk org
- Cc: alexl redhat com
- Subject: Two insights about underline accelerators
- Date: 14 Mar 2001 21:35:20 -0500
So, I was looking at Alex's patches for stock items and notebook
accelerators, and simply could not reconcile myself to the
handling of mnemonics/underline accelerators in those patches:
- There is an explosion of accelerator group arguments
- There is a new hacky mechanism introduced to try to
get noteebook pages to work correctly that requires
much manual intervention.
These are the most pressing problems with uline accelerators:
- Setting them up is inconvienient
- You can't have multiple accelerators on different
pages of notebooks.
The first insight was Havoc's. He pointed out, we know exactly the way
it _should_ work:
label = gtk_label_new_with_accel ("_Name");
gtk_label_set_accel_widget (GTK_LABEL (label), entry);
And the accel group handling should be automatic.
Since that API is obviously right, and we certainly can make that work
with a bit of private hackery inside GTK+, we should simply start from
the assumption that we will add that API, we might as well do it for
GTK+-2.0, and we will make it work.
The second insight was mine and was about implementing the
above without lots of gross hackery.
The gross hackery involved in trying to get the notebook
stuff to work has to do with trying to monitor which notebook
page is visible, and remove/block, add/unblock accelerators.
But, if we can do late resolution - that is, install
conflicting accelerators on the accel group, and decide
when activating the accelerator which one to use, then
it all becomes easy.
The accelerator group just looks at all accelerators for a given key,
removes the ones that point to unviewable widgets) (viewable means
widget and all parents are mapped, and activates one of the remaining
ones.
Now, clearly, we can't simply always install conflicting
accelerators without breaking our beloved hacker feature of
dynamically changeable menu accelerators, but what we
can do is use two accel groups for a toplevel with a
menu, one with the uline accels, one with the menu
accels, then have a toggle for accel groups:
- Menu accelerator group with the current transitive-closure
removal of conflicted accelerators.
- Uline accelerator group with activate-time conflict
resolution of conflicted accelerators. (I'm going
to use the term "overloaded" from here on out to
mean multiple conflicting accelerators installed.)
Details and extensions
======================
* GtkLabels would add or remove themselves from the appropriate accel
group to add or remove themselves them by using :hierarchy_changed
signal.
* To avoid breaking the small amount of code that does things
the old way, we need to to revert the change to the behavior
of gtk_label_parse_uline and make:
- gtk_label_parse_uline simply set the pattern and return
the keyval
- Add a new function gtk_label_set_text_accel() that
stores the keyval in label->accel_keyval, and do
the auto-hookup only if label->accel_keyval is set.
* It is not very hard to go from the above to the Windows
style where you cycle between the different accelerators
for overloaded mnemonics.
What you need to do for that is:
- Have the accel group store the last activated accelerator
in a overloaded set and cycle between the viewable
accelerators in a overloaded set.
- Have GtkAccelGroup look at the signature of the supplied
signal and recognize an extended activate signature:
signal_func (GtkObject *object,
gboolean overloaded,
gpointer accel_data);
The reason for the overloaded argument is to allow
buttons to use ::activate normally, but ::grab_focus
if overloaded.
* The accel data in the above is not necessary for overload
resolution, but along with an add_accelerator_with_data()
provides a quick fix for people who want to use accelerators
without a widget per accelerator. (And you don't want
widget/signal per accelerator for most language bindings)
* You can add automatic association of labels with accelerated
widgets:
- GtkLabel always adds itself/::run_accelerator to the
accelerator group instead of the accel widget.
- When the accelerator is activated, the label first
checks to see if the accel_widget property is set.
If so, it uses that widget. If not, it finds an
accelerator widget by walking up the tree and for
each widget, checking:
- Is this widget "acceleratable"? If so, use it.
- If not, is there an "acceleratable" "next" child after the
child containing the label? If so use it.
"acceleratable" and "next" can be made simple or complicated
as desired.
acceleratable:
Simple: activatable or CAN_FOCUS (only CAN_FOCUS if the mnemonic was
overloaded.)
Complex: Add a GtkAcceleratable interface with
can_accelerate() and accelerate(gboolean overloaded)
members.
Simple: special case GtkBox and GtkTable. Those cover
99% of the cases where people have labels packed with
other widgets.
Complex: Add a container virtual method "if child X
were focusable, would what be the next focusable child
after that". (Bad hack - make the label focusable
temporarily, focus it, focus next, accelerate the
next widget.)
Since there is always the fallback to setting the accelerator
widget explicitely, the simple methods are probably good
enough for GTK+-2.0.
(One big win with automatic association is that you could
simply make:
gtk_notebook_append_page (notebook, child,
gtk_label_new_accel ("_Background"));
work without needing any new API.)
So, that's my thoughts. I think we really need to implement
enough of the above to have:
- Automatic handling of accelerator groups. (And get
rid of the accel_group to gtk_button_new_accel() and
friends.
- Proper handling of accelerators on non-viewable
widgets.
Overload handling, per-accelerator data, and automatic association
could be nice, but are not essential in the same way.
Regards,
Owen
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]