From 0ad84736c0bd60b754a4c0b7a6b07eed2362946a Mon Sep 17 00:00:00 2001 From: Elias Steurer Date: Thu, 25 Jul 2024 18:16:17 +0200 Subject: [PATCH] Refactor timelines Fix monitorlistmodel and add mock monitor list for easier testing Add working active wallpaper preview based on selected timeline Change many calls to use coroutines that make the async handling of wallpaper closing 1000x easier Fix wallpaper count based on actual connected wallpaper and not based on started Add LineIndicator user selected indicator --- Content/wallpaper_qml/main.qml | 4 +- Content/widget_xkcd/main.qml | 16 +- ScreenPlay/inc/public/ScreenPlay/app.h | 5 +- .../inc/public/ScreenPlay/monitorlistmodel.h | 18 +- .../inc/public/ScreenPlay/screenplaymanager.h | 15 +- .../ScreenPlay/screenplaytimelinemanager.h | 21 +- .../inc/public/ScreenPlay/wallpaperdata.h | 7 +- .../ScreenPlay/wallpapertimelinesection.h | 13 +- ScreenPlay/main.qml | 87 ++-- ScreenPlay/qml/Components/LineHandle.qml | 18 +- ScreenPlay/qml/Components/LineIndicator.qml | 39 +- ScreenPlay/qml/Components/Timeline.qml | 456 ++++++++++-------- .../ContentSettings/ContentSettingsView.qml | 4 +- .../qml/ContentSettings/MonitorSelection.qml | 92 ++-- .../ContentSettings/MonitorSelectionItem.qml | 17 +- ScreenPlay/qml/Installed/InstalledDrawer.qml | 60 ++- ScreenPlay/src/monitorlistmodel.cpp | 124 +++-- ScreenPlay/src/screenplaymanager.cpp | 112 +++-- ScreenPlay/src/screenplaytimelinemanager.cpp | 119 ++++- ScreenPlay/src/wallpapertimelinesection.cpp | 57 ++- ScreenPlayUtil/qml/Popup.qml | 4 +- ScreenPlayUtil/src/util.cpp | 2 +- ScreenPlayWallpaper/qml/Wallpaper.qml | 3 +- ScreenPlayWallpaper/src/basewindow.h | 1 - 24 files changed, 765 insertions(+), 529 deletions(-) diff --git a/Content/wallpaper_qml/main.qml b/Content/wallpaper_qml/main.qml index 4b8b941d..b708198a 100644 --- a/Content/wallpaper_qml/main.qml +++ b/Content/wallpaper_qml/main.qml @@ -31,8 +31,8 @@ Rectangle { text: "Exit" onClicked: { Qt.callLater(function () { - Wallpaper.terminate(); - }); + Wallpaper.terminate(); + }); } } } diff --git a/Content/widget_xkcd/main.qml b/Content/widget_xkcd/main.qml index 917c0cf0..09379a5f 100644 --- a/Content/widget_xkcd/main.qml +++ b/Content/widget_xkcd/main.qml @@ -27,14 +27,14 @@ Item { Component.onCompleted: { request("http://xkcd.com/info.0.json", function (o) { - if (o.status === 200) { - var d = eval('new Object(' + o.responseText + ')'); - console.log(o.responseText); - img.source = d.img; - } else { - console.log("Some error has occurred"); - } - }); + if (o.status === 200) { + var d = eval('new Object(' + o.responseText + ')'); + console.log(o.responseText); + img.source = d.img; + } else { + console.log("Some error has occurred"); + } + }); } Image { diff --git a/ScreenPlay/inc/public/ScreenPlay/app.h b/ScreenPlay/inc/public/ScreenPlay/app.h index db80a44f..5b6412a2 100644 --- a/ScreenPlay/inc/public/ScreenPlay/app.h +++ b/ScreenPlay/inc/public/ScreenPlay/app.h @@ -1,8 +1,6 @@ // SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only #pragma once -#include - #include "ScreenPlay/create.h" #include "ScreenPlay/globalvariables.h" #include "ScreenPlay/installedlistfilter.h" @@ -13,8 +11,9 @@ #include "ScreenPlay/settings.h" #include "ScreenPlay/wizards.h" #include "ScreenPlayUtil/util.h" -#include +#include +#include #include #if defined(Q_OS_WIN) diff --git a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h index 91076c50..b3a8fae8 100644 --- a/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h +++ b/ScreenPlay/inc/public/ScreenPlay/monitorlistmodel.h @@ -13,6 +13,7 @@ #include #include "ScreenPlay/wallpapertimelinesection.h" +#include "ScreenPlayUtil/contenttypes.h" namespace ScreenPlay { @@ -28,6 +29,9 @@ struct Monitor { int m_index { 0 }; QRect m_geometry; + QString m_wallpaperPreviewImage; + QString m_appID; + ContentTypes::InstalledType m_installedType = ContentTypes::InstalledType::Unknown; }; class MonitorListModel : public QAbstractListModel { @@ -50,15 +54,15 @@ public: QHash roleNames() const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - - void setWallpaperMonitor(const std::shared_ptr& timelineSection, - const QVector monitors); + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; std::optional getAppIDByMonitorIndex(const int index) const; Q_INVOKABLE void reset(); - Q_INVOKABLE QRect absoluteDesktopSize() const; + Q_INVOKABLE QRect absoluteDesktopSize() const; + Q_INVOKABLE QSize totalDesktopSize() const; + + bool setData(const QModelIndex& index, const QVariant& value, int role) override; signals: void monitorReloadCompleted(); void setNewActiveMonitor(int index, QString path); @@ -82,7 +86,7 @@ private: private: QVector m_monitorList; - std::shared_ptr m_activeTimelineSection; + bool m_useMockMonitors = false; + QVector> m_mockMonitorList; }; - } diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h index 5e09a60e..f33a9b7f 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaymanager.h @@ -19,7 +19,6 @@ namespace ScreenPlay { - class ScreenPlayManager : public QObject { Q_OBJECT QML_ELEMENT @@ -36,13 +35,9 @@ public: const std::shared_ptr& mlm, const std::shared_ptr& settings); - std::shared_ptr startWallpaper( - WallpaperData wallpaperData, - const bool saveToProfilesConfigFile); - - Q_INVOKABLE bool removeAllWallpapers(bool saveToProfile = false); + Q_INVOKABLE QCoro::QmlTask removeAllWallpapers(bool saveToProfile = false); Q_INVOKABLE bool removeAllWidgets(bool saveToProfile = false); - Q_INVOKABLE bool removeWallpaperAt(const int index); + Q_INVOKABLE QCoro::QmlTask removeWallpaperAt(const int timelineIndex, const QString timelineIdentifier, const int monitorIndex); Q_INVOKABLE ScreenPlayWallpaper* getWallpaperByAppID(const QString& appID); @@ -58,7 +53,7 @@ public: Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections(); Q_INVOKABLE bool removeTimelineAt(const int index); Q_INVOKABLE QJsonArray initialSectionsList(); - Q_INVOKABLE bool setWallpaperAtTimelineIndex( + Q_INVOKABLE QCoro::QmlTask setWallpaperAtTimelineIndex( const ScreenPlay::ContentTypes::InstalledType type, const QString& absolutePath, const QString& previewImage, @@ -77,12 +72,14 @@ 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(); int activeWallpaperCounter() const { return m_activeWallpaperCounter; } int activeWidgetsCounter() const { return m_activeWidgetsCounter; } diff --git a/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h b/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h index 0302d610..5b05db18 100644 --- a/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h +++ b/ScreenPlay/inc/public/ScreenPlay/screenplaytimelinemanager.h @@ -6,6 +6,7 @@ #include #include +#include "ScreenPlay/monitorlistmodel.h" #include "ScreenPlay/wallpapertimelinesection.h" namespace ScreenPlay { @@ -19,6 +20,8 @@ public: std::optional> activeWallpaperSectionByAppID(const QString& appID); std::shared_ptr findActiveWallpaperTimelineSection(); std::shared_ptr findTimelineForCurrentTime(); + + void startup(); bool addTimelineFromSettings(const QJsonObject& timelineObj); bool deactivateCurrentTimeline(); bool moveTimelineAt(const int index, const QString identifier, const float relativePosition, QString positionTimeString); @@ -26,24 +29,38 @@ public: bool addTimelineAt(const int index, const float reltiaveLinePosition, QString identifier); bool removeTimelineAt(const int index); QCoro::Task removeAllTimlineSections(); + QCoro::Task removeAllWallpaperFromActiveTimlineSections(); + QCoro::Task removeWallpaperAt( + const int timelineIndex, + const QString timelineIdentifier, + const int monitorIndex); void updateIndices(); void printTimelines(); - bool setWallpaperAtTimelineIndex(WallpaperData wallpaperData, + QCoro::Task setWallpaperAtTimelineIndex( + WallpaperData wallpaperData, const int timelineIndex, const QString& identifier); QJsonArray initialSectionsList(); QJsonArray timelineWallpaperList(); void setGlobalVariables(const std::shared_ptr& globalVariables); void setSettings(const std::shared_ptr& settings); - void startupFirstTimeline(); + void setMonitorListModel(const std::shared_ptr& monitorListModel); + void updateMonitorListModelData(const int selectedTimelineIndex); + private slots: void checkActiveWallpaperTimeline(); signals: void requestSaveProfiles(); + void activeWallpaperCountChanged(const int count); + +private: + std::optional> activeWallpaperSection(const int timelineIndex, const QString timelineIdentifier); private: QVector> m_wallpaperTimelineSectionsList; + std::shared_ptr m_monitorListModel; + // We use a 24 hour system const QString m_timelineTimeFormat = "hh:mm:ss"; QTimer m_contentTimer; diff --git a/ScreenPlay/inc/public/ScreenPlay/wallpaperdata.h b/ScreenPlay/inc/public/ScreenPlay/wallpaperdata.h index 5bc9dea7..94938259 100644 --- a/ScreenPlay/inc/public/ScreenPlay/wallpaperdata.h +++ b/ScreenPlay/inc/public/ScreenPlay/wallpaperdata.h @@ -2,17 +2,17 @@ #pragma once +#include "ScreenPlayUtil/contenttypes.h" #include #include #include -#include #include +#include #include #include #include #include - -#include "ScreenPlayUtil/contenttypes.h" +#include namespace ScreenPlay { @@ -30,6 +30,5 @@ struct WallpaperData { QJsonObject serialize() const; static std::optional loadWallpaperConfig(const QJsonObject& wallpaperObj); - }; } diff --git a/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h b/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h index af913874..8c655e48 100644 --- a/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h +++ b/ScreenPlay/inc/public/ScreenPlay/wallpapertimelinesection.h @@ -45,15 +45,22 @@ public: std::shared_ptr globalVariables; std::shared_ptr settings; + +public: // Check if currentTime falls within the timeline section bool containsTime(const QTime& time) const; - QJsonObject serialize() const; - bool activateTimeline(); - QCoro::Task deactivateTimeline(); + QCoro::Task deactivateTimeline(); + QCoro::Task removeWallpaper(const int monitorIndex); +private slots: + void updateActiveWallpaperCounter(); + +private: + std::optional> wallpaperByMonitorIndex(const int index); signals: void requestSaveProfiles(); + void activeWallpaperCountChanged(const int count); }; } diff --git a/ScreenPlay/main.qml b/ScreenPlay/main.qml index 596e7b68..18bd2b16 100644 --- a/ScreenPlay/main.qml +++ b/ScreenPlay/main.qml @@ -8,64 +8,57 @@ import QtCore as QCore ApplicationWindow { id: root - color: Material.theme === Material.Dark ? Qt.darker( - Material.background) : Material.background + color: Material.theme === Material.Dark ? Qt.darker(Material.background) : Material.background // Set visible if the -silent parameter was not set (see app.cpp end of constructor). visible: false width: 1400 height: 810 - minimumHeight: 450 minimumWidth: 1050 Component.onCompleted: { // App is now a qml singleton to fix QtC autocompletion. // This also now means we have to make sure to init it here // and do _not_ access any other properties before we called init. - App.init() - - - setTheme(App.settings.theme) + App.init(); + setTheme(App.settings.theme); if (!App.settings.silentStart) { - App.showDockIcon(true) - root.show() + App.showDockIcon(true); + root.show(); } - baseLoader.setSource("qrc:/qml/ScreenPlayApp/qml/MainApp.qml") - - const isSteamVersion = App.globalVariables.isSteamVersion() - let platform = "" + baseLoader.setSource("qrc:/qml/ScreenPlayApp/qml/MainApp.qml"); + const isSteamVersion = App.globalVariables.isSteamVersion(); + let platform = ""; if (isSteamVersion) - platform = qsTr("Steam") - const isProVersion = App.globalVariables.isProVersion() - let featureLevel = "" + platform = qsTr("Steam"); + const isProVersion = App.globalVariables.isProVersion(); + let featureLevel = ""; if (isProVersion) - featureLevel = qsTr("Pro") - const isUltraVersion = App.globalVariables.isUltraVersion() + featureLevel = qsTr("Pro"); + const isUltraVersion = App.globalVariables.isUltraVersion(); if (isUltraVersion) - featureLevel = qsTr("Ultra") - - root.title = "ScreenPlay - v" + App.version() + " " + featureLevel + " " + platform + featureLevel = qsTr("Ultra"); + root.title = "ScreenPlay - v" + App.version() + " " + featureLevel + " " + platform; } - Connections { target: App - function onRequestExit(){ - Qt.exit(0) + function onRequestExit() { + Qt.exit(0); } } function setTheme(theme) { switch (theme) { case Settings.Theme.System: - root.Material.theme = Material.System - break + root.Material.theme = Material.System; + break; case Settings.Theme.Dark: - root.Material.theme = Material.Dark - break + root.Material.theme = Material.Dark; + break; case Settings.Theme.Light: - root.Material.theme = Material.Light - break + root.Material.theme = Material.Light; + break; } } @@ -74,7 +67,7 @@ ApplicationWindow { Material.accent: Material.color(Material.Orange) onVisibilityChanged: { if (root.visibility !== 2) - return + return; } QCore.Settings { @@ -82,23 +75,21 @@ ApplicationWindow { } onClosing: close => { - close.accepted = false - if (App.screenPlayManager.activeWallpaperCounter === 0 - && App.screenPlayManager.activeWidgetsCounter === 0) { - App.exit() - } - const alwaysMinimize = settings.value("alwaysMinimize", null) - if (alwaysMinimize === null) { - console.error( - "Unable to retreive alwaysMinimize setting") - } - if (alwaysMinimize === "true") { - root.hide() - App.showDockIcon(false) - return - } - baseLoader.item.openExitDialog() - } + close.accepted = false; + if (App.screenPlayManager.activeWallpaperCounter === 0 && App.screenPlayManager.activeWidgetsCounter === 0) { + App.exit(); + } + const alwaysMinimize = settings.value("alwaysMinimize", null); + if (alwaysMinimize === null) { + console.error("Unable to retreive alwaysMinimize setting"); + } + if (alwaysMinimize === "true") { + root.hide(); + App.showDockIcon(false); + return; + } + baseLoader.item.openExitDialog(); + } Loader { id: baseLoader diff --git a/ScreenPlay/qml/Components/LineHandle.qml b/ScreenPlay/qml/Components/LineHandle.qml index 53978602..4a5d28c0 100644 --- a/ScreenPlay/qml/Components/LineHandle.qml +++ b/ScreenPlay/qml/Components/LineHandle.qml @@ -6,10 +6,13 @@ Item { property real lineWidth: 1 property real linePosition: (root.x / lineWidth).toFixed(4) property string timeString: { - const normalized = root.x / root.lineWidth; // Your existing normalization + const normalized = root.x / root.lineWidth; + // Your existing normalization let totalHours = normalized * 24; - let hours = Math.floor(totalHours); // Gets the whole hour part - let minutes = Math.round((totalHours - hours) * 60); // Calculates the minutes + let hours = Math.floor(totalHours); + // Gets the whole hour part + let minutes = Math.round((totalHours - hours) * 60); + // Calculates the minutes // Check if minutes rolled over to 60, adjust hours and minutes accordingly if (minutes === 60) { hours += 1; // Increment hours by 1 @@ -35,15 +38,18 @@ Item { width: 20 height: width Rectangle { + id: handleCircle visible: !root.isLast radius: width color: dragHandler.active ? "orange" : "white" - anchors.fill: parent + width: 20 + height: width } + + // To block ➕ MouseArea { hoverEnabled: true propagateComposedEvents: false - anchors.centerIn: parent width: 50 height: 50 } @@ -54,7 +60,7 @@ Item { color: "white" visible: !root.isLast anchors { - horizontalCenter: parent.horizontalCenter + horizontalCenter: handleCircle.horizontalCenter bottom: parent.bottom bottomMargin: -20 } diff --git a/ScreenPlay/qml/Components/LineIndicator.qml b/ScreenPlay/qml/Components/LineIndicator.qml index b2978e7b..44c2d3ff 100644 --- a/ScreenPlay/qml/Components/LineIndicator.qml +++ b/ScreenPlay/qml/Components/LineIndicator.qml @@ -6,7 +6,8 @@ Rectangle { z: selected ? 99 : 0 property int index: 0 property string identifier - property bool selected: false + property bool selected: false // User selected + property bool isActive: false // Active based on time property bool isLast: false property alias text: text.text @@ -24,6 +25,22 @@ Rectangle { } } + Rectangle { + opacity: root.isActive ? 1 : 0 + color: "gold" + height: root.height + anchors { + right: parent.right + left: parent.left + bottom: parent.bottom + } + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + } + Rectangle { visible: root.selected color: "gold" @@ -45,10 +62,17 @@ Rectangle { top: parent.bottom topMargin: 0 } + + Behavior on color { + + ColorAnimation { + duration: 200 + } + } } Rectangle { - id: background + id: monitorBackground width: 70 height: 48 radius: 5 @@ -60,6 +84,13 @@ Rectangle { top: indicatorLineVertical.bottom topMargin: -1 } + + Behavior on color { + + ColorAnimation { + duration: 200 + } + } MouseArea { anchors.fill: parent hoverEnabled: true @@ -80,8 +111,8 @@ Rectangle { font.pointSize: 10 anchors { - left: background.right - bottom: background.top + left: monitorBackground.right + bottom: monitorBackground.top margins: -20 } } diff --git a/ScreenPlay/qml/Components/Timeline.qml b/ScreenPlay/qml/Components/Timeline.qml index e519f5a1..5d3084da 100644 --- a/ScreenPlay/qml/Components/Timeline.qml +++ b/ScreenPlay/qml/Components/Timeline.qml @@ -14,23 +14,31 @@ Control { leftPadding: 20 rightPadding: 20 - property int activeTimelineIndex: -1 - property int length: timeLine.sectionsList.length - - function getActiveTimeline() { - return timeLine.sectionsList[root.activeTimelineIndex] - } + // User selected + property int selectedTimelineIndex: -1 + property var selectedTimeline: timeline.sectionsList[root.selectedTimelineIndex] + property int length: timeline.sectionsList.length function removeAll() { - timeLine.removeAll() + timeline.removeAll(); } - function printTimelines() { - print("################# qml:") - for (var i = 0; i < timeLine.sectionsList.length; i++) { - print(timeLine.sectionsList[i].index, - timeLine.sectionsList[i].identifier, - timeLine.sectionsList[i].relativeLinePosition) + function reset() { + timeline.reset(); + } + LoggingCategory { + id: timelineLogging + name: "timeline" + defaultLogLevel: LoggingCategory.Debug + } + + Connections { + target: App.screenPlayManager + function onPrintQmlTimeline() { + console.debug(timelineLogging, "################# qml:"); + for (var i = 0; i < timeline.sectionsList.length; i++) { + console.debug(timelineLogging, timeline.sectionsList[i].index, timeline.sectionsList[i].identifier, timeline.sectionsList[i].relativeLinePosition); + } } } @@ -40,60 +48,71 @@ Control { property string identifier property int index: 0 property real relativeLinePosition: lineHandle.linePosition - onRelativeLinePositionChanged: print("relativelinepos: ", - relativeLinePosition) + onRelativeLinePositionChanged: console.debug("relativelinepos: ", relativeLinePosition) property LineHandle lineHandle property LineIndicator lineIndicator } } contentItem: Item { - id: timeLine + id: timeline + height: 160 + implicitWidth: 600 property var sectionsList: [] property var lineColors: ["#1E88E5", "#00897B", "#43A047", "#C0CA33", "#FFB300", "#FB8C00", "#F4511E", "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB"] - onWidthChanged: timeLine.updatePositions() - property var initialSectionsList: [] - Component.onCompleted: { - let sectionObects = App.screenPlayManager.initialSectionsList() - for (let sectionObject in sectionObects) { - initialSectionsList.push(sectionObects[sectionObject]) + + onWidthChanged: timeline.updatePositions() + Component.onCompleted: reset() + + Timer { + running: true + repeat: true + interval: 500 + onTriggered: { + const activeTimelineIndex = App.screenPlayManager.activeTimelineIndex(); + if (activeTimelineIndex === -1) { + return; + } + for (var i = 0; i < timeline.sectionsList.length; i++) { + let section = timeline.sectionsList[i]; + section.lineIndicator.isActive = (activeTimelineIndex === i); + } + } + } + + function reset() { + removeAll(); + let initialSectionsList = App.screenPlayManager.initialSectionsList(); + if (App.globalVariables.isBasicVersion()) { + if (initialSectionsList.length > 1) { + console.error(timelineLogging, "Invalid section list count for basic version"); + // Create dummy + addSection("INVALID", 1); + } + return; } initialSectionsList.sort(function (a, b) { - return b.index - a.index - }) + return b.index - a.index; + }); for (let index in initialSectionsList) { - let section = initialSectionsList[index] - addSection(section.identifier, section.relativePosition) + let section = initialSectionsList[index]; + addSection(section.identifier, section.relativePosition); } } function removeAll() { - print("removeAll", timeLine.sectionsList.length) - for (var i = 0; i < timeLine.sectionsList.length; i++) { + console.debug(timelineLogging, "removeAll", timeline.sectionsList.length); + for (var i = 0; i < timeline.sectionsList.length; i++) { // ORDER is important here! Destory the children first - print("remove index ", i) - let section = timeLine.sectionsList[i] - section.lineHandle.destroy() - section.lineIndicator.destroy() - section.destroy() + console.debug(timelineLogging, "remove index ", i); + let section = timeline.sectionsList[i]; + section.lineHandle.destroy(); + section.lineIndicator.destroy(); + section.destroy(); } - timeLine.sectionsList = [] - App.screenPlayManager.removeAllTimlineSections().then(result => { - - if (!result.success) { - console.error("removeAllTimlineSections failed") - return - } - - 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) - }) + timeline.sectionsList = []; + root.selectedTimelineIndex = -1; } // IMPORTANT: The new element is always on the left. The first @@ -101,215 +120,208 @@ Control { // user can never delete it. It only gets "pushed" further // to the right, by decreasing its size. function addSection(identifier, stopPosition) { - print("stopPosition", stopPosition) + console.debug(timelineLogging, "stopPosition", stopPosition); // Make sure to limit float precision - const fixedStopPosition = stopPosition - print("addSection at: ", fixedStopPosition) + const fixedStopPosition = stopPosition; + console.debug(timelineLogging, "addSection at: ", fixedStopPosition); if (stopPosition < 0 || fixedStopPosition > 1) { - console.error("Invalid position:", fixedStopPosition) - return + console.error(timelineLogging, "Invalid position:", fixedStopPosition); + return; } - let sectionObject = sectionComp.createObject(timeLine, { - "identifier": identifier, - "relativeLinePosition": fixedStopPosition - }) - timeLine.sectionsList.push(sectionObject) - timeLine.sectionsList.sort(function (a, b) { - return a.relativeLinePosition - b.relativeLinePosition - }) - const index = timeLine.sectionsList.indexOf(sectionObject) - console.log("Addsection:", index) - createSection(index, fixedStopPosition, sectionObject, identifier) - updatePositions() - return sectionObject + let sectionObject = sectionComp.createObject(timeline, { + "identifier": identifier, + "relativeLinePosition": fixedStopPosition + }); + timeline.sectionsList.push(sectionObject); + timeline.sectionsList.sort(function (a, b) { + return a.relativeLinePosition - b.relativeLinePosition; + }); + const index = timeline.sectionsList.indexOf(sectionObject); + console.debug(timelineLogging, "Addsection:", index); + createSection(index, fixedStopPosition, sectionObject, identifier); + updatePositions(); + return sectionObject; } function createSection(index, stopPosition, section, identifier) { - console.log("Adding at:", index, stopPosition, identifier) + console.debug(timelineLogging, "Adding at:", index, stopPosition, identifier); //console.assert(isFloat(stopPosition)) - let haComponent = Qt.createComponent("LineHandle.qml") + let haComponent = Qt.createComponent("LineHandle.qml"); if (haComponent.status === Component.Error) { - console.assert(haComponent.errorString()) - return + console.assert(timelineLogging, haComponent.errorString()); + return; } - section.lineHandle = haComponent.createObject(handleWrapper) - section.lineHandle.lineWidth = timeLine.width - section.lineHandle.x = Math.round( - handleWrapper.width * timeLine.sectionsList[index].relativeLinePosition) - section.lineHandle.y = -section.lineHandle.height / 2 + section.lineHandle = haComponent.createObject(handleWrapper); + section.lineHandle.lineWidth = timeline.width; + section.lineHandle.x = Math.round(handleWrapper.width * timeline.sectionsList[index].relativeLinePosition); + section.lineHandle.y = -section.lineHandle.height / 2; // Will be set later - section.lineHandle.lineMinimum = timeLine.x - section.lineHandle.lineMaximum = timeLine.x - section.lineHandle.handleMoved.connect(timeLine.onHandleMoved) - let liComponent = Qt.createComponent("LineIndicator.qml") + section.lineHandle.lineMinimum = timeline.x; + section.lineHandle.lineMaximum = timeline.x; + section.lineHandle.handleMoved.connect(timeline.onHandleMoved); + let liComponent = Qt.createComponent("LineIndicator.qml"); if (liComponent.status === Component.Error) { - console.assert(liComponent.errorString()) - return + console.assert(timelineLogging, liComponent.errorString()); + return; } // Set color initially so we do not have a weird color animation at start const lineIndicatorProperties = { "color": getColorAtIndex(index) - } - section.lineIndicator = liComponent.createObject( - lineIndicatorWrapper, lineIndicatorProperties) - section.lineIndicator.height = lineIndicatorWrapper.height - section.lineIndicator.index = index - section.lineIndicator.identifier = identifier - section.lineIndicator.color = getColorAtIndex(index) - section.lineIndicator.remove.connect(timeLine.removeSection) - section.lineIndicator.lineSelected.connect( - timeLine.lineIndicatorSelected) + }; + section.lineIndicator = liComponent.createObject(lineIndicatorWrapper, lineIndicatorProperties); + section.lineIndicator.height = lineIndicatorWrapper.height; + section.lineIndicator.index = index; + section.lineIndicator.identifier = identifier; + section.lineIndicator.color = getColorAtIndex(index); + section.lineIndicator.remove.connect(timeline.removeSection); + section.lineIndicator.lineSelected.connect(timeline.lineIndicatorSelected); } function sectionFromHandle(lineHandle) { - for (var i = 0; i < timeLine.sectionsList.length; i++) { - if (timeLine.sectionsList[i].lineHandle === lineHandle) - return timeLine.sectionsList[i] + for (var i = 0; i < timeline.sectionsList.length; i++) { + if (timeline.sectionsList[i].lineHandle === lineHandle) + return timeline.sectionsList[i]; } - return null + return null; } function onHandleMoved(lineHandle) { - updatePositions() - const section = sectionFromHandle(lineHandle) + updatePositions(); + const section = sectionFromHandle(lineHandle); if (section === null) { - print(lineHandle.linePosition) - console.error("Unable to match handle to section list") - return + console.debug(timelineLogging, lineHandle.linePosition); + console.error(timelineLogging, "Unable to match handle to section list"); + return; } - App.screenPlayManager.moveTimelineAt(section.index, - section.identifier, - lineHandle.linePosition, - lineHandle.timeString) + App.screenPlayManager.moveTimelineAt(section.index, section.identifier, lineHandle.linePosition, lineHandle.timeString); } - function lineIndicatorSelected(activeTimelineIndex) { - for (var i = 0; i < timeLine.sectionsList.length; i++) { - if (i === activeTimelineIndex) { - timeLine.sectionsList[i].lineIndicator.selected = true - continue + function lineIndicatorSelected(selectedTimelineIndex) { + console.debug(timelineLogging, "selectedTimelineIndex:", selectedTimelineIndex, "section cout: ", timeline.sectionsList.length); + for (var i = 0; i < timeline.sectionsList.length; i++) { + if (i === selectedTimelineIndex) { + timeline.sectionsList[i].lineIndicator.selected = true; + continue; } - timeLine.sectionsList[i].lineIndicator.selected = false + timeline.sectionsList[i].lineIndicator.selected = false; } - root.activeTimelineIndex = activeTimelineIndex + root.selectedTimelineIndex = selectedTimelineIndex; + App.screenPlayManager.setSelectedTimelineIndex(selectedTimelineIndex); } // We must update all indexes when removing/adding an element function updateIndicatorIndexes() { - if (timeLine.sectionsList === null - || timeLine.sectionsList === undefined) - return - timeLine.sectionsList.sort(function (a, b) { - return a.relativeLinePosition - b.relativeLinePosition - }) - for (var i = 0; i < timeLine.sectionsList.length; i++) { - timeLine.sectionsList[i].index = i - timeLine.sectionsList[i].lineIndicator.index = i - //print("updateIndicatorIndexes:", timeLine.sectionsList[i].index, timeLine.sectionsList[i].relativeLinePosition) + if (timeline.sectionsList === null || timeline.sectionsList === undefined) + return; + timeline.sectionsList.sort(function (a, b) { + return a.relativeLinePosition - b.relativeLinePosition; + }); + for (var i = 0; i < timeline.sectionsList.length; i++) { + timeline.sectionsList[i].index = i; + timeline.sectionsList[i].lineIndicator.index = i; + //console.debug("updateIndicatorIndexes:", timeline.sectionsList[i].index, timeline.sectionsList[i].relativeLinePosition) } } function removeSection(index) { - print(timeLine.stopPositionList) - print(timeLine.sectionList) - const isLast = index === timeLine.sectionsList.length - 1 + console.debug(timelineLogging, timeline.stopPositionList); + console.debug(timelineLogging, timeline.sectionList); + const isLast = index === timeline.sectionsList.length - 1; if (isLast) - return + return; // ORDER is important here! First destory the object // and then remove i f - let section = timeLine.sectionsList[index] - section.lineHandle.destroy() - section.lineIndicator.destroy() - section.destroy() - timeLine.sectionsList.splice(index, 1) - updatePositions() - App.screenPlayManager.removeTimelineAt(index) + let section = timeline.sectionsList[index]; + section.lineHandle.destroy(); + section.lineIndicator.destroy(); + section.destroy(); + timeline.sectionsList.splice(index, 1); + updatePositions(); + App.screenPlayManager.removeTimelineAt(index); } function updatePositions() { // Iterate through each handle in the 'sectionList' array - for (var i = 0; i < timeLine.sectionsList.length; i++) { - let handle = timeLine.sectionsList[i].lineHandle + for (var i = 0; i < timeline.sectionsList.length; i++) { + let handle = timeline.sectionsList[i].lineHandle; // Determine the minimum position for the current handle - let prevPos + let prevPos; if (i === 0) { // If it's the first handle, its minimum is 0 - prevPos = 0 + prevPos = 0; } else { // Otherwise, it's directly the position of the previous handle - prevPos = timeLine.sectionsList[i - 1].lineHandle.x + prevPos = timeline.sectionsList[i - 1].lineHandle.x; } // Determine the maximum position for the current handle - let nextPos - if (i === timeLine.sectionsList.length - 1) { + let nextPos; + if (i === timeline.sectionsList.length - 1) { // If it's the last handle, its maximum is the width of the line - nextPos = timeLine.width + nextPos = timeline.width; } else { // Otherwise, it's directly the position of the next handle - nextPos = timeLine.sectionsList[i + 1].lineHandle.x + nextPos = timeline.sectionsList[i + 1].lineHandle.x; } // Set the determined minimum and maximum positions for the current handle - handle.lineMinimum = prevPos - handle.lineMaximum = nextPos + handle.lineMinimum = prevPos; + handle.lineMaximum = nextPos; - //timeLine.sectionsList[i].relativeLinePosition =prevPos / timeLine.width - // print("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeLine.sectionsList[i].relativeLinePosition) + //timeline.sectionsList[i].relativeLinePosition =prevPos / timeline.width + // console.debug("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeline.sectionsList[i].relativeLinePosition) } - for (var i = 0; i < timeLine.sectionsList.length; i++) { - let section = timeLine.sectionsList[i] - section.relativeLinePosition = section.lineHandle.linePosition - // print(section.relativeLinePosition, section.lineHandle.lineMinimum, section.lineHandle.lineMaximum) + for (var j = 0; j < timeline.sectionsList.length; j++) { + let section = timeline.sectionsList[j]; + section.relativeLinePosition = section.lineHandle.linePosition; + // console.debug(section.relativeLinePosition, section.lineHandle.lineMinimum, section.lineHandle.lineMaximum) } - updateIndicatorPositions() - updateLastHandle() - updateIndicatorColor() - updateIndicatorIndexes() + updateIndicatorPositions(); + updateLastHandle(); + updateIndicatorColor(); + updateIndicatorIndexes(); } function getColorAtIndex(index) { - let i = index + let i = index; // Start from the beginnging again - if (index >= timeLine.lineColors.length) { - i = index % timeLine.lineColors.length + if (index >= timeline.lineColors.length) { + i = index % timeline.lineColors.length; } - return timeLine.lineColors[i] + return timeline.lineColors[i]; } function updateIndicatorColor() { - for (var i = 0; i < timeLine.sectionsList.length; i++) { - let lineIndicator = timeLine.sectionsList[i].lineIndicator - lineIndicator.color = getColorAtIndex(i) + for (var i = 0; i < timeline.sectionsList.length; i++) { + let lineIndicator = timeline.sectionsList[i].lineIndicator; + lineIndicator.color = getColorAtIndex(i); } } function updateLastHandle() { - for (var i = 0; i < timeLine.sectionsList.length; i++) { - timeLine.sectionsList[i].lineHandle.isLast = i === timeLine.sectionsList.length - 1 - timeLine.sectionsList[i].lineIndicator.isLast = i - === timeLine.sectionsList.length - 1 + for (var i = 0; i < timeline.sectionsList.length; i++) { + timeline.sectionsList[i].lineHandle.isLast = i === timeline.sectionsList.length - 1; + timeline.sectionsList[i].lineIndicator.isLast = i === timeline.sectionsList.length - 1; } } function updateIndicatorPositions() { - for (var i = 0; i < timeLine.sectionsList.length; i++) { - const lineIndicator = timeLine.sectionsList[i].lineIndicator - //print(i, lineIndicator.x, lineIndicator.width, timeLine.sectionsList[i].relativeLinePosition) - const handle = timeLine.sectionsList[i].lineHandle - lineIndicator.x = handle.dragHandler.xAxis.minimum - lineIndicator.width = (handle.linePosition * handle.lineWidth).toFixed( - 2) - lineIndicator.x + for (var i = 0; i < timeline.sectionsList.length; i++) { + const lineIndicator = timeline.sectionsList[i].lineIndicator; + //console.debug(i, lineIndicator.x, lineIndicator.width, timeline.sectionsList[i].relativeLinePosition) + const handle = timeline.sectionsList[i].lineHandle; + lineIndicator.x = handle.dragHandler.xAxis.minimum; + lineIndicator.width = (handle.linePosition * handle.lineWidth).toFixed(2) - lineIndicator.x; } } // https://stackoverflow.com/a/3885844 function isFloat(n) { - return n === +n && n !== (n | 0) + return n === +n && n !== (n | 0); } Rectangle { @@ -332,13 +344,10 @@ Control { color: Material.color(Material.BlueGrey) width: 2 height: 30 - y: (addHandleWrapper.height - height) - / 2 // Vertically center within addHandleWrapper + y: (addHandleWrapper.height - height) / 2 // Vertically center within addHandleWrapper property int totalSeconds: 86400 // Total seconds in a day - property int currentSeconds: (new Date().getHours( - ) * 3600) + (new Date().getMinutes( - ) * 60) + new Date().getSeconds() + property int currentSeconds: 0 x: addHandleWrapper.width * (currentSeconds / totalSeconds) @@ -347,15 +356,9 @@ Control { repeat: true running: true onTriggered: { - currentTimeIndicator.currentSeconds - = (new Date().getHours( - ) * 3600) + (new Date().getMinutes( - ) * 60) + new Date().getSeconds( - ) - currentTimeIndicator.x = addHandleWrapper.width - * (currentTimeIndicator.currentSeconds / currentTimeIndicator.totalSeconds) - currentTimeText.text = Qt.formatTime(new Date(), - "hh:mm:ss") + currentTimeIndicator.currentSeconds = (new Date().getHours() * 3600) + (new Date().getMinutes() * 60) + new Date().getSeconds(); + currentTimeIndicator.x = addHandleWrapper.width * (currentTimeIndicator.currentSeconds / currentTimeIndicator.totalSeconds); + currentTimeText.text = Qt.formatTime(new Date(), "hh:mm:ss"); } } } @@ -372,14 +375,22 @@ Control { } RowLayout { - anchors.fill: parent + height: 30 + uniformCellSizes: true + anchors { + top: parent.top + right: parent.right + left: parent.left + leftMargin: -5 + } Repeater { - model: 24 + model: 25 Item { width: 20 - height: 60 + height: 30 required property int index Text { + id: txtHours color: "gray" text: index anchors { @@ -390,9 +401,9 @@ Control { Rectangle { color: "gray" width: 1 - height: 5 + height: 10 anchors { - horizontalCenter: parent.horizontalCenter + horizontalCenter: txtHours.horizontalCenter bottom: parent.bottom } } @@ -403,15 +414,18 @@ Control { ToolButton { text: "➕" onClicked: { - const p = this.x / timeLine.width - const position = p.toFixed(4) - const identifier = App.util.generateRandomString(4) - const sectionObject = timeLine.addSection(identifier, - position) - App.screenPlayManager.addTimelineAt( - sectionObject.index, - sectionObject.relativeLinePosition, - sectionObject.identifier) + if (App.globalVariables.isBasicVersion()) { + screenPlayProView.open(); + return; + } + const p = this.x / timeline.width; + const position = p.toFixed(4); + const identifier = App.util.generateRandomString(4); + const sectionObject = timeline.addSection(identifier, position); + App.screenPlayManager.addTimelineAt(sectionObject.index, sectionObject.relativeLinePosition, sectionObject.identifier); + } + ScreenPlayProPopup { + id: screenPlayProView } x: hoverHandler.point.position.x - width * .5 @@ -436,23 +450,49 @@ Control { top: addHandleWrapper.bottom } } - Item { + Rectangle { height: 18 - width: 5 + color: "#757575" + width: 2 anchors { right: parent.left - top: addHandleWrapper.bottom - topMargin: -9 + verticalCenter: lineIndicatorWrapper.verticalCenter } } Rectangle { height: 18 - width: 5 + width: 2 color: "#757575" anchors { right: parent.right - top: addHandleWrapper.bottom - topMargin: -9 + verticalCenter: lineIndicatorWrapper.verticalCenter + } + } + + ToolButton { + text: "❌ Reset" //qsTr("Remove all timeline ranges") + z: 99 + anchors { + right: parent.right + top: parent.top + } + onClicked: { + timeline.removeAll(); + App.screenPlayManager.removeAllTimlineSections().then(result => { + if (!result.success) { + console.error("removeAllTimlineSections failed"); + return; + } + 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); + }); + } + anchors { + right: parent.right + top: parent.top + topMargin: -height } } } diff --git a/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml b/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml index 35e2698c..35fd5621 100644 --- a/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml +++ b/ScreenPlay/qml/ContentSettings/ContentSettingsView.qml @@ -19,6 +19,7 @@ Util.Popup { height: 768 onOpened: { monitorSelection.selectMonitorAt(0); + timeline.reset(); } background: Rectangle { anchors.fill: parent @@ -72,6 +73,7 @@ Util.Popup { } Timeline { + id: timeline Layout.fillWidth: true Layout.fillHeight: true } @@ -123,8 +125,6 @@ Util.Popup { width: parent.width * 0.9 multipleMonitorsSelectable: false monitorWithoutContentSelectable: false - availableWidth: width - 20 - availableHeight: 150 onRequestProjectSettings: function (index, installedType, appID) { if (installedType === Util.ContentTypes.InstalledType.VideoWallpaper) { videoControlWrapper.state = "visible"; diff --git a/ScreenPlay/qml/ContentSettings/MonitorSelection.qml b/ScreenPlay/qml/ContentSettings/MonitorSelection.qml index 7ac62919..51e628d3 100644 --- a/ScreenPlay/qml/ContentSettings/MonitorSelection.qml +++ b/ScreenPlay/qml/ContentSettings/MonitorSelection.qml @@ -13,7 +13,6 @@ Rectangle { property bool monitorWithoutContentSelectable: true property bool multipleMonitorsSelectable: false property bool isSelected: false - // We preselect the main monitor property var activeMonitors: [] property alias background: root.color property alias bgRadius: root.radius @@ -22,6 +21,11 @@ Rectangle { resize(); selectOnly(0); } + LoggingCategory { + id: logger + name: "MonitorSelection" + defaultLogLevel: LoggingCategory.Debug + } signal requestProjectSettings(var index, var installedType, var appID) @@ -50,8 +54,6 @@ Rectangle { if (rp.itemAt(i).isSelected) root.activeMonitors.push(rp.itemAt(i).index); } - // Must be called manually. When QML properties are getting altered in js the - // property binding breaks root.activeMonitorsChanged(); root.isSelected = root.activeMonitors.length > 0; return root.activeMonitors; @@ -68,42 +70,45 @@ Rectangle { } function resize() { - print("resize"); - var absoluteDesktopSize = App.monitorListModel.absoluteDesktopSize(); - var isWidthGreaterThanHeight = false; - var windowsDelta = 0; - if (absoluteDesktopSize.width < absoluteDesktopSize.height) { - windowsDelta = absoluteDesktopSize.width / absoluteDesktopSize.height; - isWidthGreaterThanHeight = false; - } else { - windowsDelta = absoluteDesktopSize.height / absoluteDesktopSize.width; - isWidthGreaterThanHeight = true; - } - if (rp.count === 1) - availableWidth = availableWidth * 0.66; - var dynamicHeight = availableWidth * windowsDelta; - var dynamicWidth = availableHeight * windowsDelta; - // Delta (height/width) - var monitorHeightRationDelta = 0; - var monitorWidthRationDelta = 0; - if (isWidthGreaterThanHeight) { - monitorHeightRationDelta = dynamicHeight / absoluteDesktopSize.height; - monitorWidthRationDelta = availableWidth / absoluteDesktopSize.width; - } else { - monitorHeightRationDelta = availableHeight / absoluteDesktopSize.height; - monitorWidthRationDelta = dynamicWidth / absoluteDesktopSize.width; - } + console.debug(logger, "MonitorSelection resize started"); + + // 1. Get the total desktop size + let totalDesktopSize = App.monitorListModel.totalDesktopSize(); + console.debug(logger, "Total desktop size:", totalDesktopSize.width, "x", totalDesktopSize.height); + + // 2. Get root item dimensions + let rootWidth = root.width; + let rootHeight = root.height; + console.debug(logger, "Root dimensions:", rootWidth, "x", rootHeight); + + // 3. Calculate scaling factor + let margin = 10; + let availableWidth = rootWidth - 2 * margin; + let availableHeight = rootHeight - 2 * margin; + let scaleX = availableWidth / totalDesktopSize.width; + let scaleY = availableHeight / totalDesktopSize.height; + let scaleFactor = Math.min(scaleX, scaleY, 1); + + // Ensure we don't scale up + console.debug(logger, "Scale factor:", scaleFactor); + + // 4. Resize and position repeater items + let scaledWidth = totalDesktopSize.width * scaleFactor; + let scaledHeight = totalDesktopSize.height * scaleFactor; for (var i = 0; i < rp.count; i++) { - rp.itemAt(i).index = i; - rp.itemAt(i).height = rp.itemAt(i).height * monitorHeightRationDelta; - rp.itemAt(i).width = rp.itemAt(i).width * monitorWidthRationDelta; - rp.itemAt(i).x = rp.itemAt(i).x * monitorWidthRationDelta; - rp.itemAt(i).y = rp.itemAt(i).y * monitorHeightRationDelta; - rp.contentWidth += rp.itemAt(i).width; - rp.contentHeight += rp.itemAt(i).height; + let item = rp.itemAt(i); + if (item) { + item.width = item.geometry.width * scaleFactor; + item.height = item.geometry.height * scaleFactor; + item.x = item.geometry.x * scaleFactor; + item.y = item.geometry.y * scaleFactor; + } } - rp.contentWidth += 200; - rp.contentHeight += 200; + + // 6. Center content within Flickable + flickable.contentWidth = scaledWidth; + flickable.contentHeight = scaledHeight; + console.debug(logger, "MonitorSelection resize completed", flickable.contentWidth, flickable.contentHeight); } color: Material.theme === Material.Light ? Material.background : Qt.darker(Material.background) @@ -148,18 +153,11 @@ Rectangle { onMonitorSelected: function (index) { root.selectMonitorAt(index); } + // onRemoveWallpaper: function(index) { + + // } } } - - ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AlwaysOff - snapMode: ScrollBar.SnapOnRelease - } - - ScrollBar.horizontal: ScrollBar { - policy: ScrollBar.AlwaysOff - snapMode: ScrollBar.SnapOnRelease - } } ToolButton { diff --git a/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml b/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml index df9b9ab1..d28a7d3d 100644 --- a/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml +++ b/ScreenPlay/qml/ContentSettings/MonitorSelectionItem.qml @@ -26,6 +26,7 @@ Item { property bool isSelected: false signal monitorSelected(var index) + signal remoteWallpaper(var index) onIsSelectedChanged: root.state = isSelected ? "selected" : "default" onPreviewImageChanged: { @@ -81,11 +82,23 @@ Item { cursorShape: Qt.PointingHandCursor onClicked: { if (monitorWithoutContentSelectable) { - monitorSelected(index); + root.monitorSelected(index); return; } if (root.hasContent && !root.monitorWithoutContentSelectable) - monitorSelected(index); + root.monitorSelected(index); + } + } + + ToolButton { + text: "❌" + enabled: root.hasContent && !root.monitorWithoutContentSelectable + visible: enabled + onClicked: root.remoteWallpaper(index) + + anchors { + top: parent.top + right: parent.right } } } diff --git a/ScreenPlay/qml/Installed/InstalledDrawer.qml b/ScreenPlay/qml/Installed/InstalledDrawer.qml index 0b7b4073..67241bf2 100644 --- a/ScreenPlay/qml/Installed/InstalledDrawer.qml +++ b/ScreenPlay/qml/Installed/InstalledDrawer.qml @@ -18,6 +18,9 @@ Drawer { background: Rectangle { color: Material.color(Material.Grey, Material.Shade900) } + onOpened: { + timeline.reset(); + } property bool hasPreviewGif: false property var type: ContentTypes.InstalledType.QMLWallpaper @@ -25,27 +28,29 @@ Drawer { function setInstalledDrawerItem(folderName, type) { - // Toggle sidebar if clicked on the same content twice - if (root.contentFolderName === folderName) - return; + // Toggle drawer if clicked on the same content twice + if (root.contentFolderName === folderName) { + if (root.visible) { + root.close(); + return; + } + } if (!App.util.isWallpaper(type)) { return; } + print("setInstalledDrawerItem", root.contentFolderName, folderName, typeof (folderName), type); root.contentFolderName = folderName; root.type = type; - print("setInstalledDrawerItem", folderName,typeof(folderName), type); if (type === ContentTypes.InstalledType.VideoWallpaper) installedDrawerWrapper.state = "wallpaper"; else installedDrawerWrapper.state = "scene"; - root.open(); } // This is used for removing wallpaper. We need to clear // the preview image/gif so we can release the file for deletion. function clear() { - root.close(); root.contentFolderName = ""; root.type = ContentTypes.InstalledType.Unknown; @@ -56,11 +61,10 @@ Drawer { } onContentFolderNameChanged: { - if (root.contentFolderName === ""){ - console.error("empty folder name") - return + if (root.contentFolderName === "") { + console.error("empty folder name"); + return; } - const item = App.installedListModel.get(root.contentFolderName); print(root.contentFolderName); txtHeadline.text = item.m_title; @@ -108,22 +112,6 @@ Drawer { Layout.topMargin: 50 Layout.fillWidth: true Layout.fillHeight: true - Connections { - target: App.screenPlayManager - function onPrintQmlTimeline() { - timeline.printTimelines(); - } - } - - ToolButton { - text: "❌" //qsTr("Remove all timeline ranges") - // enabled: timeline.length > 1 - onClicked: timeline.removeAll() - anchors { - right: parent.right - top: parent.top - } - } } } @@ -144,11 +132,8 @@ Drawer { MonitorSelection { id: monitorSelection objectName: "monitorSelection" - height: 180 Layout.fillWidth: true Layout.fillHeight: true - availableWidth: width - availableHeight: height - 20 fontSize: 11 } } @@ -249,8 +234,8 @@ Drawer { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom objectName: "btnLaunchContent" - text: qsTr("Set Wallpaper"); - // enabled: App.util.isWidget(root.type) && activeMonitors.length > 0 ? true : monitorSelection.isSelected + text: qsTr("Set Wallpaper") + enabled: monitorSelection.isSelected && timeline.selectedTimelineIndex > -1 icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_plus.svg" icon.color: "white" font.pointSize: 12 @@ -281,9 +266,18 @@ Drawer { root.close(); return; } - const activeTimeline = timeline.getActiveTimeline(); + const selectedTimeline = timeline.selectedTimeline; + if (selectedTimeline === undefined) { + console.error("No active timeline"); + return; + } const file = item.m_file; - let success = App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, timeline.activeTimelineIndex, activeTimeline.identifier, true); + let success = App.screenPlayManager.setWallpaperAtTimelineIndex(root.type, absoluteStoragePath, previewImage, file, activeMonitors, selectedTimeline.index, selectedTimeline.identifier, true).then(result => { + if (!result.success) { + console.error("setWallpaperAtTimelineIndex failed"); + return; + } + }); } root.close(); monitorSelection.reset(); diff --git a/ScreenPlay/src/monitorlistmodel.cpp b/ScreenPlay/src/monitorlistmodel.cpp index 39e6e8c2..f7b114f0 100644 --- a/ScreenPlay/src/monitorlistmodel.cpp +++ b/ScreenPlay/src/monitorlistmodel.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace ScreenPlay { @@ -33,11 +34,33 @@ namespace ScreenPlay { MonitorListModel::MonitorListModel(QObject* parent) : QAbstractListModel(parent) { - loadMonitors(); auto* guiAppInst = dynamic_cast(QGuiApplication::instance()); connect(guiAppInst, &QGuiApplication::screenAdded, this, &MonitorListModel::screenAdded); connect(guiAppInst, &QGuiApplication::screenRemoved, this, &MonitorListModel::screenRemoved); + + // Setup 1: Two Full HD monitors + m_mockMonitorList.append({ Monitor(0, QRect(0, 0, 1920, 1080)), + Monitor(1, QRect(1920, 0, 1920, 1080)) }); + + // Setup 2: One 4K monitor and one Full HD above + m_mockMonitorList.append({ Monitor(0, QRect(0, 0, 3840, 2160)), + Monitor(1, QRect((-3840 / 2), 3840, 1920, 1080)) }); + + // Setup 3: One WQHD and one Full HD + m_mockMonitorList.append({ Monitor(0, QRect(0, 0, 2560, 1440)), + Monitor(1, QRect(2560, 0, 1920, 1080)) }); + + // Setup 4: Three Full HD monitors (two horizontal, one vertical) + m_mockMonitorList.append({ Monitor(0, QRect(0, 0, 1920, 1080)), + Monitor(1, QRect(1920, 0, 1920, 1080)), + Monitor(2, QRect(3840, 0, 1080, 1920)) }); + + // Setup 5: One ultrawide and one regular monitor + m_mockMonitorList.append({ Monitor(0, QRect(0, 0, 3440, 1440)), + Monitor(1, QRect(3440, 0, 1920, 1080)) }); + + loadMonitors(); } /*! @@ -79,40 +102,23 @@ QVariant MonitorListModel::data(const QModelIndex& index, int role) const return QVariant(); } - - auto roleEnum = static_cast(role); if (row > rowCount()) - return {}; + return QVariant(); switch (roleEnum) { case MonitorRole::AppID: - return 1; - // if (m_monitorList.at(row).m_activeWallpaper) { - // return m_monitorList.at(row).m_activeWallpaper->appID(); - // } else { - // return QVariant(""); - // } + return m_monitorList.at(row).m_appID; case MonitorRole::Index: return m_monitorList.at(row).m_index; case MonitorRole::Geometry: return m_monitorList.at(row).m_geometry; case MonitorRole::InstalledType: - // if (m_monitorList.at(row).m_activeWallpaper) { - // return static_cast(m_monitorList.at(row).m_activeWallpaper->type()); - // } else { - return { "" }; - // } + return QVariant::fromValue(m_monitorList.at(row).m_installedType); case MonitorRole::PreviewImage: - // if (m_monitorList.at(row).m_activeWallpaper) { - // QString absolutePath = m_monitorList.at(row).m_activeWallpaper->absolutePath(); - // return absolutePath + "/" + m_monitorList.at(row).m_activeWallpaper->previewImage(); - // } else { - return QVariant(""); - } - - + return m_monitorList.at(row).m_wallpaperPreviewImage; + } return QVariant(); } @@ -121,9 +127,27 @@ QVariant MonitorListModel::data(const QModelIndex& index, int role) const */ void MonitorListModel::loadMonitors() { + if (m_useMockMonitors) { + + // Generate a random index + const int selectedMockIndex = QRandomGenerator::global()->bounded(m_mockMonitorList.size()); + const auto& mockMonitorList = m_mockMonitorList[selectedMockIndex]; + qDebug() << "Using mock" << selectedMockIndex + << "of" << m_mockMonitorList.size() + << " with monitor count:" << mockMonitorList.count(); + beginInsertRows(index(rowCount()), rowCount(), rowCount() + mockMonitorList.count() - 1); + // Use the randomly selected mock monitor setup + for (const auto& monitor : mockMonitorList) { + m_monitorList.append(monitor); + qDebug() << "Adding mock monitor: " << monitor.m_index << monitor.m_geometry; + } + endInsertRows(); + + emit monitorReloadCompleted(); + return; + } #ifdef Q_OS_WIN - QModelIndex index; auto monitors = WindowsIntegration().getAllMonitors(); // This offset lets us center the monitor selection view in the center @@ -151,13 +175,12 @@ void MonitorListModel::loadMonitors() y + offsetY, width, height); - beginInsertRows(index, m_monitorList.size(), m_monitorList.size()); + beginInsertRows(index(rowCount()), m_monitorList.size(), m_monitorList.size()); m_monitorList.append(Monitor { i, geometry }); endInsertRows(); i++; } #else - QModelIndex index; int offsetX = 0; int offsetY = 0; @@ -178,7 +201,7 @@ void MonitorListModel::loadMonitors() if (screen->geometry().width() == 0 || screen->geometry().height() == 0) continue; - beginInsertRows(index, m_monitorList.size(), m_monitorList.size()); + beginInsertRows(index(rowCount()), m_monitorList.size(), m_monitorList.size()); m_monitorList.append(Monitor { i, screen->geometry() }); endInsertRows(); } @@ -191,31 +214,22 @@ void MonitorListModel::loadMonitors() * \brief MonitorListModel::getAbsoluteDesktopSize * \return */ -QRect MonitorListModel::absoluteDesktopSize() const +QSize MonitorListModel::totalDesktopSize() const { - auto* guiAppInst = dynamic_cast(QGuiApplication::instance()); - return guiAppInst->screens().at(0)->availableVirtualGeometry(); + QRect totalRect; + for (const auto& monitor : m_monitorList) { + totalRect = totalRect.united(monitor.m_geometry); + } + return totalRect.size(); } -/*! - \brief Sets a shared_ptr to the monitor list. This should be used to set and - remove the shared_ptr. -*/ -void MonitorListModel::setWallpaperMonitor( - const std::shared_ptr& timelineSection, const QVector monitors) +QRect MonitorListModel::absoluteDesktopSize() const { - m_activeTimelineSection = timelineSection; - for (const int monitor : monitors) { - // m_monitorList[monitor].m_activeWallpaper = wallpaper; - - emit dataChanged( - index(monitor, 0), - index(monitor, 0), - QVector { - static_cast(MonitorRole::PreviewImage), - static_cast(MonitorRole::InstalledType), - static_cast(MonitorRole::AppID) }); + QRect totalRect; + for (const auto& monitor : m_monitorList) { + totalRect = totalRect.united(monitor.m_geometry); } + return totalRect; } /*! @@ -243,6 +257,22 @@ void MonitorListModel::reset() loadMonitors(); } +bool MonitorListModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == static_cast(MonitorRole::PreviewImage)) { + m_monitorList[index.column()].m_wallpaperPreviewImage = value.toString(); + emit dataChanged(index, index, { role }); + } + if (role == static_cast(MonitorRole::AppID)) { + m_monitorList[index.column()].m_appID = value.toString(); + 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 }); + } + return true; +} } #include "moc_monitorlistmodel.cpp" diff --git a/ScreenPlay/src/screenplaymanager.cpp b/ScreenPlay/src/screenplaymanager.cpp index 191955b7..1b924f83 100644 --- a/ScreenPlay/src/screenplaymanager.cpp +++ b/ScreenPlay/src/screenplaymanager.cpp @@ -26,6 +26,7 @@ ScreenPlayManager::ScreenPlayManager( m_server = std::make_unique(); 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); m_server->setSocketOptions(QLocalServer::WorldAccessOption); if (!m_server->listen("ScreenPlay")) { qCritical("Could not open Local Socket with the name ScreenPlay!"); @@ -56,6 +57,7 @@ void ScreenPlayManager::init( m_screenPlayTimelineManager.setGlobalVariables(m_globalVariables); m_screenPlayTimelineManager.setSettings(m_settings); + m_screenPlayTimelineManager.setMonitorListModel(m_monitorListModel); // Reset to default settings if we are unable to load // the existing one @@ -68,7 +70,7 @@ void ScreenPlayManager::init( /*! \brief Sets the wallpaper at a spesific timeline. */ -bool ScreenPlayManager::setWallpaperAtTimelineIndex( +QCoro::QmlTask ScreenPlayManager::setWallpaperAtTimelineIndex( const ScreenPlay::ContentTypes::InstalledType type, const QString& absolutePath, const QString& previewImage, @@ -85,20 +87,28 @@ bool ScreenPlayManager::setWallpaperAtTimelineIndex( wallpaperData.file = file; wallpaperData.monitors = monitorIndex; wallpaperData.fillMode = m_settings->videoFillMode(); - const bool success = m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier); - if (!success) { - qCritical() << "Invalid timeline index or identifier: " << timelineIndex << identifier; - m_screenPlayTimelineManager.printTimelines(); - emit printQmlTimeline(); - return false; - } + return QCoro::QmlTask([this, wallpaperData, timelineIndex, identifier]() -> QCoro::Task { + if (timelineIndex < 0 || identifier.isEmpty()) { - // We do not start the wallpaper here, but let - // ScreenPlayTimelineManager::checkActiveWallpaperTimeline decide - // if the wallpaper - emit requestSaveProfiles(); - return true; + co_return Result { false }; + } + + const bool success = co_await m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier); + + if (!success) { + qCritical() << "Invalid timeline index or identifier: " << timelineIndex << identifier; + m_screenPlayTimelineManager.printTimelines(); + emit printQmlTimeline(); + co_return Result { success }; + } + + // We do not start the wallpaper here, but let + // ScreenPlayTimelineManager::checkActiveWallpaperTimeline decide + // if the wallpaper + emit requestSaveProfiles(); + co_return Result { success }; + }()); } /*! @@ -147,37 +157,23 @@ bool ScreenPlayManager::startWidget( return true; } +void ScreenPlayManager::setSelectedTimelineIndex(const int selectedTimelineIndex) +{ + m_screenPlayTimelineManager.updateMonitorListModelData(selectedTimelineIndex); +} + /*! \brief Removes all wallpaper entries in the profiles.json. */ -bool ScreenPlayManager::removeAllWallpapers(bool saveToProfile) +QCoro::QmlTask ScreenPlayManager::removeAllWallpapers(bool saveToProfile) { - // TODO - // if (m_screenPlayTimelineManager.m_wallpaperTimelineSectionsList.empty()) { - // return false; - // } - - // QStringList appIDs; - // auto activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection(); - // if (!activeTimelineSection) { - // qWarning() << "Trying to remove all Wallpapers while findActiveSection is empty."; - // return false; - // } - // // Do not remove items from the vector you iterate on. - // for (auto& client : activeTimelineSection->activeWallpaperList) { - // appIDs.append(client->appID()); - // } - - // for (const auto& appID : appIDs) { - // if (!removeWallpaper(appID)) { - // return false; - // } - // } - - // if (saveToProfile) - // emit requestSaveProfiles(); - - return true; + return QCoro::QmlTask([this]() -> QCoro::Task { + // call with coro + const bool success = co_await m_screenPlayTimelineManager.removeAllWallpaperFromActiveTimlineSections(); + qDebug() << "Task: removeAllWallpaperFromActiveTimlineSections" << success; + // emit requestSaveProfiles(); + co_return Result { success }; + }()); } /*! @@ -212,18 +208,16 @@ bool ScreenPlayManager::removeAllWidgets(bool saveToProfile) given monitor index and then closes the sdk connection, removes the entries in the monitor list model and decreases the active wallpaper counter property of ScreenPlayManager. */ -bool ScreenPlayManager::removeWallpaperAt(int index) +QCoro::QmlTask ScreenPlayManager::removeWallpaperAt(int timelineIndex, QString timelineIdentifier, int monitorIndex) { - if (auto appID = m_monitorListModel->getAppIDByMonitorIndex(index)) { - if (removeWallpaper(*appID)) { - emit requestSaveProfiles(); - return true; - } - } - - qWarning() << "Could not remove Wallpaper at index:" << index; - return false; + 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; + // crash? mit requestSaveProfiles(); + co_return Result { success }; + }()); } /*! @@ -531,6 +525,16 @@ bool ScreenPlayManager::setWallpaperValue(const QString& appID, const QString& k return false; } +int ScreenPlayManager::activeTimelineIndex() +{ + std::shared_ptr activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection(); + if (!activeTimelineSection) { + qCritical() << "setWallpaperValue failed, because no active timeline section was found"; + return -1; + } + return activeTimelineSection->index; +} + /*! \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. @@ -604,6 +608,7 @@ bool ScreenPlayManager::loadProfiles() QJsonObject wallpaperObj = timelineWallpaper.toObject(); if (!m_screenPlayTimelineManager.addTimelineFromSettings(wallpaperObj)) { qCritical() << "Unable to add wallpaper timeline"; + containsInvalidData = true; continue; } } @@ -618,10 +623,11 @@ bool ScreenPlayManager::loadProfiles() // The can happen if the user unpluggs a wallpaper but it still exists // in the profiles.json. For this we save all profiles with now active // content. - if (containsInvalidData) + if (containsInvalidData) { saveProfiles(); - - m_screenPlayTimelineManager.startupFirstTimeline(); + } else { + m_screenPlayTimelineManager.startup(); + } return true; } diff --git a/ScreenPlay/src/screenplaytimelinemanager.cpp b/ScreenPlay/src/screenplaytimelinemanager.cpp index 6cb6c921..37fde672 100644 --- a/ScreenPlay/src/screenplaytimelinemanager.cpp +++ b/ScreenPlay/src/screenplaytimelinemanager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace ScreenPlay { @@ -24,7 +25,7 @@ ScreenPlayTimelineManager::ScreenPlayTimelineManager( // Do not start the timer here. This will be done after // we have loaded all timeline wallpaper from the config.json QObject::connect(&m_contentTimer, &QTimer::timeout, this, &ScreenPlayTimelineManager::checkActiveWallpaperTimeline); - m_contentTimer.start(1000); + m_contentTimer.setInterval(250); } /*! @@ -92,32 +93,33 @@ bool ScreenPlayTimelineManager::addTimelineFromSettings(const QJsonObject& timel } // TODO check license - auto timelineSection = std::make_shared(); - QObject::connect(timelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); - timelineSection->startTime = startTime; - timelineSection->endTime = endTime; - timelineSection->settings = m_settings; - timelineSection->globalVariables = m_globalVariables; - timelineSection->relativePosition = Util().calculateRelativePosition(endTime); + auto newTimelineSection = std::make_shared(); + QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); + QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::activeWallpaperCountChanged, this, &ScreenPlayTimelineManager::activeWallpaperCountChanged); + newTimelineSection->startTime = startTime; + newTimelineSection->endTime = endTime; + newTimelineSection->settings = m_settings; + newTimelineSection->globalVariables = m_globalVariables; + newTimelineSection->relativePosition = Util().calculateRelativePosition(endTime); const auto wallpaperList = timelineObj.value("wallpaper").toArray(); for (auto& wallpaper : wallpaperList) { std::optional wallpaperDataOpt = WallpaperData::loadWallpaperConfig(wallpaper.toObject()); if (!wallpaperDataOpt.has_value()) return false; - timelineSection->wallpaperDataList.push_back(wallpaperDataOpt.value()); + newTimelineSection->wallpaperDataList.push_back(wallpaperDataOpt.value()); } // Todo: Should we use addTimelineAt? - timelineSection->index = m_wallpaperTimelineSectionsList.length(); - timelineSection->identifier = Util().generateRandomString(4); + newTimelineSection->index = m_wallpaperTimelineSectionsList.length(); + newTimelineSection->identifier = Util().generateRandomString(4); - qInfo() << timelineSection->index - << timelineSection->startTime - << timelineSection->endTime; + qInfo() << newTimelineSection->index + << newTimelineSection->startTime + << newTimelineSection->endTime; // Todo: Should we use addTimelineAt? - m_wallpaperTimelineSectionsList.append(timelineSection); - return false; + m_wallpaperTimelineSectionsList.append(newTimelineSection); + return true; } /*! @@ -181,12 +183,26 @@ void ScreenPlayTimelineManager::checkActiveWallpaperTimeline() } } +std::optional> ScreenPlayTimelineManager::activeWallpaperSection(const int timelineIndex, const QString timelineIdentifier) +{ + for (const auto& section : m_wallpaperTimelineSectionsList) { + const bool indexMatches = section->index == timelineIndex; + const bool timelineIdentifierMatches = section->identifier == timelineIdentifier; + if (indexMatches && timelineIdentifierMatches) { + return section; + } + } + qCritical() << "No matching timeline for index:" << timelineIndex << "timelineIdentifier: " << timelineIdentifier; + + return nullptr; +} + void ScreenPlayTimelineManager::setSettings(const std::shared_ptr& settings) { m_settings = settings; } -void ScreenPlayTimelineManager::startupFirstTimeline() +void ScreenPlayTimelineManager::startup() { std::shared_ptr currentTimeline = findTimelineForCurrentTime(); if (!currentTimeline) { @@ -194,6 +210,31 @@ void ScreenPlayTimelineManager::startupFirstTimeline() return; } currentTimeline->activateTimeline(); + m_contentTimer.start(); +} + +void ScreenPlayTimelineManager::setMonitorListModel(const std::shared_ptr& monitorListModel) +{ + m_monitorListModel = monitorListModel; +} + +void ScreenPlayTimelineManager::updateMonitorListModelData(const int selectedTimelineIndex) +{ + + auto selectedTimeline = m_wallpaperTimelineSectionsList | std::views::drop(selectedTimelineIndex) | std::views::take(1); + // Clear current list model. This is needed to make sure + // that we do not show any old active wallpaper + m_monitorListModel->reset(); + + if (!selectedTimeline.empty()) { + auto& timeline = selectedTimeline.front(); + for (const auto& wallpaper : timeline->wallpaperDataList) { + const auto previewImg = wallpaper.absolutePath + "/" + wallpaper.previewImage; + m_monitorListModel->setData(m_monitorListModel->index(0, wallpaper.monitors.first()), previewImg, (int)MonitorListModel::MonitorRole::PreviewImage); + } + } else { + qCritical() << "No selectedTimelineIndex found" << selectedTimelineIndex; + } } void ScreenPlayTimelineManager::setGlobalVariables(const std::shared_ptr& globalVariables) @@ -281,6 +322,7 @@ bool ScreenPlayTimelineManager::addTimelineAt(const int index, const float relti auto newTimelineSection = std::make_shared(); QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); + QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::activeWallpaperCountChanged, this, &ScreenPlayTimelineManager::activeWallpaperCountChanged); newTimelineSection->settings = m_settings; newTimelineSection->globalVariables = m_globalVariables; newTimelineSection->index = index; @@ -332,6 +374,39 @@ QCoro::Task ScreenPlayTimelineManager::removeAllTimlineSections() co_return true; } +/*! + \brief Qml function that removes all wallpaper for the current Timeline section. +*/ +QCoro::Task ScreenPlayTimelineManager::removeAllWallpaperFromActiveTimlineSections() +{ + + // First check if there is any active wallpaper and disable it + auto activeTimelineSection = findActiveWallpaperTimelineSection(); + if (activeTimelineSection) { + // First deactivate so the wallpaper has time to shutdown + activeTimelineSection->deactivateTimeline(); + activeTimelineSection->wallpaperDataList.clear(); + } + co_return true; +} + +/*! + \brief Qml function that removes all wallpaper for the current Timeline section. +*/ +QCoro::Task ScreenPlayTimelineManager::removeWallpaperAt(const int timelineIndex, const QString timelineIdentifier, const int monitorIndex) +{ + m_contentTimer.stop(); + auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); }); + + auto sectionOpt = activeWallpaperSection(timelineIndex, timelineIdentifier); + if (!sectionOpt) + co_return false; + + const bool success = co_await sectionOpt.value()->removeWallpaper(monitorIndex); + + co_return success; +} + /*! \brief Removes a timeline at a given index. Expands the timeline next to it to fill the space. @@ -405,7 +480,10 @@ void ScreenPlayTimelineManager::printTimelines() } } -bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpaperData, const int timelineIndex, const QString& identifier) +QCoro::Task ScreenPlayTimelineManager::setWallpaperAtTimelineIndex( + WallpaperData wallpaperData, + const int timelineIndex, + const QString& identifier) { bool found = false; for (auto& timelineSection : m_wallpaperTimelineSectionsList) { @@ -426,6 +504,9 @@ bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpa if (it != timelineSection->wallpaperDataList.end()) { // Overwrite the existing wallpaper *it = wallpaperData; + // TODO: Do not replace but + co_await timelineSection->deactivateTimeline(); + timelineSection->activateTimeline(); } else { // Append the new wallpaper data timelineSection->wallpaperDataList.push_back(wallpaperData); @@ -436,7 +517,7 @@ bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpa break; } } - return found; + co_return found; } QJsonArray ScreenPlayTimelineManager::initialSectionsList() diff --git a/ScreenPlay/src/wallpapertimelinesection.cpp b/ScreenPlay/src/wallpapertimelinesection.cpp index 983bbf68..29189503 100644 --- a/ScreenPlay/src/wallpapertimelinesection.cpp +++ b/ScreenPlay/src/wallpapertimelinesection.cpp @@ -71,22 +71,6 @@ bool WallpaperTimelineSection::activateTimeline() const QString appID = Util().generateRandomString(); qInfo() << "Start wallpaper" << wallpaperData.absolutePath << appID; - // Only support remove wallpaper that spans over 1 monitor - // if (wallpaper.monitors.length() == 1) { - // int i = 0; - // for (auto& wallpaper : m_screenPlayWallpapers) { - // if (wallpaper->monitors().length() == 1) { - // if (monitors.at(0) == wallpaper->monitors().at(0)) { - // return wallpaper->replace( - // wallpaper, - // settings->checkWallpaperVisible()); - // m_monitorListModel->setWallpaperMonitor(wallpaper, wallpaper.monitors); - // } - // } - // i++; - // } - // } - auto screenPlayWallpaper = std::make_shared( globalVariables, appID, @@ -100,6 +84,7 @@ bool WallpaperTimelineSection::activateTimeline() QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestClose, this, []() { // , &ScreenPlayManager::removeWallpaper); }); + QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::isConnectedChanged, this, &WallpaperTimelineSection::updateActiveWallpaperCounter); // QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::error, this, this, []() { // // , &ScreenPlayManager::displayErrorPopup); @@ -144,4 +129,44 @@ QCoro::Task WallpaperTimelineSection::deactivateTimeline() co_return false; } +QCoro::Task WallpaperTimelineSection::removeWallpaper(const int monitorIndex) +{ + auto wallpaperOpt = wallpaperByMonitorIndex(monitorIndex); + if (!wallpaperOpt.has_value()) { + qCritical() << "No wallpaper found for monitor index:" << monitorIndex; + co_return false; + } + + QTimer timer; + timer.start(250); + const int maxRetries = 30; + wallpaperOpt.value()->close(); + for (int i = 1; i <= maxRetries; ++i) { + // Wait for the timer to tick + co_await timer; + if (!wallpaperOpt.value()->isConnected()) { + co_return true; + } + } + co_return false; +} + +void WallpaperTimelineSection::updateActiveWallpaperCounter() +{ + quint64 activeWallpaperCount = 0; + for (const auto& screenPlayWallpaper : activeWallpaperList) { + if (screenPlayWallpaper->isConnected()) + activeWallpaperCount++; + } + emit activeWallpaperCountChanged(activeWallpaperCount); +} + +std::optional> WallpaperTimelineSection::wallpaperByMonitorIndex(const int monitorIndex) +{ + for (const auto& screenPlayWallpaper : activeWallpaperList) { + if (screenPlayWallpaper->monitors().contains(monitorIndex)) + return screenPlayWallpaper; + } + return std::nullopt; +} } diff --git a/ScreenPlayUtil/qml/Popup.qml b/ScreenPlayUtil/qml/Popup.qml index c7705f4a..2b0898a7 100644 --- a/ScreenPlayUtil/qml/Popup.qml +++ b/ScreenPlayUtil/qml/Popup.qml @@ -1,11 +1,11 @@ import QtQuick -import QtQuick.Controls +import QtQuick.Controls as QQC import Qt5Compat.GraphicalEffects import QtQuick.Controls.Material import QtQuick.Layouts import ScreenPlayUtil -Popup { +QQC.Popup { id: root property Item modalSource // Workaround for missing animation on hide diff --git a/ScreenPlayUtil/src/util.cpp b/ScreenPlayUtil/src/util.cpp index dc5bc672..751ab3b0 100644 --- a/ScreenPlayUtil/src/util.cpp +++ b/ScreenPlayUtil/src/util.cpp @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only #include "ScreenPlayUtil/util.h" -#include "qguiapplication.h" #include #include +#include #include #include diff --git a/ScreenPlayWallpaper/qml/Wallpaper.qml b/ScreenPlayWallpaper/qml/Wallpaper.qml index 97d7572e..b4786ee3 100644 --- a/ScreenPlayWallpaper/qml/Wallpaper.qml +++ b/ScreenPlayWallpaper/qml/Wallpaper.qml @@ -58,11 +58,10 @@ Rectangle { return; } // For example if background is a solid color - if(Wallpaper.windowsDesktopProperties.wallpaperPath === ""){ + if (Wallpaper.windowsDesktopProperties.wallpaperPath === "") { root.canFadeByWallpaperFillMode = false; return; } - imgCover.source = Qt.resolvedUrl("file:///" + Wallpaper.windowsDesktopProperties.wallpaperPath); switch (Wallpaper.windowsDesktopProperties.wallpaperStyle) { case 10: diff --git a/ScreenPlayWallpaper/src/basewindow.h b/ScreenPlayWallpaper/src/basewindow.h index 85554c33..9ac835aa 100644 --- a/ScreenPlayWallpaper/src/basewindow.h +++ b/ScreenPlayWallpaper/src/basewindow.h @@ -356,7 +356,6 @@ protected: QVector m_activeScreensList; QFileSystemWatcher m_fileSystemWatcher; QTimer m_liveReloadLimiter; - QSysInfo m_sysinfo; std::unique_ptr m_sdk; QUrl m_projectSourceFileAbsolute; ScreenPlay::Video::VideoCodec m_videoCodec = ScreenPlay::Video::VideoCodec::Unknown;