Re: Entry / TextBuffer that does not listen to accelerators?

Stephan Brunner said:
I am not able to enter any 'u' into the Entry.

The reason this should work is, that the active widget gets the input.
If the widget does not handle it, it passes it on to it's parent. And so
on up to the window, which handles the accels.

Not quite; key events are delivered first to the toplevel window (an X quirk),
and propagated from there to child widgets.  gtk_window_key_press_event()
looks like this:

  static gint
  gtk_window_key_press_event (GtkWidget   *widget,
                            GdkEventKey *event)
    GtkWindow *window = GTK_WINDOW (widget);
    gboolean handled = FALSE;

    /* handle mnemonics and accelerators */
    if (!handled)
      handled = gtk_window_activate_key (window, event);

    /* handle focus widget key events */
    if (!handled)
      handled = gtk_window_propagate_key_event (window, event);

    /* Chain up, invokes binding set */
    if (!handled)
      handled = GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);

    return handled;

Which means that window-wide mnemonics and accelerators take precedence over
per-widget keystrokes.  And, if you think about it, it *has* to work that way,
or something like a TextView would eat all of the keys that are supposed to be
menu accelerators.

Reading this, I wonder even more why I can't get it to work the way I want to.
Is there some subtle mistake or usage-error in my code?

No.  Your key is handled by the UI manager, which means, by the logic above,
it takes precedence over the widget's handling of the event.  In fact, your
Entry or TextView never even gets the key-press-event.

This sort of clash is the reason for using modifiers on accelerators in the
first place.  The problem would be easily solved by making your accel be
"<Ctrl>U" instead of just "U"... but since that's not what you asked...

In this instance you need to have the widget get the first crack at the
unadorned key.  The guys on gtk-app-devel-list or in #gtk+ will probably know
better than i, but here's what i would try:

a) Don't put the accelerator in the UIManager descriptions, but handle it by
hand in key_press_event on the toplevel window, connected by
signal_connect_after().  By removing it from the UI manager, it doesn't get
handled in the accelerator table, and by having it connected _after, your
handler runs *after* the key has been propagated to the other widgets, which
gives the TextView and Entry first crack at it.  The drawback is that you lose
the helpful accelerator hint in the menu.

To try this out, change

     [ "u_action",  undef,    "Do nothing", "u", undef, \&u_action ]


     [ "u_action",  undef,    "Do nothing", undef, undef, undef ]

and add

  my $button = Gtk2::Button->new('focus me and press u');
  $Top{vbox}->pack_start($button, 0, 0, 0);
  $Top{window}->signal_connect (key_press_event => sub {
      my ($widget, $event) = @_;
      use Gtk2::Gdk::Keysyms;
      if ($event->keyval == $Gtk2::Gdk::Keysyms{u}) {
          u_action ();
          return 1;
      return 0;

just before your $Top{window}->show_all call.

b) Leave the accelerator in the UIManager, and add special code in your action
handler to route the event as necessary.

  sub u_action {
      # if the key focus is currently on the toplevel window, we may
      # need to do further key routing:
      my $widget = Gtk2->get_event_widget (Gtk2->get_current_event);
      if ($widget->isa ('Gtk2::Window')) {
          # check the type of the widget that currently has focus:
          my $focus_widget = $widget->get_focus;
          if ($focus_widget &&
              ($focus_widget->isa ('Gtk2::Editable') ||
               $focus_widget->isa ('Gtk2::TextView'))) {
              # he needs this event more than we do.
              $focus_widget->event (Gtk2->get_current_event);
      print "$count: This is sub u_action...\n";

I don't like this approach very much because it feels fragile, and requires
placing this behavior in all of the handlers for unadorned accels, rather than
just connecting them in a different place.  On the other hand, you get your
handlers through UIManager, which is nice.

muppet <scott at asofyet dot org>

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