modelmenu: listen for toplevel changes on the attach widget

Right now, when we create a GtkModelMenu for a GMenuModel, we listen to
changes to the menu's attach-widget to detect when a toplevel
GtkApplicationWindow becomes available to fetch actions from it.

This unfortunately breaks this simple code:

  GtkWidget *application_window = gtk_application_window_new();
  GtkWidget *menu_button = gtk_menu_button_new();
  GMenuModel *menu_model = get_menu_model();

  gtk_menu_button_set_menu_model(menu_button, menu_model);
  gtk_container_add(GTK_CONTAINER(application_window), menu_button);

Since GtkMenuButton creates a GtkModelMenu and sets itself as its attach
widget before it's added to a hierarchy containing a
GtkApplicationWindow.

Fix the bug by simply listening for changes in the window hierarchy, and
creating the menu model when the attach widget is added to an
application window.

https://bugzilla.gnome.org/show_bug.cgi?id=679454
This commit is contained in:
Cosimo Cecchi 2012-07-05 10:49:39 -04:00
parent f81bd6c52d
commit 5dbf3a576a

View File

@ -29,6 +29,8 @@
#include "gtkmodelmenuitem.h"
#include "gtkapplicationprivate.h"
#define MODEL_MENU_WIDGET_DATA "gtk-model-menu-widget-data"
typedef struct {
GActionObservable *actions;
GMenuModel *model;
@ -275,25 +277,61 @@ gtk_model_menu_create_menu (GMenuModel *model,
return menu;
}
static void
gtk_model_menu_connect_app_window (GtkMenu *menu,
GtkApplicationWindow *window)
{
GActionObservable *actions;
GtkAccelGroup *accels;
actions = gtk_application_window_get_observable (window);
accels = gtk_application_window_get_accel_group (window);
gtk_menu_set_accel_group (menu, accels);
gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
}
static void
attach_widget_hierarchy_changed (GtkWidget *attach_widget,
GtkWidget *previous_toplevel,
gpointer user_data)
{
GtkWidget *toplevel;
GtkMenu *menu = user_data;
toplevel = gtk_widget_get_toplevel (attach_widget);
if (GTK_IS_APPLICATION_WINDOW (toplevel))
gtk_model_menu_connect_app_window (menu, GTK_APPLICATION_WINDOW (toplevel));
}
static void
notify_attach (GtkMenu *menu,
GParamSpec *pspec,
gpointer data)
{
GtkWidget *widget;
GtkWidget *toplevel;
GActionObservable *actions;
GtkAccelGroup *accels;
GtkWidget *attach_widget, *toplevel;
widget = gtk_menu_get_attach_widget (menu);
toplevel = gtk_widget_get_toplevel (widget);
attach_widget = g_object_get_data (G_OBJECT (menu), MODEL_MENU_WIDGET_DATA);
if (attach_widget != NULL)
{
g_signal_handlers_disconnect_by_func (attach_widget, attach_widget_hierarchy_changed, menu);
g_object_set_data (G_OBJECT (menu), MODEL_MENU_WIDGET_DATA, NULL);
}
attach_widget = gtk_menu_get_attach_widget (menu);
if (!attach_widget)
return;
toplevel = gtk_widget_get_toplevel (attach_widget);
if (GTK_IS_APPLICATION_WINDOW (toplevel))
{
actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
gtk_menu_set_accel_group (menu, accels);
gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
gtk_model_menu_connect_app_window (menu, GTK_APPLICATION_WINDOW (toplevel));
}
else
{
g_object_set_data (G_OBJECT (menu), MODEL_MENU_WIDGET_DATA, attach_widget);
g_signal_connect_object (attach_widget, "hierarchy-changed",
G_CALLBACK (attach_widget_hierarchy_changed), menu, 0);
}
}