summaryrefslogtreecommitdiff
path: root/gtk/gtkmenushell.c
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2013-05-08 08:20:23 -0400
committerJasper St. Pierre <jstpierre@mecheye.net>2013-05-13 16:33:43 -0400
commita4276a6c7959854e0654791e0cfdcca5b82ec213 (patch)
tree3636f9cc9ba5ac14c752148e3b79f4d96469b332 /gtk/gtkmenushell.c
parent03235bf027c30e568d8a45419e504c7ab6a09d4f (diff)
downloadgtk+-a4276a6c7959854e0654791e0cfdcca5b82ec213.tar.gz
add GtkMenuTrackerItem
Add a new class, GtkMenuTrackerItem that represents a menu item, to be used with GtkMenuTracker. GtkMenuTracker's insert callback now works in terms of this new type (instead of passing reference to the model and an index to the item). GtkMenuShell now handles all of the binding tasks internally, mostly through the use of property bindings. Having bindings for the label and visibility attributes, in partiular, will help with supporting upcoming extensions to GMenuModel. GtkModelMenu has been reduced to a helper class that has nothing to do with GMenuModel. It represents something closer to an "ideal" API for GtkMenuItem if we didn't have compatibility concerns (eg: not emitting "activate" when setting toggle state, no separate subclasses per menu item type, supporting icons, etc.) Improvements to GtkMenuItem could eventually shrink the size of this class or remove the need for it entirely. Some GtkActionHelper functionality has been duplicated in GtkMenuTracker, which is suboptimal. The duplication exists so that other codebases (such as Unity and gnome-shell) can reuse the GtkMenuTracker code, whereas GtkActionHelper is very much tied to GtkWidget. Supporting binding arbitrary GtkWidgets to actions vs. supporting the full range of GMenuModel features for menu items turns out to be two overlapping but not entirely similar problems. Some of the duplication (such as roles) can be removed from GtkActionHelper once Gtk's internal Mac OS menubar support is ported to GtkMenuTracker. The intent to reuse the code outside of Gtk is also the reason for the unusual treatment of the enum type introduced in this comment. This adds no new "public" API to the Gtk library, other than types that we cannot make private due to GType limitations.
Diffstat (limited to 'gtk/gtkmenushell.c')
-rw-r--r--gtk/gtkmenushell.c127
1 files changed, 104 insertions, 23 deletions
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index a5ee2d465b..07ec537795 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -52,6 +52,7 @@
#include "gtkintl.h"
#include "gtktypebuiltins.h"
#include "gtkmodelmenuitem.h"
+#include "gtkwidgetprivate.h"
#include "deprecated/gtktearoffmenuitem.h"
@@ -2039,33 +2040,30 @@ gtk_menu_shell_get_parent_shell (GtkMenuShell *menu_shell)
}
static void
-gtk_menu_shell_tracker_insert_func (gint position,
- GMenuModel *model,
- gint item_index,
- const gchar *action_namespace,
- gboolean is_separator,
- gpointer user_data)
+gtk_menu_shell_item_activate (GtkMenuItem *menuitem,
+ gpointer user_data)
{
- GtkMenuShell *menu_shell = user_data;
- GtkWidget *item;
+ GtkMenuTrackerItem *item = user_data;
- if (is_separator)
- {
- gchar *label;
+ gtk_menu_tracker_item_activated (item);
+}
- item = gtk_separator_menu_item_new ();
+static void
+gtk_menu_shell_submenu_shown (GtkWidget *submenu,
+ gpointer user_data)
+{
+ GtkMenuTrackerItem *item = user_data;
- if (g_menu_model_get_item_attribute (model, item_index, G_MENU_ATTRIBUTE_LABEL, "s", &label))
- {
- gtk_menu_item_set_label (GTK_MENU_ITEM (item), label);
- g_free (label);
- }
- }
- else
- item = gtk_model_menu_item_new (model, item_index, action_namespace);
+ gtk_menu_tracker_item_request_submenu_shown (item, TRUE);
+}
- gtk_menu_shell_insert (menu_shell, item, position);
- gtk_widget_show (item);
+static void
+gtk_menu_shell_submenu_hidden (GtkWidget *submenu,
+ gpointer user_data)
+{
+ GtkMenuTrackerItem *item = user_data;
+
+ gtk_menu_tracker_item_request_submenu_shown (item, FALSE);
}
static void
@@ -2083,6 +2081,84 @@ gtk_menu_shell_tracker_remove_func (gint position,
gtk_widget_destroy (child);
}
+static void
+gtk_menu_shell_tracker_insert_func (GtkMenuTrackerItem *item,
+ gint position,
+ gpointer user_data)
+{
+ GtkMenuShell *menu_shell = user_data;
+ GtkWidget *widget;
+
+ if (gtk_menu_tracker_item_get_is_separator (item))
+ {
+ widget = gtk_separator_menu_item_new ();
+
+ /* For separators, we bind to the "label" property in case there
+ * is a section heading.
+ */
+ g_object_bind_property (item, "label", widget, "label", G_BINDING_SYNC_CREATE);
+ }
+ else if (gtk_menu_tracker_item_get_has_submenu (item))
+ {
+ GtkMenuShell *submenu;
+
+ widget = gtk_model_menu_item_new ();
+ g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
+
+ submenu = GTK_MENU_SHELL (gtk_menu_new ());
+
+ /* We recurse directly here: we could use an idle instead to
+ * prevent arbitrary recursion depth. We could also do it
+ * lazy...
+ */
+ submenu->priv->tracker = gtk_menu_tracker_new_for_item_submenu (item,
+ gtk_menu_shell_tracker_insert_func,
+ gtk_menu_shell_tracker_remove_func,
+ submenu);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (widget), GTK_WIDGET (submenu));
+
+ if (gtk_menu_tracker_item_get_should_request_show (item))
+ {
+ /* We don't request show in the strictest sense of the
+ * word: we just notify when we are showing and don't
+ * bother waiting for the reply.
+ *
+ * This could be fixed one day, but it would be slightly
+ * complicated and would have a strange interaction with
+ * the submenu pop-up delay.
+ *
+ * Note: 'item' is already kept alive from above.
+ */
+ g_signal_connect (submenu, "show", G_CALLBACK (gtk_menu_shell_submenu_shown), item);
+ g_signal_connect (submenu, "hide", G_CALLBACK (gtk_menu_shell_submenu_hidden), item);
+ }
+ }
+ else
+ {
+ widget = gtk_model_menu_item_new ();
+
+ /* We bind to "text" instead of "label" because GtkModelMenuItem
+ * uses this property (along with "icon") to control its child
+ * widget. Once this is merged into GtkMenuItem we can go back to
+ * using "label".
+ */
+ g_object_bind_property (item, "label", widget, "text", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "icon", widget, "icon", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "sensitive", widget, "sensitive", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "visible", widget, "visible", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "role", widget, "action-role", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "toggled", widget, "toggled", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (item, "accel", widget, "accel", G_BINDING_SYNC_CREATE);
+
+ g_signal_connect (widget, "activate", G_CALLBACK (gtk_menu_shell_item_activate), item);
+ }
+
+ /* TODO: drop this when we have bindings that ref the source */
+ g_object_set_data_full (G_OBJECT (widget), "GtkMenuTrackerItem", g_object_ref (item), g_object_unref);
+
+ gtk_menu_shell_insert (menu_shell, widget, position);
+}
+
/**
* gtk_menu_shell_bind_model:
* @menu_shell: a #GtkMenuShell
@@ -2134,16 +2210,21 @@ gtk_menu_shell_bind_model (GtkMenuShell *menu_shell,
const gchar *action_namespace,
gboolean with_separators)
{
+ GtkActionMuxer *muxer;
+
g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
g_return_if_fail (model == NULL || G_IS_MENU_MODEL (model));
+ muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (menu_shell));
+
g_clear_pointer (&menu_shell->priv->tracker, gtk_menu_tracker_free);
while (menu_shell->priv->children)
gtk_container_remove (GTK_CONTAINER (menu_shell), menu_shell->priv->children->data);
if (model)
- menu_shell->priv->tracker = gtk_menu_tracker_new (model, with_separators, action_namespace,
+ menu_shell->priv->tracker = gtk_menu_tracker_new (GTK_ACTION_OBSERVABLE (muxer),
+ model, with_separators, action_namespace,
gtk_menu_shell_tracker_insert_func,
gtk_menu_shell_tracker_remove_func,
menu_shell);