Close to system tray option on Windows (#1159)

Displays a system tray icon when enabled. Closing the window (clicking the X or Alt+F4) hides the window.

The window can be restored by clicking the icon, or via its context menu. The application can be quit via the context menu or Ctrl+Q.

Default: Disabled

Closes #470
This commit is contained in:
Eric Gregory authored and GitHub committed 2026-02-24 21:07:47 -05:00
1 parent 00c44bc74d
commit 2bc0c4fe8c
4 files changed
+72 -2

No files matched your search

@@ -163,6 +163,12 @@
"default": true, "default": true,
"platforms": [ "windows" ] "platforms": [ "windows" ]
}, },
{
"value": "enableWindowsTrayIcon",
"display_name": "Enable Windows System Tray",
"default": false,
"platforms": [ "windows" ]
},
{ {
"value": "enableMPV", "value": "enableMPV",
"display_name": "Enable MPV Player", "display_name": "Enable MPV Player",
+11
View File
@@ -21,6 +21,11 @@
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
SettingsComponent::SettingsComponent(QObject *parent) : ComponentBase(parent), m_settingsVersion(-1) SettingsComponent::SettingsComponent(QObject *parent) : ComponentBase(parent), m_settingsVersion(-1)
{ {
connect(this, &SettingsComponent::sectionValueUpdate, this, [this](const QString& section, const QVariantMap& values)
{
if (section == SETTINGS_SECTION_MAIN && values.contains("enableWindowsTrayIcon"))
emit windowsTrayIconChanged();
});
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@@ -790,6 +795,12 @@ bool SettingsComponent::allowBrowserZoom()
return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "allowBrowserZoom").toBool(); return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "allowBrowserZoom").toBool();
} }
/////////////////////////////////////////////////////////////////////////////////////////
bool SettingsComponent::enableWindowsTrayIcon()
{
return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "enableWindowsTrayIcon").toBool();
}
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
QString SettingsComponent::detectCertBundlePath() QString SettingsComponent::detectCertBundlePath()
{ {
+5
View File
@@ -31,6 +31,7 @@ class SettingsSection;
class SettingsComponent : public ComponentBase class SettingsComponent : public ComponentBase
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool windowsTrayIcon READ enableWindowsTrayIcon NOTIFY windowsTrayIconChanged)
DEFINE_SINGLETON(SettingsComponent); DEFINE_SINGLETON(SettingsComponent);
public: public:
@@ -67,6 +68,7 @@ public:
Q_INVOKABLE bool autodetectCertBundle(); Q_INVOKABLE bool autodetectCertBundle();
Q_INVOKABLE QString detectCertBundlePath(); Q_INVOKABLE QString detectCertBundlePath();
Q_INVOKABLE bool allowBrowserZoom(); Q_INVOKABLE bool allowBrowserZoom();
bool enableWindowsTrayIcon();
// host commands // host commands
Q_SLOT Q_INVOKABLE void cycleSettingCommand(const QString& args); Q_SLOT Q_INVOKABLE void cycleSettingCommand(const QString& args);
@@ -87,6 +89,9 @@ public:
// part of the map. // part of the map.
Q_SIGNAL void sectionValueUpdate(const QString& section, const QVariantMap& values); Q_SIGNAL void sectionValueUpdate(const QString& section, const QVariantMap& values);
// Windows system tray icon enabled/disabled.
Q_SIGNAL void windowsTrayIconChanged();
// A hack to load a value from the config file at very early init time, before // A hack to load a value from the config file at very early init time, before
// the SettingsComponent is created. // the SettingsComponent is created.
// //
+50 -2
View File
@@ -4,6 +4,7 @@ import QtWebEngine
import QtWebChannel import QtWebChannel
import QtQuick.Window import QtQuick.Window
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.platform as Labs
Window Window
{ {
@@ -24,6 +25,9 @@ Window
property string videoInfo: "" property string videoInfo: ""
property string webUrl: "" property string webUrl: ""
property bool showSystemTrayIcon: webDesktopMode && components.system.isWindows &&
components.settings.windowsTrayIcon
signal reloadWebClient() signal reloadWebClient()
Component.onCompleted: { Component.onCompleted: {
@@ -32,6 +36,14 @@ Window
} }
} }
onClosing: function(close) {
if (showSystemTrayIcon) {
// Minimize to tray on close.
close.accepted = false
mainWindow.hide()
}
}
function toggleFullscreen() { function toggleFullscreen() {
visibility = (visibility === Window.FullScreen) ? Window.Windowed : Window.FullScreen visibility = (visibility === Window.FullScreen) ? Window.Windowed : Window.FullScreen
} }
@@ -49,6 +61,12 @@ Window
visibility = Window.Minimized visibility = Window.Minimized
} }
function restoreWindow() {
mainWindow.show()
mainWindow.raise()
mainWindow.requestActivate()
}
function runWebAction(action) function runWebAction(action)
{ {
if (mainWindow.webDesktopMode) if (mainWindow.webDesktopMode)
@@ -98,8 +116,8 @@ Window
Action Action
{ {
enabled: mainWindow.webDesktopMode enabled: mainWindow.webDesktopMode
shortcut: StandardKey.Quit shortcut: components.system.isWindows ? "Ctrl+Q" : StandardKey.Quit
onTriggered: mainWindow.close() onTriggered: Qt.quit()
} }
Action Action
@@ -400,4 +418,34 @@ Window
} }
property QtObject webChannel: web.webChannel property QtObject webChannel: web.webChannel
Labs.SystemTrayIcon {
visible: showSystemTrayIcon
icon.source: "qrc:/images/icon.png"
tooltip: "Jellyfin Desktop"
onActivated: function(reason) {
if (reason === Labs.SystemTrayIcon.Context) {
// Right click: open context menu
contextMenu.open()
components.window.setCursorVisibility(true)
} else {
// All other clicks: restore window
restoreWindow()
}
}
menu: Labs.Menu {
id: contextMenu
Labs.MenuItem {
text: qsTr("Restore")
onTriggered: restoreWindow()
}
Labs.MenuSeparator {}
Labs.MenuItem {
text: qsTr("Quit")
onTriggered: Qt.quit()
}
}
}
} }