From d358c4f9208682b14318a8406dc0ae5c8adfe858 Mon Sep 17 00:00:00 2001 From: Elias Steurer Date: Wed, 14 Aug 2024 14:06:53 +0200 Subject: [PATCH] WIP Refactor wallpaper timeline logic TODO: - Fix qcoro // QMetaObject::invokeMethod( requestSaveProfiles call --- ScreenPlay/CMakeLists.txt | 46 ++--- .../inc/public/ScreenPlay/monitorlistmodel.h | 3 + .../inc/public/ScreenPlay/screenplaymanager.h | 33 ++-- .../ScreenPlay/screenplaytimelinemanager.h | 12 +- .../public/ScreenPlay/screenplaywallpaper.h | 21 ++- .../ScreenPlay/wallpapertimelinesection.h | 10 +- ScreenPlay/qml/Components/LineIndicator.qml | 24 +-- ScreenPlay/qml/Components/Timeline.qml | 9 +- .../ContentSettings/ContentSettingsView.qml | 28 +-- .../qml/ContentSettings/MonitorSelection.qml | 1 + .../ContentSettings/MonitorSelectionItem.qml | 20 ++ ScreenPlay/qml/Installed/InstalledDrawer.qml | 39 ++-- .../{ScreenPlayItem.qml => InstalledItem.qml} | 2 +- ...ayItemImage.qml => InstalledItemImage.qml} | 0 ScreenPlay/qml/Installed/InstalledView.qml | 2 +- ScreenPlay/qml/Navigation/Navigation.qml | 1 + ScreenPlay/src/monitorlistmodel.cpp | 12 ++ ScreenPlay/src/screenplaymanager.cpp | 108 +++++------ ScreenPlay/src/screenplaytimelinemanager.cpp | 81 +++++++-- ScreenPlay/src/screenplaywallpaper.cpp | 60 ++++-- ScreenPlay/src/wallpapertimelinesection.cpp | 32 ++-- ScreenPlaySDK/src/screenplaysdk.cpp | 2 +- ScreenPlayUtil/CMakeLists.txt | 4 + .../inc/public/ScreenPlayUtil/globalenums.h | 27 +++ ScreenPlayUtil/qml/ErrorPopup.qml | 34 ++++ ScreenPlayUtil/qml/InstantPopup.js | 32 ++++ ScreenPlayUtil/qml/RainbowGradient.qml | 172 ++++++++++++++++++ ScreenPlayWorkshop/CMakeLists.txt | 4 +- .../{ScreenPlayItem.qml => InstalledItem.qml} | 2 +- ...ayItemImage.qml => InstalledItemImage.qml} | 0 ScreenPlayWorkshop/qml/WorkshopItem.qml | 2 +- ThirdParty/CMakeLists.txt | 1 - 32 files changed, 601 insertions(+), 223 deletions(-) rename ScreenPlay/qml/Installed/{ScreenPlayItem.qml => InstalledItem.qml} (99%) rename ScreenPlay/qml/Installed/{ScreenPlayItemImage.qml => InstalledItemImage.qml} (100%) create mode 100644 ScreenPlayUtil/inc/public/ScreenPlayUtil/globalenums.h create mode 100644 ScreenPlayUtil/qml/ErrorPopup.qml create mode 100644 ScreenPlayUtil/qml/InstantPopup.js create mode 100644 ScreenPlayUtil/qml/RainbowGradient.qml rename ScreenPlayWorkshop/qml/{ScreenPlayItem.qml => InstalledItem.qml} (99%) rename ScreenPlayWorkshop/qml/{ScreenPlayItemImage.qml => InstalledItemImage.qml} (100%) diff --git a/ScreenPlay/CMakeLists.txt b/ScreenPlay/CMakeLists.txt index 77609460..580a6d4f 100644 --- a/ScreenPlay/CMakeLists.txt +++ b/ScreenPlay/CMakeLists.txt @@ -10,9 +10,6 @@ include(GenerateCMakeVariableHeader) set(SOURCES # cmake-format: sort - src/wallpaperdata.cpp - src/wallpapertimelinesection.cpp - src/screenplaytimelinemanager.cpp src/app.cpp src/applicationengine.cpp src/create.cpp @@ -24,17 +21,17 @@ set(SOURCES src/profilelistmodel.cpp src/projectsettingslistmodel.cpp src/screenplaymanager.cpp + src/screenplaytimelinemanager.cpp src/screenplaywallpaper.cpp src/screenplaywidget.cpp src/sdkconnection.cpp src/settings.cpp + src/wallpaperdata.cpp + src/wallpapertimelinesection.cpp src/wizards.cpp) set(HEADER # cmake-format: sort - inc/public/ScreenPlay/wallpaperdata.h - inc/public/ScreenPlay/wallpapertimelinesection.h - inc/public/ScreenPlay/screenplaytimelinemanager.h inc/public/ScreenPlay/app.h inc/public/ScreenPlay/applicationengine.h inc/public/ScreenPlay/create.h @@ -48,24 +45,32 @@ set(HEADER inc/public/ScreenPlay/profilelistmodel.h inc/public/ScreenPlay/projectsettingslistmodel.h inc/public/ScreenPlay/screenplaymanager.h + inc/public/ScreenPlay/screenplaytimelinemanager.h inc/public/ScreenPlay/screenplaywallpaper.h inc/public/ScreenPlay/screenplaywidget.h inc/public/ScreenPlay/sdkconnection.h inc/public/ScreenPlay/settings.h + inc/public/ScreenPlay/wallpaperdata.h + inc/public/ScreenPlay/wallpapertimelinesection.h inc/public/ScreenPlay/wizards.h) set(QML # cmake-format: sort main.qml - qml/MainApp.qml - qml/Components/TrayIcon.qml + qml/Community/CommunityNavItem.qml + qml/Community/CommunityView.qml + qml/Community/XMLNewsfeed.qml qml/Components/LineHandle.qml qml/Components/LineIndicator.qml qml/Components/ScreenPlayProPopup.qml qml/Components/Timeline.qml - qml/Community/CommunityNavItem.qml - qml/Community/CommunityView.qml - qml/Community/XMLNewsfeed.qml + qml/Components/TrayIcon.qml + qml/ContentSettings/ContentSettingsView.qml + qml/ContentSettings/DefaultVideoControls.qml + qml/ContentSettings/MonitorSelection.qml + qml/ContentSettings/MonitorSelectionItem.qml + qml/ContentSettings/MonitorsProjectSettingItem.qml + qml/ContentSettings/SaveNotification.qml qml/Create/CreateSidebar.qml qml/Create/CreateView.qml qml/Create/StartInfo.qml @@ -84,18 +89,13 @@ set(QML qml/Create/Wizards/QMLWidget.qml qml/Create/Wizards/WebsiteWallpaper.qml qml/Create/Wizards/WizardPage.qml + qml/Installed/InstalledDrawer.qml + qml/Installed/InstalledItem.qml + qml/Installed/InstalledItemImage.qml qml/Installed/InstalledNavigation.qml qml/Installed/InstalledView.qml qml/Installed/InstalledWelcomeScreen.qml - qml/Installed/ScreenPlayItem.qml - qml/Installed/ScreenPlayItemImage.qml - qml/Installed/InstalledDrawer.qml - qml/ContentSettings/DefaultVideoControls.qml - qml/ContentSettings/MonitorSelection.qml - qml/ContentSettings/MonitorSelectionItem.qml - qml/ContentSettings/MonitorsProjectSettingItem.qml - qml/ContentSettings/ContentSettingsView.qml - qml/ContentSettings/SaveNotification.qml + qml/MainApp.qml qml/Navigation/ExitPopup.qml qml/Navigation/Navigation.qml qml/Settings/SettingBool.qml @@ -135,9 +135,9 @@ set(RESOURCES assets/icons/brand_twitch.svg assets/icons/brand_twitter.svg assets/icons/exclamation-triangle-solid.svg - assets/icons/font-awsome/lock-solid.svg assets/icons/font-awsome/close.svg assets/icons/font-awsome/frown-o.svg + assets/icons/font-awsome/lock-solid.svg assets/icons/font-awsome/patreon-brands.svg assets/icons/font-awsome/rotate-right-solid.svg assets/icons/icon_arrow_left.svg @@ -194,8 +194,6 @@ set(RESOURCES assets/icons/item_banner_new.svg assets/icons/monitor_setup.svg assets/icons/steam_default_avatar.png - assets/images/rocket_3d.png - assets/images/pro_version.png assets/images/Intro.png assets/images/Intro_PC.png assets/images/Intro_shine.png @@ -204,6 +202,8 @@ set(RESOURCES assets/images/missingPreview.png assets/images/noisy-texture-3.png assets/images/noisy-texture.png + assets/images/pro_version.png + assets/images/rocket_3d.png assets/images/scale_window_indicator.png assets/images/steam_offline.png assets/images/trayIcon_osx.png diff --git a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h index 339905c0..37dba044 100644 --- a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h +++ b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h @@ -14,6 +14,7 @@ #include "ScreenPlay/wallpapertimelinesection.h" #include "ScreenPlayUtil/contenttypes.h" +#include "ScreenPlayUtil/globalenums.h" namespace ScreenPlay { @@ -31,6 +32,7 @@ struct Monitor { QRect m_geometry; QString m_wallpaperPreviewImage; QString m_appID; + ScreenPlayEnums::AppState m_appState = ScreenPlayEnums::AppState::Inactive; ContentTypes::InstalledType m_installedType = ContentTypes::InstalledType::Unknown; }; @@ -45,6 +47,7 @@ public: enum class MonitorRole { AppID = Qt::UserRole, + AppState, Index, Geometry, PreviewImage, diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h index a1eb6083..70b20ec1 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h @@ -23,8 +23,9 @@ class ScreenPlayManager : public QObject { QML_ELEMENT QML_UNCREATABLE("") - Q_PROPERTY(int activeWallpaperCounter READ activeWallpaperCounter WRITE setActiveWallpaperCounter NOTIFY activeWallpaperCounterChanged) - Q_PROPERTY(int activeWidgetsCounter READ activeWidgetsCounter WRITE setActiveWidgetsCounter NOTIFY activeWidgetsCounterChanged) + Q_PROPERTY(int activeWallpaperCounter READ activeWallpaperCounter WRITE setActiveWallpaperCounter NOTIFY activeWallpaperCounterChanged FINAL) + Q_PROPERTY(int activeWidgetsCounter READ activeWidgetsCounter WRITE setActiveWidgetsCounter NOTIFY activeWidgetsCounterChanged FINAL) + Q_PROPERTY(int selectedTimelineIndex READ selectedTimelineIndex WRITE setSelectedTimelineIndex NOTIFY selectedTimelineIndexChanged FINAL) public: explicit ScreenPlayManager(QObject* parent = nullptr); @@ -37,7 +38,6 @@ public: Q_INVOKABLE QCoro::QmlTask removeAllRunningWallpapers(bool saveToProfile = false); Q_INVOKABLE bool removeAllRunningWidgets(bool saveToProfile = false); Q_INVOKABLE QCoro::QmlTask removeWallpaperAt(const int timelineIndex, const QString timelineIdentifier, const int monitorIndex); - Q_INVOKABLE ScreenPlayWallpaper* getWallpaperByAppID(const QString& appID); Q_INVOKABLE bool moveTimelineAt( @@ -49,9 +49,10 @@ public: const int index, const float reltiaveLinePosition, QString identifier); - Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections(); Q_INVOKABLE bool removeTimelineAt(const int index); Q_INVOKABLE QJsonArray timelineSections(); + Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections(); + Q_INVOKABLE QCoro::QmlTask setWallpaperAtTimelineIndex( const ScreenPlay::ContentTypes::InstalledType type, const QString& absolutePath, @@ -71,28 +72,31 @@ public: const QJsonObject& properties, const bool saveToProfilesConfigFile); - Q_INVOKABLE void setSelectedTimelineIndex(const int selectedTimelineIndex); - Q_INVOKABLE bool requestProjectSettingsAtMonitorIndex(const int index); Q_INVOKABLE bool setWallpaperValueAtMonitorIndex(const int index, const QString& key, const QString& value); Q_INVOKABLE bool setWallpaperFillModeAtMonitorIndex(const int index, const int fillmode); Q_INVOKABLE bool setAllWallpaperValue(const QString& key, const QString& value); Q_INVOKABLE bool setWallpaperValue(const QString& appID, const QString& key, const QString& value); Q_INVOKABLE int activeTimelineIndex(); + Q_INVOKABLE void requestSaveProfiles(); int activeWallpaperCounter() const { return m_activeWallpaperCounter; } int activeWidgetsCounter() const { return m_activeWidgetsCounter; } + int selectedTimelineIndex() const { return m_selectedTimelineIndex; } + +public slots: + void setSelectedTimelineIndex(int selectedTimelineIndex); signals: void activeWallpaperCounterChanged(int activeWallpaperCounter); void activeWidgetsCounterChanged(int activeWidgetsCounter); void monitorConfigurationChanged(); - void projectSettingsListModelResult(ScreenPlay::ProjectSettingsListModel* li = nullptr); - void requestSaveProfiles(); + void requestRaise(); void profilesSaved(); void printQmlTimeline(); void displayErrorPopup(const QString& msg); + void selectedTimelineIndexChanged(int selectedTimelineIndex); private slots: bool saveProfiles(); @@ -102,12 +106,11 @@ private slots: private: bool loadProfiles(); - bool checkIsAnotherScreenPlayInstanceRunning(); - bool removeWallpaper(const QString& appID); bool removeWidget(const QString& appID); - bool loadWidgetConfig(const QJsonObject& widget); + +private: std::shared_ptr m_globalVariables; std::shared_ptr m_monitorListModel; std::shared_ptr m_settings; @@ -116,14 +119,12 @@ private: std::vector> m_unconnectedClients; ScreenPlayTimelineManager m_screenPlayTimelineManager; - int m_activeWallpaperCounter { 0 }; - int m_activeWidgetsCounter { 0 }; - QTimer m_saveLimiter; - Util m_util; + int m_activeWallpaperCounter { 0 }; + int m_activeWidgetsCounter { 0 }; + int m_selectedTimelineIndex { 0 }; const quint16 m_webSocketPort = 16395; }; - } diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h b/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h index e02951d3..d8632af0 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h @@ -13,6 +13,7 @@ namespace ScreenPlay { class ScreenPlayTimelineManager : public QObject { Q_OBJECT + Q_PROPERTY(int selectedTimelineIndex READ selectedTimelineIndex WRITE setSelectedTimelineIndex NOTIFY selectedTimelineIndexChanged FINAL) public: explicit ScreenPlayTimelineManager(QObject* parent = nullptr); @@ -45,6 +46,11 @@ public: void setGlobalVariables(const std::shared_ptr& globalVariables); void setSettings(const std::shared_ptr& settings); void setMonitorListModel(const std::shared_ptr& monitorListModel); + + int selectedTimelineIndex() const; + void setSelectedTimelineIndex(int selectedTimelineIndex); + +public slots: void updateMonitorListModelData(const int selectedTimelineIndex); private slots: @@ -53,9 +59,12 @@ private slots: signals: void requestSaveProfiles(); void activeWallpaperCountChanged(const int count); + void selectedTimelineIndexChanged(int selectedTimelineIndex); private: - std::optional> wallpaperSection(const int timelineIndex, const QString timelineIdentifier); + std::optional> wallpaperSection( + const int timelineIndex, + const QString timelineIdentifier); private: QVector> m_wallpaperTimelineSectionsList; @@ -66,5 +75,6 @@ private: QTimer m_contentTimer; std::shared_ptr m_globalVariables; std::shared_ptr m_settings; + int m_selectedTimelineIndex { 0 }; }; } diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h b/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h index d6dfbcd5..b940b240 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaywallpaper.h @@ -14,18 +14,18 @@ #include "ScreenPlay/projectsettingslistmodel.h" #include "ScreenPlay/sdkconnection.h" #include "ScreenPlay/settings.h" -#include "ScreenPlayUtil/processmanager.h" #include "ScreenPlay/wallpaperdata.h" #include "ScreenPlay/wallpapertimelinesection.h" - +#include "ScreenPlayUtil/globalenums.h" +#include "ScreenPlayUtil/processmanager.h" namespace ScreenPlay { - class ScreenPlayWallpaper : public QObject { Q_OBJECT QML_ELEMENT QML_UNCREATABLE("") + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") Q_PROPERTY(bool isConnected READ isConnected WRITE setIsConnected NOTIFY isConnectedChanged) Q_PROPERTY(QVector monitors READ monitors WRITE setMonitors NOTIFY monitorsChanged) @@ -36,9 +36,10 @@ class ScreenPlayWallpaper : public QObject { Q_PROPERTY(QString absolutePath READ absolutePath WRITE setAbsolutePath NOTIFY absolutePathChanged) Q_PROPERTY(QString previewImage READ previewImage WRITE setPreviewImage NOTIFY previewImageChanged) Q_PROPERTY(QString appID READ appID WRITE setAppID NOTIFY appIDChanged) - Q_PROPERTY(Video::FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) - Q_PROPERTY(ContentTypes::InstalledType type READ type WRITE setType NOTIFY typeChanged) Q_PROPERTY(qint64 processID READ processID WRITE setProcessID NOTIFY processIDChanged FINAL) + Q_PROPERTY(ScreenPlay::Video::FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) + Q_PROPERTY(ScreenPlay::ContentTypes::InstalledType type READ type WRITE setType NOTIFY typeChanged) + Q_PROPERTY(ScreenPlay::ScreenPlayEnums::AppState state READ state WRITE setState NOTIFY stateChanged FINAL) public: explicit ScreenPlayWallpaper( @@ -74,6 +75,9 @@ public: const WallpaperData& wallpaperData(); + ScreenPlay::ScreenPlayEnums::AppState state() const; + void setState(ScreenPlay::ScreenPlayEnums::AppState state); + signals: void monitorsChanged(QVector monitors); void previewImageChanged(QString previewImage); @@ -93,6 +97,8 @@ signals: void requestClose(const QString& appID); void error(const QString& msg); + void stateChanged(ScreenPlay::ScreenPlayEnums::AppState state); + public slots: void close(); void processExit(int exitCode, QProcess::ExitStatus exitStatus); @@ -126,7 +132,7 @@ public slots: emit appIDChanged(m_appID); } - void setType(ContentTypes::InstalledType type) + void setType(ScreenPlay::ContentTypes::InstalledType type) { if (m_wallpaperData.type == type) return; @@ -144,7 +150,7 @@ public slots: emit fileChanged(m_wallpaperData.file); } - void setFillMode(Video::FillMode fillMode) + void setFillMode(ScreenPlay::Video::FillMode fillMode) { if (m_wallpaperData.fillMode == fillMode) return; @@ -218,6 +224,7 @@ private: QJsonObject m_projectJson; QProcess m_process; QString m_appID; + ScreenPlay::ScreenPlayEnums::AppState m_state = ScreenPlay::ScreenPlayEnums::AppState::Inactive; WallpaperData m_wallpaperData; diff --git a/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h b/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h index 48e8c2a1..f0f599f9 100644 --- a/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h +++ b/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h @@ -23,6 +23,7 @@ class WallpaperTimelineSection : public QObject { QML_UNCREATABLE("") Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") public: + // TODO: replace with wallpaper state? enum class State { Inactive, Starting, @@ -46,6 +47,11 @@ public: std::shared_ptr globalVariables; std::shared_ptr settings; +signals: + void requestSaveProfiles(); + void requestUpdateMonitorListModel(); + void activeWallpaperCountChanged(const int count); + public: // Check if currentTime falls within the timeline section bool containsTime(const QTime& time) const; @@ -58,9 +64,5 @@ private slots: private: std::optional> wallpaperByMonitorIndex(const int index); - -signals: - void requestSaveProfiles(); - void activeWallpaperCountChanged(const int count); }; } diff --git a/ScreenPlay/qml/Components/LineIndicator.qml b/ScreenPlay/qml/Components/LineIndicator.qml index 083d7a77..c7557027 100644 --- a/ScreenPlay/qml/Components/LineIndicator.qml +++ b/ScreenPlay/qml/Components/LineIndicator.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import ScreenPlayUtil as Util Rectangle { id: root @@ -26,9 +27,8 @@ Rectangle { } } - Rectangle { + Util.RainbowGradient { opacity: root.isActive ? 1 : 0 - color: "gold" height: root.height anchors { right: parent.right @@ -42,27 +42,21 @@ Rectangle { } } - Rectangle { - visible: root.selected - color: "gold" - height: 3 - anchors { - right: parent.right - left: parent.left - top: parent.bottom - } - } - Rectangle { id: indicatorLineVertical width: 5 - height: 30 + height: root.selected ? 40 : 30 color: root.selected ? "gold" : parent.color anchors { horizontalCenter: parent.horizontalCenter top: parent.bottom topMargin: 0 } + Behavior on height { + NumberAnimation { + duration: 200 + } + } Behavior on color { @@ -94,6 +88,7 @@ Rectangle { radius: 5 clip: true color: Qt.darker(monitorBackground.color) + Image { id: imgWallpaper opacity: imgWallpaper.status === Image.Ready ? 1 : 0 @@ -115,6 +110,7 @@ Rectangle { MouseArea { anchors.fill: parent hoverEnabled: true + cursorShape: Qt.PointingHandCursor onClicked: { root.lineSelected(root.index); } diff --git a/ScreenPlay/qml/Components/Timeline.qml b/ScreenPlay/qml/Components/Timeline.qml index 2e44d3b5..da240c7b 100644 --- a/ScreenPlay/qml/Components/Timeline.qml +++ b/ScreenPlay/qml/Components/Timeline.qml @@ -5,6 +5,7 @@ import QtQuick.Controls import QtQuick.Layouts import ScreenPlayApp import ScreenPlayUtil +import "../../../ScreenPlayUtil/qml/InstantPopup.js" as InstantPopup Control { id: root @@ -121,7 +122,7 @@ Control { if (timelineSection.wallpaperData.length === 0) continue; let firstWallpaper = timelineSection.wallpaperData[0]; - lineIndicator.wallpaperPreviewImage = Qt.resolvedUrl("file:///" + firstWallpaper.absolutePath + "/" + firstWallpaper.previewImage); + lineIndicator.wallpaperPreviewImage = "file:///" + firstWallpaper.absolutePath + "/" + firstWallpaper.previewImage; } } @@ -509,7 +510,7 @@ Control { btnReset.resetting = true; App.screenPlayManager.removeAllRunningWallpapers().then(result => { if (!result.success) { - console.error("removeAllTimlineSections failed"); + InstantPopup.openErrorPopup(timeline, result.message); btnReset.resetting = false; return; } @@ -523,7 +524,9 @@ Control { const position = 1.0; const identifier = App.util.generateRandomString(4); const sectionObject = timeline.addSection(identifier, position); - App.screenPlayManager.addTimelineAt(sectionObject.index, sectionObject.relativeLinePosition, sectionObject.identifier); + const success = App.screenPlayManager.addTimelineAt(sectionObject.index, sectionObject.relativeLinePosition, sectionObject.identifier); + if (!success) + InstantPopup.openErrorPopup(timeline, qsTr("Unable to add Timeline")); btnReset.resetting = false; }); }); diff --git a/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml b/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml index 66a2c4ce..0cc006a0 100644 --- a/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml +++ b/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml @@ -6,7 +6,7 @@ import QtQuick.Layouts import QtQuick.Controls.Material.impl import ScreenPlayApp import ScreenPlayUtil as Util - +import "../../../ScreenPlayUtil/qml/InstantPopup.js" as InstantPopup import "../Components" Util.Popup { @@ -149,7 +149,12 @@ Util.Popup { monitorSelection.enabled = false; App.screenPlayManager.removeWallpaperAt(index, selectedTimeline.identifier, selectedTimeline.index).then(result => { monitorSelection.enabled = true; - if (!result.success) {} else {} + if (result.success) { + App.screenPlayManager.requestSaveProfiles(); + } else { + if (!success) + InstantPopup.openErrorPopup(this, result); + } }); } @@ -169,7 +174,7 @@ Util.Popup { } } - ColumnLayout { + RowLayout { spacing: 5 anchors { @@ -179,23 +184,10 @@ Util.Popup { margins: 20 } - Button { - id: btnRemoveSelectedWallpaper - Material.background: Material.accent - highlighted: true - text: qsTr("Remove selected") - font.family: App.settings.font - enabled: monitorSelection.activeMonitors.length == 1 && App.screenPlayManager.activeWallpaperCounter > 0 - onClicked: { - if (!App.screenPlayManager.removeWallpaperAt(monitorSelection.activeMonitors[0])) - print("Unable to close singel wallpaper"); - } - } - Button { id: btnRemoveAllWallpaper - text: qsTr("Remove all ") + App.screenPlayManager.activeWallpaperCounter + " " + qsTr("Wallpapers") + text: qsTr("Remove all running") + App.screenPlayManager.activeWallpaperCounter + " " + qsTr("Wallpapers") Material.background: Material.accent highlighted: true font.family: App.settings.font @@ -209,7 +201,7 @@ Util.Popup { Button { id: btnRemoveAllWidgets - text: qsTr("Remove all ") + App.screenPlayManager.activeWidgetsCounter + " " + qsTr("Widgets") + text: qsTr("Remove all running") + App.screenPlayManager.activeWidgetsCounter + " " + qsTr("Widgets") Material.background: Material.accent Material.foreground: Material.primaryTextColor highlighted: true diff --git a/ScreenPlay/qml/ContentSettings/MonitorSelection.qml b/ScreenPlay/qml/ContentSettings/MonitorSelection.qml index 3d5beec1..1ff489ec 100644 --- a/ScreenPlay/qml/ContentSettings/MonitorSelection.qml +++ b/ScreenPlay/qml/ContentSettings/MonitorSelection.qml @@ -173,6 +173,7 @@ Rectangle { index: m_index previewImage: m_previewImage installedType: m_installedType + appState: m_appState monitorWithoutContentSelectable: root.monitorWithoutContentSelectable onMonitorSelected: function (index) { root.selectMonitorAt(index); diff --git a/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml b/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml index e83b6ed3..bb252ebe 100644 --- a/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml +++ b/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml @@ -9,6 +9,7 @@ Item { property string previewImage property string appID property var installedType: ContentTypes.InstalledType.QMLWallpaper + property var appState: ScreenPlayEnums.AppState.Inactive property bool monitorWithoutContentSelectable: true property bool hasContent: appID !== "" property int fontSize: 10 @@ -85,6 +86,25 @@ Item { asynchronous: true fillMode: Image.PreserveAspectCrop } + Text { + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: { + switch (root.appState) { + case ScreenPlayEnums.AppState.Inactive: + return qsTr("Inactive"); + case ScreenPlayEnums.AppState.Starting: + return qsTr("Starting"); + case ScreenPlayEnums.AppState.Closing: + return qsTr("Closing"); + case ScreenPlayEnums.AppState.Active: + return qsTr("Active"); + default: + console.error("Invalid state"); + } + } + } MouseArea { anchors.fill: parent diff --git a/ScreenPlay/qml/Installed/InstalledDrawer.qml b/ScreenPlay/qml/Installed/InstalledDrawer.qml index dafc57b3..c0c57823 100644 --- a/ScreenPlay/qml/Installed/InstalledDrawer.qml +++ b/ScreenPlay/qml/Installed/InstalledDrawer.qml @@ -7,6 +7,7 @@ import QtQuick.Controls.Material import QtQuick.Controls.Material.impl import ScreenPlayApp import ScreenPlayUtil +import "../../../ScreenPlayUtil/qml/InstantPopup.js" as InstantPopup import "../ContentSettings" import "../Components" @@ -135,7 +136,7 @@ Drawer { onRequestRemoveWallpaper: monitorIndex => { const selectedTimeline = timeline.getSelectedTimeline(); if (selectedTimeline === undefined) { - console.error("No active timeline to remove wallpaper ", index); + InstantPopup.openErrorPopup(timeline, qsTr("No active timeline to remove wallpaper")); return; } monitorSelection.enabled = false; @@ -144,6 +145,9 @@ Drawer { if (result.success) { // Reset to update the wallpaper preview image timeline.reset(); + App.screenPlayManager.requestSaveProfiles(); + } else { + InstantPopup.openErrorPopup(timeline, result.message); } }); } @@ -235,16 +239,6 @@ Drawer { } } - Dialog { - id: dialog - standardButtons: Dialog.Ok - title: qsTr("Export Godot project") - property alias message: messageText.text - Text { - id: messageText - } - } - Button { id: btnLaunchContent Layout.fillWidth: true @@ -256,12 +250,14 @@ Drawer { icon.color: "white" font.pointSize: 12 onClicked: { + btnLaunchContent.enabled = false; const item = App.installedListModel.get(root.contentFolderName); const absoluteStoragePath = item.m_absoluteStoragePath; const previewImage = item.m_preview; if (App.util.isWallpaper(root.type)) { if (type === ContentTypes.InstalledType.GodotWallpaper) { if (App.globalVariables.isBasicVersion()) { + InstantPopup.openErrorPopup(timeline, qsTr("You are not allowed to do that!")); installedDrawerWrapper.state = "inactive"; return; } @@ -270,14 +266,19 @@ Drawer { if (type === ContentTypes.InstalledType.GodotWallpaper) { App.util.exportGodotProject(absoluteStoragePath, App.globalVariables.godotEditorExecutablePath).then(result => { if (!result.success) { - dialog.title = ("Error exporting Godot"); - dialog.message = result.message; - dialog.open(); + btnLaunchContent.enabled = true; + InstantPopup.openErrorPopup(timeline, result.message); } else { const file = item.m_file; let volume = 1; const selectedTimeline = timeline.getSelectedTimeline(); - let success = App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, selectedTimeline.index, selectedTimeline.identifier, true); + App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, selectedTimeline.index, selectedTimeline.identifier, true).then(result => { + btnLaunchContent.enabled = true; + if (!result.success) { + InstantPopup.openErrorPopup(timeline, result.message); + return; + } + }); } }); root.close(); @@ -285,15 +286,17 @@ Drawer { } const selectedTimeline = timeline.getSelectedTimeline(); const file = item.m_file; - let success = App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, selectedTimeline.index, selectedTimeline.identifier, true).then(result => { + App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, selectedTimeline.index, selectedTimeline.identifier, true).then(result => { + btnLaunchContent.enabled = true; if (!result.success) { - console.error("setWallpaperAtTimelineIndex failed"); + InstantPopup.openErrorPopup(timeline, result.message); return; } }); } + btnLaunchContent.enabled = true; root.close(); - monitorSelection.reset(); + // monitorSelection.reset() } } } diff --git a/ScreenPlay/qml/Installed/ScreenPlayItem.qml b/ScreenPlay/qml/Installed/InstalledItem.qml similarity index 99% rename from ScreenPlay/qml/Installed/ScreenPlayItem.qml rename to ScreenPlay/qml/Installed/InstalledItem.qml index 3807c172..caccf724 100644 --- a/ScreenPlay/qml/Installed/ScreenPlayItem.qml +++ b/ScreenPlay/qml/Installed/InstalledItem.qml @@ -155,7 +155,7 @@ Item { horizontalAlignment: Text.AlignHCenter } - ScreenPlayItemImage { + InstalledItemImage { id: screenPlayItemImage opacity: root.hasLicense ? 1 : 0.3 anchors.fill: parent diff --git a/ScreenPlay/qml/Installed/ScreenPlayItemImage.qml b/ScreenPlay/qml/Installed/InstalledItemImage.qml similarity index 100% rename from ScreenPlay/qml/Installed/ScreenPlayItemImage.qml rename to ScreenPlay/qml/Installed/InstalledItemImage.qml diff --git a/ScreenPlay/qml/Installed/InstalledView.qml b/ScreenPlay/qml/Installed/InstalledView.qml index 73569be4..1d2af491 100644 --- a/ScreenPlay/qml/Installed/InstalledView.qml +++ b/ScreenPlay/qml/Installed/InstalledView.qml @@ -215,7 +215,7 @@ Item { } } - delegate: ScreenPlayItem { + delegate: InstalledItem { id: delegate objectName: "installedItem" + index focus: true diff --git a/ScreenPlay/qml/Navigation/Navigation.qml b/ScreenPlay/qml/Navigation/Navigation.qml index 91561518..83882bd0 100644 --- a/ScreenPlay/qml/Navigation/Navigation.qml +++ b/ScreenPlay/qml/Navigation/Navigation.qml @@ -314,6 +314,7 @@ Rectangle { rightMargin: 10 bottom: parent.bottom } + Material.accent: contentActive ? "gold" : Material.secondaryTextColor property bool contentActive: App.screenPlayManager.activeWallpaperCounter > 0 diff --git a/ScreenPlay/src/monitorlistmodel.cpp b/ScreenPlay/src/monitorlistmodel.cpp index f7b114f0..01e867e6 100644 --- a/ScreenPlay/src/monitorlistmodel.cpp +++ b/ScreenPlay/src/monitorlistmodel.cpp @@ -11,6 +11,9 @@ #include #include +#include "ScreenPlay/CMakeVariables.h" +#include + namespace ScreenPlay { /*! @@ -34,6 +37,8 @@ namespace ScreenPlay { MonitorListModel::MonitorListModel(QObject* parent) : QAbstractListModel(parent) { + static_assert(SCREENPLAY_DEPLOY_VERSION == 0 || !std::is_same_v, + "Mock monitors should not be available in deploy version"); auto* guiAppInst = dynamic_cast(QGuiApplication::instance()); connect(guiAppInst, &QGuiApplication::screenAdded, this, &MonitorListModel::screenAdded); @@ -70,6 +75,7 @@ QHash MonitorListModel::roleNames() const { static const QHash roles { { static_cast(MonitorRole::AppID), "m_appID" }, + { static_cast(MonitorRole::AppState), "m_appState" }, { static_cast(MonitorRole::Index), "m_index" }, { static_cast(MonitorRole::Geometry), "m_geometry" }, { static_cast(MonitorRole::PreviewImage), "m_previewImage" }, @@ -110,6 +116,8 @@ QVariant MonitorListModel::data(const QModelIndex& index, int role) const switch (roleEnum) { case MonitorRole::AppID: return m_monitorList.at(row).m_appID; + case MonitorRole::AppState: + return QVariant::fromValue(m_monitorList.at(row).m_appState); case MonitorRole::Index: return m_monitorList.at(row).m_index; case MonitorRole::Geometry: @@ -267,6 +275,10 @@ bool MonitorListModel::setData(const QModelIndex& index, const QVariant& value, m_monitorList[index.column()].m_appID = value.toString(); emit dataChanged(index, index, { role }); } + if (role == static_cast(MonitorRole::AppState)) { + m_monitorList[index.column()].m_appState = static_cast(value.toInt()); + emit dataChanged(index, index, { role }); + } if (role == static_cast(MonitorRole::InstalledType)) { m_monitorList[index.column()].m_installedType = static_cast(value.toInt()); emit dataChanged(index, index, { role }); diff --git a/ScreenPlay/src/screenplaymanager.cpp b/ScreenPlay/src/screenplaymanager.cpp index 6311df84..371c77d4 100644 --- a/ScreenPlay/src/screenplaymanager.cpp +++ b/ScreenPlay/src/screenplaymanager.cpp @@ -2,6 +2,7 @@ #include "ScreenPlay/screenplaymanager.h" #include "ScreenPlayUtil/util.h" +#include "core/qcorothread.h" #include namespace ScreenPlay { @@ -27,6 +28,10 @@ ScreenPlayManager::ScreenPlayManager( QObject::connect(m_server.get(), &QLocalServer::newConnection, this, &ScreenPlayManager::newConnection); QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::requestSaveProfiles, this, &ScreenPlayManager::requestSaveProfiles); QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::activeWallpaperCountChanged, this, &ScreenPlayManager::setActiveWallpaperCounter); + + QObject::connect(this, &ScreenPlayManager::selectedTimelineIndexChanged, &m_screenPlayTimelineManager, &ScreenPlayTimelineManager::setSelectedTimelineIndex); + QObject::connect(this, &ScreenPlayManager::selectedTimelineIndexChanged, &m_screenPlayTimelineManager, &ScreenPlayTimelineManager::updateMonitorListModelData); + m_server->setSocketOptions(QLocalServer::WorldAccessOption); if (!m_server->listen("ScreenPlay")) { qCritical("Could not open Local Socket with the name ScreenPlay!"); @@ -37,9 +42,6 @@ ScreenPlayManager::ScreenPlayManager( // we limit ourself to m_saveLimiters interval below: m_saveLimiter.setInterval(1000); QObject::connect(&m_saveLimiter, &QTimer::timeout, this, &ScreenPlayManager::saveProfiles); - QObject::connect(this, &ScreenPlayManager::requestSaveProfiles, this, [this]() { - m_saveLimiter.start(); - }); } /*! @@ -90,23 +92,29 @@ QCoro::QmlTask ScreenPlayManager::setWallpaperAtTimelineIndex( return QCoro::QmlTask([this, wallpaperData, timelineIndex, identifier]() -> QCoro::Task { if (timelineIndex < 0 || identifier.isEmpty()) { - - co_return Result { false }; + const QString msg = QString("Invalid timeline index %1 identifier %2").arg(QString::number(timelineIndex), identifier); + Result result; + result.setSuccess(false); + result.setMessage(msg); + co_return result; } const bool success = co_await m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier); if (!success) { - qCritical() << "Invalid timeline index or identifier: " << timelineIndex << identifier; + const QString msg = QString("Unable to setWallpaperAtTimelineIndex"); m_screenPlayTimelineManager.printTimelines(); emit printQmlTimeline(); - co_return Result { success }; + Result result; + result.setSuccess(false); + result.setMessage(msg); + co_return result; } // We do not start the wallpaper here, but let // ScreenPlayTimelineManager::checkActiveWallpaperTimeline decide // if the wallpaper - emit requestSaveProfiles(); + // QMetaObject::invokeMethod(this, &ScreenPlayManager::requestSaveProfiles, Qt::QueuedConnection); co_return Result { success }; }()); } @@ -125,7 +133,7 @@ bool ScreenPlayManager::startWidget( auto saveToProfile = qScopeGuard([=, this] { // Do not save on app start if (saveToProfilesConfigFile) { - emit requestSaveProfiles(); + requestSaveProfiles(); } }); @@ -157,11 +165,6 @@ bool ScreenPlayManager::startWidget( return true; } -void ScreenPlayManager::setSelectedTimelineIndex(const int selectedTimelineIndex) -{ - m_screenPlayTimelineManager.updateMonitorListModelData(selectedTimelineIndex); -} - /*! \brief Removes all wallpaper entries in the profiles.json. */ @@ -172,7 +175,8 @@ QCoro::QmlTask ScreenPlayManager::removeAllRunningWallpapers(bool saveToProfile) const bool success = co_await m_screenPlayTimelineManager.removeAllWallpaperFromActiveTimlineSections(); qDebug() << "Task: removeAllWallpaperFromActiveTimlineSections" << success; if (saveToProfile) - emit requestSaveProfiles(); + // QMetaObject::invokeMethod(this, &ScreenPlayManager::requestSaveProfiles, Qt::QueuedConnection); + m_screenPlayTimelineManager.updateMonitorListModelData(selectedTimelineIndex()); co_return Result { success }; }()); } @@ -199,7 +203,7 @@ bool ScreenPlayManager::removeAllRunningWidgets(bool saveToProfile) } if (saveToProfile) - emit requestSaveProfiles(); + requestSaveProfiles(); return true; } @@ -211,12 +215,16 @@ bool ScreenPlayManager::removeAllRunningWidgets(bool saveToProfile) */ QCoro::QmlTask ScreenPlayManager::removeWallpaperAt(int timelineIndex, QString timelineIdentifier, int monitorIndex) { - + qInfo() << "this: " << this; return QCoro::QmlTask([this, timelineIndex, timelineIdentifier, monitorIndex]() -> QCoro::Task { - // call with coro const bool success = co_await m_screenPlayTimelineManager.removeWallpaperAt(timelineIndex, timelineIdentifier, monitorIndex); qDebug() << "Task: removeAllWallpaperFromActiveTimlineSections" << success; - emit requestSaveProfiles(); + + // Use QMetaObject::invokeMethod to call requestSaveProfiles on the main thread + QMetaObject::invokeMethod(nullptr, "requestSaveProfiles", Qt::QueuedConnection); + + m_screenPlayTimelineManager.updateMonitorListModelData(selectedTimelineIndex()); + co_return Result { success }; }()); } @@ -331,6 +339,7 @@ QCoro::QmlTask ScreenPlayManager::removeAllTimlineSections() qDebug() << "Task: removeAllTimlineSections" << success; // emit requestSaveProfiles(); // removeAllRunningWallpapers(); + // QMetaObject::invokeMethod(this, &ScreenPlayManager::requestSaveProfiles, Qt::QueuedConnection); co_return Result { success }; }()); } @@ -338,7 +347,7 @@ QCoro::QmlTask ScreenPlayManager::removeAllTimlineSections() bool ScreenPlayManager::removeTimelineAt(const int index) { const bool success = m_screenPlayTimelineManager.removeTimelineAt(index); - emit requestSaveProfiles(); + requestSaveProfiles(); return success; } /*! @@ -426,52 +435,6 @@ void ScreenPlayManager::setActiveWidgetsCounter(int activeWidgetsCounter) emit activeWidgetsCounterChanged(m_activeWidgetsCounter); } -/*! - \brief Removes a wallpaper from the given appID. Returns true on success. -*/ -bool ScreenPlayManager::removeWallpaper(const QString& appID) -{ - - auto wallpaperSectionOpt = m_screenPlayTimelineManager.activeWallpaperSectionByAppID(appID); - if (!wallpaperSectionOpt.has_value()) { - qCritical() << "No wallpaper found."; - return false; - } - if (!wallpaperSectionOpt.value()) { - qCritical() << "No wallpaperSectionOpt invalid."; - return false; - } - auto& wallpaperSection = wallpaperSectionOpt.value(); - - wallpaperSection->activeWallpaperList.erase( - std::remove_if( - wallpaperSection->activeWallpaperList.begin(), - wallpaperSection->activeWallpaperList.end(), - [this, appID](std::shared_ptr& wallpaper) { - if (wallpaper->appID() != appID) { - return false; - } - - qInfo() << "Remove wallpaper " << wallpaper->file() << "at monitor " << wallpaper->monitors(); - - // The MonitorListModel contains a shared_ptr of this object that needs to be removed - // for shared_ptr to release the object. - // m_monitorListModel->setWallpaperMonitor({}, wallpaper->monitors()); - - wallpaper->close(); - - return true; - })); - - if (activeWallpaperCounter() != wallpaperSection->activeWallpaperList.size()) { - qWarning() << "activeWallpaperCounter value: " << activeWallpaperCounter() - << "does not match m_screenPlayWallpapers length:" << wallpaperSection->activeWallpaperList.size(); - return false; - } - - return true; -} - /*! \brief Removes a Widget from the given appID. Returns true on success. */ @@ -536,6 +499,11 @@ int ScreenPlayManager::activeTimelineIndex() return activeTimelineSection->index; } +void ScreenPlayManager::requestSaveProfiles() +{ + m_saveLimiter.start(); +} + /*! \brief Saves a given wallpaper \a newProfileObject to a \a profileName. We ignore the profileName argument because we currently only support one profile. Returns \c true if successfuly saved to profiles.json, otherwise \c false. @@ -658,6 +626,14 @@ bool ScreenPlayManager::loadWidgetConfig(const QJsonObject& widgetObj) } return true; } + +void ScreenPlayManager::setSelectedTimelineIndex(int selectedTimelineIndex) +{ + // if (m_selectedTimelineIndex == selectedTimelineIndex) + // return; + m_selectedTimelineIndex = selectedTimelineIndex; + emit selectedTimelineIndexChanged(m_selectedTimelineIndex); +} } #include "moc_screenplaymanager.cpp" diff --git a/ScreenPlay/src/screenplaytimelinemanager.cpp b/ScreenPlay/src/screenplaytimelinemanager.cpp index d0100ec2..d3149b63 100644 --- a/ScreenPlay/src/screenplaytimelinemanager.cpp +++ b/ScreenPlay/src/screenplaytimelinemanager.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace ScreenPlay { @@ -94,6 +95,9 @@ bool ScreenPlayTimelineManager::addTimelineFromSettings(const QJsonObject& timel // TODO check license auto newTimelineSection = std::make_shared(); + QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestUpdateMonitorListModel, this, [this]() { + updateMonitorListModelData(selectedTimelineIndex()); + }); QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::activeWallpaperCountChanged, this, &ScreenPlayTimelineManager::activeWallpaperCountChanged); newTimelineSection->startTime = startTime; @@ -181,6 +185,10 @@ void ScreenPlayTimelineManager::checkActiveWallpaperTimeline() if (currentTimeline != activeTimeline) { activeTimeline->deactivateTimeline(); currentTimeline->activateTimeline(); + } else { + if (activeTimeline->state == WallpaperTimelineSection::State::Inactive) { + currentTimeline->activateTimeline(); + } } } @@ -221,41 +229,57 @@ void ScreenPlayTimelineManager::setMonitorListModel(const std::shared_ptr& timeline = selectedTimeline.front(); - m_monitorListModel->reset(); + // Create a set of monitor indices that have active wallpapers + std::unordered_set activeMonitors; + for (const auto& activeWallpaper : timeline->activeWallpaperList) { + for (const auto& monitorIndex : activeWallpaper->monitors()) { + activeMonitors.insert(monitorIndex); + } + } for (int i = 0; i < m_monitorListModel->rowCount(); ++i) { - // One wallpaper can span across multiple monitors bool ok; const int monitorIndex = m_monitorListModel->data(m_monitorListModel->index(i), (int)MonitorListModel::MonitorRole::Index).toInt(&ok); if (!ok) { qCritical() << "Invalid monitor index at: " << i; return; } - for (const auto& wallpaper : timeline->wallpaperDataList) { - if (wallpaper.monitors.contains(monitorIndex)) { - const auto previewImg = wallpaper.absolutePath + "/" + wallpaper.previewImage; - const auto mondelIndex = m_monitorListModel->index(0, monitorIndex); - m_monitorListModel->setData(mondelIndex, previewImg, (int)MonitorListModel::MonitorRole::PreviewImage); - // TODO - m_monitorListModel->setData(mondelIndex, "dummy", (int)MonitorListModel::MonitorRole::AppID); - m_monitorListModel->setData(mondelIndex, (int)wallpaper.type, (int)MonitorListModel::MonitorRole::InstalledType); - break; + + const auto modelIndex = m_monitorListModel->index(0, monitorIndex); + + if (activeMonitors.find(monitorIndex) != activeMonitors.end()) { + // Monitor has an active wallpaper + for (const auto& activeWallpaper : timeline->activeWallpaperList) { + if (activeWallpaper->monitors().contains(monitorIndex)) { + const auto previewImg = activeWallpaper->absolutePath() + "/" + activeWallpaper->previewImage(); + m_monitorListModel->setData(modelIndex, activeWallpaper->appID(), (int)MonitorListModel::MonitorRole::AppID); + m_monitorListModel->setData(modelIndex, (int)activeWallpaper->state(), (int)MonitorListModel::MonitorRole::AppState); + m_monitorListModel->setData(modelIndex, previewImg, (int)MonitorListModel::MonitorRole::PreviewImage); + m_monitorListModel->setData(modelIndex, (int)activeWallpaper->type(), (int)MonitorListModel::MonitorRole::InstalledType); + break; + } } + } else { + // Reset monitor data to empty values + m_monitorListModel->setData(modelIndex, "", (int)MonitorListModel::MonitorRole::AppID); + m_monitorListModel->setData(modelIndex, 0, (int)MonitorListModel::MonitorRole::AppState); + m_monitorListModel->setData(modelIndex, "", (int)MonitorListModel::MonitorRole::PreviewImage); + m_monitorListModel->setData(modelIndex, 0, (int)MonitorListModel::MonitorRole::InstalledType); } } } - void ScreenPlayTimelineManager::setGlobalVariables(const std::shared_ptr& globalVariables) { m_globalVariables = globalVariables; @@ -368,7 +392,7 @@ bool ScreenPlayTimelineManager::addTimelineAt(const int index, const float relat updateIndices(); printTimelines(); - emit requestSaveProfiles(); + // emit requestSaveProfiles(); return true; } @@ -421,8 +445,10 @@ QCoro::Task ScreenPlayTimelineManager::removeWallpaperAt(const int timelin }); std::optional> sectionOpt = wallpaperSection(timelineIndex, timelineIdentifier); - if (!sectionOpt) + if (!sectionOpt) { + qCritical() << "No timeline section for timelineIndex" << timelineIndex << "timelineIdentifier" << timelineIdentifier << "monitorIndex" << monitorIndex; co_return false; + } const bool success = co_await sectionOpt.value()->removeWallpaper(monitorIndex); @@ -530,13 +556,19 @@ QCoro::Task ScreenPlayTimelineManager::setWallpaperAtTimelineIndex( *it = wallpaperData; // TODO: Do not replace but co_await timelineSection->deactivateTimeline(); - timelineSection->activateTimeline(); } else { // IMPORTANT: Append the new wallpaper data, // but do not start it! The selected timelineSection // is not always be the currently running. // Besides for this we have m_contentTimer checkActiveWallpaperTimeline() timelineSection->wallpaperDataList.push_back(wallpaperData); + + // If the updated timeline section is already active, we + // need to trigger a activateTimeline by deactivating it first + if (timelineSection->state == WallpaperTimelineSection::State::Active) { + qDebug() << "Deactivate current timeline first"; + co_await timelineSection->deactivateTimeline(); + } } break; @@ -572,4 +604,17 @@ QJsonArray ScreenPlayTimelineManager::timelineWallpaperList() } return timelineWallpaperList; } + +int ScreenPlayTimelineManager::selectedTimelineIndex() const +{ + return m_selectedTimelineIndex; +} + +void ScreenPlayTimelineManager::setSelectedTimelineIndex(int selectedTimelineIndex) +{ + if (m_selectedTimelineIndex == selectedTimelineIndex) + return; + m_selectedTimelineIndex = selectedTimelineIndex; + emit selectedTimelineIndexChanged(m_selectedTimelineIndex); +} } diff --git a/ScreenPlay/src/screenplaywallpaper.cpp b/ScreenPlay/src/screenplaywallpaper.cpp index 5c27b7fe..8d19ed06 100644 --- a/ScreenPlay/src/screenplaywallpaper.cpp +++ b/ScreenPlay/src/screenplaywallpaper.cpp @@ -156,27 +156,36 @@ QJsonObject ScreenPlayWallpaper::getActiveSettingsJson() */ void ScreenPlayWallpaper::close() { + setState(ScreenPlayEnums::AppState::Closing); qInfo() << "Close wallpaper with appID:" << m_appID; + + m_pingAliveTimer.stop(); // Stop the timer when closing + // When the wallpaper never connected, this is invalid if (!m_connection) { qCritical() << "Cannot request quit, wallpaper never connected!"; + setState(ScreenPlayEnums::AppState::Inactive); return; } if (!m_connection->close()) { qCritical() << "Cannot close wallpaper!"; + setState(ScreenPlayEnums::AppState::Inactive); return; } - m_isExiting = true; + // The state will be set to Inactive in the disconnected callback } - /*! \brief Prints the exit code if != 0. */ void ScreenPlayWallpaper::processExit(int exitCode, QProcess::ExitStatus exitStatus) { - if (exitCode != 0) - qWarning() << "WARNING EXIT CODE: " << exitCode << exitStatus; + setState(ScreenPlay::ScreenPlayEnums::AppState::Inactive); + if (exitCode != 0) { + qCritical() << "ERROR: Wallpaper closed with appID: " << m_appID << " EXIT CODE: " << exitCode << exitStatus; + return; + } + qDebug() << "Wallpaper closed with appID: " << m_appID; } /*! @@ -184,6 +193,7 @@ void ScreenPlayWallpaper::processExit(int exitCode, QProcess::ExitStatus exitSta */ void ScreenPlayWallpaper::processError(QProcess::ProcessError error) { + setState(ScreenPlay::ScreenPlayEnums::AppState::Inactive); qWarning() << "EX: " << error; } @@ -192,10 +202,13 @@ void ScreenPlayWallpaper::processError(QProcess::ProcessError error) playbackRate or fillMode. Otherwise it is a simple key, value json pair. */ + bool ScreenPlayWallpaper::setWallpaperValue(const QString& key, const QString& value, const bool save) { - if (m_isExiting) + if (state() != ScreenPlayEnums::AppState::Active) { + qWarning() << "Cannot set value for inactive or closing wallpaper!"; return false; + } if (!m_connection) { qWarning() << "Cannot set value for unconnected wallpaper!"; @@ -223,6 +236,16 @@ bool ScreenPlayWallpaper::setWallpaperValue(const QString& key, const QString& v return success; } +ScreenPlay::ScreenPlayEnums::AppState ScreenPlayWallpaper::state() const +{ + return m_state; +} + +void ScreenPlayWallpaper::setState(ScreenPlay::ScreenPlayEnums::AppState state) +{ + m_state = state; +} + const WallpaperData& ScreenPlayWallpaper::wallpaperData() { return m_wallpaperData; @@ -237,11 +260,15 @@ void ScreenPlayWallpaper::setSDKConnection(std::unique_ptr connec m_connection = std::move(connection); qInfo() << "[4/4] SDKConnection (Wallpaper) saved!"; setIsConnected(true); + setState(ScreenPlayEnums::AppState::Active); QObject::connect(m_connection.get(), &SDKConnection::disconnected, this, [this]() { setIsConnected(false); + setState(ScreenPlayEnums::AppState::Inactive); + m_pingAliveTimer.stop(); // Stop the timer when disconnected qInfo() << "Wallpaper:" << m_connection->appID() << "disconnected"; }); + QTimer::singleShot(1000, this, [this]() { if (playbackRate() != 1.0) { setWallpaperValue("playbackRate", QString::number(playbackRate()), false); @@ -249,6 +276,7 @@ void ScreenPlayWallpaper::setSDKConnection(std::unique_ptr connec QObject::connect(&m_pingAliveTimer, &QTimer::timeout, this, [this]() { qInfo() << "For " << m_pingAliveTimer.interval() << "ms no alive signal received. This means the Wallpaper is dead and likely crashed!"; + setState(ScreenPlayEnums::AppState::Closing); emit requestClose(m_appID); }); m_pingAliveTimer.start(GlobalVariables::contentPingAliveIntervalMS); @@ -256,15 +284,17 @@ void ScreenPlayWallpaper::setSDKConnection(std::unique_ptr connec // Check every X seconds if the wallpaper is still alive QObject::connect(m_connection.get(), &SDKConnection::pingAliveReceived, this, [this]() { - m_pingAliveTimer.stop(); - m_pingAliveTimer.start(GlobalVariables::contentPingAliveIntervalMS); + if (state() == ScreenPlayEnums::AppState::Active) { + m_pingAliveTimer.stop(); + m_pingAliveTimer.start(GlobalVariables::contentPingAliveIntervalMS); - std::optional running = m_processManager.isRunning(m_processID); + std::optional running = m_processManager.isRunning(m_processID); - if (running.has_value()) { - qInfo() << "running:" << running.value(); - } else { - qInfo() << "INVALID PID:" << m_processID; + if (running.has_value()) { + // qInfo() << "running:" << running.value(); + } else { + qInfo() << "INVALID PID:" << m_processID; + } } }); } @@ -276,9 +306,10 @@ bool ScreenPlayWallpaper::replace( const WallpaperData wallpaperData, const bool checkWallpaperVisible) { - - if (m_isExiting) + if (state() != ScreenPlayEnums::AppState::Active) { + qWarning() << "Cannot replace inactive or closing wallpaper!"; return false; + } if (!m_connection) { qWarning() << "Cannot replace for unconnected wallpaper!"; @@ -300,7 +331,6 @@ bool ScreenPlayWallpaper::replace( emit requestSave(); return success; } - } #include "moc_screenplaywallpaper.cpp" diff --git a/ScreenPlay/src/wallpapertimelinesection.cpp b/ScreenPlay/src/wallpapertimelinesection.cpp index fe7e6000..c6a0d2be 100644 --- a/ScreenPlay/src/wallpapertimelinesection.cpp +++ b/ScreenPlay/src/wallpapertimelinesection.cpp @@ -41,7 +41,7 @@ QJsonObject WallpaperTimelineSection::serialize() const // Start all ScreenPlayWallpaper processes of this current timeline bool WallpaperTimelineSection::activateTimeline() { - if (state == State::Active) { + if (state != State::Inactive) { qCritical() << " timeline:" << index << identifier << "is already active with state: " << state; return false; } @@ -80,6 +80,7 @@ bool WallpaperTimelineSection::activateTimeline() // &ScreenPlayManager::requestSaveProfiles emit requestSaveProfiles(); }); + QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::stateChanged, this, &WallpaperTimelineSection::requestUpdateMonitorListModel); QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestClose, this, [this]() { // , &ScreenPlayManager::removeWallpaper); }); @@ -100,6 +101,11 @@ bool WallpaperTimelineSection::activateTimeline() QCoro::Task WallpaperTimelineSection::deactivateTimeline() { + if (state != State::Active) { + qCritical() << " timeline:" << index << identifier << "is already active with state: " << state; + co_return false; + } + state = WallpaperTimelineSection::State::Closing; if (activeWallpaperList.empty()) { state = State::Inactive; @@ -149,33 +155,30 @@ QCoro::Task WallpaperTimelineSection::deactivateTimeline() // WallpaperData! QCoro::Task WallpaperTimelineSection::removeWallpaper(const int monitorIndex) { - + // Remove WallpaperData first size_t removedCount = std::erase_if(wallpaperDataList, [monitorIndex](const auto& wallpaperData) { return wallpaperData.monitors.contains(monitorIndex); }); - if (removedCount == 0) { qCritical() << "No wallpaper data found for monitor index:" << monitorIndex; co_return false; } std::shared_ptr runningScreenPlayWallpaper; - bool found = false; - for (const auto& screenPlayWallpaper : activeWallpaperList) { - if (screenPlayWallpaper->monitors().contains(monitorIndex)) { - runningScreenPlayWallpaper = screenPlayWallpaper; - found = true; - break; - } - } + auto it = std::find_if(activeWallpaperList.begin(), activeWallpaperList.end(), + [monitorIndex](const auto& screenPlayWallpaper) { + return screenPlayWallpaper->monitors().contains(monitorIndex); + }); + // The user always can select a not running timeline section // and remove the wallpaper there. This means that it is // fine to just return here. - if (!found) { + if (it == activeWallpaperList.end()) { qDebug() << "No running wallpaper found for monitor index:" << monitorIndex; co_return true; } + runningScreenPlayWallpaper = *it; QTimer timer; timer.start(250); const int maxRetries = 30; @@ -184,9 +187,14 @@ QCoro::Task WallpaperTimelineSection::removeWallpaper(const int monitorInd // Wait for the timer to tick co_await timer; if (!runningScreenPlayWallpaper->isConnected()) { + // Remove the wallpaper from the activeWallpaperList + activeWallpaperList.erase(it); + updateActiveWallpaperCounter(); co_return true; } } + + qCritical() << "Failed to close wallpaper for monitor index:" << monitorIndex; co_return false; } diff --git a/ScreenPlaySDK/src/screenplaysdk.cpp b/ScreenPlaySDK/src/screenplaysdk.cpp index 4a03cc23..c9753f02 100644 --- a/ScreenPlaySDK/src/screenplaysdk.cpp +++ b/ScreenPlaySDK/src/screenplaysdk.cpp @@ -38,7 +38,7 @@ void ScreenPlaySDK::start() m_socket.connectToServer("ScreenPlay"); if (!m_socket.waitForConnected(1000)) { - emit disconnected(); + disconnected(); } } diff --git a/ScreenPlayUtil/CMakeLists.txt b/ScreenPlayUtil/CMakeLists.txt index 9f1695fc..5efa5407 100644 --- a/ScreenPlayUtil/CMakeLists.txt +++ b/ScreenPlayUtil/CMakeLists.txt @@ -23,6 +23,7 @@ set(QML qml/Dialogs/CriticalError.qml qml/Dialogs/MonitorConfiguration.qml qml/Dialogs/SteamNotAvailable.qml + qml/ErrorPopup.qml qml/FileDropAnimation.qml qml/FileSelector.qml qml/Grow.qml @@ -30,11 +31,13 @@ set(QML qml/Headline.qml qml/HeadlineSection.qml qml/ImageSelector.qml + qml/InstantPopup.js qml/LabelSlider.qml qml/LicenseSelector.qml qml/ModalBackgroundBlur.qml qml/MouseHoverBlocker.qml qml/Popup.qml + qml/RainbowGradient.qml qml/RippleEffect.qml qml/Shake.qml qml/Tag.qml @@ -57,6 +60,7 @@ set(HEADER inc/public/ScreenPlayUtil/archive.h inc/public/ScreenPlayUtil/contenttypes.h inc/public/ScreenPlayUtil/exitcodes.h + inc/public/ScreenPlayUtil/globalenums.h inc/public/ScreenPlayUtil/HelpersCommon.h inc/public/ScreenPlayUtil/ListPropertyHelper.h inc/public/ScreenPlayUtil/logginghandler.h diff --git a/ScreenPlayUtil/inc/public/ScreenPlayUtil/globalenums.h b/ScreenPlayUtil/inc/public/ScreenPlayUtil/globalenums.h new file mode 100644 index 00000000..ddddd01f --- /dev/null +++ b/ScreenPlayUtil/inc/public/ScreenPlayUtil/globalenums.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only + +#pragma once +#include +#include +#include + +namespace ScreenPlay { + +class ScreenPlayEnums : public QObject { + Q_OBJECT + QML_ELEMENT + Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") + /*! + \brief Used in the "Installed" tab. + */ +public: + enum class AppState { + Inactive, + Starting, + Closing, + Active, + }; + Q_ENUM(AppState) +}; + +} diff --git a/ScreenPlayUtil/qml/ErrorPopup.qml b/ScreenPlayUtil/qml/ErrorPopup.qml new file mode 100644 index 00000000..a369a6cd --- /dev/null +++ b/ScreenPlayUtil/qml/ErrorPopup.qml @@ -0,0 +1,34 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Window + +Popup { + id: root + modal: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + anchors.centerIn: Overlay.overlay + + property string errorMessage: "" + + contentItem: Column { + spacing: 10 + Label { + text: qsTr("⚠️ Fatal error encountered:\n") + font.pointSize: 16 + font.bold: true + } + Label { + text: root.errorMessage + wrapMode: Text.WordWrap + } + Row { + spacing: 10 + Button { + text: qsTr("Ok") + onClicked: { + root.close(); + } + } + } + } +} diff --git a/ScreenPlayUtil/qml/InstantPopup.js b/ScreenPlayUtil/qml/InstantPopup.js new file mode 100644 index 00000000..4684bbd6 --- /dev/null +++ b/ScreenPlayUtil/qml/InstantPopup.js @@ -0,0 +1,32 @@ +.pragma library +.import QtQuick as QtQ + +var errorPopup + +/* + Creates error popups in js +*/ +function openErrorPopup(parent, errorMsg) { + console.error(errorMsg) + // Check if parent is a valid QML Item + if (!(parent instanceof QtQ.Item)) { + console.error( + "Error: Invalid parent object provided. Parent must be a QML Item or inherit from it.", + typeof (parent)) + return + } + + if (!errorPopup) { + var errorComponent = Qt.createComponent( + "qrc:/qml/ScreenPlayUtil/qml/ErrorPopup.qml") + if (errorComponent.status === QtQ.Component.Ready) { + errorPopup = errorComponent.createObject(parent) + } else { + console.error("Error creating ErrorPopup:", + errorComponent.errorString()) + return + } + } + errorPopup.errorMessage = errorMsg + errorPopup.open() +} diff --git a/ScreenPlayUtil/qml/RainbowGradient.qml b/ScreenPlayUtil/qml/RainbowGradient.qml new file mode 100644 index 00000000..d0f73c31 --- /dev/null +++ b/ScreenPlayUtil/qml/RainbowGradient.qml @@ -0,0 +1,172 @@ +import QtQuick + +Rectangle { + id: root + implicitHeight: 100 + implicitWidth: 100 + property int animationDuration: 2000 + property bool running: true + gradient: Gradient { + orientation: Gradient.Horizontal + GradientStop { + id: stop1 + position: -0.25 + color: "#f79533" + } + GradientStop { + id: stop2 + position: -0.11 + color: "#f37055" + } + GradientStop { + id: stop3 + position: 0.03 + color: "#ef4e7b" + } + GradientStop { + id: stop4 + position: 0.17 + color: "#a166ab" + } + GradientStop { + id: stop5 + position: 0.31 + color: "#5073b8" + } + GradientStop { + id: stop6 + position: 0.45 + color: "#1098ad" + } + GradientStop { + id: stop7 + position: 0.59 + color: "#07b39b" + } + GradientStop { + id: stop8 + position: 0.75 + color: "#6fba82" + } + } + SequentialAnimation { + running: root.running + loops: Animation.Infinite + ParallelAnimation { + NumberAnimation { + target: stop1 + property: "position" + from: -0.25 + to: 0.25 + duration: root.animationDuration + } + NumberAnimation { + target: stop2 + property: "position" + from: -0.11 + to: 0.39 + duration: root.animationDuration + } + NumberAnimation { + target: stop3 + property: "position" + from: 0.03 + to: 0.53 + duration: root.animationDuration + } + NumberAnimation { + target: stop4 + property: "position" + from: 0.17 + to: 0.67 + duration: root.animationDuration + } + NumberAnimation { + target: stop5 + property: "position" + from: 0.31 + to: 0.81 + duration: root.animationDuration + } + NumberAnimation { + target: stop6 + property: "position" + from: 0.45 + to: 0.95 + duration: root.animationDuration + } + NumberAnimation { + target: stop7 + property: "position" + from: 0.59 + to: 1.09 + duration: root.animationDuration + } + NumberAnimation { + target: stop8 + property: "position" + from: 0.75 + to: 1.25 + duration: root.animationDuration + } + } + ParallelAnimation { + NumberAnimation { + target: stop1 + property: "position" + from: 0.25 + to: -0.25 + duration: root.animationDuration + } + NumberAnimation { + target: stop2 + property: "position" + from: 0.39 + to: -0.11 + duration: root.animationDuration + } + NumberAnimation { + target: stop3 + property: "position" + from: 0.53 + to: 0.03 + duration: root.animationDuration + } + NumberAnimation { + target: stop4 + property: "position" + from: 0.67 + to: 0.17 + duration: root.animationDuration + } + NumberAnimation { + target: stop5 + property: "position" + from: 0.81 + to: 0.31 + duration: root.animationDuration + } + NumberAnimation { + target: stop6 + property: "position" + from: 0.95 + to: 0.45 + duration: root.animationDuration + } + NumberAnimation { + target: stop7 + property: "position" + from: 1.09 + to: 0.59 + duration: root.animationDuration + } + NumberAnimation { + target: stop8 + property: "position" + from: 1.25 + to: 0.75 + duration: root.animationDuration + } + } + } +} diff --git a/ScreenPlayWorkshop/CMakeLists.txt b/ScreenPlayWorkshop/CMakeLists.txt index c85abed0..6d29d730 100644 --- a/ScreenPlayWorkshop/CMakeLists.txt +++ b/ScreenPlayWorkshop/CMakeLists.txt @@ -18,10 +18,10 @@ set(QML # cmake-format: sort qml/Background.qml qml/Forum.qml + qml/InstalledItem.qml + qml/InstalledItemImage.qml qml/Navigation.qml qml/PopupOffline.qml - qml/ScreenPlayItem.qml - qml/ScreenPlayItemImage.qml qml/Sidebar.qml qml/SteamProfile.qml qml/SteamWorkshop.qml diff --git a/ScreenPlayWorkshop/qml/ScreenPlayItem.qml b/ScreenPlayWorkshop/qml/InstalledItem.qml similarity index 99% rename from ScreenPlayWorkshop/qml/ScreenPlayItem.qml rename to ScreenPlayWorkshop/qml/InstalledItem.qml index 20c54499..e321ae3f 100644 --- a/ScreenPlayWorkshop/qml/ScreenPlayItem.qml +++ b/ScreenPlayWorkshop/qml/InstalledItem.qml @@ -147,7 +147,7 @@ Item { anchors.fill: parent visible: false - ScreenPlayItemImage { + InstalledItemImage { id: screenPlayItemImage anchors.fill: parent diff --git a/ScreenPlayWorkshop/qml/ScreenPlayItemImage.qml b/ScreenPlayWorkshop/qml/InstalledItemImage.qml similarity index 100% rename from ScreenPlayWorkshop/qml/ScreenPlayItemImage.qml rename to ScreenPlayWorkshop/qml/InstalledItemImage.qml diff --git a/ScreenPlayWorkshop/qml/WorkshopItem.qml b/ScreenPlayWorkshop/qml/WorkshopItem.qml index 749149df..33c423dd 100644 --- a/ScreenPlayWorkshop/qml/WorkshopItem.qml +++ b/ScreenPlayWorkshop/qml/WorkshopItem.qml @@ -141,7 +141,7 @@ Item { margins: 5 } - ScreenPlayItemImage { + InstalledItemImage { id: screenPlayItemImage anchors.fill: parent diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index a0b9a9fa..a19ea137 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -8,7 +8,6 @@ FetchContent_Populate( # https://bugreports.qt.io/browse/QTCREATORBUG-27083 SOURCE_DIR ${THIRD_PARTY_PATH}/QArchive) - FetchContent_Populate( qcoro GIT_REPOSITORY https://github.com/danvratil/qcoro.git