diff --git a/resources/settings/settings_description.json b/resources/settings/settings_description.json index ee3e021..3b8e70e 100644 --- a/resources/settings/settings_description.json +++ b/resources/settings/settings_description.json @@ -163,6 +163,12 @@ "default": true, "platforms": [ "windows" ] }, + { + "value": "enableWindowsTrayIcon", + "display_name": "Enable Windows System Tray", + "default": false, + "platforms": [ "windows" ] + }, { "value": "enableMPV", "display_name": "Enable MPV Player", diff --git a/src/settings/SettingsComponent.cpp b/src/settings/SettingsComponent.cpp index 79cdc5e..c99c528 100644 --- a/src/settings/SettingsComponent.cpp +++ b/src/settings/SettingsComponent.cpp @@ -21,6 +21,11 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// 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(); } +///////////////////////////////////////////////////////////////////////////////////////// +bool SettingsComponent::enableWindowsTrayIcon() +{ + return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "enableWindowsTrayIcon").toBool(); +} + ///////////////////////////////////////////////////////////////////////////////////////// QString SettingsComponent::detectCertBundlePath() { diff --git a/src/settings/SettingsComponent.h b/src/settings/SettingsComponent.h index 997cc13..d347ba9 100644 --- a/src/settings/SettingsComponent.h +++ b/src/settings/SettingsComponent.h @@ -31,6 +31,7 @@ class SettingsSection; class SettingsComponent : public ComponentBase { Q_OBJECT + Q_PROPERTY(bool windowsTrayIcon READ enableWindowsTrayIcon NOTIFY windowsTrayIconChanged) DEFINE_SINGLETON(SettingsComponent); public: @@ -67,6 +68,7 @@ public: Q_INVOKABLE bool autodetectCertBundle(); Q_INVOKABLE QString detectCertBundlePath(); Q_INVOKABLE bool allowBrowserZoom(); + bool enableWindowsTrayIcon(); // host commands Q_SLOT Q_INVOKABLE void cycleSettingCommand(const QString& args); @@ -87,6 +89,9 @@ public: // part of the map. 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 // the SettingsComponent is created. // diff --git a/src/ui/webview.qml b/src/ui/webview.qml index ee3c5f0..e4ff05d 100644 --- a/src/ui/webview.qml +++ b/src/ui/webview.qml @@ -4,6 +4,7 @@ import QtWebEngine import QtWebChannel import QtQuick.Window import QtQuick.Controls +import Qt.labs.platform as Labs Window { @@ -24,6 +25,9 @@ Window property string videoInfo: "" property string webUrl: "" + property bool showSystemTrayIcon: webDesktopMode && components.system.isWindows && + components.settings.windowsTrayIcon + signal reloadWebClient() Component.onCompleted: { @@ -32,6 +36,14 @@ Window } } + onClosing: function(close) { + if (showSystemTrayIcon) { + // Minimize to tray on close. + close.accepted = false + mainWindow.hide() + } + } + function toggleFullscreen() { visibility = (visibility === Window.FullScreen) ? Window.Windowed : Window.FullScreen } @@ -49,6 +61,12 @@ Window visibility = Window.Minimized } + function restoreWindow() { + mainWindow.show() + mainWindow.raise() + mainWindow.requestActivate() + } + function runWebAction(action) { if (mainWindow.webDesktopMode) @@ -98,8 +116,8 @@ Window Action { enabled: mainWindow.webDesktopMode - shortcut: StandardKey.Quit - onTriggered: mainWindow.close() + shortcut: components.system.isWindows ? "Ctrl+Q" : StandardKey.Quit + onTriggered: Qt.quit() } Action @@ -400,4 +418,34 @@ Window } 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() + } + } + } }