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;