Two insights about underline accelerators



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]