gonzaarcr / Fildem

Fildem global menu
GNU General Public License v3.0
802 stars 86 forks source link

Dinamically update menus #94

Open gonzaarcr opened 3 years ago

gonzaarcr commented 3 years ago

This is a feature that I’ve been wanting to add for a long time: the ability to update the menu on demand (without doing a full refresh). Currently, the menu updates only when a switch of application. Both interfaces (com.canonical.dbusmenu and org.gtk.Menus) provide signals to handle this (ItemsPropertiesUpdated and Changed, respectevely, among others), but none seems to work. The signal Changed from org.gtk.Actions seems to be working, but not all events arrive.

I decided to create this issue to find external help because I’ve made no progress for a long time. I already contacted Lester, the author of Gnome Global Appmenu extension and he was the one that proposed to make this discussion public, wich I think is a great idea.

I will try to update this thread with more info and examples when I have time.

lestcape commented 3 years ago

This is a copy paste of my response: Well, I need to say that you do not use the same logic I used to do my implementation. Also, what I did was inefficient for Gtk and it should be improved, but I never found the time to do some experimentation with it and then improve it. In my opinion Gtk is not as good as DBusMenu can be in that regard.

Basic idea of what i did with DBusMenu:

  1. Call AboutToShow only from the root to know the root items (panel items).
  2. Call AboutToShow always from the items that will be shown currently and its children (but never the children of the children because of performance).
  3. Reused the item's id instead of creating and destroying it every layout update. The same items id correspond to the same object between layout updates (That does not occur unfortunately in jayatana or Gtk).
  4. I request the properties with GetGroupProperties (https://github.com/gnustep/libs-dbuskit/blob/master/Bundles/DBusMenu/com.canonical.dbusmenu.xml#L229) after a layout update when I call GetLayout (probably the case of why you do not get it) and not only when I receive the ItemsPropertiesUpdated signal.
  5. Sometimes the children of the items change. So, you need to be prepared to really relayout everything if needed.

Basic idea of what i did with Gtk:

  1. What i did seems pretty close to what you are doing, but not exactly that way.
  2. In Gtk there are no items id, so you lose all between layout updates, but as actually most of submenu layout does not update really update anything I construct my own id with base in on some rules (https://gitlab.com/lestcape/Gnome-Global-AppMenu/-/blob/master/gnomeGlobalAppMenu@lestcape/dbusMenu.js#L1016-1019).
  3. You also need to be prepared to rebuild the menu if your attempt to preserve the items was not helpful.
  4. I was calling DescribeAll for all 3 Gtk proxies, when I made a full layout update (an Start call does a full Layout update). Seems like you are not.
  5. On Gtk, the equivalent of an AboutToShow is called a Start and End again which causes a full update of the layout. Also when I receive the Changed signal I call a Start and End again which is again a layout update.
  6. I think Gtk can be improved if the Changed function is used to what is created, but that is the point where I think this needs experimentation. I never understand how I can use the parameters that this signal provides, but seem to be that i don't need a full relayout if I can use those parameters.
lestcape commented 3 years ago

As I also said I think it's much more productive if this conversation will take place in git, because i think there are people that also can help if they see the issue. So, maybe @rilian-la-te can help us a little here as he is the author of the vala-panel.

gonzaarcr commented 3 years ago

One of the few examples that works as it should: “Show sidebar” on Clementine. The app is made with Qt and uses com.canonical.dbusmenu. When the item is clicked, it sends ItemsPropertiesUpdated with the following parameters:

updated=dbus.Array([dbus.Struct((dbus.Int32(54), dbus.Dictionary({dbus.String('enabled'): dbus.Boolean(True, variant_level=1), dbus.String('label'): dbus.String('Mostrar barra lateral', variant_level=1), dbus.String('toggle-state'): dbus.Int32(0, variant_level=1), dbus.String('toggle-type'): dbus.String('checkmark', variant_level=1), dbus.String('visible'): dbus.Boolean(True, variant_level=1)}, signature=dbus.Signature('sv'))), signature=None)], signature=dbus.Signature('(ia{sv})')) removed=dbus.Array([], signature=dbus.Signature('(ias)'))

Clean version:

updated=[(54, {'enabled': True, 'label': 'Mostrar barra lateral', 'toggle-state': 0, 'toggle-type': 'checkmark', 'visible': True})]
removed=[]

Basically, it sends the id and the new info. Since Qt programs seems to behave nicely, I should search for one with more complex menus. FreeCAD, mentioned in #79, seems to be a good candidate (it has to be installed with an AppImage, I hope that does not interfere).