GtkModelMenuItem: add a submenu action attribute
Add support for a stateful action associated with a submenu. The action state is set to TRUE when the menu is shown and FALSE when it is unshown. This is useful to avoid unnecessary processing for menus that have frequently-changing content. A possible future feature is to add support for asynchronously filling the initial state of the menu by waiting until the action actually emits its state-change signal to TRUE before showing the menu. A silly example has been added to Bloatpad to demonstrate the new feature. https://bugzilla.gnome.org/show_bug.cgi?id=682630
This commit is contained in:
@ -191,7 +191,14 @@ bloat_pad_open (GApplication *application,
|
||||
new_window (application, files[i]);
|
||||
}
|
||||
|
||||
typedef GtkApplication BloatPad;
|
||||
typedef struct
|
||||
{
|
||||
GtkApplication parent_instance;
|
||||
|
||||
GMenu *time;
|
||||
guint timeout;
|
||||
} BloatPad;
|
||||
|
||||
typedef GtkApplicationClass BloatPadClass;
|
||||
|
||||
G_DEFINE_TYPE (BloatPad, bloat_pad, GTK_TYPE_APPLICATION)
|
||||
@ -234,15 +241,65 @@ quit_activated (GSimpleAction *action,
|
||||
g_application_quit (app);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_time (gpointer user_data)
|
||||
{
|
||||
BloatPad *bloatpad = user_data;
|
||||
GDateTime *now;
|
||||
gchar *time;
|
||||
|
||||
while (g_menu_model_get_n_items (G_MENU_MODEL (bloatpad->time)))
|
||||
g_menu_remove (bloatpad->time, 0);
|
||||
|
||||
g_message ("Updating the time menu (which should be open now)...");
|
||||
|
||||
now = g_date_time_new_now_local ();
|
||||
time = g_date_time_format (now, "%c");
|
||||
g_menu_append (bloatpad->time, time, NULL);
|
||||
g_date_time_unref (now);
|
||||
g_free (time);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
time_active_changed (GSimpleAction *action,
|
||||
GVariant *state,
|
||||
gpointer user_data)
|
||||
{
|
||||
BloatPad *bloatpad = user_data;
|
||||
|
||||
if (g_variant_get_boolean (state))
|
||||
{
|
||||
if (!bloatpad->timeout)
|
||||
{
|
||||
bloatpad->timeout = g_timeout_add (1000, update_time, bloatpad);
|
||||
update_time (bloatpad);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bloatpad->timeout)
|
||||
{
|
||||
g_source_remove (bloatpad->timeout);
|
||||
bloatpad->timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_simple_action_set_state (action, state);
|
||||
}
|
||||
|
||||
static GActionEntry app_entries[] = {
|
||||
{ "new", new_activated, NULL, NULL, NULL },
|
||||
{ "about", about_activated, NULL, NULL, NULL },
|
||||
{ "quit", quit_activated, NULL, NULL, NULL },
|
||||
{ "time-active", NULL, NULL, "false", time_active_changed }
|
||||
};
|
||||
|
||||
static void
|
||||
bloat_pad_startup (GApplication *application)
|
||||
{
|
||||
BloatPad *bloatpad = (BloatPad*) application;
|
||||
GtkBuilder *builder;
|
||||
|
||||
G_APPLICATION_CLASS (bloat_pad_parent_class)
|
||||
@ -301,13 +358,33 @@ bloat_pad_startup (GApplication *application)
|
||||
" </item>"
|
||||
" </section>"
|
||||
" </submenu>"
|
||||
" <submenu id='time-menu'>"
|
||||
" <attribute name='label' translatable='yes'>Time</attribute>"
|
||||
" <attribute name='submenu-action'>app.time-active</attribute>"
|
||||
" </submenu>"
|
||||
" </menu>"
|
||||
"</interface>", -1, NULL);
|
||||
gtk_application_set_app_menu (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
|
||||
gtk_application_set_menubar (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "menubar")));
|
||||
bloatpad->time = G_MENU (gtk_builder_get_object (builder, "time-menu"));
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
bloat_pad_shutdown (GApplication *application)
|
||||
{
|
||||
BloatPad *bloatpad = (BloatPad *) application;
|
||||
|
||||
if (bloatpad->timeout)
|
||||
{
|
||||
g_source_remove (bloatpad->timeout);
|
||||
bloatpad->timeout = 0;
|
||||
}
|
||||
|
||||
G_APPLICATION_CLASS (bloat_pad_parent_class)
|
||||
->shutdown (application);
|
||||
}
|
||||
|
||||
static void
|
||||
bloat_pad_init (BloatPad *app)
|
||||
{
|
||||
@ -320,6 +397,7 @@ bloat_pad_class_init (BloatPadClass *class)
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
application_class->startup = bloat_pad_startup;
|
||||
application_class->shutdown = bloat_pad_shutdown;
|
||||
application_class->activate = bloat_pad_activate;
|
||||
application_class->open = bloat_pad_open;
|
||||
|
||||
@ -330,7 +408,7 @@ bloat_pad_class_init (BloatPadClass *class)
|
||||
BloatPad *
|
||||
bloat_pad_new (void)
|
||||
{
|
||||
GtkApplication *bloat_pad;
|
||||
BloatPad *bloat_pad;
|
||||
|
||||
g_type_init ();
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "gtkaccelmapprivate.h"
|
||||
#include "gtkactionhelper.h"
|
||||
#include "gtkmodelmenu.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkModelMenuItem
|
||||
{
|
||||
@ -80,6 +81,28 @@ gtk_actionable_set_namespaced_action_name (GtkActionable *actionable,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_model_menu_item_submenu_shown (GtkWidget *widget,
|
||||
gpointer user_data)
|
||||
{
|
||||
const gchar *action_name = user_data;
|
||||
GActionMuxer *muxer;
|
||||
|
||||
muxer = _gtk_widget_get_action_muxer (widget);
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (muxer), action_name, g_variant_new_boolean (TRUE));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_model_menu_item_submenu_hidden (GtkWidget *widget,
|
||||
gpointer user_data)
|
||||
{
|
||||
const gchar *action_name = user_data;
|
||||
GActionMuxer *muxer;
|
||||
|
||||
muxer = _gtk_widget_get_action_muxer (widget);
|
||||
g_action_group_change_action_state (G_ACTION_GROUP (muxer), action_name, g_variant_new_boolean (FALSE));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_model_menu_item_setup (GtkModelMenuItem *item,
|
||||
GMenuModel *model,
|
||||
@ -129,6 +152,28 @@ gtk_model_menu_item_setup (GtkModelMenuItem *item,
|
||||
else if (g_str_equal (key, "target"))
|
||||
gtk_actionable_set_action_target_value (GTK_ACTIONABLE (item), value);
|
||||
|
||||
else if (g_str_equal (key, "submenu-action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
GtkWidget *submenu;
|
||||
|
||||
submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
|
||||
|
||||
if (submenu != NULL)
|
||||
{
|
||||
const gchar *action = g_variant_get_string (value, NULL);
|
||||
gchar *full_action;
|
||||
|
||||
if (action_namespace)
|
||||
full_action = g_strjoin (".", action_namespace, action, NULL);
|
||||
else
|
||||
full_action = g_strdup (action);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (submenu), "gtkmodelmenu-visibility-action", full_action, g_free);
|
||||
g_signal_connect (submenu, "show", G_CALLBACK (gtk_model_menu_item_submenu_shown), full_action);
|
||||
g_signal_connect (submenu, "hide", G_CALLBACK (gtk_model_menu_item_submenu_hidden), full_action);
|
||||
}
|
||||
}
|
||||
|
||||
g_variant_unref (value);
|
||||
}
|
||||
g_object_unref (iter);
|
||||
|
Reference in New Issue
Block a user