After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 688421 - GMenuModel menus should have a hint to hide items
GMenuModel menus should have a hint to hide items
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: gapplication
unspecified
Other Linux
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks: 699002 721797 745329
 
 
Reported: 2012-11-15 20:18 UTC by Cosimo Cecchi
Modified: 2015-04-21 14:31 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
GMenuModel menus should have a hint to hide items (2.96 KB, patch)
2012-11-15 21:03 UTC, Allison Karlitskaya (desrt)
none Details | Review
GtkMenuTracker: cache result of hash lookup (5.56 KB, patch)
2014-01-04 08:11 UTC, Allison Karlitskaya (desrt)
committed Details | Review
GtkMenuTrackerItem: small logic tweak (1.20 KB, patch)
2014-01-04 08:11 UTC, Allison Karlitskaya (desrt)
committed Details | Review
GtkMenuTracker: rework action removal a bit (2.26 KB, patch)
2014-01-04 08:11 UTC, Allison Karlitskaya (desrt)
committed Details | Review
GtkMenuTrackerItem: add an internal 'visible' flag (6.74 KB, patch)
2014-01-04 08:11 UTC, Allison Karlitskaya (desrt)
committed Details | Review
GtkMenuTracker: remove hidden items from the menu (6.33 KB, patch)
2014-01-04 08:12 UTC, Allison Karlitskaya (desrt)
committed Details | Review
bloatpad: test hidden-when='' (4.23 KB, patch)
2014-01-04 08:12 UTC, Allison Karlitskaya (desrt)
committed Details | Review
GtkMenuTracker: fix hidden-when='' vs. separators (2.50 KB, patch)
2014-01-08 19:32 UTC, Allison Karlitskaya (desrt)
committed Details | Review

Description Cosimo Cecchi 2012-11-15 20:18:38 UTC
Right now, when an action is disabled, GTK will make the corresponding menu items insensitive. This sounds fine as a default behavior, but in some cases, it's desirable for applications to hide disabled actions entirely from the menu.

We agreed with Ryan an easy and simple way to implement this is to have GTK understand an additional attribute in the action XML description, something like

<attribute name='when-disabled'>hide</attribute>

or

<attribute name="hide-when-disabled">true</attribute>
Comment 1 Allison Karlitskaya (desrt) 2012-11-15 21:03:10 UTC
Created attachment 229098 [details] [review]
GMenuModel menus should have a hint to hide items

Add a new GMenuModel attribute for hiding menu items for disabled
actions.

  <attribute name='when-disabled'>hide</attribute>

Will cause a menu item to be hidden instead of greyed out if the
associated action is disabled.

In order to make this work properly, be a bit more careful about calling
gtk_widget_show() on items and gtk_widget_show_all() on menu shells.
Comment 2 Cosimo Cecchi 2012-11-16 14:19:06 UTC
(there's another show_all too much in gtk_menu_button_set_menu_model)

This works fine for simple or toggle actions; sometimes though, what you want to do is to disable individual states of the same action.
For example, I have a "sort-by" (parameter_type: 's', state: 's') action, that is used in to build a menu like this

    <section>
      <item>
        <attribute name="action">win.sort-by</attribute>
        <attribute name="target">name</attribute>
        <attribute name="label" translatable="yes">By _Name</attribute>
      </item>
      <item>
        <attribute name="action">win.sort-by</attribute>
        <attribute name="target">size</attribute>
        <attribute name="label" translatable="yes">By _Size</attribute>
      </item>
      <item>
        <attribute name="action">win.sort-by</attribute>
        <attribute name="target">type</attribute>
        <attribute name="label" translatable="yes">By _Type</attribute>
      </item>
      <item>
        <attribute name="action">win.sort-by</attribute>
        <attribute name="target">trash-time</attribute>
        <attribute name="label" translatable="yes">By T_rash Time</attribute>
      </item>
    </section>

In this case, I would like to hide the "trash-time" menu item (but not the whole sort-by action), when the window is not in the trash directory, but the only option I have at that point is to manipulate the GMenuModel directly...not sure what's the best approach to make this easier.
Comment 3 Allison Karlitskaya (desrt) 2012-11-16 14:26:01 UTC
Interesting.

This is what the state-hint was supposed to do: return a valid range of values for the state.

It was only ever intended to support 'debugging introspection' type purposes those (ie: tab-complete for commandline tools... that sort of thing).
Comment 4 Cosimo Cecchi 2012-11-19 21:18:48 UTC
Here's another desirable behavior that this approach really doesn't cover, encountered while porting Nautilus: Nautilus does searches, and allows you to save one, so let's say you have a "save-search" action on your GtkApplicationWindow. You want the action to be visible in the gear menu only in case a search directory is being displayed (so "save-search" would set when-disabled=hide), but you also want to make it insensitive while the search parameters didn't change since you last saved it.
Comment 5 Allison Karlitskaya (desrt) 2012-11-19 21:35:52 UTC
Could be a difference between a disabled and a non-existent action?
Comment 6 Cosimo Cecchi 2012-11-19 21:38:43 UTC
(In reply to comment #5)
> Could be a difference between a disabled and a non-existent action?

It could, yeah - we could add the action dynamically to the action group when we load a search directory. Currently though, IIRC non-existent actions are displayed insensitive in the menu. Do you have in mind something like an additional <attribute name='when-missing'>hide</attribute>?
Comment 7 Allison Karlitskaya (desrt) 2012-11-19 21:40:24 UTC
Sounds more like we're pushing into the direction of

  hide-when=disabled

or

  hide-when=missing
Comment 8 Cosimo Cecchi 2012-11-19 21:44:14 UTC
This could work - I can't think of a case right now where you would need to set both states to hide.
Comment 9 Matthias Clasen 2013-04-28 21:46:57 UTC
Did we want to move forward with something here ?
Comment 10 Matthias Clasen 2013-08-14 04:34:23 UTC
could still happen for 3.10, if a patch appears
Comment 11 Allison Karlitskaya (desrt) 2014-01-04 08:11:47 UTC
Created attachment 265288 [details] [review]
GtkMenuTracker: cache result of hash lookup

Remove a hash lookup from the separator sync logic (which is run every
time we change a menu).  Instead, we do the lookup when creating the
section and cache the result.

This refactor will also help us in a future commit to add support for
hiding menu items based on missing actions.
Comment 12 Allison Karlitskaya (desrt) 2014-01-04 08:11:51 UTC
Created attachment 265289 [details] [review]
GtkMenuTrackerItem: small logic tweak

Strictly speaking, can_activate should always be set back to FALSE when
the action disappears from the muxer (since we can't activate it
anymore) but we forgot to do that.

This 'bug' could never cause a problem because 'can_activate' is never
directly queried for anything at all and the item would get marked
insensitive anyway.  As soon as the action was re-added, can_activate
would be recalculated based on the new action before anything else could
happen.

All the same, this should be cleared here.
Comment 13 Allison Karlitskaya (desrt) 2014-01-04 08:11:54 UTC
Created attachment 265290 [details] [review]
GtkMenuTracker: rework action removal a bit

Refactor the code in the action observer remove function in order to
make way for the (efficient) handling of hiding of the item in the case
that hidden-when='' is given.
Comment 14 Allison Karlitskaya (desrt) 2014-01-04 08:11:57 UTC
Created attachment 265291 [details] [review]
GtkMenuTrackerItem: add an internal 'visible' flag

Add an internal API for checking if a GtkMenuTrackerItem is visible,
along with a signal for reporting changes in that flag.  The item will
become invisible in situations according to the new hidden-when=''
attribute, which can be set to 'action-disabled' or 'action-missing'.

This new flag doesn't actually do anything yet, and none of the
consumers of GtkMenuTracker do anything with it (nor should they).  A
followup patch will address the issue.
Comment 15 Allison Karlitskaya (desrt) 2014-01-04 08:12:00 UTC
Created attachment 265292 [details] [review]
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.
Comment 16 Allison Karlitskaya (desrt) 2014-01-04 08:12:04 UTC
Created attachment 265293 [details] [review]
bloatpad: test hidden-when=''

Cook up some silly cases to test out the hidden-when='' attribute.

 - make sure hidden-when='action-missing' shows/hides items based on
   actions being created and destroyed

 - make sure hidden-when='action-disabled' shows/hides items based on
   actions being enabled and disabled

 - make sure hidden-when='action-missing' doesn't hide items when the
   action is merely disabled
Comment 17 Lars Karlitski 2014-01-04 11:35:09 UTC
Review of attachment 265288 [details] [review]:

ok
Comment 18 Lars Karlitski 2014-01-04 11:37:05 UTC
Review of attachment 265289 [details] [review]:

agreed
Comment 19 Lars Karlitski 2014-01-04 11:38:32 UTC
Review of attachment 265290 [details] [review]:

ok
Comment 20 Lars Karlitski 2014-01-04 11:44:01 UTC
Review of attachment 265291 [details] [review]:

ok
Comment 21 Lars Karlitski 2014-01-04 11:58:23 UTC
Review of attachment 265292 [details] [review]:

::: gtk/gtkmenutracker.c
@@ +72,3 @@
 struct _GtkMenuTrackerSection
 {
+  gpointer    model;   /* may be a GtkMenuTrackerItem or a GMenuModel */

Not sure I like where this is going... (but your extensive comment in add_items() makes this bearable ;) )

@@ +376,3 @@
+           * 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...

Please do that anyway and throw a warning to make it easy to find the reason stuff breaks when somebody adds an item without a <section>.
Comment 22 Lars Karlitski 2014-01-04 12:13:00 UTC
Review of attachment 265292 [details] [review]:

::: gtk/gtkmenutracker.c
@@ +72,3 @@
 struct _GtkMenuTrackerSection
 {
+  gpointer    model;   /* may be a GtkMenuTrackerItem or a GMenuModel */

Not sure I like where this is going... (but your extensive comment in add_items() makes this bearable ;) )

@@ +376,3 @@
+           * 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...

Please do that anyway and throw a warning to make it easy to find the reason stuff breaks when somebody adds an item without a <section>.
Comment 23 Lars Karlitski 2014-01-04 12:14:29 UTC
Review of attachment 265293 [details] [review]:

ok
Comment 24 Allison Karlitskaya (desrt) 2014-01-04 16:46:36 UTC
(In reply to comment #22)
> +  gpointer    model;   /* may be a GtkMenuTrackerItem or a GMenuModel */
> 
> Not sure I like where this is going... (but your extensive comment in
> add_items() makes this bearable ;) )

I turned out to be less of a fan of this than I thought.  The goal here is two-fold: help to save memory (by not growing a commonly-allocated struct) and help to reduce complexity (by being able to remove the signal and unref the same thing on destroy, and use the same search function on changes).

I think maybe I'd be more comfortable with this being a GObject -- at least that way we'd get a bit more typesafety in the case that someone making changes "forgets" that it may not be a GMenuModel and tries to pass it to a GMenuModel API unconditionally..

> @@ +376,3 @@
> +           * 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...
> 
> Please do that anyway and throw a warning to make it easy to find the reason
> stuff breaks when somebody adds an item without a <section>.

Meh.  See the comment in the GtkMenuTrackerItem patch about invalid values for hidden-when=''.... This code will run in context of Unity and gnome-shell, and it sohuldn't spew warnings/criticals just because it got a bad menu from an application...
Comment 25 Allison Karlitskaya (desrt) 2014-01-04 18:50:47 UTC
Comment on attachment 229098 [details] [review]
GMenuModel menus should have a hint to hide items

This was pre-menutracker (and never worked properly anyway, with respect to separators).
Comment 26 Ignacio Casal Quinteiro (nacho) 2014-01-04 19:49:47 UTC
I guess I am doing somethign wrong but I get the items duplicated:

    def _insert_directory(self, directory, menu):
        for d in sorted(directory.subdirs, key=lambda x: x.name.lower()):
            submenu = Gio.Menu()
            item = Gio.MenuItem.new_submenu(d.name.replace('_', '__'), submenu)
            menu.append_item(item)
            section = Gio.Menu()
            item_section = Gio.MenuItem.new_section(None, section)
            submenu.append_item(item_section)

            self._insert_directory(d, section)

        for tool in sorted(directory.tools, key=lambda x: x.name.lower()):
            action_name = 'external-tool_%X_%X' % (id(tool), id(tool.name))
            self._action_tools[action_name] = tool

            action = Gio.SimpleAction(name=action_name)
            action.connect('activate', capture_menu_action, self._window, self._panel, tool)
            self._window.add_action(action)

            item = Gio.MenuItem.new(tool.name.replace('_', '__'), "win.%s" % action_name)
            item.set_attribute_value("hidden-when", GLib.Variant.new_string("action-disabled"))
            print("tool", tool.name)
            menu.append_item(item)
Comment 27 Ignacio Casal Quinteiro (nacho) 2014-01-06 15:53:26 UTC
So I was missing a section which is now fixed. I guess that I would like to have some warning about this, but apart from that it works fine for me
Comment 28 Bastien Nocera 2014-01-08 15:40:02 UTC
The API is just fine by me. It would be nice to have a helper for it, probably in GIO where GMenu is implemented, and document the attribute there too.
Comment 29 Allison Karlitskaya (desrt) 2014-01-08 19:25:42 UTC
Attachment 265288 [details] pushed as fb14a78 - GtkMenuTracker: cache result of hash lookup
Attachment 265289 [details] pushed as 8256b88 - GtkMenuTrackerItem: small logic tweak
Attachment 265290 [details] pushed as 8efb140 - GtkMenuTracker: rework action removal a bit
Attachment 265291 [details] pushed as 2b1aa12 - GtkMenuTrackerItem: add an internal 'visible' flag
Comment 30 Allison Karlitskaya (desrt) 2014-01-08 19:32:39 UTC
Created attachment 265746 [details] [review]
GtkMenuTracker: fix hidden-when='' vs. separators

Ensure that adding hidden-when='' to a menu item does not produce an
extra separator item as a side effect.
Comment 31 Allison Karlitskaya (desrt) 2014-01-08 19:32:54 UTC
Attachment 265292 [details] pushed as 2112af7 - GtkMenuTracker: remove hidden items from the menu
Attachment 265293 [details] pushed as 2bcb4fc - bloatpad: test hidden-when=''
Attachment 265746 [details] pushed as d31bf77 - GtkMenuTracker: fix hidden-when='' vs. separators

Leaving open for GIO patches.
Comment 32 Matthias Clasen 2015-03-29 18:41:02 UTC
What gio patches are those ? everything attached to this bug is committed...
Comment 33 Allison Karlitskaya (desrt) 2015-04-21 14:31:41 UTC
(In reply to Ryan Lortie (desrt) from comment #31)
> Leaving open for GIO patches.

wat?