Skip to content
This repository was archived by the owner on Mar 28, 2026. It is now read-only.

Commit 2bc0c4f

Browse files
authored
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
1 parent 00c44bc commit 2bc0c4f

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

resources/settings/settings_description.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@
163163
"default": true,
164164
"platforms": [ "windows" ]
165165
},
166+
{
167+
"value": "enableWindowsTrayIcon",
168+
"display_name": "Enable Windows System Tray",
169+
"default": false,
170+
"platforms": [ "windows" ]
171+
},
166172
{
167173
"value": "enableMPV",
168174
"display_name": "Enable MPV Player",

src/settings/SettingsComponent.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
///////////////////////////////////////////////////////////////////////////////////////////////////
2222
SettingsComponent::SettingsComponent(QObject *parent) : ComponentBase(parent), m_settingsVersion(-1)
2323
{
24+
connect(this, &SettingsComponent::sectionValueUpdate, this, [this](const QString& section, const QVariantMap& values)
25+
{
26+
if (section == SETTINGS_SECTION_MAIN && values.contains("enableWindowsTrayIcon"))
27+
emit windowsTrayIconChanged();
28+
});
2429
}
2530

2631
/////////////////////////////////////////////////////////////////////////////////////////
@@ -790,6 +795,12 @@ bool SettingsComponent::allowBrowserZoom()
790795
return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "allowBrowserZoom").toBool();
791796
}
792797

798+
/////////////////////////////////////////////////////////////////////////////////////////
799+
bool SettingsComponent::enableWindowsTrayIcon()
800+
{
801+
return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "enableWindowsTrayIcon").toBool();
802+
}
803+
793804
/////////////////////////////////////////////////////////////////////////////////////////
794805
QString SettingsComponent::detectCertBundlePath()
795806
{

src/settings/SettingsComponent.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class SettingsSection;
3131
class SettingsComponent : public ComponentBase
3232
{
3333
Q_OBJECT
34+
Q_PROPERTY(bool windowsTrayIcon READ enableWindowsTrayIcon NOTIFY windowsTrayIconChanged)
3435
DEFINE_SINGLETON(SettingsComponent);
3536

3637
public:
@@ -67,6 +68,7 @@ class SettingsComponent : public ComponentBase
6768
Q_INVOKABLE bool autodetectCertBundle();
6869
Q_INVOKABLE QString detectCertBundlePath();
6970
Q_INVOKABLE bool allowBrowserZoom();
71+
bool enableWindowsTrayIcon();
7072

7173
// host commands
7274
Q_SLOT Q_INVOKABLE void cycleSettingCommand(const QString& args);
@@ -87,6 +89,9 @@ class SettingsComponent : public ComponentBase
8789
// part of the map.
8890
Q_SIGNAL void sectionValueUpdate(const QString& section, const QVariantMap& values);
8991

92+
// Windows system tray icon enabled/disabled.
93+
Q_SIGNAL void windowsTrayIconChanged();
94+
9095
// A hack to load a value from the config file at very early init time, before
9196
// the SettingsComponent is created.
9297
//

src/ui/webview.qml

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import QtWebEngine
44
import QtWebChannel
55
import QtQuick.Window
66
import QtQuick.Controls
7+
import Qt.labs.platform as Labs
78

89
Window
910
{
@@ -24,6 +25,9 @@ Window
2425
property string videoInfo: ""
2526
property string webUrl: ""
2627

28+
property bool showSystemTrayIcon: webDesktopMode && components.system.isWindows &&
29+
components.settings.windowsTrayIcon
30+
2731
signal reloadWebClient()
2832

2933
Component.onCompleted: {
@@ -32,6 +36,14 @@ Window
3236
}
3337
}
3438

39+
onClosing: function(close) {
40+
if (showSystemTrayIcon) {
41+
// Minimize to tray on close.
42+
close.accepted = false
43+
mainWindow.hide()
44+
}
45+
}
46+
3547
function toggleFullscreen() {
3648
visibility = (visibility === Window.FullScreen) ? Window.Windowed : Window.FullScreen
3749
}
@@ -49,6 +61,12 @@ Window
4961
visibility = Window.Minimized
5062
}
5163

64+
function restoreWindow() {
65+
mainWindow.show()
66+
mainWindow.raise()
67+
mainWindow.requestActivate()
68+
}
69+
5270
function runWebAction(action)
5371
{
5472
if (mainWindow.webDesktopMode)
@@ -98,8 +116,8 @@ Window
98116
Action
99117
{
100118
enabled: mainWindow.webDesktopMode
101-
shortcut: StandardKey.Quit
102-
onTriggered: mainWindow.close()
119+
shortcut: components.system.isWindows ? "Ctrl+Q" : StandardKey.Quit
120+
onTriggered: Qt.quit()
103121
}
104122

105123
Action
@@ -400,4 +418,34 @@ Window
400418
}
401419

402420
property QtObject webChannel: web.webChannel
421+
422+
Labs.SystemTrayIcon {
423+
visible: showSystemTrayIcon
424+
icon.source: "qrc:/images/icon.png"
425+
tooltip: "Jellyfin Desktop"
426+
427+
onActivated: function(reason) {
428+
if (reason === Labs.SystemTrayIcon.Context) {
429+
// Right click: open context menu
430+
contextMenu.open()
431+
components.window.setCursorVisibility(true)
432+
} else {
433+
// All other clicks: restore window
434+
restoreWindow()
435+
}
436+
}
437+
438+
menu: Labs.Menu {
439+
id: contextMenu
440+
Labs.MenuItem {
441+
text: qsTr("Restore")
442+
onTriggered: restoreWindow()
443+
}
444+
Labs.MenuSeparator {}
445+
Labs.MenuItem {
446+
text: qsTr("Quit")
447+
onTriggered: Qt.quit()
448+
}
449+
}
450+
}
403451
}

0 commit comments

Comments
 (0)