Re: Accelerators and gtk_grab_add

On Wed, 26 Jul 2000, Matt Carlson wrote:

> Tim Janik wrote:

> > ok, that's something to work from. the problem is the accelerator
> > you setup programmatically:
> > 
> >         /* Just for kicks let's add an accelerator programmatically */
> >         gtk_widget_add_accelerator( menuitem, "activate",
> >                                     gtk_accel_group_get_default(),
> >                                     GDK_N, 0, GTK_ACCEL_VISIBLE );
> > 
> > you're using the global default accelerator group that _all_ windows will
> > resort to if they can't handle the keypress.
> > what you actually want is to create a new accelerator group per menubar,
> > install the accelerators on that and add it to the window with
> > gtk_window_add_accel_group().
> > 
> > i can see why you used this though, i'll put the foolowing on my 2.0 todo list:
> > 
> > 1) provide gtk_menu_bar_get_accel_group() (creates the menubar accel group for you,
> >    you just need to add it to the window then)
> > 2) gtk_window_get_default_accel_group() (should be provided by havoc's recent patches
> >    already), to be used for non-menu, per-window accelerators
> > 3) rename gtk_accel_group_get_default() to gtk_accel_group_get_global(), since that's
> >    how it's really used.
> > 
> > [i didn't actually run your programm when i saw your usage of
> >  gtk_accel_group_get_default(), so if the above advice doesn't fix
> >  things for you, bug me again ;)]
> Hmmm...  I may be misunderstanding your proposed solution.  The reason I used
> gtk_accel_group_default() is because that is where the user defined
> accelerators for the menus wind up.  Here is the substitution if you would like
> to see it.

what do you mean "that is where the user defined accelerators for the menus
wind up"? who would put them there except you?
there's only one global default_accelerator group, and you rarely want to
really use it, it's featured by every window at every time.

> #if 0
>    GtkWidget * menuitem = gtk_menu_item_new_with_label( "Do Something" );
>    gtk_menu_append( GTK_MENU(submenu), menuitem );
>    gtk_signal_connect( GTK_OBJECT(menuitem), "activate",
>                        GTK_SIGNAL_FUNC(MenuItemEventHandler), NULL );
>    /* Just for kicks let's add an accelerator programmatically */
>    gtk_widget_add_accelerator( menuitem, "activate",
>                                gtk_accel_group_get_default(),
>                                GDK_N, 0, GTK_ACCEL_VISIBLE );
>    gtk_widget_show( menuitem );
> #else
>    GtkWidget * menuitem1 = gtk_menu_item_new_with_label( "Do Something" );
>    gtk_menu_append( GTK_MENU(submenu), menuitem1 );
>    gtk_signal_connect( GTK_OBJECT(menuitem1), "activate",
>                        GTK_SIGNAL_FUNC(MenuItemEventHandler), NULL );
>    GtkAccelGroup* accelgroup = gtk_accel_group_new();
                                 creates an accel_group with ref_count=1

>    gtk_accel_group_add( accelgroup, GDK_N, (GdkModifierType)0,
>                         GTK_ACCEL_VISIBLE, GTK_OBJECT(menuitem), "activate" );

gtk_accel_group_add() is an internal function, take a look at the header
file. use gtk_widget_add_accelerator() as you did above.

>    gtk_accel_group_attach( accelgroup, GTK_OBJECT(menu) );

gtk_accel_group_attach() is an internal function also, i said
"add it to the window with gtk_window_add_accel_group()" ;)

>    gtk_widget_show( menuitem );
>    /* Now let's add another menu option to test where the user
>     * defined accelerator will be assigned.  Our accelerator or the default?
>     */
>    GtkWidget * menuitem2 = gtk_menu_item_new_with_label( "Do Something Else" );
>    gtk_menu_append( GTK_MENU(submenu), menuitem2 );
>    gtk_signal_connect( GTK_OBJECT(menuitem2), "activate",
>                        GTK_SIGNAL_FUNC(MenuItemEventHandler), NULL );
>    gtk_accel_group_attach( accelgroup, GTK_OBJECT(menuitem2) );
>    gtk_widget_show( menuitem2 );

this code doesn't make much sense ;)

> #endif

but that's probably due to me not being clear enough, lemme
set out pseudo flow here:

w = create_window ();
mb = menubar_new ();
mb_ag = gtk_accel_group_new (); // create menubar's accel group

/* save it for later reference if needed: */
gtk_object_set_data_full (mb, "accel-group", mb_ag, gtk_accel_group_unref);

/* create menu items and install accelerators */
mi1 = menu_item_new ();
gtk_widget_add_accelerator(mi1, "activate", mb_ag, GDK_N, 0, GTK_ACCEL_VISIBLE);
mi2 = menu_item_new ();
gtk_widget_add_accelerator(mi2, "activate", mb_ag, GDK_X, 0, GTK_ACCEL_VISIBLE);

/* complete widget tree */
add (w, mb);
m = menu_new();
add (mb, m);
add (m, mi1);
add (m, mi2);

/* for mi1 and mi2 we've now accelerators setup in mb_ag, now we
 * have to install it on some key-handling object to take effect.
 * that key handling object could only meaningfull be the window
 * that we added the menubar to, so we do:
gtk_window_add_accel_group (w, mb_ag);

if you create a _new_ window with a new menu bar, you also use
a new accel group for the menu bar there, don't mix them with the
accel group setup above.
don't also use an accelgroup per submenu it doesn't make a whole
lot of sense, one per menubar is fine, provided it is installed
on the window the menubar is used in.
a case where you _would_ want a new accel group is e.g. if in that
window you don't only use the menus/menuitems of the menu bar,
but also have a popup menu, that'd e.g. be popped up on button3
clicks. for that you'd do:

pm = menu_new ();
pm_ag = gtk_accel_group_new ();
mi1 = menu_item_new ();
mi2 = menu_item_new ();
add (pm, mi1);
add (pm, mi2);
/* install accels */
gtk_widget_add_accelerator(mi1, "activate", pm_ag, GDK_A, 0, GTK_ACCEL_VISIBLE);
gtk_widget_add_accelerator(mi2, "activate", pm_ag, GDK_B, 0, GTK_ACCEL_VISIBLE);
/* feature this accel group in the windows where we use this popup menu */
gtk_window_add_accel_group (w, mb_ag);
gtk_window_add_accel_group (other_window, mb_ag);
/* we didn't store away pm_ag like in the above case where we used
 * the destroy notifier to get rid of the initial reference count,
 * so we do this on our own this time:
gtk_accel_group_unref (pm_ag);

then you could e.g. use gtk_menu_popup(pm,...) in w and in other_window,
and have those windows automatically feature the popup's accelrators.
not that now both w and other_window feature accelerators from pm_ag,
while only w features accelerators from mb_ag.

> The user defined assignment happens at runtime, not at creation time.  The
> function you identified was only to avoid the hassle of resetting the
> accelerator key during test cases, if multiple runs were required to nail
> down the problem.  Sorry <:)
> I imagine the solution would look something more like :
>    1) Create the menubar accelerator at creation time.

the _accelerator group_ for the menubar.

>    2) Do all of what you proposed.  I'd imagine the
>       gtk_menu_bar_get_accel_group() would be reduced to an accessibility
>       function.

yes, think of it in terms of:

gtk_menu_bar_get_accel_group(GtkMenuBar *menu_bar)
  GtkAccelGroup *ag;

  g_return_val_if_fail (GTK_IS_MENU_BAR (menu_bar), NULL);
  ag = gtk_object_get_data (GTK_OBJECT (menu_bar), "gtk-mb-accel-group");
  if (!ag)
      ag = gtk_accel_group_new ();
      gtk_object_set_data_full (GTK_OBJECT (menu_bar),
                                (GtkDestroyNotify) gtk_accel_group_unref);
  return ag;

in fact, you can use this code with 1.2.x (haven't compiled this though ;).

>    3) Change menu bar accelerator assignment so that they get assigned to the
>       menubar's accelerator group instead of the default accelerator group.

use gtk_widget_add_accelerator(..., gtk_menu_bar_get_accel_group (mb), ...)
to install accelerators on the menu items that get added to mb.
don't use the default accelerator group, don't install accelerators on
menus or menu bars, just use the menu item's menu bar accelerator group
and don't forget to add that to the window the menu bar is iused in:
gtk_window_add_accel_group (window_that_holds_mb, gtk_menu_bar_get_accel_group (mb));

> If this is what you meant, then sorry for wasting the bandwidth.

i hope i made myself more explicit this time ;)


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