Skip to content

indicatorStatusIcon: Add hide mode to toggle visibility of individual tray icons#625

Open
elkh510 wants to merge 9 commits into
ubuntu:masterfrom
elkh510:feature/hide-mode
Open

indicatorStatusIcon: Add hide mode to toggle visibility of individual tray icons#625
elkh510 wants to merge 9 commits into
ubuntu:masterfrom
elkh510:feature/hide-mode

Conversation

@elkh510
Copy link
Copy Markdown

@elkh510 elkh510 commented Apr 6, 2026

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

  • Hide mode (blacklist approach): all icons visible by default, right-click → "Hide from Panel" to move specific icons to overflow
  • Overflow button (˅): shows hidden icons with app submenus + "Show on Panel" action; left-click activates the app window
  • Preferences UI: new "Indicators" tab listing all known indicators with Hide toggle and Remove button
  • Electron app support: unique appId per Electron app (Bitwarden, Pritunl, etc.) instead of shared chrome_status_icon_1
  • Click behavior: left-click = toggle app window, right-click = context menu, middle-click = SecondaryActivate
  • Menu isolation: prevent hover-switching between tray menus and system indicators

New files

  • overflowButton.js — overflow popup menu for hidden icons
  • overflowManager.js — hide/unhide logic, visibility management
  • windowManager.js — toggle windows on left-click (minimize if focused, activate if not)
  • preferences/indicatorPage.js — Indicators preferences page

New GSettings keys

Key Type Description
pin-mode-enabled b Enable/disable hide mode
hidden-icons as List of hidden appId strings
known-indicators a(ss) Pairs of (appId, title) seen by extension

Technical notes

  • Disables Clutter.ClickGesture on tray icons (GNOME 50+ uses gesture instead of vfunc_event)
  • vfunc_event defined in BaseStatusIcon (first GJS subclass of C type) for reliable vfunc override
  • appId getter uses executable basename from /proc/PID/cmdline for Electron apps
  • Delayed re-check (3s after ready) to resolve Electron appIds after commandLine loads

Closes: #175

@elkh510
Copy link
Copy Markdown
Author

elkh510 commented Apr 6, 2026

Screenshot

Screenshot from 2026-04-06 12-36-43

Overflow menu showing hidden tray icons (Pritunl, TelegramDesktop) with app submenus. Visible icons (VLC, Bitwarden) remain on the panel.

@elkh510
Copy link
Copy Markdown
Author

elkh510 commented Apr 6, 2026

@3v1n0 pls take a look

@3v1n0
Copy link
Copy Markdown
Collaborator

3v1n0 commented Apr 7, 2026

While AI-assisted PRs can be accepted, in order to be at least reviewable this must be:

  • Each atomic change should go in a different commit
  • Preparation changes must go alone
  • Every commit should explain the rationale, and not what it does

elkh510 added 9 commits April 8, 2026 15:17
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.
@elkh510 elkh510 force-pushed the feature/hide-mode branch from dc22c38 to ee13744 Compare April 8, 2026 12:27
@elkh510
Copy link
Copy Markdown
Author

elkh510 commented Apr 8, 2026

@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:

  1. `appIndicator: Distinguish Electron apps with a stable appId` — prep
  2. `schemas: Add settings keys for hide mode` — prep
  3. `windowManager: Add helper to toggle/activate app windows` — prep
  4. `overflowManager: Introduce overflow container for hidden tray icons`
  5. `indicatorStatusIcon: Let icons participate in the overflow`
  6. `indicatorStatusIcon: Add Hide/Show from Panel context menu entry`
  7. `preferences: Add Indicators page to manage hidden icons`
  8. `prefs: Wire the Pin Mode toggle and the Indicators page`
  9. `extension: Initialize and tear down OverflowManager`

Please take another look when you have time.

@andrzej-az
Copy link
Copy Markdown

Great job @elkh510 !
IMO, this is must have functionality. I've done similar changes for myself. Merging this should allow to use the original extension
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Request] Toggle on and off specific icons

3 participants