[gtk+/wip/gtkmenutrackeritem] GtkMenuTrackerItem: track submenu shown state
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/gtkmenutrackeritem] GtkMenuTrackerItem: track submenu shown state
- Date: Mon, 13 May 2013 13:23:15 +0000 (UTC)
commit 4ca9f40769cf9d9114420f2725518033c18c3f56
Author: Ryan Lortie <desrt desrt ca>
Date: Mon May 13 09:22:39 2013 -0400
GtkMenuTrackerItem: track submenu shown state
Before, the best you could do is request submenu shown and then just
show the submenu. It's now possible to track when the application tells
you that it has become ready.
gtk/gtkmenutrackeritem.c | 180 +++++++++++++++++++++++++++++++++++++++++++---
gtk/gtkmenutrackeritem.h | 2 +
2 files changed, 171 insertions(+), 11 deletions(-)
---
diff --git a/gtk/gtkmenutrackeritem.c b/gtk/gtkmenutrackeritem.c
index ce80223..c0cc19f 100644
--- a/gtk/gtkmenutrackeritem.c
+++ b/gtk/gtkmenutrackeritem.c
@@ -14,6 +14,8 @@ struct _GtkMenuTrackerItem
guint can_activate : 1;
guint sensitive : 1;
guint toggled : 1;
+ guint submenu_shown : 1;
+ guint submenu_requested : 1;
};
enum {
@@ -29,6 +31,7 @@ enum {
PROP_ACCEL,
PROP_SUBMENU,
PROP_SUBMENU_NAMESPACE,
+ PROP_SUBMENU_SHOWN,
N_PROPS
};
@@ -104,6 +107,9 @@ gtk_menu_tracker_item_get_property (GObject *object,
case PROP_SUBMENU_NAMESPACE:
g_value_set_string (value, gtk_menu_tracker_item_get_submenu_namespace (self));
break;
+ case PROP_SUBMENU_SHOWN:
+ g_value_set_boolean (value, gtk_menu_tracker_item_get_submenu_shown (self));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -160,6 +166,8 @@ gtk_menu_tracker_item_class_init (GtkMenuTrackerItemClass *class)
g_param_spec_object ("submenu", "", "", G_TYPE_MENU_MODEL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
gtk_menu_tracker_item_pspecs[PROP_SUBMENU_NAMESPACE] =
g_param_spec_string ("submenu-namespace", "", "", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
+ gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN] =
+ g_param_spec_boolean ("submenu-shown", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
g_object_class_install_properties (class, N_PROPS, gtk_menu_tracker_item_pspecs);
}
@@ -503,6 +511,24 @@ gtk_menu_tracker_item_get_should_request_show (GtkMenuTrackerItem *self)
return g_menu_item_get_attribute (self->item, "submenu-action", "&s", NULL);
}
+gboolean
+gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self)
+{
+ return self->submenu_shown;
+}
+
+/* only called from the opener, internally */
+static void
+gtk_menu_tracker_item_set_submenu_shown (GtkMenuTrackerItem *self,
+ gboolean submenu_shown)
+{
+ if (submenu_shown == self->submenu_shown)
+ return;
+
+ self->submenu_shown = submenu_shown;
+ g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN]);
+}
+
void
gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self)
{
@@ -532,26 +558,158 @@ gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self)
g_variant_unref (action_target);
}
+typedef struct
+{
+ GtkMenuTrackerItem *item;
+ gchar *submenu_action;
+ gboolean first_time;
+} GtkMenuTrackerOpener;
+
+static void
+gtk_menu_tracker_opener_update (GtkMenuTrackerOpener *opener)
+{
+ GActionGroup *group = G_ACTION_GROUP (opener->item->observable);
+ gboolean is_open = TRUE;
+
+ /* We consider the menu as being "open" if the action does not exist
+ * or if there is another problem (no state, wrong state type, etc.).
+ * If the action exists, with the correct state then we consider it
+ * open if we have ever seen this state equal to TRUE.
+ *
+ * In the event that we see the state equal to FALSE, we force it back
+ * to TRUE. We do not signal that the menu was closed because this is
+ * likely to create UI thrashing.
+ *
+ * The only way the menu can have a true-to-false submenu-shown
+ * transition is if the user calls _request_submenu_shown (FALSE).
+ * That is handled in _free() below.
+ */
+
+ if (g_action_group_has_action (group, opener->submenu_action))
+ {
+ GVariant *state = g_action_group_get_action_state (group, opener->submenu_action);
+
+ if (state)
+ {
+ if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+ is_open = g_variant_get_boolean (state);
+ g_variant_unref (state);
+ }
+ }
+
+ /* If it is already open, signal that.
+ *
+ * If it is not open, ask it to open.
+ */
+ if (is_open)
+ gtk_menu_tracker_item_set_submenu_shown (opener->item, TRUE);
+
+ if (!is_open || opener->first_time)
+ {
+ g_action_group_change_action_state (group, opener->submenu_action, g_variant_new_boolean (TRUE));
+ opener->first_time = FALSE;
+ }
+}
+
+static void
+gtk_menu_tracker_opener_added (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ GtkMenuTrackerOpener *opener = user_data;
+
+ if (g_str_equal (action_name, opener->submenu_action))
+ gtk_menu_tracker_opener_update (opener);
+}
+
+static void
+gtk_menu_tracker_opener_removed (GActionGroup *action_group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ GtkMenuTrackerOpener *opener = user_data;
+
+ if (g_str_equal (action_name, opener->submenu_action))
+ gtk_menu_tracker_opener_update (opener);
+}
+
+static void
+gtk_menu_tracker_opener_changed (GActionGroup *action_group,
+ const gchar *action_name,
+ GVariant *new_state,
+ gpointer user_data)
+{
+ GtkMenuTrackerOpener *opener = user_data;
+
+ if (g_str_equal (action_name, opener->submenu_action))
+ gtk_menu_tracker_opener_update (opener);
+}
+
+static void
+gtk_menu_tracker_opener_free (gpointer data)
+{
+ GtkMenuTrackerOpener *opener = data;
+
+ g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_added, opener);
+ g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_removed, opener);
+ g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_changed, opener);
+
+ g_action_group_change_action_state (G_ACTION_GROUP (opener->item->observable),
+ opener->submenu_action,
+ g_variant_new_boolean (FALSE));
+
+ gtk_menu_tracker_item_set_submenu_shown (opener->item, FALSE);
+
+ g_free (opener->submenu_action);
+
+ g_slice_free (GtkMenuTrackerOpener, opener);
+}
+
+static GtkMenuTrackerOpener *
+gtk_menu_tracker_opener_new (GtkMenuTrackerItem *item,
+ const gchar *submenu_action)
+{
+ GtkMenuTrackerOpener *opener;
+
+ opener = g_slice_new (GtkMenuTrackerOpener);
+ opener->first_time = TRUE;
+ opener->item = item;
+
+ if (item->action_namespace)
+ opener->submenu_action = g_strjoin (".", item->action_namespace, submenu_action, NULL);
+ else
+ opener->submenu_action = g_strdup (submenu_action);
+
+ g_signal_connect (item->observable, "action-added", G_CALLBACK (gtk_menu_tracker_opener_added), opener);
+ g_signal_connect (item->observable, "action-removed", G_CALLBACK (gtk_menu_tracker_opener_removed),
opener);
+ g_signal_connect (item->observable, "action-state-changed", G_CALLBACK (gtk_menu_tracker_opener_changed),
opener);
+
+ gtk_menu_tracker_opener_update (opener);
+
+ return opener;
+}
+
void
gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self,
gboolean shown)
{
const gchar *submenu_action;
- GVariant *value;
+ gboolean okay;
- if (!g_menu_item_get_attribute (self->item, "submenu-action", "&s", &submenu_action))
+ if (shown == self->submenu_requested)
return;
- value = g_variant_new_boolean (shown);
+ /* Should not be getting called unless we have submenu-action.
+ */
+ okay = g_menu_item_get_attribute (self->item, "submenu-action", "&s", &submenu_action);
+ g_assert (okay);
- if (self->action_namespace)
- {
- gchar *full_action;
+ self->submenu_requested = shown;
- full_action = g_strjoin (".", self->action_namespace, submenu_action, NULL);
- g_action_group_change_action_state (G_ACTION_GROUP (self->observable), full_action, value);
- g_free (full_action);
- }
+ if (shown)
+ g_object_set_data_full (G_OBJECT (self), "submenu-opener",
+ gtk_menu_tracker_opener_new (self, submenu_action),
+ gtk_menu_tracker_opener_free);
else
- g_action_group_change_action_state (G_ACTION_GROUP (self->observable), submenu_action, value);
+ g_object_set_data (G_OBJECT (self), "submenu-opener", NULL);
}
diff --git a/gtk/gtkmenutrackeritem.h b/gtk/gtkmenutrackeritem.h
index 46c93c3..4f292a3 100644
--- a/gtk/gtkmenutrackeritem.h
+++ b/gtk/gtkmenutrackeritem.h
@@ -79,4 +79,6 @@ void gtk_menu_tracker_item_activated (GtkMenu
void gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self,
gboolean shown);
+gboolean gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self);
+
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]