gnome-shell-extension-appindicator
gnome-shell-extension-appindicator copied to clipboard
do LayoutUpdate for menus with no entries
This PR (partially) fixes #494.
Edit:
This problem is caused by the "empty menus":
- For a
LayoutUpdatesignal, classDBusClientonly requests a layout update whenthis._activeistrue(indicating the menu is currently open), or it is delayed untilthis._activebecomestrue(when the menu is opened later). this._activeis only set totruewhen anopen-state-changedsignal is emitted, which is triggered by opening the popup menu.- But an empty menu will never open, so
this._activekeepsfalse, so the delayedLayoutUpdatehandler never runs.
QQ is an IM app who provides no menu entries before user logged in, and updates it when logged in. But it probably does not emit a LayoutUpdated signal before updating menu so its tray icon menu can never open until appindicator restarts (by disabling the extension or ending gnome session). Here we try updating entries before opening an empty menu. Only primary click and secondary click will trigger an update.
Wondering if there are better solutions...
This also fixes Dropbox's tray icon menu not opening.
Mhmh, since _updateMenu is triggered by the menu update, I'm wondering if we can instead react to some signal happening in dbusMenu.js to actually do this...
I can't personally reproduce this with QQ, not sure if that happens after logging in or if I can test it before.
The system tray icon in Blueman exhibits a similar persistence issue - once the application launches, the context menu associated with the tray icon becomes static and does not refresh to reflect state changes.
After launching Blueman, performing Bluetooth enable/disable operations through the interface will not update the visual state of the tray icon
The icon state only refreshes after either:
-
User session restart (logout/login cycle)
-
Manual reload of the desktop extension (disabling and re-enabling the extension)
This behavioral pattern suggests a fundamental refresh mechanism limitation in the tray icon implementation. Similar behavior can be expected in analogous scenarios where system tray components require dynamic state updates without full application/desktop environment reloads.
https://github.com/ubuntu/gnome-shell-extension-appindicator/issues/494#issuecomment-2574247477
A minimal example to reproduce the issue. The tray should have Logout item after clicking the Login button in the application. However, you could not observe expected behavior until you lock the screen (this somehow updates the tray menu).
import gi
from gi.repository import AyatanaAppIndicator3 as AppIndicator3
from gi.repository import Gtk
class IndicatorApp:
def __init__(self):
# Create the main window
self.window = Gtk.Window(title="Login Example")
self.window.set_default_size(200, 100)
self.window.connect("destroy", self.on_quit)
# Create a button
self.button = Gtk.Button(label="Login")
self.button.connect("clicked", self.on_login_clicked)
self.window.add(self.button)
# Create the app indicator
self.indicator = AppIndicator3.Indicator.new(
"indicator-example",
"system-run-symbolic", # Default icon
AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
# Initial empty menu
self.menu = Gtk.Menu()
self.indicator.set_menu(self.menu)
# Show the window
self.window.show_all()
# Track login state
self.logged_in = False
def on_login_clicked(self, widget):
if not self.logged_in:
# Update button text
self.button.set_label("Logout")
# Update tray menu
self.update_tray_menu()
self.logged_in = True
else:
# Logout action
self.button.set_label("Login")
self.clear_tray_menu()
self.logged_in = False
def update_tray_menu(self):
# Clear existing menu items
self.clear_tray_menu()
# Create new menu items
menu = Gtk.Menu()
# Add logout item
item_logout = Gtk.MenuItem(label="Logout")
item_logout.connect("activate", self.on_logout_clicked)
menu.append(item_logout)
# Add separator
menu.append(Gtk.SeparatorMenuItem())
# Add quit item
item_quit = Gtk.MenuItem(label="Quit")
item_quit.connect("activate", self.on_quit)
menu.append(item_quit)
# Show all menu items
menu.show_all()
# Set the new menu
self.indicator.set_menu(menu)
def clear_tray_menu(self):
# Create an empty menu
menu = Gtk.Menu()
self.indicator.set_menu(menu)
def on_logout_clicked(self, widget):
# Reset to login state
self.button.set_label("Login")
self.clear_tray_menu()
self.logged_in = False
def on_quit(self, widget=None):
Gtk.main_quit()
if __name__ == "__main__":
app = IndicatorApp()
Gtk.main()
Mhmh, since
_updateMenuis triggered by the menu update, I'm wondering if we can instead react to some signal happening indbusMenu.jsto actually do this...
Yes I believe that there should be better solutions to this.
I was unable to inspect what exactly happened during the communication between QQ and StatusNotifierHost but with Bustle I can see how it is going now.
I can't personally reproduce this with QQ, not sure if that happens after logging in or if I can test it before.
~Yes, this happens after logging in.~
It seems that QQ does not have the problem any more, probably because it does not provide an empty menu before login.
Thanks to DerryAlex who provided another minimal reproduce.
The code changes in this PR are different since @AmionSky's comment that this fixes the Dropbox tray icon, so I just wanted to chime in and say that the latest revision still works. I applied it in a minimalist way to v43 in Debian 12 (Gnome 43.9):
diff --git a/dbusMenu.js.bak b/dbusMenu.js
index a30502a..f74899e 100644
--- a/dbusMenu.js.bak
+++ b/dbusMenu.js
@@ -470,14 +470,14 @@ var DBusClient = GObject.registerClass({
_onSignal(_sender, signal, params) {
if (signal === 'LayoutUpdated') {
- if (!this._active) {
+ if (!this._active && this.getRoot()?.getChildren().length) {
this._flagLayoutUpdateRequired = true;
return;
}
this._requestLayoutUpdate();
} else if (signal === 'ItemsPropertiesUpdated') {
- if (!this._active) {
+ if (!this._active && this.getRoot()?.getChildren().length) {
this._flagItemsUpdateRequired = true;
return;
}
Now, when I boot my system and log in, the Dropbox tray icon works when I click it. I used to have to lock the screen and then unlock it before the icon would work.
@3v1n0 - any chance this could be re-reviewed/merged?