From ad596a6b6feefe152bb73eda7be27a67707bd71a Mon Sep 17 00:00:00 2001 From: fdev31 Date: Sun, 5 Oct 2025 22:05:08 +0200 Subject: [PATCH] Allow system tray on every screen --- modules/systemtray.py | 90 +++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/modules/systemtray.py b/modules/systemtray.py index ba0ba2bc..9a3cdb34 100644 --- a/modules/systemtray.py +++ b/modules/systemtray.py @@ -11,6 +11,48 @@ logger = logging.getLogger(__name__) +# --- Global State --- +_watcher = None +_items_by_id = {} +_instances = [] + +def _refresh_ui_for_all_instances(identifier: str, item: Gray.Item): + for instance in _instances: + btn = instance.buttons_by_id.get(identifier) + if btn: + instance._refresh_item_ui(identifier, item, btn) + +def _global_on_watcher_item_added(_, identifier: str): + item = _watcher.get_item_for_identifier(identifier) + if not item: + return + + _items_by_id[identifier] = item + + def on_item_removed(removed_item): + if _items_by_id.get(identifier) is removed_item: + _items_by_id.pop(identifier, None) + for instance in _instances: + instance.on_item_instance_removed(identifier, removed_item) + + item.connect("removed", lambda itm: on_item_removed(itm)) + item.connect("notify::icon-pixmaps", lambda itm, pspec: _refresh_ui_for_all_instances(identifier, itm)) + item.connect("notify::icon-name", lambda itm, pspec: _refresh_ui_for_all_instances(identifier, itm)) + try: + item.connect("updated", lambda itm: _refresh_ui_for_all_instances(identifier, itm)) + except TypeError: + pass + + for instance in _instances: + instance.on_item_added(identifier) + +def _init_watcher(): + global _watcher + if _watcher is None: + _watcher = Gray.Watcher() + _watcher.connect("item-added", _global_on_watcher_item_added) + + class SystemTray(Box): def __init__(self, pixel_size: int = 20, refresh_interval: int = 1, **kwargs) -> None: orientation = Gtk.Orientation.HORIZONTAL if not data.VERTICAL else Gtk.Orientation.VERTICAL @@ -26,12 +68,19 @@ def __init__(self, pixel_size: int = 20, refresh_interval: int = 1, **kwargs) -> self.refresh_interval = refresh_interval self.buttons_by_id = {} - self.items_by_id = {} - self.watcher = Gray.Watcher() - self.watcher.connect("item-added", self.on_watcher_item_added) + _instances.append(self) + _init_watcher() + + for identifier in list(_items_by_id.keys()): + self.on_item_added(identifier) GLib.timeout_add_seconds(self.refresh_interval, self._refresh_all_items) + self.connect("destroy", self._on_destroy) + + def _on_destroy(self, *args): + if self in _instances: + _instances.remove(self) def set_visible(self, visible: bool): self.enabled = visible @@ -91,38 +140,23 @@ def _refresh_item_ui(self, identifier: str, item: Gray.Item, button: Gtk.Button) button.set_has_tooltip(False) def _refresh_all_items(self) -> bool: - - for ident, item in self.items_by_id.items(): - btn = self.buttons_by_id.get(ident) - if btn: + for ident, btn in self.buttons_by_id.items(): + item = _items_by_id.get(ident) + if item: self._refresh_item_ui(ident, item, btn) return True - def on_watcher_item_added(self, _, identifier: str): - item = self.watcher.get_item_for_identifier(identifier) + def on_item_added(self, identifier: str): + item = _items_by_id.get(identifier) if not item: return if identifier in self.buttons_by_id: self.buttons_by_id[identifier].destroy() del self.buttons_by_id[identifier] - del self.items_by_id[identifier] btn = self.do_bake_item_button(item) self.buttons_by_id[identifier] = btn - self.items_by_id[identifier] = item - - item.connect("notify::icon-pixmaps", - lambda itm, pspec: self._refresh_item_ui(identifier, itm, btn)) - item.connect("notify::icon-name", - lambda itm, pspec: self._refresh_item_ui(identifier, itm, btn)) - - try: - item.connect("updated", lambda itm: self._refresh_item_ui(identifier, itm, btn)) - except TypeError: - pass - - item.connect("removed", lambda itm: self.on_item_instance_removed(identifier, itm)) self.add(btn) btn.show_all() @@ -139,12 +173,10 @@ def do_bake_item_button(self, item: Gray.Item) -> Gtk.Button: return btn def on_item_instance_removed(self, identifier: str, removed_item: Gray.Item): - if self.items_by_id.get(identifier) is removed_item: - btn = self.buttons_by_id.pop(identifier, None) - self.items_by_id.pop(identifier, None) - if btn: - btn.destroy() - self._update_visibility() + btn = self.buttons_by_id.pop(identifier, None) + if btn: + btn.destroy() + self._update_visibility() def on_button_click(self, button: Gtk.Button, item: Gray.Item, event: Gdk.EventButton): if event.button == Gdk.BUTTON_PRIMARY: