1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-11-21 18:22:29 +01:00

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
This commit is contained in:
Elias Steurer 2024-07-25 18:16:17 +02:00
parent ee47a6d968
commit 0ad84736c0
24 changed files with 765 additions and 529 deletions

View File

@ -31,8 +31,8 @@ Rectangle {
text: "Exit" text: "Exit"
onClicked: { onClicked: {
Qt.callLater(function () { Qt.callLater(function () {
Wallpaper.terminate(); Wallpaper.terminate();
}); });
} }
} }
} }

View File

@ -27,14 +27,14 @@ Item {
Component.onCompleted: { Component.onCompleted: {
request("http://xkcd.com/info.0.json", function (o) { request("http://xkcd.com/info.0.json", function (o) {
if (o.status === 200) { if (o.status === 200) {
var d = eval('new Object(' + o.responseText + ')'); var d = eval('new Object(' + o.responseText + ')');
console.log(o.responseText); console.log(o.responseText);
img.source = d.img; img.source = d.img;
} else { } else {
console.log("Some error has occurred"); console.log("Some error has occurred");
} }
}); });
} }
Image { Image {

View File

@ -1,8 +1,6 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only // SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#pragma once #pragma once
#include <QString>
#include "ScreenPlay/create.h" #include "ScreenPlay/create.h"
#include "ScreenPlay/globalvariables.h" #include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/installedlistfilter.h" #include "ScreenPlay/installedlistfilter.h"
@ -13,8 +11,9 @@
#include "ScreenPlay/settings.h" #include "ScreenPlay/settings.h"
#include "ScreenPlay/wizards.h" #include "ScreenPlay/wizards.h"
#include "ScreenPlayUtil/util.h" #include "ScreenPlayUtil/util.h"
#include <QQmlEngine>
#include <QQmlEngine>
#include <QString>
#include <memory> #include <memory>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)

View File

@ -13,6 +13,7 @@
#include <optional> #include <optional>
#include "ScreenPlay/wallpapertimelinesection.h" #include "ScreenPlay/wallpapertimelinesection.h"
#include "ScreenPlayUtil/contenttypes.h"
namespace ScreenPlay { namespace ScreenPlay {
@ -28,6 +29,9 @@ struct Monitor {
int m_index { 0 }; int m_index { 0 };
QRect m_geometry; QRect m_geometry;
QString m_wallpaperPreviewImage;
QString m_appID;
ContentTypes::InstalledType m_installedType = ContentTypes::InstalledType::Unknown;
}; };
class MonitorListModel : public QAbstractListModel { class MonitorListModel : public QAbstractListModel {
@ -50,15 +54,15 @@ public:
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
void setWallpaperMonitor(const std::shared_ptr<WallpaperTimelineSection>& timelineSection,
const QVector<int> monitors);
std::optional<QString> getAppIDByMonitorIndex(const int index) const; std::optional<QString> getAppIDByMonitorIndex(const int index) const;
Q_INVOKABLE void reset(); 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: signals:
void monitorReloadCompleted(); void monitorReloadCompleted();
void setNewActiveMonitor(int index, QString path); void setNewActiveMonitor(int index, QString path);
@ -82,7 +86,7 @@ private:
private: private:
QVector<Monitor> m_monitorList; QVector<Monitor> m_monitorList;
std::shared_ptr<WallpaperTimelineSection> m_activeTimelineSection; bool m_useMockMonitors = false;
QVector<QVector<Monitor>> m_mockMonitorList;
}; };
} }

View File

@ -19,7 +19,6 @@
namespace ScreenPlay { namespace ScreenPlay {
class ScreenPlayManager : public QObject { class ScreenPlayManager : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
@ -36,13 +35,9 @@ public:
const std::shared_ptr<MonitorListModel>& mlm, const std::shared_ptr<MonitorListModel>& mlm,
const std::shared_ptr<Settings>& settings); const std::shared_ptr<Settings>& settings);
std::shared_ptr<ScreenPlayWallpaper> startWallpaper( Q_INVOKABLE QCoro::QmlTask removeAllWallpapers(bool saveToProfile = false);
WallpaperData wallpaperData,
const bool saveToProfilesConfigFile);
Q_INVOKABLE bool removeAllWallpapers(bool saveToProfile = false);
Q_INVOKABLE bool removeAllWidgets(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); Q_INVOKABLE ScreenPlayWallpaper* getWallpaperByAppID(const QString& appID);
@ -58,7 +53,7 @@ public:
Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections(); Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections();
Q_INVOKABLE bool removeTimelineAt(const int index); Q_INVOKABLE bool removeTimelineAt(const int index);
Q_INVOKABLE QJsonArray initialSectionsList(); Q_INVOKABLE QJsonArray initialSectionsList();
Q_INVOKABLE bool setWallpaperAtTimelineIndex( Q_INVOKABLE QCoro::QmlTask setWallpaperAtTimelineIndex(
const ScreenPlay::ContentTypes::InstalledType type, const ScreenPlay::ContentTypes::InstalledType type,
const QString& absolutePath, const QString& absolutePath,
const QString& previewImage, const QString& previewImage,
@ -77,12 +72,14 @@ public:
const QJsonObject& properties, const QJsonObject& properties,
const bool saveToProfilesConfigFile); const bool saveToProfilesConfigFile);
Q_INVOKABLE void setSelectedTimelineIndex(const int selectedTimelineIndex);
Q_INVOKABLE bool requestProjectSettingsAtMonitorIndex(const int index); Q_INVOKABLE bool requestProjectSettingsAtMonitorIndex(const int index);
Q_INVOKABLE bool setWallpaperValueAtMonitorIndex(const int index, const QString& key, const QString& value); 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 setWallpaperFillModeAtMonitorIndex(const int index, const int fillmode);
Q_INVOKABLE bool setAllWallpaperValue(const QString& key, const QString& value); 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 bool setWallpaperValue(const QString& appID, const QString& key, const QString& value);
Q_INVOKABLE int activeTimelineIndex();
int activeWallpaperCounter() const { return m_activeWallpaperCounter; } int activeWallpaperCounter() const { return m_activeWallpaperCounter; }
int activeWidgetsCounter() const { return m_activeWidgetsCounter; } int activeWidgetsCounter() const { return m_activeWidgetsCounter; }

View File

@ -6,6 +6,7 @@
#include <QTimer> #include <QTimer>
#include <memory> #include <memory>
#include "ScreenPlay/monitorlistmodel.h"
#include "ScreenPlay/wallpapertimelinesection.h" #include "ScreenPlay/wallpapertimelinesection.h"
namespace ScreenPlay { namespace ScreenPlay {
@ -19,6 +20,8 @@ public:
std::optional<std::shared_ptr<WallpaperTimelineSection>> activeWallpaperSectionByAppID(const QString& appID); std::optional<std::shared_ptr<WallpaperTimelineSection>> activeWallpaperSectionByAppID(const QString& appID);
std::shared_ptr<WallpaperTimelineSection> findActiveWallpaperTimelineSection(); std::shared_ptr<WallpaperTimelineSection> findActiveWallpaperTimelineSection();
std::shared_ptr<WallpaperTimelineSection> findTimelineForCurrentTime(); std::shared_ptr<WallpaperTimelineSection> findTimelineForCurrentTime();
void startup();
bool addTimelineFromSettings(const QJsonObject& timelineObj); bool addTimelineFromSettings(const QJsonObject& timelineObj);
bool deactivateCurrentTimeline(); bool deactivateCurrentTimeline();
bool moveTimelineAt(const int index, const QString identifier, const float relativePosition, QString positionTimeString); 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 addTimelineAt(const int index, const float reltiaveLinePosition, QString identifier);
bool removeTimelineAt(const int index); bool removeTimelineAt(const int index);
QCoro::Task<bool> removeAllTimlineSections(); QCoro::Task<bool> removeAllTimlineSections();
QCoro::Task<bool> removeAllWallpaperFromActiveTimlineSections();
QCoro::Task<bool> removeWallpaperAt(
const int timelineIndex,
const QString timelineIdentifier,
const int monitorIndex);
void updateIndices(); void updateIndices();
void printTimelines(); void printTimelines();
bool setWallpaperAtTimelineIndex(WallpaperData wallpaperData, QCoro::Task<bool> setWallpaperAtTimelineIndex(
WallpaperData wallpaperData,
const int timelineIndex, const int timelineIndex,
const QString& identifier); const QString& identifier);
QJsonArray initialSectionsList(); QJsonArray initialSectionsList();
QJsonArray timelineWallpaperList(); QJsonArray timelineWallpaperList();
void setGlobalVariables(const std::shared_ptr<GlobalVariables>& globalVariables); void setGlobalVariables(const std::shared_ptr<GlobalVariables>& globalVariables);
void setSettings(const std::shared_ptr<Settings>& settings); void setSettings(const std::shared_ptr<Settings>& settings);
void startupFirstTimeline(); void setMonitorListModel(const std::shared_ptr<MonitorListModel>& monitorListModel);
void updateMonitorListModelData(const int selectedTimelineIndex);
private slots: private slots:
void checkActiveWallpaperTimeline(); void checkActiveWallpaperTimeline();
signals: signals:
void requestSaveProfiles(); void requestSaveProfiles();
void activeWallpaperCountChanged(const int count);
private:
std::optional<std::shared_ptr<WallpaperTimelineSection>> activeWallpaperSection(const int timelineIndex, const QString timelineIdentifier);
private: private:
QVector<std::shared_ptr<WallpaperTimelineSection>> m_wallpaperTimelineSectionsList; QVector<std::shared_ptr<WallpaperTimelineSection>> m_wallpaperTimelineSectionsList;
std::shared_ptr<MonitorListModel> m_monitorListModel;
// We use a 24 hour system // We use a 24 hour system
const QString m_timelineTimeFormat = "hh:mm:ss"; const QString m_timelineTimeFormat = "hh:mm:ss";
QTimer m_contentTimer; QTimer m_contentTimer;

View File

@ -2,17 +2,17 @@
#pragma once #pragma once
#include "ScreenPlayUtil/contenttypes.h"
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFileInfoList> #include <QFileInfoList>
#include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject>
#include <QObject> #include <QObject>
#include <QProcess> #include <QProcess>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QUuid>
#include "ScreenPlayUtil/contenttypes.h"
namespace ScreenPlay { namespace ScreenPlay {
@ -30,6 +30,5 @@ struct WallpaperData {
QJsonObject serialize() const; QJsonObject serialize() const;
static std::optional<WallpaperData> loadWallpaperConfig(const QJsonObject& wallpaperObj); static std::optional<WallpaperData> loadWallpaperConfig(const QJsonObject& wallpaperObj);
}; };
} }

View File

@ -45,15 +45,22 @@ public:
std::shared_ptr<GlobalVariables> globalVariables; std::shared_ptr<GlobalVariables> globalVariables;
std::shared_ptr<Settings> settings; std::shared_ptr<Settings> settings;
public:
// Check if currentTime falls within the timeline section // Check if currentTime falls within the timeline section
bool containsTime(const QTime& time) const; bool containsTime(const QTime& time) const;
QJsonObject serialize() const; QJsonObject serialize() const;
bool activateTimeline(); bool activateTimeline();
QCoro::Task<bool> deactivateTimeline();
QCoro::Task<bool> deactivateTimeline();
QCoro::Task<bool> removeWallpaper(const int monitorIndex);
private slots:
void updateActiveWallpaperCounter();
private:
std::optional<std::shared_ptr<ScreenPlayWallpaper>> wallpaperByMonitorIndex(const int index);
signals: signals:
void requestSaveProfiles(); void requestSaveProfiles();
void activeWallpaperCountChanged(const int count);
}; };
} }

View File

@ -8,64 +8,57 @@ import QtCore as QCore
ApplicationWindow { ApplicationWindow {
id: root id: root
color: Material.theme === Material.Dark ? Qt.darker( color: Material.theme === Material.Dark ? Qt.darker(Material.background) : Material.background
Material.background) : Material.background
// Set visible if the -silent parameter was not set (see app.cpp end of constructor). // Set visible if the -silent parameter was not set (see app.cpp end of constructor).
visible: false visible: false
width: 1400 width: 1400
height: 810 height: 810
minimumHeight: 450 minimumHeight: 450
minimumWidth: 1050 minimumWidth: 1050
Component.onCompleted: { Component.onCompleted: {
// App is now a qml singleton to fix QtC autocompletion. // App is now a qml singleton to fix QtC autocompletion.
// This also now means we have to make sure to init it here // This also now means we have to make sure to init it here
// and do _not_ access any other properties before we called init. // and do _not_ access any other properties before we called init.
App.init() App.init();
setTheme(App.settings.theme);
setTheme(App.settings.theme)
if (!App.settings.silentStart) { if (!App.settings.silentStart) {
App.showDockIcon(true) App.showDockIcon(true);
root.show() root.show();
} }
baseLoader.setSource("qrc:/qml/ScreenPlayApp/qml/MainApp.qml") baseLoader.setSource("qrc:/qml/ScreenPlayApp/qml/MainApp.qml");
const isSteamVersion = App.globalVariables.isSteamVersion();
const isSteamVersion = App.globalVariables.isSteamVersion() let platform = "";
let platform = ""
if (isSteamVersion) if (isSteamVersion)
platform = qsTr("Steam") platform = qsTr("Steam");
const isProVersion = App.globalVariables.isProVersion() const isProVersion = App.globalVariables.isProVersion();
let featureLevel = "" let featureLevel = "";
if (isProVersion) if (isProVersion)
featureLevel = qsTr("Pro") featureLevel = qsTr("Pro");
const isUltraVersion = App.globalVariables.isUltraVersion() const isUltraVersion = App.globalVariables.isUltraVersion();
if (isUltraVersion) if (isUltraVersion)
featureLevel = qsTr("Ultra") featureLevel = qsTr("Ultra");
root.title = "ScreenPlay - v" + App.version() + " " + featureLevel + " " + platform;
root.title = "ScreenPlay - v" + App.version() + " " + featureLevel + " " + platform
} }
Connections { Connections {
target: App target: App
function onRequestExit(){ function onRequestExit() {
Qt.exit(0) Qt.exit(0);
} }
} }
function setTheme(theme) { function setTheme(theme) {
switch (theme) { switch (theme) {
case Settings.Theme.System: case Settings.Theme.System:
root.Material.theme = Material.System root.Material.theme = Material.System;
break break;
case Settings.Theme.Dark: case Settings.Theme.Dark:
root.Material.theme = Material.Dark root.Material.theme = Material.Dark;
break break;
case Settings.Theme.Light: case Settings.Theme.Light:
root.Material.theme = Material.Light root.Material.theme = Material.Light;
break break;
} }
} }
@ -74,7 +67,7 @@ ApplicationWindow {
Material.accent: Material.color(Material.Orange) Material.accent: Material.color(Material.Orange)
onVisibilityChanged: { onVisibilityChanged: {
if (root.visibility !== 2) if (root.visibility !== 2)
return return;
} }
QCore.Settings { QCore.Settings {
@ -82,23 +75,21 @@ ApplicationWindow {
} }
onClosing: close => { onClosing: close => {
close.accepted = false close.accepted = false;
if (App.screenPlayManager.activeWallpaperCounter === 0 if (App.screenPlayManager.activeWallpaperCounter === 0 && App.screenPlayManager.activeWidgetsCounter === 0) {
&& App.screenPlayManager.activeWidgetsCounter === 0) { App.exit();
App.exit() }
} const alwaysMinimize = settings.value("alwaysMinimize", null);
const alwaysMinimize = settings.value("alwaysMinimize", null) if (alwaysMinimize === null) {
if (alwaysMinimize === null) { console.error("Unable to retreive alwaysMinimize setting");
console.error( }
"Unable to retreive alwaysMinimize setting") if (alwaysMinimize === "true") {
} root.hide();
if (alwaysMinimize === "true") { App.showDockIcon(false);
root.hide() return;
App.showDockIcon(false) }
return baseLoader.item.openExitDialog();
} }
baseLoader.item.openExitDialog()
}
Loader { Loader {
id: baseLoader id: baseLoader

View File

@ -6,10 +6,13 @@ Item {
property real lineWidth: 1 property real lineWidth: 1
property real linePosition: (root.x / lineWidth).toFixed(4) property real linePosition: (root.x / lineWidth).toFixed(4)
property string timeString: { 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 totalHours = normalized * 24;
let hours = Math.floor(totalHours); // Gets the whole hour part let hours = Math.floor(totalHours);
let minutes = Math.round((totalHours - hours) * 60); // Calculates the minutes // 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 // Check if minutes rolled over to 60, adjust hours and minutes accordingly
if (minutes === 60) { if (minutes === 60) {
hours += 1; // Increment hours by 1 hours += 1; // Increment hours by 1
@ -35,15 +38,18 @@ Item {
width: 20 width: 20
height: width height: width
Rectangle { Rectangle {
id: handleCircle
visible: !root.isLast visible: !root.isLast
radius: width radius: width
color: dragHandler.active ? "orange" : "white" color: dragHandler.active ? "orange" : "white"
anchors.fill: parent width: 20
height: width
} }
// To block
MouseArea { MouseArea {
hoverEnabled: true hoverEnabled: true
propagateComposedEvents: false propagateComposedEvents: false
anchors.centerIn: parent
width: 50 width: 50
height: 50 height: 50
} }
@ -54,7 +60,7 @@ Item {
color: "white" color: "white"
visible: !root.isLast visible: !root.isLast
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: handleCircle.horizontalCenter
bottom: parent.bottom bottom: parent.bottom
bottomMargin: -20 bottomMargin: -20
} }

View File

@ -6,7 +6,8 @@ Rectangle {
z: selected ? 99 : 0 z: selected ? 99 : 0
property int index: 0 property int index: 0
property string identifier 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 bool isLast: false
property alias text: text.text 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 { Rectangle {
visible: root.selected visible: root.selected
color: "gold" color: "gold"
@ -45,10 +62,17 @@ Rectangle {
top: parent.bottom top: parent.bottom
topMargin: 0 topMargin: 0
} }
Behavior on color {
ColorAnimation {
duration: 200
}
}
} }
Rectangle { Rectangle {
id: background id: monitorBackground
width: 70 width: 70
height: 48 height: 48
radius: 5 radius: 5
@ -60,6 +84,13 @@ Rectangle {
top: indicatorLineVertical.bottom top: indicatorLineVertical.bottom
topMargin: -1 topMargin: -1
} }
Behavior on color {
ColorAnimation {
duration: 200
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
@ -80,8 +111,8 @@ Rectangle {
font.pointSize: 10 font.pointSize: 10
anchors { anchors {
left: background.right left: monitorBackground.right
bottom: background.top bottom: monitorBackground.top
margins: -20 margins: -20
} }
} }

View File

@ -14,23 +14,31 @@ Control {
leftPadding: 20 leftPadding: 20
rightPadding: 20 rightPadding: 20
property int activeTimelineIndex: -1 // User selected
property int length: timeLine.sectionsList.length property int selectedTimelineIndex: -1
property var selectedTimeline: timeline.sectionsList[root.selectedTimelineIndex]
function getActiveTimeline() { property int length: timeline.sectionsList.length
return timeLine.sectionsList[root.activeTimelineIndex]
}
function removeAll() { function removeAll() {
timeLine.removeAll() timeline.removeAll();
} }
function printTimelines() { function reset() {
print("################# qml:") timeline.reset();
for (var i = 0; i < timeLine.sectionsList.length; i++) { }
print(timeLine.sectionsList[i].index, LoggingCategory {
timeLine.sectionsList[i].identifier, id: timelineLogging
timeLine.sectionsList[i].relativeLinePosition) 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 string identifier
property int index: 0 property int index: 0
property real relativeLinePosition: lineHandle.linePosition property real relativeLinePosition: lineHandle.linePosition
onRelativeLinePositionChanged: print("relativelinepos: ", onRelativeLinePositionChanged: console.debug("relativelinepos: ", relativeLinePosition)
relativeLinePosition)
property LineHandle lineHandle property LineHandle lineHandle
property LineIndicator lineIndicator property LineIndicator lineIndicator
} }
} }
contentItem: Item { contentItem: Item {
id: timeLine id: timeline
height: 160
implicitWidth: 600
property var sectionsList: [] property var sectionsList: []
property var lineColors: ["#1E88E5", "#00897B", "#43A047", "#C0CA33", "#FFB300", "#FB8C00", "#F4511E", "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB"] property var lineColors: ["#1E88E5", "#00897B", "#43A047", "#C0CA33", "#FFB300", "#FB8C00", "#F4511E", "#E53935", "#D81B60", "#8E24AA", "#5E35B1", "#3949AB"]
onWidthChanged: timeLine.updatePositions()
property var initialSectionsList: [] onWidthChanged: timeline.updatePositions()
Component.onCompleted: { Component.onCompleted: reset()
let sectionObects = App.screenPlayManager.initialSectionsList()
for (let sectionObject in sectionObects) { Timer {
initialSectionsList.push(sectionObects[sectionObject]) 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) { initialSectionsList.sort(function (a, b) {
return b.index - a.index return b.index - a.index;
}) });
for (let index in initialSectionsList) { for (let index in initialSectionsList) {
let section = initialSectionsList[index] let section = initialSectionsList[index];
addSection(section.identifier, section.relativePosition) addSection(section.identifier, section.relativePosition);
} }
} }
function removeAll() { function removeAll() {
print("removeAll", timeLine.sectionsList.length) console.debug(timelineLogging, "removeAll", timeline.sectionsList.length);
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
// ORDER is important here! Destory the children first // ORDER is important here! Destory the children first
print("remove index ", i) console.debug(timelineLogging, "remove index ", i);
let section = timeLine.sectionsList[i] let section = timeline.sectionsList[i];
section.lineHandle.destroy() section.lineHandle.destroy();
section.lineIndicator.destroy() section.lineIndicator.destroy();
section.destroy() section.destroy();
} }
timeLine.sectionsList = [] timeline.sectionsList = [];
App.screenPlayManager.removeAllTimlineSections().then(result => { root.selectedTimelineIndex = -1;
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)
})
} }
// IMPORTANT: The new element is always on the left. The first // 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 // user can never delete it. It only gets "pushed" further
// to the right, by decreasing its size. // to the right, by decreasing its size.
function addSection(identifier, stopPosition) { function addSection(identifier, stopPosition) {
print("stopPosition", stopPosition) console.debug(timelineLogging, "stopPosition", stopPosition);
// Make sure to limit float precision // Make sure to limit float precision
const fixedStopPosition = stopPosition const fixedStopPosition = stopPosition;
print("addSection at: ", fixedStopPosition) console.debug(timelineLogging, "addSection at: ", fixedStopPosition);
if (stopPosition < 0 || fixedStopPosition > 1) { if (stopPosition < 0 || fixedStopPosition > 1) {
console.error("Invalid position:", fixedStopPosition) console.error(timelineLogging, "Invalid position:", fixedStopPosition);
return return;
} }
let sectionObject = sectionComp.createObject(timeLine, { let sectionObject = sectionComp.createObject(timeline, {
"identifier": identifier, "identifier": identifier,
"relativeLinePosition": fixedStopPosition "relativeLinePosition": fixedStopPosition
}) });
timeLine.sectionsList.push(sectionObject) timeline.sectionsList.push(sectionObject);
timeLine.sectionsList.sort(function (a, b) { timeline.sectionsList.sort(function (a, b) {
return a.relativeLinePosition - b.relativeLinePosition return a.relativeLinePosition - b.relativeLinePosition;
}) });
const index = timeLine.sectionsList.indexOf(sectionObject) const index = timeline.sectionsList.indexOf(sectionObject);
console.log("Addsection:", index) console.debug(timelineLogging, "Addsection:", index);
createSection(index, fixedStopPosition, sectionObject, identifier) createSection(index, fixedStopPosition, sectionObject, identifier);
updatePositions() updatePositions();
return sectionObject return sectionObject;
} }
function createSection(index, stopPosition, section, identifier) { 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)) //console.assert(isFloat(stopPosition))
let haComponent = Qt.createComponent("LineHandle.qml") let haComponent = Qt.createComponent("LineHandle.qml");
if (haComponent.status === Component.Error) { if (haComponent.status === Component.Error) {
console.assert(haComponent.errorString()) console.assert(timelineLogging, haComponent.errorString());
return return;
} }
section.lineHandle = haComponent.createObject(handleWrapper) section.lineHandle = haComponent.createObject(handleWrapper);
section.lineHandle.lineWidth = timeLine.width section.lineHandle.lineWidth = timeline.width;
section.lineHandle.x = Math.round( section.lineHandle.x = Math.round(handleWrapper.width * timeline.sectionsList[index].relativeLinePosition);
handleWrapper.width * timeLine.sectionsList[index].relativeLinePosition) section.lineHandle.y = -section.lineHandle.height / 2;
section.lineHandle.y = -section.lineHandle.height / 2
// Will be set later // Will be set later
section.lineHandle.lineMinimum = timeLine.x section.lineHandle.lineMinimum = timeline.x;
section.lineHandle.lineMaximum = timeLine.x section.lineHandle.lineMaximum = timeline.x;
section.lineHandle.handleMoved.connect(timeLine.onHandleMoved) section.lineHandle.handleMoved.connect(timeline.onHandleMoved);
let liComponent = Qt.createComponent("LineIndicator.qml") let liComponent = Qt.createComponent("LineIndicator.qml");
if (liComponent.status === Component.Error) { if (liComponent.status === Component.Error) {
console.assert(liComponent.errorString()) console.assert(timelineLogging, liComponent.errorString());
return return;
} }
// Set color initially so we do not have a weird color animation at start // Set color initially so we do not have a weird color animation at start
const lineIndicatorProperties = { const lineIndicatorProperties = {
"color": getColorAtIndex(index) "color": getColorAtIndex(index)
} };
section.lineIndicator = liComponent.createObject( section.lineIndicator = liComponent.createObject(lineIndicatorWrapper, lineIndicatorProperties);
lineIndicatorWrapper, lineIndicatorProperties) section.lineIndicator.height = lineIndicatorWrapper.height;
section.lineIndicator.height = lineIndicatorWrapper.height section.lineIndicator.index = index;
section.lineIndicator.index = index section.lineIndicator.identifier = identifier;
section.lineIndicator.identifier = identifier section.lineIndicator.color = getColorAtIndex(index);
section.lineIndicator.color = getColorAtIndex(index) section.lineIndicator.remove.connect(timeline.removeSection);
section.lineIndicator.remove.connect(timeLine.removeSection) section.lineIndicator.lineSelected.connect(timeline.lineIndicatorSelected);
section.lineIndicator.lineSelected.connect(
timeLine.lineIndicatorSelected)
} }
function sectionFromHandle(lineHandle) { function sectionFromHandle(lineHandle) {
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
if (timeLine.sectionsList[i].lineHandle === lineHandle) if (timeline.sectionsList[i].lineHandle === lineHandle)
return timeLine.sectionsList[i] return timeline.sectionsList[i];
} }
return null return null;
} }
function onHandleMoved(lineHandle) { function onHandleMoved(lineHandle) {
updatePositions() updatePositions();
const section = sectionFromHandle(lineHandle) const section = sectionFromHandle(lineHandle);
if (section === null) { if (section === null) {
print(lineHandle.linePosition) console.debug(timelineLogging, lineHandle.linePosition);
console.error("Unable to match handle to section list") console.error(timelineLogging, "Unable to match handle to section list");
return return;
} }
App.screenPlayManager.moveTimelineAt(section.index, App.screenPlayManager.moveTimelineAt(section.index, section.identifier, lineHandle.linePosition, lineHandle.timeString);
section.identifier,
lineHandle.linePosition,
lineHandle.timeString)
} }
function lineIndicatorSelected(activeTimelineIndex) { function lineIndicatorSelected(selectedTimelineIndex) {
for (var i = 0; i < timeLine.sectionsList.length; i++) { console.debug(timelineLogging, "selectedTimelineIndex:", selectedTimelineIndex, "section cout: ", timeline.sectionsList.length);
if (i === activeTimelineIndex) { for (var i = 0; i < timeline.sectionsList.length; i++) {
timeLine.sectionsList[i].lineIndicator.selected = true if (i === selectedTimelineIndex) {
continue 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 // We must update all indexes when removing/adding an element
function updateIndicatorIndexes() { function updateIndicatorIndexes() {
if (timeLine.sectionsList === null if (timeline.sectionsList === null || timeline.sectionsList === undefined)
|| timeLine.sectionsList === undefined) return;
return timeline.sectionsList.sort(function (a, b) {
timeLine.sectionsList.sort(function (a, b) { return a.relativeLinePosition - b.relativeLinePosition;
return a.relativeLinePosition - b.relativeLinePosition });
}) for (var i = 0; i < timeline.sectionsList.length; i++) {
for (var i = 0; i < timeLine.sectionsList.length; i++) { timeline.sectionsList[i].index = i;
timeLine.sectionsList[i].index = i timeline.sectionsList[i].lineIndicator.index = i;
timeLine.sectionsList[i].lineIndicator.index = i //console.debug("updateIndicatorIndexes:", timeline.sectionsList[i].index, timeline.sectionsList[i].relativeLinePosition)
//print("updateIndicatorIndexes:", timeLine.sectionsList[i].index, timeLine.sectionsList[i].relativeLinePosition)
} }
} }
function removeSection(index) { function removeSection(index) {
print(timeLine.stopPositionList) console.debug(timelineLogging, timeline.stopPositionList);
print(timeLine.sectionList) console.debug(timelineLogging, timeline.sectionList);
const isLast = index === timeLine.sectionsList.length - 1 const isLast = index === timeline.sectionsList.length - 1;
if (isLast) if (isLast)
return return;
// ORDER is important here! First destory the object // ORDER is important here! First destory the object
// and then remove i f // and then remove i f
let section = timeLine.sectionsList[index] let section = timeline.sectionsList[index];
section.lineHandle.destroy() section.lineHandle.destroy();
section.lineIndicator.destroy() section.lineIndicator.destroy();
section.destroy() section.destroy();
timeLine.sectionsList.splice(index, 1) timeline.sectionsList.splice(index, 1);
updatePositions() updatePositions();
App.screenPlayManager.removeTimelineAt(index) App.screenPlayManager.removeTimelineAt(index);
} }
function updatePositions() { function updatePositions() {
// Iterate through each handle in the 'sectionList' array // Iterate through each handle in the 'sectionList' array
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
let handle = timeLine.sectionsList[i].lineHandle let handle = timeline.sectionsList[i].lineHandle;
// Determine the minimum position for the current handle // Determine the minimum position for the current handle
let prevPos let prevPos;
if (i === 0) { if (i === 0) {
// If it's the first handle, its minimum is 0 // If it's the first handle, its minimum is 0
prevPos = 0 prevPos = 0;
} else { } else {
// Otherwise, it's directly the position of the previous handle // 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 // Determine the maximum position for the current handle
let nextPos let nextPos;
if (i === timeLine.sectionsList.length - 1) { if (i === timeline.sectionsList.length - 1) {
// If it's the last handle, its maximum is the width of the line // If it's the last handle, its maximum is the width of the line
nextPos = timeLine.width nextPos = timeline.width;
} else { } else {
// Otherwise, it's directly the position of the next handle // 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 // Set the determined minimum and maximum positions for the current handle
handle.lineMinimum = prevPos handle.lineMinimum = prevPos;
handle.lineMaximum = nextPos handle.lineMaximum = nextPos;
//timeLine.sectionsList[i].relativeLinePosition =prevPos / timeLine.width //timeline.sectionsList[i].relativeLinePosition =prevPos / timeline.width
// print("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeLine.sectionsList[i].relativeLinePosition) // console.debug("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeline.sectionsList[i].relativeLinePosition)
} }
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var j = 0; j < timeline.sectionsList.length; j++) {
let section = timeLine.sectionsList[i] let section = timeline.sectionsList[j];
section.relativeLinePosition = section.lineHandle.linePosition section.relativeLinePosition = section.lineHandle.linePosition;
// print(section.relativeLinePosition, section.lineHandle.lineMinimum, section.lineHandle.lineMaximum) // console.debug(section.relativeLinePosition, section.lineHandle.lineMinimum, section.lineHandle.lineMaximum)
} }
updateIndicatorPositions() updateIndicatorPositions();
updateLastHandle() updateLastHandle();
updateIndicatorColor() updateIndicatorColor();
updateIndicatorIndexes() updateIndicatorIndexes();
} }
function getColorAtIndex(index) { function getColorAtIndex(index) {
let i = index let i = index;
// Start from the beginnging again // Start from the beginnging again
if (index >= timeLine.lineColors.length) { if (index >= timeline.lineColors.length) {
i = index % timeLine.lineColors.length i = index % timeline.lineColors.length;
} }
return timeLine.lineColors[i] return timeline.lineColors[i];
} }
function updateIndicatorColor() { function updateIndicatorColor() {
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
let lineIndicator = timeLine.sectionsList[i].lineIndicator let lineIndicator = timeline.sectionsList[i].lineIndicator;
lineIndicator.color = getColorAtIndex(i) lineIndicator.color = getColorAtIndex(i);
} }
} }
function updateLastHandle() { function updateLastHandle() {
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
timeLine.sectionsList[i].lineHandle.isLast = i === timeLine.sectionsList.length - 1 timeline.sectionsList[i].lineHandle.isLast = i === timeline.sectionsList.length - 1;
timeLine.sectionsList[i].lineIndicator.isLast = i timeline.sectionsList[i].lineIndicator.isLast = i === timeline.sectionsList.length - 1;
=== timeLine.sectionsList.length - 1
} }
} }
function updateIndicatorPositions() { function updateIndicatorPositions() {
for (var i = 0; i < timeLine.sectionsList.length; i++) { for (var i = 0; i < timeline.sectionsList.length; i++) {
const lineIndicator = timeLine.sectionsList[i].lineIndicator const lineIndicator = timeline.sectionsList[i].lineIndicator;
//print(i, lineIndicator.x, lineIndicator.width, timeLine.sectionsList[i].relativeLinePosition) //console.debug(i, lineIndicator.x, lineIndicator.width, timeline.sectionsList[i].relativeLinePosition)
const handle = timeLine.sectionsList[i].lineHandle const handle = timeline.sectionsList[i].lineHandle;
lineIndicator.x = handle.dragHandler.xAxis.minimum lineIndicator.x = handle.dragHandler.xAxis.minimum;
lineIndicator.width = (handle.linePosition * handle.lineWidth).toFixed( lineIndicator.width = (handle.linePosition * handle.lineWidth).toFixed(2) - lineIndicator.x;
2) - lineIndicator.x
} }
} }
// https://stackoverflow.com/a/3885844 // https://stackoverflow.com/a/3885844
function isFloat(n) { function isFloat(n) {
return n === +n && n !== (n | 0) return n === +n && n !== (n | 0);
} }
Rectangle { Rectangle {
@ -332,13 +344,10 @@ Control {
color: Material.color(Material.BlueGrey) color: Material.color(Material.BlueGrey)
width: 2 width: 2
height: 30 height: 30
y: (addHandleWrapper.height - height) y: (addHandleWrapper.height - height) / 2 // Vertically center within addHandleWrapper
/ 2 // Vertically center within addHandleWrapper
property int totalSeconds: 86400 // Total seconds in a day property int totalSeconds: 86400 // Total seconds in a day
property int currentSeconds: (new Date().getHours( property int currentSeconds: 0
) * 3600) + (new Date().getMinutes(
) * 60) + new Date().getSeconds()
x: addHandleWrapper.width * (currentSeconds / totalSeconds) x: addHandleWrapper.width * (currentSeconds / totalSeconds)
@ -347,15 +356,9 @@ Control {
repeat: true repeat: true
running: true running: true
onTriggered: { onTriggered: {
currentTimeIndicator.currentSeconds currentTimeIndicator.currentSeconds = (new Date().getHours() * 3600) + (new Date().getMinutes() * 60) + new Date().getSeconds();
= (new Date().getHours( currentTimeIndicator.x = addHandleWrapper.width * (currentTimeIndicator.currentSeconds / currentTimeIndicator.totalSeconds);
) * 3600) + (new Date().getMinutes( currentTimeText.text = Qt.formatTime(new Date(), "hh:mm:ss");
) * 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 { RowLayout {
anchors.fill: parent height: 30
uniformCellSizes: true
anchors {
top: parent.top
right: parent.right
left: parent.left
leftMargin: -5
}
Repeater { Repeater {
model: 24 model: 25
Item { Item {
width: 20 width: 20
height: 60 height: 30
required property int index required property int index
Text { Text {
id: txtHours
color: "gray" color: "gray"
text: index text: index
anchors { anchors {
@ -390,9 +401,9 @@ Control {
Rectangle { Rectangle {
color: "gray" color: "gray"
width: 1 width: 1
height: 5 height: 10
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: txtHours.horizontalCenter
bottom: parent.bottom bottom: parent.bottom
} }
} }
@ -403,15 +414,18 @@ Control {
ToolButton { ToolButton {
text: "" text: ""
onClicked: { onClicked: {
const p = this.x / timeLine.width if (App.globalVariables.isBasicVersion()) {
const position = p.toFixed(4) screenPlayProView.open();
const identifier = App.util.generateRandomString(4) return;
const sectionObject = timeLine.addSection(identifier, }
position) const p = this.x / timeline.width;
App.screenPlayManager.addTimelineAt( const position = p.toFixed(4);
sectionObject.index, const identifier = App.util.generateRandomString(4);
sectionObject.relativeLinePosition, const sectionObject = timeline.addSection(identifier, position);
sectionObject.identifier) App.screenPlayManager.addTimelineAt(sectionObject.index, sectionObject.relativeLinePosition, sectionObject.identifier);
}
ScreenPlayProPopup {
id: screenPlayProView
} }
x: hoverHandler.point.position.x - width * .5 x: hoverHandler.point.position.x - width * .5
@ -436,23 +450,49 @@ Control {
top: addHandleWrapper.bottom top: addHandleWrapper.bottom
} }
} }
Item { Rectangle {
height: 18 height: 18
width: 5 color: "#757575"
width: 2
anchors { anchors {
right: parent.left right: parent.left
top: addHandleWrapper.bottom verticalCenter: lineIndicatorWrapper.verticalCenter
topMargin: -9
} }
} }
Rectangle { Rectangle {
height: 18 height: 18
width: 5 width: 2
color: "#757575" color: "#757575"
anchors { anchors {
right: parent.right right: parent.right
top: addHandleWrapper.bottom verticalCenter: lineIndicatorWrapper.verticalCenter
topMargin: -9 }
}
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
} }
} }
} }

View File

@ -19,6 +19,7 @@ Util.Popup {
height: 768 height: 768
onOpened: { onOpened: {
monitorSelection.selectMonitorAt(0); monitorSelection.selectMonitorAt(0);
timeline.reset();
} }
background: Rectangle { background: Rectangle {
anchors.fill: parent anchors.fill: parent
@ -72,6 +73,7 @@ Util.Popup {
} }
Timeline { Timeline {
id: timeline
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
} }
@ -123,8 +125,6 @@ Util.Popup {
width: parent.width * 0.9 width: parent.width * 0.9
multipleMonitorsSelectable: false multipleMonitorsSelectable: false
monitorWithoutContentSelectable: false monitorWithoutContentSelectable: false
availableWidth: width - 20
availableHeight: 150
onRequestProjectSettings: function (index, installedType, appID) { onRequestProjectSettings: function (index, installedType, appID) {
if (installedType === Util.ContentTypes.InstalledType.VideoWallpaper) { if (installedType === Util.ContentTypes.InstalledType.VideoWallpaper) {
videoControlWrapper.state = "visible"; videoControlWrapper.state = "visible";

View File

@ -13,7 +13,6 @@ Rectangle {
property bool monitorWithoutContentSelectable: true property bool monitorWithoutContentSelectable: true
property bool multipleMonitorsSelectable: false property bool multipleMonitorsSelectable: false
property bool isSelected: false property bool isSelected: false
// We preselect the main monitor
property var activeMonitors: [] property var activeMonitors: []
property alias background: root.color property alias background: root.color
property alias bgRadius: root.radius property alias bgRadius: root.radius
@ -22,6 +21,11 @@ Rectangle {
resize(); resize();
selectOnly(0); selectOnly(0);
} }
LoggingCategory {
id: logger
name: "MonitorSelection"
defaultLogLevel: LoggingCategory.Debug
}
signal requestProjectSettings(var index, var installedType, var appID) signal requestProjectSettings(var index, var installedType, var appID)
@ -50,8 +54,6 @@ Rectangle {
if (rp.itemAt(i).isSelected) if (rp.itemAt(i).isSelected)
root.activeMonitors.push(rp.itemAt(i).index); 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.activeMonitorsChanged();
root.isSelected = root.activeMonitors.length > 0; root.isSelected = root.activeMonitors.length > 0;
return root.activeMonitors; return root.activeMonitors;
@ -68,42 +70,45 @@ Rectangle {
} }
function resize() { function resize() {
print("resize"); console.debug(logger, "MonitorSelection resize started");
var absoluteDesktopSize = App.monitorListModel.absoluteDesktopSize();
var isWidthGreaterThanHeight = false; // 1. Get the total desktop size
var windowsDelta = 0; let totalDesktopSize = App.monitorListModel.totalDesktopSize();
if (absoluteDesktopSize.width < absoluteDesktopSize.height) { console.debug(logger, "Total desktop size:", totalDesktopSize.width, "x", totalDesktopSize.height);
windowsDelta = absoluteDesktopSize.width / absoluteDesktopSize.height;
isWidthGreaterThanHeight = false; // 2. Get root item dimensions
} else { let rootWidth = root.width;
windowsDelta = absoluteDesktopSize.height / absoluteDesktopSize.width; let rootHeight = root.height;
isWidthGreaterThanHeight = true; console.debug(logger, "Root dimensions:", rootWidth, "x", rootHeight);
}
if (rp.count === 1) // 3. Calculate scaling factor
availableWidth = availableWidth * 0.66; let margin = 10;
var dynamicHeight = availableWidth * windowsDelta; let availableWidth = rootWidth - 2 * margin;
var dynamicWidth = availableHeight * windowsDelta; let availableHeight = rootHeight - 2 * margin;
// Delta (height/width) let scaleX = availableWidth / totalDesktopSize.width;
var monitorHeightRationDelta = 0; let scaleY = availableHeight / totalDesktopSize.height;
var monitorWidthRationDelta = 0; let scaleFactor = Math.min(scaleX, scaleY, 1);
if (isWidthGreaterThanHeight) {
monitorHeightRationDelta = dynamicHeight / absoluteDesktopSize.height; // Ensure we don't scale up
monitorWidthRationDelta = availableWidth / absoluteDesktopSize.width; console.debug(logger, "Scale factor:", scaleFactor);
} else {
monitorHeightRationDelta = availableHeight / absoluteDesktopSize.height; // 4. Resize and position repeater items
monitorWidthRationDelta = dynamicWidth / absoluteDesktopSize.width; let scaledWidth = totalDesktopSize.width * scaleFactor;
} let scaledHeight = totalDesktopSize.height * scaleFactor;
for (var i = 0; i < rp.count; i++) { for (var i = 0; i < rp.count; i++) {
rp.itemAt(i).index = i; let item = rp.itemAt(i);
rp.itemAt(i).height = rp.itemAt(i).height * monitorHeightRationDelta; if (item) {
rp.itemAt(i).width = rp.itemAt(i).width * monitorWidthRationDelta; item.width = item.geometry.width * scaleFactor;
rp.itemAt(i).x = rp.itemAt(i).x * monitorWidthRationDelta; item.height = item.geometry.height * scaleFactor;
rp.itemAt(i).y = rp.itemAt(i).y * monitorHeightRationDelta; item.x = item.geometry.x * scaleFactor;
rp.contentWidth += rp.itemAt(i).width; item.y = item.geometry.y * scaleFactor;
rp.contentHeight += rp.itemAt(i).height; }
} }
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) color: Material.theme === Material.Light ? Material.background : Qt.darker(Material.background)
@ -148,18 +153,11 @@ Rectangle {
onMonitorSelected: function (index) { onMonitorSelected: function (index) {
root.selectMonitorAt(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 { ToolButton {

View File

@ -26,6 +26,7 @@ Item {
property bool isSelected: false property bool isSelected: false
signal monitorSelected(var index) signal monitorSelected(var index)
signal remoteWallpaper(var index)
onIsSelectedChanged: root.state = isSelected ? "selected" : "default" onIsSelectedChanged: root.state = isSelected ? "selected" : "default"
onPreviewImageChanged: { onPreviewImageChanged: {
@ -81,11 +82,23 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (monitorWithoutContentSelectable) { if (monitorWithoutContentSelectable) {
monitorSelected(index); root.monitorSelected(index);
return; return;
} }
if (root.hasContent && !root.monitorWithoutContentSelectable) 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
} }
} }
} }

View File

@ -18,6 +18,9 @@ Drawer {
background: Rectangle { background: Rectangle {
color: Material.color(Material.Grey, Material.Shade900) color: Material.color(Material.Grey, Material.Shade900)
} }
onOpened: {
timeline.reset();
}
property bool hasPreviewGif: false property bool hasPreviewGif: false
property var type: ContentTypes.InstalledType.QMLWallpaper property var type: ContentTypes.InstalledType.QMLWallpaper
@ -25,27 +28,29 @@ Drawer {
function setInstalledDrawerItem(folderName, type) { function setInstalledDrawerItem(folderName, type) {
// Toggle sidebar if clicked on the same content twice // Toggle drawer if clicked on the same content twice
if (root.contentFolderName === folderName) if (root.contentFolderName === folderName) {
return; if (root.visible) {
root.close();
return;
}
}
if (!App.util.isWallpaper(type)) { if (!App.util.isWallpaper(type)) {
return; return;
} }
print("setInstalledDrawerItem", root.contentFolderName, folderName, typeof (folderName), type);
root.contentFolderName = folderName; root.contentFolderName = folderName;
root.type = type; root.type = type;
print("setInstalledDrawerItem", folderName,typeof(folderName), type);
if (type === ContentTypes.InstalledType.VideoWallpaper) if (type === ContentTypes.InstalledType.VideoWallpaper)
installedDrawerWrapper.state = "wallpaper"; installedDrawerWrapper.state = "wallpaper";
else else
installedDrawerWrapper.state = "scene"; installedDrawerWrapper.state = "scene";
root.open(); root.open();
} }
// This is used for removing wallpaper. We need to clear // This is used for removing wallpaper. We need to clear
// the preview image/gif so we can release the file for deletion. // the preview image/gif so we can release the file for deletion.
function clear() { function clear() {
root.close(); root.close();
root.contentFolderName = ""; root.contentFolderName = "";
root.type = ContentTypes.InstalledType.Unknown; root.type = ContentTypes.InstalledType.Unknown;
@ -56,11 +61,10 @@ Drawer {
} }
onContentFolderNameChanged: { onContentFolderNameChanged: {
if (root.contentFolderName === ""){ if (root.contentFolderName === "") {
console.error("empty folder name") console.error("empty folder name");
return return;
} }
const item = App.installedListModel.get(root.contentFolderName); const item = App.installedListModel.get(root.contentFolderName);
print(root.contentFolderName); print(root.contentFolderName);
txtHeadline.text = item.m_title; txtHeadline.text = item.m_title;
@ -108,22 +112,6 @@ Drawer {
Layout.topMargin: 50 Layout.topMargin: 50
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: 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 { MonitorSelection {
id: monitorSelection id: monitorSelection
objectName: "monitorSelection" objectName: "monitorSelection"
height: 180
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
availableWidth: width
availableHeight: height - 20
fontSize: 11 fontSize: 11
} }
} }
@ -249,8 +234,8 @@ Drawer {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
objectName: "btnLaunchContent" objectName: "btnLaunchContent"
text: qsTr("Set Wallpaper"); text: qsTr("Set Wallpaper")
// enabled: App.util.isWidget(root.type) && activeMonitors.length > 0 ? true : monitorSelection.isSelected enabled: monitorSelection.isSelected && timeline.selectedTimelineIndex > -1
icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_plus.svg" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_plus.svg"
icon.color: "white" icon.color: "white"
font.pointSize: 12 font.pointSize: 12
@ -281,9 +266,18 @@ Drawer {
root.close(); root.close();
return; return;
} }
const activeTimeline = timeline.getActiveTimeline(); const selectedTimeline = timeline.selectedTimeline;
if (selectedTimeline === undefined) {
console.error("No active timeline");
return;
}
const file = item.m_file; 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(); root.close();
monitorSelection.reset(); monitorSelection.reset();

View File

@ -9,6 +9,7 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <QDebug> #include <QDebug>
#include <QRandomGenerator>
namespace ScreenPlay { namespace ScreenPlay {
@ -33,11 +34,33 @@ namespace ScreenPlay {
MonitorListModel::MonitorListModel(QObject* parent) MonitorListModel::MonitorListModel(QObject* parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{ {
loadMonitors();
auto* guiAppInst = dynamic_cast<QGuiApplication*>(QGuiApplication::instance()); auto* guiAppInst = dynamic_cast<QGuiApplication*>(QGuiApplication::instance());
connect(guiAppInst, &QGuiApplication::screenAdded, this, &MonitorListModel::screenAdded); connect(guiAppInst, &QGuiApplication::screenAdded, this, &MonitorListModel::screenAdded);
connect(guiAppInst, &QGuiApplication::screenRemoved, this, &MonitorListModel::screenRemoved); 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(); return QVariant();
} }
auto roleEnum = static_cast<MonitorRole>(role); auto roleEnum = static_cast<MonitorRole>(role);
if (row > rowCount()) if (row > rowCount())
return {}; return QVariant();
switch (roleEnum) { switch (roleEnum) {
case MonitorRole::AppID: case MonitorRole::AppID:
return 1; return m_monitorList.at(row).m_appID;
// if (m_monitorList.at(row).m_activeWallpaper) {
// return m_monitorList.at(row).m_activeWallpaper->appID();
// } else {
// return QVariant("");
// }
case MonitorRole::Index: case MonitorRole::Index:
return m_monitorList.at(row).m_index; return m_monitorList.at(row).m_index;
case MonitorRole::Geometry: case MonitorRole::Geometry:
return m_monitorList.at(row).m_geometry; return m_monitorList.at(row).m_geometry;
case MonitorRole::InstalledType: case MonitorRole::InstalledType:
// if (m_monitorList.at(row).m_activeWallpaper) { return QVariant::fromValue(m_monitorList.at(row).m_installedType);
// return static_cast<int>(m_monitorList.at(row).m_activeWallpaper->type());
// } else {
return { "" };
// }
case MonitorRole::PreviewImage: case MonitorRole::PreviewImage:
// if (m_monitorList.at(row).m_activeWallpaper) { return m_monitorList.at(row).m_wallpaperPreviewImage;
// QString absolutePath = m_monitorList.at(row).m_activeWallpaper->absolutePath(); }
// return absolutePath + "/" + m_monitorList.at(row).m_activeWallpaper->previewImage();
// } else {
return QVariant("");
}
return QVariant(); return QVariant();
} }
@ -121,9 +127,27 @@ QVariant MonitorListModel::data(const QModelIndex& index, int role) const
*/ */
void MonitorListModel::loadMonitors() 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 #ifdef Q_OS_WIN
QModelIndex index;
auto monitors = WindowsIntegration().getAllMonitors(); auto monitors = WindowsIntegration().getAllMonitors();
// This offset lets us center the monitor selection view in the center // This offset lets us center the monitor selection view in the center
@ -151,13 +175,12 @@ void MonitorListModel::loadMonitors()
y + offsetY, y + offsetY,
width, width,
height); height);
beginInsertRows(index, m_monitorList.size(), m_monitorList.size()); beginInsertRows(index(rowCount()), m_monitorList.size(), m_monitorList.size());
m_monitorList.append(Monitor { i, geometry }); m_monitorList.append(Monitor { i, geometry });
endInsertRows(); endInsertRows();
i++; i++;
} }
#else #else
QModelIndex index;
int offsetX = 0; int offsetX = 0;
int offsetY = 0; int offsetY = 0;
@ -178,7 +201,7 @@ void MonitorListModel::loadMonitors()
if (screen->geometry().width() == 0 || screen->geometry().height() == 0) if (screen->geometry().width() == 0 || screen->geometry().height() == 0)
continue; 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() }); m_monitorList.append(Monitor { i, screen->geometry() });
endInsertRows(); endInsertRows();
} }
@ -191,31 +214,22 @@ void MonitorListModel::loadMonitors()
* \brief MonitorListModel::getAbsoluteDesktopSize * \brief MonitorListModel::getAbsoluteDesktopSize
* \return * \return
*/ */
QRect MonitorListModel::absoluteDesktopSize() const QSize MonitorListModel::totalDesktopSize() const
{ {
auto* guiAppInst = dynamic_cast<QGuiApplication*>(QGuiApplication::instance()); QRect totalRect;
return guiAppInst->screens().at(0)->availableVirtualGeometry(); for (const auto& monitor : m_monitorList) {
totalRect = totalRect.united(monitor.m_geometry);
}
return totalRect.size();
} }
/*! QRect MonitorListModel::absoluteDesktopSize() const
\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<WallpaperTimelineSection>& timelineSection, const QVector<int> monitors)
{ {
m_activeTimelineSection = timelineSection; QRect totalRect;
for (const int monitor : monitors) { for (const auto& monitor : m_monitorList) {
// m_monitorList[monitor].m_activeWallpaper = wallpaper; totalRect = totalRect.united(monitor.m_geometry);
emit dataChanged(
index(monitor, 0),
index(monitor, 0),
QVector<int> {
static_cast<int>(MonitorRole::PreviewImage),
static_cast<int>(MonitorRole::InstalledType),
static_cast<int>(MonitorRole::AppID) });
} }
return totalRect;
} }
/*! /*!
@ -243,6 +257,22 @@ void MonitorListModel::reset()
loadMonitors(); loadMonitors();
} }
bool MonitorListModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (role == static_cast<int>(MonitorRole::PreviewImage)) {
m_monitorList[index.column()].m_wallpaperPreviewImage = value.toString();
emit dataChanged(index, index, { role });
}
if (role == static_cast<int>(MonitorRole::AppID)) {
m_monitorList[index.column()].m_appID = value.toString();
emit dataChanged(index, index, { role });
}
if (role == static_cast<int>(MonitorRole::InstalledType)) {
m_monitorList[index.column()].m_installedType = static_cast<ContentTypes::InstalledType>(value.toInt());
emit dataChanged(index, index, { role });
}
return true;
}
} }
#include "moc_monitorlistmodel.cpp" #include "moc_monitorlistmodel.cpp"

View File

@ -26,6 +26,7 @@ ScreenPlayManager::ScreenPlayManager(
m_server = std::make_unique<QLocalServer>(); m_server = std::make_unique<QLocalServer>();
QObject::connect(m_server.get(), &QLocalServer::newConnection, this, &ScreenPlayManager::newConnection); QObject::connect(m_server.get(), &QLocalServer::newConnection, this, &ScreenPlayManager::newConnection);
QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::requestSaveProfiles, this, &ScreenPlayManager::requestSaveProfiles); QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::requestSaveProfiles, this, &ScreenPlayManager::requestSaveProfiles);
QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::activeWallpaperCountChanged, this, &ScreenPlayManager::setActiveWallpaperCounter);
m_server->setSocketOptions(QLocalServer::WorldAccessOption); m_server->setSocketOptions(QLocalServer::WorldAccessOption);
if (!m_server->listen("ScreenPlay")) { if (!m_server->listen("ScreenPlay")) {
qCritical("Could not open Local Socket with the name 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.setGlobalVariables(m_globalVariables);
m_screenPlayTimelineManager.setSettings(m_settings); m_screenPlayTimelineManager.setSettings(m_settings);
m_screenPlayTimelineManager.setMonitorListModel(m_monitorListModel);
// Reset to default settings if we are unable to load // Reset to default settings if we are unable to load
// the existing one // the existing one
@ -68,7 +70,7 @@ void ScreenPlayManager::init(
/*! /*!
\brief Sets the wallpaper at a spesific timeline. \brief Sets the wallpaper at a spesific timeline.
*/ */
bool ScreenPlayManager::setWallpaperAtTimelineIndex( QCoro::QmlTask ScreenPlayManager::setWallpaperAtTimelineIndex(
const ScreenPlay::ContentTypes::InstalledType type, const ScreenPlay::ContentTypes::InstalledType type,
const QString& absolutePath, const QString& absolutePath,
const QString& previewImage, const QString& previewImage,
@ -85,20 +87,28 @@ bool ScreenPlayManager::setWallpaperAtTimelineIndex(
wallpaperData.file = file; wallpaperData.file = file;
wallpaperData.monitors = monitorIndex; wallpaperData.monitors = monitorIndex;
wallpaperData.fillMode = m_settings->videoFillMode(); wallpaperData.fillMode = m_settings->videoFillMode();
const bool success = m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier);
if (!success) { return QCoro::QmlTask([this, wallpaperData, timelineIndex, identifier]() -> QCoro::Task<Result> {
qCritical() << "Invalid timeline index or identifier: " << timelineIndex << identifier; if (timelineIndex < 0 || identifier.isEmpty()) {
m_screenPlayTimelineManager.printTimelines();
emit printQmlTimeline();
return false;
}
// We do not start the wallpaper here, but let co_return Result { false };
// ScreenPlayTimelineManager::checkActiveWallpaperTimeline decide }
// if the wallpaper
emit requestSaveProfiles(); const bool success = co_await m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier);
return true;
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; return true;
} }
void ScreenPlayManager::setSelectedTimelineIndex(const int selectedTimelineIndex)
{
m_screenPlayTimelineManager.updateMonitorListModelData(selectedTimelineIndex);
}
/*! /*!
\brief Removes all wallpaper entries in the profiles.json. \brief Removes all wallpaper entries in the profiles.json.
*/ */
bool ScreenPlayManager::removeAllWallpapers(bool saveToProfile) QCoro::QmlTask ScreenPlayManager::removeAllWallpapers(bool saveToProfile)
{ {
// TODO return QCoro::QmlTask([this]() -> QCoro::Task<Result> {
// if (m_screenPlayTimelineManager.m_wallpaperTimelineSectionsList.empty()) { // call with coro
// return false; const bool success = co_await m_screenPlayTimelineManager.removeAllWallpaperFromActiveTimlineSections();
// } qDebug() << "Task: removeAllWallpaperFromActiveTimlineSections" << success;
// emit requestSaveProfiles();
// QStringList appIDs; co_return Result { success };
// 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;
} }
/*! /*!
@ -212,18 +208,16 @@ bool ScreenPlayManager::removeAllWidgets(bool saveToProfile)
given monitor index and then closes the sdk connection, removes the entries in the 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. 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)) { return QCoro::QmlTask([this, timelineIndex, timelineIdentifier, monitorIndex]() -> QCoro::Task<Result> {
if (removeWallpaper(*appID)) { // call with coro
emit requestSaveProfiles(); const bool success = co_await m_screenPlayTimelineManager.removeWallpaperAt(timelineIndex, timelineIdentifier, monitorIndex);
return true; qDebug() << "Task: removeAllWallpaperFromActiveTimlineSections" << success;
} // crash? mit requestSaveProfiles();
} co_return Result { success };
}());
qWarning() << "Could not remove Wallpaper at index:" << index;
return false;
} }
/*! /*!
@ -531,6 +525,16 @@ bool ScreenPlayManager::setWallpaperValue(const QString& appID, const QString& k
return false; return false;
} }
int ScreenPlayManager::activeTimelineIndex()
{
std::shared_ptr<WallpaperTimelineSection> 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 \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. 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(); QJsonObject wallpaperObj = timelineWallpaper.toObject();
if (!m_screenPlayTimelineManager.addTimelineFromSettings(wallpaperObj)) { if (!m_screenPlayTimelineManager.addTimelineFromSettings(wallpaperObj)) {
qCritical() << "Unable to add wallpaper timeline"; qCritical() << "Unable to add wallpaper timeline";
containsInvalidData = true;
continue; continue;
} }
} }
@ -618,10 +623,11 @@ bool ScreenPlayManager::loadProfiles()
// The can happen if the user unpluggs a wallpaper but it still exists // 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 // in the profiles.json. For this we save all profiles with now active
// content. // content.
if (containsInvalidData) if (containsInvalidData) {
saveProfiles(); saveProfiles();
} else {
m_screenPlayTimelineManager.startupFirstTimeline(); m_screenPlayTimelineManager.startup();
}
return true; return true;
} }

View File

@ -6,6 +6,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <iostream> #include <iostream>
#include <ranges>
namespace ScreenPlay { namespace ScreenPlay {
@ -24,7 +25,7 @@ ScreenPlayTimelineManager::ScreenPlayTimelineManager(
// Do not start the timer here. This will be done after // Do not start the timer here. This will be done after
// we have loaded all timeline wallpaper from the config.json // we have loaded all timeline wallpaper from the config.json
QObject::connect(&m_contentTimer, &QTimer::timeout, this, &ScreenPlayTimelineManager::checkActiveWallpaperTimeline); 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 // TODO check license
auto timelineSection = std::make_shared<WallpaperTimelineSection>(); auto newTimelineSection = std::make_shared<WallpaperTimelineSection>();
QObject::connect(timelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles);
timelineSection->startTime = startTime; QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::activeWallpaperCountChanged, this, &ScreenPlayTimelineManager::activeWallpaperCountChanged);
timelineSection->endTime = endTime; newTimelineSection->startTime = startTime;
timelineSection->settings = m_settings; newTimelineSection->endTime = endTime;
timelineSection->globalVariables = m_globalVariables; newTimelineSection->settings = m_settings;
timelineSection->relativePosition = Util().calculateRelativePosition(endTime); newTimelineSection->globalVariables = m_globalVariables;
newTimelineSection->relativePosition = Util().calculateRelativePosition(endTime);
const auto wallpaperList = timelineObj.value("wallpaper").toArray(); const auto wallpaperList = timelineObj.value("wallpaper").toArray();
for (auto& wallpaper : wallpaperList) { for (auto& wallpaper : wallpaperList) {
std::optional<WallpaperData> wallpaperDataOpt = WallpaperData::loadWallpaperConfig(wallpaper.toObject()); std::optional<WallpaperData> wallpaperDataOpt = WallpaperData::loadWallpaperConfig(wallpaper.toObject());
if (!wallpaperDataOpt.has_value()) if (!wallpaperDataOpt.has_value())
return false; return false;
timelineSection->wallpaperDataList.push_back(wallpaperDataOpt.value()); newTimelineSection->wallpaperDataList.push_back(wallpaperDataOpt.value());
} }
// Todo: Should we use addTimelineAt? // Todo: Should we use addTimelineAt?
timelineSection->index = m_wallpaperTimelineSectionsList.length(); newTimelineSection->index = m_wallpaperTimelineSectionsList.length();
timelineSection->identifier = Util().generateRandomString(4); newTimelineSection->identifier = Util().generateRandomString(4);
qInfo() << timelineSection->index qInfo() << newTimelineSection->index
<< timelineSection->startTime << newTimelineSection->startTime
<< timelineSection->endTime; << newTimelineSection->endTime;
// Todo: Should we use addTimelineAt? // Todo: Should we use addTimelineAt?
m_wallpaperTimelineSectionsList.append(timelineSection); m_wallpaperTimelineSectionsList.append(newTimelineSection);
return false; return true;
} }
/*! /*!
@ -181,12 +183,26 @@ void ScreenPlayTimelineManager::checkActiveWallpaperTimeline()
} }
} }
std::optional<std::shared_ptr<WallpaperTimelineSection>> 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>& settings) void ScreenPlayTimelineManager::setSettings(const std::shared_ptr<Settings>& settings)
{ {
m_settings = settings; m_settings = settings;
} }
void ScreenPlayTimelineManager::startupFirstTimeline() void ScreenPlayTimelineManager::startup()
{ {
std::shared_ptr<WallpaperTimelineSection> currentTimeline = findTimelineForCurrentTime(); std::shared_ptr<WallpaperTimelineSection> currentTimeline = findTimelineForCurrentTime();
if (!currentTimeline) { if (!currentTimeline) {
@ -194,6 +210,31 @@ void ScreenPlayTimelineManager::startupFirstTimeline()
return; return;
} }
currentTimeline->activateTimeline(); currentTimeline->activateTimeline();
m_contentTimer.start();
}
void ScreenPlayTimelineManager::setMonitorListModel(const std::shared_ptr<MonitorListModel>& 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>& globalVariables) void ScreenPlayTimelineManager::setGlobalVariables(const std::shared_ptr<GlobalVariables>& globalVariables)
@ -281,6 +322,7 @@ bool ScreenPlayTimelineManager::addTimelineAt(const int index, const float relti
auto newTimelineSection = std::make_shared<WallpaperTimelineSection>(); auto newTimelineSection = std::make_shared<WallpaperTimelineSection>();
QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles); QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles);
QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::activeWallpaperCountChanged, this, &ScreenPlayTimelineManager::activeWallpaperCountChanged);
newTimelineSection->settings = m_settings; newTimelineSection->settings = m_settings;
newTimelineSection->globalVariables = m_globalVariables; newTimelineSection->globalVariables = m_globalVariables;
newTimelineSection->index = index; newTimelineSection->index = index;
@ -332,6 +374,39 @@ QCoro::Task<bool> ScreenPlayTimelineManager::removeAllTimlineSections()
co_return true; co_return true;
} }
/*!
\brief Qml function that removes all wallpaper for the current Timeline section.
*/
QCoro::Task<bool> 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<bool> 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 \brief Removes a timeline at a given index. Expands the timeline next to it
to fill the space. to fill the space.
@ -405,7 +480,10 @@ void ScreenPlayTimelineManager::printTimelines()
} }
} }
bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpaperData, const int timelineIndex, const QString& identifier) QCoro::Task<bool> ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(
WallpaperData wallpaperData,
const int timelineIndex,
const QString& identifier)
{ {
bool found = false; bool found = false;
for (auto& timelineSection : m_wallpaperTimelineSectionsList) { for (auto& timelineSection : m_wallpaperTimelineSectionsList) {
@ -426,6 +504,9 @@ bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpa
if (it != timelineSection->wallpaperDataList.end()) { if (it != timelineSection->wallpaperDataList.end()) {
// Overwrite the existing wallpaper // Overwrite the existing wallpaper
*it = wallpaperData; *it = wallpaperData;
// TODO: Do not replace but
co_await timelineSection->deactivateTimeline();
timelineSection->activateTimeline();
} else { } else {
// Append the new wallpaper data // Append the new wallpaper data
timelineSection->wallpaperDataList.push_back(wallpaperData); timelineSection->wallpaperDataList.push_back(wallpaperData);
@ -436,7 +517,7 @@ bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpa
break; break;
} }
} }
return found; co_return found;
} }
QJsonArray ScreenPlayTimelineManager::initialSectionsList() QJsonArray ScreenPlayTimelineManager::initialSectionsList()

View File

@ -71,22 +71,6 @@ bool WallpaperTimelineSection::activateTimeline()
const QString appID = Util().generateRandomString(); const QString appID = Util().generateRandomString();
qInfo() << "Start wallpaper" << wallpaperData.absolutePath << appID; 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<ScreenPlayWallpaper>( auto screenPlayWallpaper = std::make_shared<ScreenPlayWallpaper>(
globalVariables, globalVariables,
appID, appID,
@ -100,6 +84,7 @@ bool WallpaperTimelineSection::activateTimeline()
QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestClose, this, []() { QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestClose, this, []() {
// , &ScreenPlayManager::removeWallpaper); // , &ScreenPlayManager::removeWallpaper);
}); });
QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::isConnectedChanged, this, &WallpaperTimelineSection::updateActiveWallpaperCounter);
// QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::error, this, this, []() { // QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::error, this, this, []() {
// // , &ScreenPlayManager::displayErrorPopup); // // , &ScreenPlayManager::displayErrorPopup);
@ -144,4 +129,44 @@ QCoro::Task<bool> WallpaperTimelineSection::deactivateTimeline()
co_return false; co_return false;
} }
QCoro::Task<bool> 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<std::shared_ptr<ScreenPlayWallpaper>> WallpaperTimelineSection::wallpaperByMonitorIndex(const int monitorIndex)
{
for (const auto& screenPlayWallpaper : activeWallpaperList) {
if (screenPlayWallpaper->monitors().contains(monitorIndex))
return screenPlayWallpaper;
}
return std::nullopt;
}
} }

View File

@ -1,11 +1,11 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls as QQC
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import QtQuick.Controls.Material import QtQuick.Controls.Material
import QtQuick.Layouts import QtQuick.Layouts
import ScreenPlayUtil import ScreenPlayUtil
Popup { QQC.Popup {
id: root id: root
property Item modalSource property Item modalSource
// Workaround for missing animation on hide // Workaround for missing animation on hide

View File

@ -1,9 +1,9 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only // SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlayUtil/util.h" #include "ScreenPlayUtil/util.h"
#include "qguiapplication.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QFile> #include <QFile>
#include <QGuiApplication>
#include <QJsonParseError> #include <QJsonParseError>
#include <QRandomGenerator> #include <QRandomGenerator>

View File

@ -58,11 +58,10 @@ Rectangle {
return; return;
} }
// For example if background is a solid color // For example if background is a solid color
if(Wallpaper.windowsDesktopProperties.wallpaperPath === ""){ if (Wallpaper.windowsDesktopProperties.wallpaperPath === "") {
root.canFadeByWallpaperFillMode = false; root.canFadeByWallpaperFillMode = false;
return; return;
} }
imgCover.source = Qt.resolvedUrl("file:///" + Wallpaper.windowsDesktopProperties.wallpaperPath); imgCover.source = Qt.resolvedUrl("file:///" + Wallpaper.windowsDesktopProperties.wallpaperPath);
switch (Wallpaper.windowsDesktopProperties.wallpaperStyle) { switch (Wallpaper.windowsDesktopProperties.wallpaperStyle) {
case 10: case 10:

View File

@ -356,7 +356,6 @@ protected:
QVector<int> m_activeScreensList; QVector<int> m_activeScreensList;
QFileSystemWatcher m_fileSystemWatcher; QFileSystemWatcher m_fileSystemWatcher;
QTimer m_liveReloadLimiter; QTimer m_liveReloadLimiter;
QSysInfo m_sysinfo;
std::unique_ptr<ScreenPlaySDK> m_sdk; std::unique_ptr<ScreenPlaySDK> m_sdk;
QUrl m_projectSourceFileAbsolute; QUrl m_projectSourceFileAbsolute;
ScreenPlay::Video::VideoCodec m_videoCodec = ScreenPlay::Video::VideoCodec::Unknown; ScreenPlay::Video::VideoCodec m_videoCodec = ScreenPlay::Video::VideoCodec::Unknown;