[gtk+] GtkMenuTracker: remove hidden items from the menu
- From: Ryan Lortie <desrt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] GtkMenuTracker: remove hidden items from the menu
- Date: Wed, 8 Jan 2014 19:32:45 +0000 (UTC)
commit 2112af719a22a395e97b31164a3de246c7cb447e
Author: Ryan Lortie <desrt desrt ca>
Date: Sat Jan 4 02:28:39 2014 -0500
GtkMenuTracker: remove hidden items from the menu
Modify the tracker so that it manages the visibility of
GtkMenuTrackerItem by issuing insert and remove callbacks to the
user of the tracker.
This works by treating the GtkMenuTrackerItem as a virtual section which
contains 1 item when the item is visible and 0 items when it is hidden.
For efficiency reasons, we only employ this trick in the case that the
item has a hidden-when='' attribute set on it.
https://bugzilla.gnome.org/show_bug.cgi?id=688421
gtk/gtkmenutracker.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 96 insertions(+), 5 deletions(-)
---
diff --git a/gtk/gtkmenutracker.c b/gtk/gtkmenutracker.c
index 007d6b4..efb67e8 100644
--- a/gtk/gtkmenutracker.c
+++ b/gtk/gtkmenutracker.c
@@ -71,7 +71,7 @@ struct _GtkMenuTracker
struct _GtkMenuTrackerSection
{
- GMenuModel *model;
+ gpointer model; /* may be a GtkMenuTrackerItem or a GMenuModel */
GSList *items;
gchar *action_namespace;
@@ -92,7 +92,7 @@ static void gtk_menu_tracker_section_free (GtkMenuTrackerS
static GtkMenuTrackerSection *
gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section,
- GMenuModel *model,
+ gpointer model,
gint *offset)
{
GSList *item;
@@ -218,6 +218,38 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section,
return n_items;
}
+static void
+gtk_menu_tracker_item_visibility_changed (GtkMenuTrackerItem *item,
+ gboolean is_now_visible,
+ gpointer user_data)
+{
+ GtkMenuTracker *tracker = user_data;
+ GtkMenuTrackerSection *section;
+ gboolean was_visible;
+ gint offset = 0;
+
+ /* remember: the item is our model */
+ section = gtk_menu_tracker_section_find_model (tracker->toplevel, item, &offset);
+
+ was_visible = section->items != NULL;
+
+ if (is_now_visible == was_visible)
+ return;
+
+ if (is_now_visible)
+ {
+ section->items = g_slist_prepend (NULL, NULL);
+ (* tracker->insert_func) (section->model, offset, tracker->user_data);
+ }
+ else
+ {
+ section->items = g_slist_delete_link (section->items, section->items);
+ (* tracker->remove_func) (offset, tracker->user_data);
+ }
+
+ gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0);
+}
+
static gint
gtk_menu_tracker_section_measure (GtkMenuTrackerSection *section)
{
@@ -310,10 +342,69 @@ gtk_menu_tracker_add_items (GtkMenuTracker *tracker,
item = _gtk_menu_tracker_item_new (tracker->observable, model, position + n_items,
section->action_namespace, FALSE);
- (* tracker->insert_func) (item, offset, tracker->user_data);
- g_object_unref (item);
- *change_point = g_slist_prepend (*change_point, NULL);
+ /* In the case that the item may disappear we handle that by
+ * treating the item that we just created as being its own
+ * subsection. This happens as so:
+ *
+ * - the subsection is created without the possibility of
+ * showing a separator
+ *
+ * - the subsection will have either 0 or 1 item in it at all
+ * times: either the shown item or not (in the case it is
+ * hidden)
+ *
+ * - the created item acts as the "model" for this section
+ * and we use its "visiblity-changed" signal in the same
+ * way that we use the "items-changed" signal from a real
+ * GMenuModel
+ *
+ * We almost never use the '->model' stored in the section for
+ * anything other than lookups and for dropped the ref and
+ * disconnecting the signal when we destroy the menu, and we
+ * need to do exactly those things in this case as well.
+ *
+ * The only other thing that '->model' is used for is in the
+ * case that we want to show a separator, but we will never do
+ * that because separators are not shown for this fake section.
+ *
+ * Because of the game we play where the menu item is
+ * essentially its own section, it is possible that the menu
+ * item itself could get added as its own separator label in
+ * the case that the item is inside of a with_separators
+ * section, but this should never happen -- the user should
+ * always have the menu item inside of a <section>, never at
+ * the toplevel. It would be easy to add an extra boolean to
+ * check for that, but we already have a lot of those...
+ */
+ if (_gtk_menu_tracker_item_may_disappear (item))
+ {
+ GtkMenuTrackerSection *fake_section;
+
+ fake_section = g_slice_new0 (GtkMenuTrackerSection);
+ fake_section->model = g_object_ref (item);
+ fake_section->handler = g_signal_connect (item, "visibility-changed",
+ G_CALLBACK
(gtk_menu_tracker_item_visibility_changed),
+ tracker);
+ *change_point = g_slist_prepend (*change_point, fake_section);
+
+ if (_gtk_menu_tracker_item_is_visible (item))
+ {
+ (* tracker->insert_func) (item, offset, tracker->user_data);
+ fake_section->items = g_slist_prepend (NULL, NULL);
+ }
+ }
+ else
+ {
+ /* In the normal case, we store NULL in the linked list.
+ * The measurement and lookup code count NULL always as
+ * exactly 1: an item that will always be there.
+ */
+ (* tracker->insert_func) (item, offset, tracker->user_data);
+ *change_point = g_slist_prepend (*change_point, NULL);
+ }
+
+ g_object_unref (item);
}
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]