システムトレイ / インジケータ
画面の隅に小さなアイコンを常駐させ、クリックでメニューを出したり状態を表示したりするUI。デスクトップアプリを3プラットフォームに出すとき、ウィンドウやメニューバー以上に実装が割れる部分。特にLinuxは「どの規格に乗るか」からして決まっていない。
呼び名が3つある
| プラットフォーム | 呼び名 | 場所 |
|---|---|---|
| macOS | メニューバーエクストラ / ステータスアイテム | 画面上部メニューバーの右側 |
| Windows | 通知領域(notification area)、俗にシステムトレイ | タスクバー右端 |
| Linux | システムトレイ / インジケータ / ステータスアイコン | パネル(DEによる) |
「システムトレイ」はWindows由来の俗称がそのまま各所で使われている状態で、Microsoftの公式ドキュメントは “notification area” と呼ぶ。
macOS
NSStatusBar::systemStatusBar()からstatusItemWithLength:でNSStatusItemを1つもらい、それにNSImageとNSMenuを付ける、という単純な話。NSStatusItemを保持している間だけ表示され、解放すると消える。
Dockアイコンを出さずにメニューバーだけに常駐したい場合は、Info.plistのLSUIElementをYESにするか、実行時に activation policy を accessory にする。GPUI Community Edition (gpui-ce)がMacActivationPolicy::Accessoryを足しているのはこの用途。
このジャンルのアプリについてはmacOSメニューバー常駐ユーティリティを参照。
Windows
Shell_NotifyIcon(Unicode版はShell_NotifyIconW)にNOTIFYICONDATAを渡してNIM_ADD / NIM_MODIFY / NIM_DELETEする。アイコン・ツールチップ・コールバックメッセージのID・状態(NIS_HIDDENなど)を構造体で指定し、クリック等はウィンドウメッセージとして飛んでくる。
厄介なのは、既定でオーバーフロー(「隠されているアイコンを表示します」)に入ること。ユーザーが明示的に出さない限りタスクバーに直接は出ない。「アイコンを出したのに見えない」の大半はこれ。
Linuxが一番ややこしい
2つの規格が併存している。
- XEmbed方式(freedesktop の System Tray Protocol) — アプリ側がX11のウィンドウを作り、パネル側がそれを自分の中に埋め込む。X11の仕組みそのものなのでWaylandでは使えない。
- StatusNotifierItem (SNI) — KDE発。D-Busでアプリがオブジェクトを公開し、
StatusNotifierWatcherに登録する。表示はパネル側の裁量(モデル/ビュー分離)。メニューはcom.canonical.dbusmenuで渡す。D-BusベースなのでWaylandでも動く。freedesktopのwikiに置かれているが、正式な標準として批准されたわけではない。
GNOMEは3.26(2017年)で従来のトレイ表示を削除した。2024年に公式の「Status Icons」拡張(作者はFlorian Müllner)がGNOME Shell Extensionsパッケージに入ったが、これはXEmbedのみ対応でAppIndicator/SNIには対応しない。作者は「もっと良い標準が出てくれば対応するかもしれないが、AppIndicatorsはそれではない」と述べている。しかもこの拡張は既定で有効ではなく、ディストリが同梱するかどうかによる。SNIを使いたい場合はコミュニティ製の「AppIndicator and KStatusNotifierItem Support」拡張を入れることになる。
つまりLinuxでは、規格を1つ選んでも全部のデスクトップ環境で出る保証がない。SNIに乗るのが今の主流だが、素のGNOMEでは拡張なしには出ない。
Rustでの実装
デファクトはtray-icon(Tauriチームが管理。メニュー側は同チームのmudaが担当する)。3プラットフォームのバックエンドはこうなっている(v0.24.2のソースで確認)。
| プラットフォーム | 使っているもの |
|---|---|
| macOS | NSStatusBar::systemStatusBar().statusItemWithLength() |
| Windows | Shell_NotifyIconW + NOTIFYICONDATAW |
| Linux/BSD | libappindicator(またはlibayatana-appindicator)+ GTK3 |
Linux側の実装で目を引くのがアイコンをメモリから渡せない点。libappindicatorはアイコンをテーマ内の名前かファイルパスでしか受け取らないので、tray-iconは一時ディレクトリにPNGを書き出し、その親ディレクトリをset_icon_theme_path()に、ファイルをset_icon_full()に渡している。アイコンを差し替えるたびに連番付きの新しいファイルを書く。
イベントループの制約も強い。Windowsではwin32のイベントループ、Linux/FreeBSDではGTKのイベントループが同一スレッドで回っている必要があり、macOSではメインスレッドでイベントループが回っている必要がある。自前のイベントループを持つGUIフレームワークに後付けする場合、ここが最初の障害になる。
もう一つの選択肢がksni。KDE/freedesktopのStatusNotifierItem仕様を純Rust + D-Busで実装していて、GTKにもlibappindicatorにも依存しない。org.kde.StatusNotifierItemとcom.canonical.dbusmenuを自前で喋る。tray-iconのLinuxバックエンドをksniに置き換える提案(Issue #11293)は出ているが、2026年9月時点では採用されていない。
crates.ioのダウンロード数(2026-09時点)。
| クレート | 最新 | total | recent |
|---|---|---|---|
tray-icon |
0.24.2 | 27,243,206 | 11,788,630 |
libappindicator |
0.9.0 | 23,581,746 | 9,182,780 |
ksni |
0.3.6 | 1,351,007 | 456,267 |
tray-item |
0.10.0 | 167,881 | 16,136 |
tray-iconとlibappindicatorの数字が近いのは、前者が後者を引いているため。
GPUIには無い
GPUI本体にも、コミュニティフォークのGPUI Community Edition (gpui-ce)にもトレイ対応は無い。NSStatusBar・Shell_NotifyIcon・StatusNotifierItemのいずれの実装も、2026年9月初旬時点でどちらのツリーにも見当たらない。
「Zed本体のスコープ外の機能はgpui-ceへ」という案内の文脈でトレイ対応が例に挙がる(GPUI KitのDiscussion #1856など)ため、gpui-ceにはあると思われがちだが、要望として挙がっただけで実装はされていない。
GPUIアプリにトレイを付けるならtray-iconかksniを別途組み合わせることになるが、上記のイベントループ制約とGPUI自身のイベントループの同居が課題になる。実際に噛み合うかは未検証。
同じく、macOS以外でOSのメニューバーを出す手段もGPUIには無い(GPUI Kitでのクロスプラットフォームなメニュー)。
出典
- StatusNotifierItem - freedesktop.org wiki
- Notifications and the Notification Area - Win32 apps | Microsoft Learn
- NSStatusItem | Apple Developer Documentation
- NSStatusBar | Apple Developer Documentation
- GNOME Now Has an Official Extension for Legacy Tray Icons - OMG! Ubuntu
- tauri-apps/tray-icon - GitHub —
src/platform_impl/{macos,windows,gtk}/mod.rs、README - iovxw/ksni - GitHub
- Use ksni crate for tray icons on Linux · tauri-apps/tauri Issue #11293