indicatorStatusIcon: Add hide mode to toggle visibility of individual tray icons#625
Open
elkh510 wants to merge 9 commits into
Open
indicatorStatusIcon: Add hide mode to toggle visibility of individual tray icons#625elkh510 wants to merge 9 commits into
elkh510 wants to merge 9 commits into
Conversation
Author
Author
|
@3v1n0 pls take a look |
Collaborator
|
While AI-assisted PRs can be accepted, in order to be at least reviewable this must be:
|
The hide-mode feature added in following commits needs to persist which icons the user has hidden. Storing the SNI id is not enough: Electron-based apps (Bitwarden, Telegram, Pritunl, Slack, …) all register their indicators with the same generic id `chrome_status_icon_1`, so hiding one of them would silently hide all the others on the next session. Derive an app-specific identifier from the executable basename in `/proc/<pid>/cmdline` whenever the SNI id starts with `chrome_status_icon`, and fall back to the original id for every other app.
Hide mode persists three pieces of state across sessions: the on/off toggle itself, the list of indicator IDs the user has chosen to hide, and a discoverable list of known indicators that the preferences UI uses to offer Hide/Show controls without requiring the user to right-click each tray icon first. Adding the keys in their own commit keeps the GSettings schema change reviewable on its own and lets following commits depend on them without mixing schema migrations with code changes.
The hide-mode overflow menu and the regular tray icons need to activate or minimize the app's windows in response to a left click. The logic for matching an SNI indicator to the corresponding `Shell.App` is non-trivial — `Shell.AppSystem` has no `search()` method in GNOME Shell 50, the SNI id rarely matches the desktop file id, and Electron apps reuse the same id — so isolating it in its own module keeps both call sites simple and allows the matching heuristics to evolve without touching the status icon code. The matching tries the desktop-file id first and then walks the running apps comparing wm_class, indicator.id and the executable basename from `_commandLine`. `toggleWindows()` returns false when no app could be matched so callers can fall back to the SNI `Activate` method.
Hide mode needs a place to put the icons the user has chosen to hide while still keeping their menus reachable. Add a singleton `OverflowManager` that tracks all `IndicatorStatusIcon`s, watches the `pin-mode-enabled` and `hidden-icons` GSettings keys, and hides or shows each tracked icon accordingly. When at least one icon is hidden, an `OverflowButton` (`˅`) is added to the panel that exposes a popup menu with one submenu per hidden indicator. Each submenu attaches the indicator's real `DBusMenu.Client`, so the user keeps full access to the app menu, plus a "Show on Panel" entry to bring the icon back. Left-clicking the row delegates to `WindowManager.toggleWindows()` and falls back to the SNI `Activate` method, mirroring the behaviour expected from the icons themselves. `_recordKnownIndicator` populates the `known-indicators` settings key as new indicators register, so the preferences UI can offer hide controls without waiting for the user to right-click each icon. Because Electron apps only resolve their `appId` after `_commandLine` is read asynchronously, we re-record indicators a few seconds after they become ready.
The new `OverflowManager` decides which `IndicatorStatusIcon`s should appear on the panel and which should be sent to the overflow menu. To do that the manager needs two things from each icon: a way to be notified when an icon is created, and a way to toggle its visibility without fighting the existing `_showIfReady` logic that is driven by the SNI status. Register every newly added `IndicatorStatusIcon` with the manager and add `setOverflowed()` on `BaseStatusIcon`. The flag is checked in `_showIfReady` so that an icon that is currently overflowed stays hidden even after the indicator emits `ready` or changes status.
The DBus menu attached to a tray icon is owned by the application and we cannot just append our own action to it permanently — `DBusMenu.Client.attachToMenu()` calls `removeAll()` whenever the client rebuilds the layout, which would drop our entry. Instead, hook the menu's `open-state-changed` signal and append the "Hide from Panel" / "Show on Panel" entry every time the menu is about to be shown. The entry is only inserted while `pin-mode-enabled` is true so users who don't use hide mode never see an extra item in their app menus. The label text follows the current state so a single entry both hides and unhides.
Right-clicking each tray icon to hide it works, but discovering that workflow requires the user to open every menu first. The preferences UI is the discoverable place to manage hide state, and it also lets users undo the action of hiding an icon they have just removed and can no longer see. Add a new "Indicators" page that lists all known indicators (populated from the `known-indicators` GSettings key written by `OverflowManager`) and exposes one switch per row to toggle the hidden state, plus a trash button to drop stale entries that are no longer registered. The list is bound to a Gio.ListStore so changes from another source — for example a Hide action performed via the tray context menu — are reflected automatically.
The schema, the overflow manager and the Indicators page are in place but completely unreachable from the UI until the preferences window registers them. Add the new Pin Mode switch on the General page and instantiate the Indicators page next to the existing General and Custom Icon pages so the user can turn the feature on and pick which icons to hide. Also expose the new GSettings keys (`pin-mode-enabled`, `hidden-icons`, `known-indicators`) through the `SettingsKey` constants table so the preferences pages don't have to repeat the raw key names.
The overflow manager is a singleton that the icon and the preferences code expect to find via `OverflowManager.getDefault()`. Without an explicit `initialize()` call at extension startup the manager would lazily appear only after the first icon registration race-wins, and could be left holding GSettings signal handlers if the extension is disabled while a tray session is active. Wire its lifecycle to the extension's `enable()`/`disable()` hooks, mirroring the pattern already used for `TrayIconsManager`, so reloads and lock-screen toggles always start with a clean manager and never leak handlers.
dc22c38 to
ee13744
Compare
Author
|
@3v1n0 thanks for the feedback. I've force-pushed the branch and split the change into 9 atomic commits, with preparation changes (appId, schemas, windowManager) in their own commits and each commit message explaining the rationale rather than the diff:
Please take another look when you have time. |
|
Great job @elkh510 ! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Adds a "Pin Mode" setting that lets users selectively hide tray icons into an overflow menu, addressing the long-requested ability to declutter the panel.
Features
appIdper Electron app (Bitwarden, Pritunl, etc.) instead of sharedchrome_status_icon_1New files
overflowButton.js— overflow popup menu for hidden iconsoverflowManager.js— hide/unhide logic, visibility managementwindowManager.js— toggle windows on left-click (minimize if focused, activate if not)preferences/indicatorPage.js— Indicators preferences pageNew GSettings keys
pin-mode-enabledbhidden-iconsasappIdstringsknown-indicatorsa(ss)Technical notes
Clutter.ClickGestureon tray icons (GNOME 50+ uses gesture instead ofvfunc_event)vfunc_eventdefined inBaseStatusIcon(first GJS subclass of C type) for reliable vfunc overrideappIdgetter uses executable basename from/proc/PID/cmdlinefor Electron appsready) to resolve Electron appIds after commandLine loadsCloses: #175