1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-09-14 22:42:34 +02:00

WIP: Move timeline related stuff into ScreenPlayTimeline

This commit is contained in:
Elias Steurer 2024-07-14 15:43:50 +02:00
parent 7831290365
commit 142b58f308
38 changed files with 1271 additions and 1012 deletions

View File

@ -12,7 +12,7 @@ set(SOURCES
# cmake-format: sort
src/wallpaperdata.cpp
src/wallpapertimelinesection.cpp
src/screenplaytimeline.cpp
src/screenplaytimelinemanager.cpp
src/app.cpp
src/applicationengine.cpp
src/create.cpp
@ -34,7 +34,7 @@ set(HEADER
# cmake-format: sort
inc/public/ScreenPlay/wallpaperdata.h
inc/public/ScreenPlay/wallpapertimelinesection.h
inc/public/ScreenPlay/screenplaytimeline.h
inc/public/ScreenPlay/screenplaytimelinemanager.h
inc/public/ScreenPlay/app.h
inc/public/ScreenPlay/applicationengine.h
inc/public/ScreenPlay/create.h

View File

@ -2,31 +2,16 @@
#pragma once
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFuture>
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QObject>
#include <QProcess>
#include <QQmlEngine>
#include <QScopedPointer>
#include <QString>
#include <QStringList>
#include <QTime>
#include <QTimer>
#include <QUrl>
#include <QtMath>
#include <memory>
#include "ScreenPlay/createimportstates.h"
#include "ScreenPlay/createimportvideo.h"
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/contenttypes.h"
namespace ScreenPlay {
@ -40,9 +25,9 @@ class Create : public QObject {
Q_PROPERTY(QString ffmpegOutput READ ffmpegOutput WRITE appendFfmpegOutput NOTIFY ffmpegOutputChanged)
public:
explicit Create(const std::shared_ptr<GlobalVariables>& globalVariables);
explicit Create(const std::shared_ptr<GlobalVariables>& globalVariables, QObject* parent = nullptr);
Create();
Create(QObject* parent = nullptr);
Q_INVOKABLE void cancel();

View File

@ -2,14 +2,9 @@
#pragma once
#include <QMetaType>
#include <QObject>
#include <QQmlEngine>
#include <QStandardPaths>
#include <QUrl>
#include <QVersionNumber>
#include <QQmlEngine>
namespace ScreenPlay {

View File

@ -2,11 +2,9 @@
#pragma once
#include <QRegularExpression>
#include <QSortFilterProxyModel>
#include <memory>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/installedlistmodel.h"
namespace ScreenPlay {

View File

@ -4,30 +4,18 @@
#include <QAbstractListModel>
#include <QByteArray>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileSystemWatcher>
#include <QFuture>
#include <QFutureWatcher>
#include <QHash>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>
#include <QString>
#include <QUrl>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
#include <memory>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/settings.h"
#include "ScreenPlayUtil/projectfile.h"
#include <memory>
namespace ScreenPlay {
class InstalledListModel : public QAbstractListModel {
@ -67,9 +55,9 @@ public:
{
return m_count;
}
Q_INVOKABLE QVariantMap get(const QString& folderName) const;
public slots:
QVariantMap get(const QString& folderName) const;
void loadInstalledContent();
void append(const QString& projectJsonFilePath);

View File

@ -4,22 +4,16 @@
#include <QAbstractListModel>
#include <QDebug>
#include <QRect>
#include <QScreen>
#include <QSize>
#include <QString>
#include <QVector>
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlay/wallpapertimelinesection.h"
#ifdef Q_OS_WIN
#include <qt_windows.h>
#endif
#include <memory>
#include <optional>
#include "ScreenPlay/wallpapertimelinesection.h"
namespace ScreenPlay {
struct Monitor {

View File

@ -3,19 +3,13 @@
#pragma once
#include <QAbstractListModel>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QStandardPaths>
#include <QString>
#include <QUrl>
#include <QVector>
#include <memory>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/profile.h"
#include <memory>
namespace ScreenPlay {
struct Profile;

View File

@ -1,18 +1,11 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#pragma once
#include "ScreenPlayUtil/contenttypes.h"
#include <QAbstractListModel>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVector>
#include "ScreenPlayUtil/util.h"
namespace ScreenPlay {
struct SettingsItem {

View File

@ -2,21 +2,20 @@
#pragma once
#include <QLocalServer>
#include <QObject>
#include <QPoint>
#include <QProcess>
#include <QVariantMap>
#include "ScreenPlay/screenplaytimeline.h"
#include "ScreenPlayUtil/util.h"
#include "globalvariables.h"
#include "monitorlistmodel.h"
#include "projectsettingslistmodel.h"
#include "screenplaywallpaper.h"
#include "screenplaywidget.h"
#include "settings.h"
#include <memory>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/monitorlistmodel.h"
#include "ScreenPlay/projectsettingslistmodel.h"
#include "ScreenPlay/screenplaytimelinemanager.h"
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlay/screenplaywidget.h"
#include "ScreenPlay/settings.h"
#include "ScreenPlayUtil/util.h"
namespace ScreenPlay {
@ -37,9 +36,6 @@ public:
const std::shared_ptr<MonitorListModel>& mlm,
const std::shared_ptr<Settings>& settings);
int activeWallpaperCounter() const { return m_activeWallpaperCounter; }
int activeWidgetsCounter() const { return m_activeWidgetsCounter; }
std::shared_ptr<ScreenPlayWallpaper> startWallpaper(
WallpaperData wallpaperData,
const bool saveToProfilesConfigFile);
@ -55,14 +51,11 @@ public:
const QString identifier,
const float relativePosition,
QString positionTimeString);
Q_INVOKABLE QString getTimeString(double relativeLinePosition);
Q_INVOKABLE bool addTimelineAt(
const int index,
const float reltiaveLinePosition,
QString identifier);
Q_INVOKABLE void removeAllTimlineSections();
Q_INVOKABLE QCoro::QmlTask removeAllTimlineSections();
Q_INVOKABLE bool removeTimelineAt(const int index);
Q_INVOKABLE QJsonArray initialSectionsList();
Q_INVOKABLE bool setWallpaperAtTimelineIndex(
@ -89,7 +82,11 @@ public:
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);
signals:
int activeWallpaperCounter() const { return m_activeWallpaperCounter; }
int activeWidgetsCounter() const { return m_activeWidgetsCounter; }
signals:
void activeWallpaperCounterChanged(int activeWallpaperCounter);
void activeWidgetsCounterChanged(int activeWidgetsCounter);
void monitorConfigurationChanged();
@ -103,21 +100,16 @@ public:
private slots:
bool saveProfiles();
void checkActiveWallpaperTimeline();
void newConnection();
void setActiveWallpaperCounter(int activeWallpaperCounter);
void setActiveWidgetsCounter(int activeWidgetsCounter);
private:
void printTimelines();
bool loadProfiles();
void updateQmlTimelines();
void updateIndices();
bool checkIsAnotherScreenPlayInstanceRunning();
bool removeWallpaper(const QString& appID);
bool removeWidget(const QString& appID);
void activateNewTimeline();
bool loadWidgetConfig(const QJsonObject& widget);
std::shared_ptr<GlobalVariables> m_globalVariables;
@ -126,13 +118,12 @@ private:
std::unique_ptr<QLocalServer> m_server;
QVector<std::shared_ptr<ScreenPlayWidget>> m_screenPlayWidgets;
std::vector<std::unique_ptr<SDKConnection>> m_unconnectedClients;
ScreenPlayTimeline m_screenPlayTimeline;
ScreenPlayTimelineManager m_screenPlayTimelineManager;
int m_activeWallpaperCounter { 0 };
int m_activeWidgetsCounter { 0 };
QTimer m_saveLimiter;
QTimer m_contentTimer;
Util m_util;

View File

@ -1,37 +0,0 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#pragma once
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QJsonObject>
#include <QObject>
#include <QProcess>
#include <QString>
#include <QStringList>
#include <memory>
#include "ScreenPlay/wallpapertimelinesection.h"
namespace ScreenPlay {
struct ScreenPlayTimeline{
float calculateRelativePosition(const QTime& endTime);
std::optional<std::shared_ptr<WallpaperTimelineSection>> loadTimelineWallpaperConfig(const QJsonObject& timelineObj);
std::shared_ptr<WallpaperTimelineSection> findActiveWallpaperTimelineSection();
std::optional<std::shared_ptr<WallpaperTimelineSection>> activeWallpaperSectionByAppID(const QString& appID);
std::shared_ptr<WallpaperTimelineSection> getCurrentTimeline();
QVector<std::shared_ptr<WallpaperTimelineSection>> m_wallpaperTimelineSectionsList;
// We use a24 hour system
const QString m_timelineTimeFormat = "hh:mm:ss";
};
}

View File

@ -0,0 +1,53 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#pragma once
#include <QObject>
#include <QTimer>
#include <memory>
#include "ScreenPlay/wallpapertimelinesection.h"
namespace ScreenPlay {
class ScreenPlayTimelineManager : public QObject {
Q_OBJECT
public:
explicit ScreenPlayTimelineManager(QObject* parent = nullptr);
std::optional<std::shared_ptr<WallpaperTimelineSection>> activeWallpaperSectionByAppID(const QString& appID);
std::shared_ptr<WallpaperTimelineSection> findActiveWallpaperTimelineSection();
std::shared_ptr<WallpaperTimelineSection> findTimelineForCurrentTime();
bool addTimelineFromSettings(const QJsonObject& timelineObj);
bool deactivateCurrentTimeline();
bool moveTimelineAt(const int index, const QString identifier, const float relativePosition, QString positionTimeString);
bool startTimeline();
bool addTimelineAt(const int index, const float reltiaveLinePosition, QString identifier);
bool removeTimelineAt(const int index);
QCoro::Task<bool> removeAllTimlineSections();
void updateIndices();
void printTimelines();
bool setWallpaperAtTimelineIndex(WallpaperData wallpaperData,
const int timelineIndex,
const QString& identifier);
QJsonArray initialSectionsList();
QJsonArray timelineWallpaperList();
void setGlobalVariables(const std::shared_ptr<GlobalVariables>& globalVariables);
void setSettings(const std::shared_ptr<Settings>& settings);
void startupFirstTimeline();
private slots:
void checkActiveWallpaperTimeline();
signals:
void requestSaveProfiles();
private:
QVector<std::shared_ptr<WallpaperTimelineSection>> m_wallpaperTimelineSectionsList;
// We use a 24 hour system
const QString m_timelineTimeFormat = "hh:mm:ss";
QTimer m_contentTimer;
std::shared_ptr<GlobalVariables> m_globalVariables;
std::shared_ptr<Settings> m_settings;
};
}

View File

@ -2,14 +2,12 @@
#pragma once
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QJsonObject>
#include <QObject>
#include <QProcess>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <memory>
#include "ScreenPlay/globalvariables.h"

View File

@ -2,21 +2,19 @@
#pragma once
#include <QCoreApplication>
#include <QDebug>
#include <QJsonObject>
#include <QLocalSocket>
#include <QObject>
#include <QPoint>
#include <QProcess>
#include <QTimer>
#include <memory>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/projectsettingslistmodel.h"
#include "ScreenPlay/sdkconnection.h"
#include "ScreenPlayUtil/processmanager.h"
#include <memory>
namespace ScreenPlay {
class ScreenPlayWidget : public QObject {

View File

@ -2,21 +2,11 @@
#pragma once
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QLocalServer>
#include <QLocalSocket>
#include <QObject>
#include <QString>
#include <QTimer>
#include <QVector>
#include <QWebSocketServer>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/util.h"
#include <memory>
namespace ScreenPlay {

View File

@ -2,41 +2,16 @@
#pragma once
#include <QDebug>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNodeList>
#include <QFile>
#include <QFontDatabase>
#include <QGuiApplication>
#include <QIODevice>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QPair>
#include <QProcess>
#include <QProcessEnvironment>
#include <QQmlPropertyMap>
#include <QSettings>
#include <QStandardPaths>
#include <QString>
#include <QSysInfo>
#include <QTextStream>
#include <QThread>
#include <QUrl>
#include <QTranslator>
#include <QVariant>
#include <QVector>
#include <QVersionNumber>
#include <QtConcurrent/QtConcurrent>
#include <QtGlobal>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/util.h"
#include "ScreenPlayUtil/contenttypes.h"
#include <memory>
#include <optional>
namespace ScreenPlay {
class ActiveProfile;

View File

@ -1,16 +1,15 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#pragma once
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QCoro/QCoroTask>
#include <QCoro/QCoroTimer>
#include <QJsonObject>
#include <QObject>
#include <QProcess>
#include <QString>
#include <QStringList>
#include <QTimer>
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/settings.h"
#include "ScreenPlay/wallpaperdata.h"
namespace ScreenPlay {
@ -18,13 +17,20 @@ class ScreenPlayWallpaper;
// Represents one line in the UI. ScreenPlayManager has a list of
// WallpaperTimeline. Only the active timeline section has
// a filled vector of ScreenPlayWallpaper
struct WallpaperTimelineSection {
// Is active is needed as an additional flag during switching.
// When timeline A is no longer in the time range, then we can
// use this flag to know that it was the last active timeline and
// remove all active wallpaper.
bool isActive = false;
class WallpaperTimelineSection : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
public:
enum class Status {
Inactive,
Starting,
Closing,
Active,
};
Q_ENUM(Status)
Status status = Status::Inactive;
QString identifier;
int index = 0; // Needed to check
float relativePosition = 0.0f;
@ -33,49 +39,21 @@ struct WallpaperTimelineSection {
// Data from the profiles.json that we need when we
// enable this section of the pipeline. We keep a copy
// here when this timeline needs to become active
std::vector<WallpaperData> wallpaperData;
std::vector<WallpaperData> wallpaperDataList;
// All active wallpaper.
std::vector<std::shared_ptr<ScreenPlayWallpaper>> activeWallpaperList;
bool close();
std::shared_ptr<GlobalVariables> globalVariables;
std::shared_ptr<Settings> settings;
// Check if currentTime falls within the timeline section
bool containsTime(const QTime& time) const
{
if (endTime < startTime) { // Timeline spans midnight
return (time >= startTime || time < endTime);
} else {
return (time >= startTime && time < endTime);
}
}
bool containsTime(const QTime& time) const;
QJsonObject serialize() const
{
QJsonObject data;
data.insert("isActive", isActive);
data.insert("identifier", identifier);
data.insert("index", index);
data.insert("relativePosition", relativePosition);
data.insert("startTime", startTime.toString());
data.insert("endTime", endTime.toString());
QJsonObject serialize() const;
// Serialize vector<WallpaperData>
QJsonArray wallpaperDataArray;
for (const auto& wallpaper : wallpaperData) {
QJsonObject wallpaperObject = wallpaper.serialize(); // Assuming WallpaperData has a serialize method
wallpaperDataArray.append(wallpaperObject);
}
data.insert("wallpaperData", wallpaperDataArray);
bool activateTimeline();
QCoro::Task<bool> deactivateTimeline();
// Serialize vector<std::shared_ptr<ScreenPlayWallpaper>>
// QJsonArray activeWallpaperArray;
// for (const auto& wallpaper : activeWallpaperList) {
// QJsonObject wallpaperObject = wallpaper->serialize(); // Assuming ScreenPlayWallpaper has a serialize method
// activeWallpaperArray.append(wallpaperObject);
// }
// data.insert("activeWallpaperList", activeWallpaperArray);
return data;
}
signals:
void requestSaveProfiles();
};
}

View File

@ -2,41 +2,24 @@
#pragma once
#include <QColor>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFuture>
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QLinearGradient>
#include <QObject>
#include <QPainter>
#include <QProcess>
#include <QQmlEngine>
#include <QScopedPointer>
#include <QString>
#include <QStringList>
#include <QTextOption>
#include <QTime>
#include <QTimer>
#include <QUrl>
#include <QtMath>
#include "ScreenPlay/CMakeVariables.h"
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/util.h"
#include <QColor>
#include <QFuture>
#include <QObject>
#include <QProcess>
#include <QQmlEngine>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <memory>
#include <optional>
#include "qcorotask.h"
#include "qml/qcoroqml.h"
#include "qml/qcoroqmltask.h"
#include <memory>
#include <optional>
namespace ScreenPlay {
class Wizards : public QObject {

View File

@ -18,17 +18,19 @@ Control {
property int length: timeLine.sectionsList.length
function getActiveTimeline() {
return timeLine.sectionsList[root.activeTimelineIndex];
return timeLine.sectionsList[root.activeTimelineIndex]
}
function removeAll() {
timeLine.removeAll();
timeLine.removeAll()
}
function printTimelines() {
print("################# qml:");
print("################# qml:")
for (var i = 0; i < timeLine.sectionsList.length; i++) {
print(timeLine.sectionsList[i].index, timeLine.sectionsList[i].identifier, timeLine.sectionsList[i].relativeLinePosition);
print(timeLine.sectionsList[i].index,
timeLine.sectionsList[i].identifier,
timeLine.sectionsList[i].relativeLinePosition)
}
}
@ -38,7 +40,8 @@ Control {
property string identifier
property int index: 0
property real relativeLinePosition: lineHandle.linePosition
onRelativeLinePositionChanged: print("relativelinepos: ", relativeLinePosition)
onRelativeLinePositionChanged: print("relativelinepos: ",
relativeLinePosition)
property LineHandle lineHandle
property LineIndicator lineIndicator
}
@ -52,35 +55,45 @@ Control {
onWidthChanged: timeLine.updatePositions()
property var initialSectionsList: []
Component.onCompleted: {
let sectionObects = App.screenPlayManager.initialSectionsList();
let sectionObects = App.screenPlayManager.initialSectionsList()
for (let sectionObject in sectionObects) {
initialSectionsList.push(sectionObects[sectionObject]);
initialSectionsList.push(sectionObects[sectionObject])
}
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);
print("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();
print("remove index ", i)
let section = timeLine.sectionsList[i]
section.lineHandle.destroy()
section.lineIndicator.destroy()
section.destroy()
}
timeLine.sectionsList = [];
App.screenPlayManager.removeAllTimlineSections();
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 = []
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)
})
}
// IMPORTANT: The new element is always on the left. The first
@ -88,206 +101,215 @@ 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);
print("stopPosition", stopPosition)
// Make sure to limit float precision
const fixedStopPosition = stopPosition;
print("addSection at: ", fixedStopPosition);
const fixedStopPosition = stopPosition
print("addSection at: ", fixedStopPosition)
if (stopPosition < 0 || fixedStopPosition > 1) {
console.error("Invalid position:", fixedStopPosition);
return;
console.error("Invalid position:", fixedStopPosition)
return
}
let sectionObject = sectionComp.createObject(timeLine, {
"identifier": identifier,
"relativeLinePosition": fixedStopPosition
});
timeLine.sectionsList.push(sectionObject);
"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;
return a.relativeLinePosition - b.relativeLinePosition
})
const index = timeLine.sectionsList.indexOf(sectionObject)
console.log("Addsection:", index)
createSection(index, fixedStopPosition, sectionObject, identifier)
updatePositions()
return sectionObject
}
function createSection(index, stopPosition, section, identifier) {
console.log("Adding at:", index, stopPosition, identifier);
console.log("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(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(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];
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;
print(lineHandle.linePosition)
console.error("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;
timeLine.sectionsList[i].lineIndicator.selected = true
continue
}
timeLine.sectionsList[i].lineIndicator.selected = false;
timeLine.sectionsList[i].lineIndicator.selected = false
}
root.activeTimelineIndex = activeTimelineIndex;
root.activeTimelineIndex = activeTimelineIndex
}
// We must update all indexes when removing/adding an element
function updateIndicatorIndexes() {
if (timeLine.sectionsList === null || timeLine.sectionsList === undefined)
return;
if (timeLine.sectionsList === null
|| timeLine.sectionsList === undefined)
return
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++) {
timeLine.sectionsList[i].index = i;
timeLine.sectionsList[i].lineIndicator.index = i;
timeLine.sectionsList[i].index = i
timeLine.sectionsList[i].lineIndicator.index = i
//print("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;
print(timeLine.stopPositionList)
print(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;
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;
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)
}
for (var i = 0; i < timeLine.sectionsList.length; i++) {
let section = timeLine.sectionsList[i];
section.relativeLinePosition = section.lineHandle.linePosition;
let section = timeLine.sectionsList[i]
section.relativeLinePosition = section.lineHandle.linePosition
// print(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;
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);
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;
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;
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;
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 {
@ -310,10 +332,13 @@ 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: (new Date().getHours(
) * 3600) + (new Date().getMinutes(
) * 60) + new Date().getSeconds()
x: addHandleWrapper.width * (currentSeconds / totalSeconds)
@ -322,9 +347,15 @@ 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,11 +403,15 @@ 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);
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)
}
x: hoverHandler.point.position.x - width * .5

View File

@ -120,12 +120,13 @@ void App::init()
// Must be called last to display a error message on startup by the qml engine
m_screenPlayManager->init(m_globalVariables, m_monitorListModel, m_settings);
QObject::connect(
m_monitorListModel.get(),
&MonitorListModel::monitorConfigurationChanged,
m_screenPlayManager.get(), [this]() {
m_screenPlayManager->removeAllWallpapers(true);
});
// TODO
// QObject::connect(
// m_monitorListModel.get(),
// &MonitorListModel::monitorConfigurationChanged,
// m_screenPlayManager.get(), [this]() {
// m_screenPlayManager->removeAllWallpapers(true);
// });
}
QString App::version() const

View File

@ -1,8 +1,22 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/create.h"
#include "ScreenPlay/createimportvideo.h"
#include "ScreenPlayUtil/util.h"
#include "qguiapplication.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QGuiApplication>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QStringList>
#include <QTime>
#include <QTimer>
#include <QUrl>
#include <QtMath>
namespace ScreenPlay {
@ -19,8 +33,8 @@ namespace ScreenPlay {
/*!
Constructor.
*/
Create::Create(const std::shared_ptr<GlobalVariables>& globalVariables)
: QObject(nullptr)
Create::Create(const std::shared_ptr<GlobalVariables>& globalVariables, QObject* parent)
: QObject(parent)
, m_globalVariables(globalVariables)
{
}
@ -28,8 +42,8 @@ Create::Create(const std::shared_ptr<GlobalVariables>& globalVariables)
/*!
Constructor for the QMLEngine.
*/
Create::Create()
: QObject(nullptr)
Create::Create(QObject* parent)
: QObject(parent)
{
}

View File

@ -2,6 +2,8 @@
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlay/CMakeVariables.h"
#include <QMetaType>
#include <QStandardPaths>
namespace ScreenPlay {

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/installedlistfilter.h"
#include "ScreenPlay/globalvariables.h"
#include <QRegularExpression>
namespace ScreenPlay {

View File

@ -2,8 +2,17 @@
#include "ScreenPlay/installedlistmodel.h"
#include "ScreenPlay/CMakeVariables.h"
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QGuiApplication>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStandardPaths>
#include <QtConcurrent/QtConcurrent>
#include <QUrl>
namespace ScreenPlay {

View File

@ -1,10 +1,15 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/monitorlistmodel.h"
#include "ScreenPlay/screenplaywallpaper.h"
#ifdef Q_OS_WIN
#include "windowsintegration.h"
#endif
#include <QGuiApplication>
#include <QDebug>
namespace ScreenPlay {
/*!

View File

@ -2,11 +2,16 @@
#include "ScreenPlay/profilelistmodel.h"
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfoList>
#include <QJsonDocument>
#include <QJsonObject>
#include <QPair>
#include <QStandardPaths>
#include <QUrl>
namespace ScreenPlay {

View File

@ -2,6 +2,13 @@
#include "ScreenPlay/projectsettingslistmodel.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace ScreenPlay {
/*!

View File

@ -3,8 +3,6 @@
#include "ScreenPlay/screenplaymanager.h"
#include "ScreenPlayUtil/util.h"
#include <iostream>
#include <QScopeGuard>
namespace ScreenPlay {
@ -26,8 +24,8 @@ ScreenPlayManager::ScreenPlayManager(
: QObject { parent }
{
m_server = std::make_unique<QLocalServer>();
QObject::connect(m_server.get(), &QLocalServer::newConnection, this, &ScreenPlayManager::newConnection);
QObject::connect(&m_screenPlayTimelineManager, &ScreenPlayTimelineManager::requestSaveProfiles, this, &ScreenPlayManager::requestSaveProfiles);
m_server->setSocketOptions(QLocalServer::WorldAccessOption);
if (!m_server->listen("ScreenPlay")) {
qCritical("Could not open Local Socket with the name ScreenPlay!");
@ -41,91 +39,30 @@ ScreenPlayManager::ScreenPlayManager(
QObject::connect(this, &ScreenPlayManager::requestSaveProfiles, this, [this]() {
m_saveLimiter.start();
});
m_contentTimer.setInterval(1000);
// 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, &ScreenPlayManager::checkActiveWallpaperTimeline);
}
/*!
\brief Checks if we need to display a different wallpaper at the current time.
CurrentTimeline => Timeline that should currently run, based on start and endtime.
ActiveTimeline => Timeline with isActive flag set to true
*/
void ScreenPlayManager::checkActiveWallpaperTimeline()
{
std::shared_ptr<WallpaperTimelineSection> currentTimeline = m_screenPlayTimeline.getCurrentTimeline();
if (!currentTimeline) {
qCritical() << "No active timeline found. There must always be an active timeline.";
return;
}
// Check for currently active timeline.
std::shared_ptr<WallpaperTimelineSection> activeTimelineSection = nullptr;
for (const auto& section : m_screenPlayTimeline.m_wallpaperTimelineSectionsList) {
if (section->isActive) {
activeTimelineSection = section;
break;
}
}
// We start the first timeline section,if there is currently no active timeline section.
// This happens at every startup.
if (!activeTimelineSection) {
activateNewTimeline();
}
// If timeline does not match current time, switch to new timeline.
if (activeTimelineSection) {
if (!activeTimelineSection->containsTime(QTime::currentTime()) || !activeTimelineSection->isActive) {
activateNewTimeline();
}
}
}
/*!
\brief Change active timeline. We need an extra flags to know if our
timeline is active and out of time range.
\brief Inits this class instead of init in the constructor. This is because we need to check
first if another ScreenPlay instance is running. If it is not the case we call this function.
*/
void ScreenPlayManager::activateNewTimeline()
void ScreenPlayManager::init(
const std::shared_ptr<GlobalVariables>& globalVariables,
const std::shared_ptr<MonitorListModel>& mlm,
const std::shared_ptr<Settings>& settings)
{
// Remove old timeline content
auto oldTimeline = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
// Will be null on startup, where there is no old timeline
if (oldTimeline) {
m_globalVariables = globalVariables;
m_monitorListModel = mlm;
m_settings = settings;
oldTimeline->isActive = false;
for (auto& activeWallpaper : oldTimeline->activeWallpaperList) {
activeWallpaper->close();
}
// Clear all active wallpaper connections, but do not
// clear WallpaperData in case we need it again later
oldTimeline->activeWallpaperList.clear();
}
m_screenPlayTimelineManager.setGlobalVariables(m_globalVariables);
m_screenPlayTimelineManager.setSettings(m_settings);
// Activate new timeline content
std::shared_ptr<WallpaperTimelineSection> newTimelineSection = m_screenPlayTimeline.getCurrentTimeline();
if (!newTimelineSection) {
qCritical() << "No active timeline found. There must always be an active timeline.";
return;
// Reset to default settings if we are unable to load
// the existing one
if (!loadProfiles()) {
qInfo() << "Reset default profiles.json at:" << m_globalVariables->localSettingsPath();
m_settings->writeDefaultProfiles();
}
newTimelineSection->isActive = true;
// Activate new wallpaper
for (const auto& wallpaper : newTimelineSection->wallpaperData) {
auto activeWallpaper = startWallpaper(wallpaper, false);
if (!activeWallpaper) {
qCritical() << "Unable to start the new wallpaper at timeline:" << newTimelineSection->identifier
<< newTimelineSection->startTime << "-" << newTimelineSection->endTime;
return;
}
newTimelineSection->activeWallpaperList.push_back(activeWallpaper);
}
qInfo() << "Timeline switched successfully.";
}
/*!
@ -148,118 +85,22 @@ bool ScreenPlayManager::setWallpaperAtTimelineIndex(
wallpaperData.file = file;
wallpaperData.monitors = monitorIndex;
wallpaperData.fillMode = m_settings->videoFillMode();
bool ok = false;
for (auto& timelineSection : m_screenPlayTimeline.m_wallpaperTimelineSectionsList) {
const bool sameIndex = timelineSection->index == timelineIndex;
const bool sameIdentifier = timelineSection->identifier == identifier;
if (sameIndex && sameIdentifier) {
// TODO support more than one wallpaper per timeline
timelineSection->wallpaperData = { wallpaperData };
ok = true;
break;
}
}
if (!ok) {
qCritical() << "Invalid sameIdentifier: " << timelineIndex << identifier;
const bool success = m_screenPlayTimelineManager.setWallpaperAtTimelineIndex(wallpaperData, timelineIndex, identifier);
printTimelines();
if (!success) {
qCritical() << "Invalid timeline index or identifier: " << timelineIndex << identifier;
m_screenPlayTimelineManager.printTimelines();
emit printQmlTimeline();
return false;
}
// We do not start the wallpaper here, but let
// ScreenPlayManager::checkActiveWallpaperTimeline decide
// ScreenPlayTimelineManager::checkActiveWallpaperTimeline decide
// if the wallpaper
emit requestSaveProfiles();
return true;
}
/*!
\brief Inits this class instead of init in the constructor. This is because we need to check
first if another ScreenPlay instance is running. If it is not the case we call this function.
*/
void ScreenPlayManager::init(
const std::shared_ptr<GlobalVariables>& globalVariables,
const std::shared_ptr<MonitorListModel>& mlm,
const std::shared_ptr<Settings>& settings)
{
m_globalVariables = globalVariables;
m_monitorListModel = mlm;
m_settings = settings;
// Reset to default settings if we are unable to load
// the existing one
if (!loadProfiles()) {
qInfo() << "Reset default profiles.json at:" << m_globalVariables->localSettingsPath();
m_settings->writeDefaultProfiles();
}
}
/*!
\brief Creates a wallpaper with a given \a monitorIndex list, \a a absoluteStoragePath folder,
a \a previewImage (relative path to the absoluteStoragePath), a default \a volume,
a \a fillMode, a \a type (htmlWallpaper, qmlWallpaper etc.), a \a saveToProfilesConfigFile bool only set to flase
if we call the method when using via the settings on startup to skip a unnecessary save.
*/
std::shared_ptr<ScreenPlayWallpaper> ScreenPlayManager::startWallpaper(
WallpaperData wallpaperData,
const bool saveToProfilesConfigFile)
{
const int screenCount = QGuiApplication::screens().count();
QJsonArray monitors;
for (const int index : wallpaperData.monitors) {
monitors.append(index);
if (index > screenCount - 1) {
qWarning() << "Configuration contains invalid monitor with index: " << index << " screen count: " << screenCount;
return nullptr;
}
}
auto saveToProfile = qScopeGuard([=, this] {
// Do not save on app start
if (saveToProfilesConfigFile) {
emit requestSaveProfiles();
}
});
// Remove file:///
wallpaperData.absolutePath = QUrl::fromUserInput(wallpaperData.absolutePath).toLocalFile();
const QString appID = Util().generateRandomString();
qInfo() << "Start wallpaper" << wallpaperData.absolutePath << appID;
// Only support remove wallpaper that spans over 1 monitor
// if (wallpaperData.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(
// wallpaperData,
// m_settings->checkWallpaperVisible());
// m_monitorListModel->setWallpaperMonitor(wallpaper, wallpaperData.monitors);
// }
// }
// i++;
// }
// }
auto wallpaper = std::make_shared<ScreenPlayWallpaper>(
m_globalVariables,
appID,
wallpaperData,
m_settings);
QObject::connect(wallpaper.get(), &ScreenPlayWallpaper::requestSave, this, &ScreenPlayManager::requestSaveProfiles);
QObject::connect(wallpaper.get(), &ScreenPlayWallpaper::requestClose, this, &ScreenPlayManager::removeWallpaper);
QObject::connect(wallpaper.get(), &ScreenPlayWallpaper::error, this, &ScreenPlayManager::displayErrorPopup);
if (!wallpaper->start()) {
return nullptr;
}
// m_monitorListModel->setWallpaperMonitor(wallpaper, wallpaperData.monitors);
return wallpaper;
}
/*!
\brief Creates a ScreenPlayWidget object via a \a absoluteStoragePath and a \a preview image (relative path).
*/
@ -311,30 +152,30 @@ bool ScreenPlayManager::startWidget(
*/
bool ScreenPlayManager::removeAllWallpapers(bool saveToProfile)
{
// TODO
// if (m_screenPlayTimelineManager.m_wallpaperTimelineSectionsList.empty()) {
// return false;
// }
if (m_screenPlayTimeline.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());
// }
QStringList appIDs;
auto activeTimelineSection = m_screenPlayTimeline.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;
// }
// }
for (const auto& appID : appIDs) {
if (!removeWallpaper(appID)) {
return false;
}
}
if (saveToProfile)
emit requestSaveProfiles();
// if (saveToProfile)
// emit requestSaveProfiles();
return true;
}
@ -390,7 +231,7 @@ bool ScreenPlayManager::removeWallpaperAt(int index)
*/
bool ScreenPlayManager::requestProjectSettingsAtMonitorIndex(const int index)
{
auto activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
auto activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
return false;
}
@ -440,7 +281,7 @@ bool ScreenPlayManager::setWallpaperFillModeAtMonitorIndex(const int index, cons
*/
bool ScreenPlayManager::setAllWallpaperValue(const QString& key, const QString& value)
{
auto activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
auto activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
return false;
}
@ -458,7 +299,7 @@ bool ScreenPlayManager::setAllWallpaperValue(const QString& key, const QString&
ScreenPlayWallpaper* ScreenPlayManager::getWallpaperByAppID(const QString& appID)
{
auto activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
auto activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
return nullptr;
}
@ -476,273 +317,40 @@ ScreenPlayWallpaper* ScreenPlayManager::getWallpaperByAppID(const QString& appID
*/
bool ScreenPlayManager::moveTimelineAt(const int index, const QString identifier, const float relativePosition, QString positionTimeString)
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto& wallpapterTimelineSection = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index);
QTime newPositionTime = QTime::fromString(positionTimeString, "hh:mm");
if (!newPositionTime.isValid()) {
qWarning() << "Unable to move with invalid time:" << positionTimeString;
return false;
}
wallpapterTimelineSection->endTime = newPositionTime;
wallpapterTimelineSection->relativePosition = relativePosition;
// We set the identifier here, because we generate it in qml
// The identiefier is only used for debugging
wallpapterTimelineSection->identifier = identifier;
const auto timelineCount = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.size();
// Only update the next timeline startTime
// if we are not end last wallpaper, that always
// must end at 24:00
if (index <= timelineCount) {
auto& wallpapterTimelineSectionNext = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index + 1);
wallpapterTimelineSectionNext->startTime = newPositionTime;
}
printTimelines();
emit requestSaveProfiles();
return true;
return m_screenPlayTimelineManager.moveTimelineAt(index, identifier, relativePosition, positionTimeString);
}
/*!
\brief Converts a range from 0.0f - 1.0f to 00:00:00 0 23:59:59
*/
QString ScreenPlayManager::getTimeString(double relativeLinePosition)
{
if (relativeLinePosition == 1.0) {
// We overwrite the endTime here
return "23:59:59";
}
const double totalHours = relativeLinePosition * 24;
int hours = static_cast<int>(std::floor(totalHours)); // Gets the whole hour part
double fractionalHours = totalHours - hours;
int minutes = static_cast<int>(std::floor(fractionalHours * 60)); // Calculates the minutes
double fractionalMinutes = fractionalHours * 60 - minutes;
int seconds = static_cast<int>(std::round(fractionalMinutes * 60)); // Calculates the seconds
// Adjust minutes and seconds if seconds rolled over to 60
if (seconds == 60) {
seconds = 0;
minutes += 1;
}
// Adjust hours and minutes if minutes rolled over to 60
if (minutes == 60) {
minutes = 0;
hours += 1;
}
// Ensure hours wrap correctly at 24
if (hours == 24) {
hours = 0;
}
// Format the output to "HH:MM:SS"
return QString("%1:%2:%3").arg(hours, 2, 10, QChar('0')).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
}
/*!
\brief Update m_screenPlayTimeline.m_wallpaperTimelineSectionsList index based on the startTime;
*/
void ScreenPlayManager::updateIndices()
{
// Sort the vector based on startTime
std::sort(m_screenPlayTimeline.m_wallpaperTimelineSectionsList.begin(), m_screenPlayTimeline.m_wallpaperTimelineSectionsList.end(),
[](const auto& a, const auto& b) {
return a->startTime < b->startTime;
});
// Update the indices based on new order
for (int i = 0; i < m_screenPlayTimeline.m_wallpaperTimelineSectionsList.size(); ++i) {
m_screenPlayTimeline.m_wallpaperTimelineSectionsList[i]->index = i;
}
}
/*!
\brief Adds a new timeline at relative position. We always shrink the timeline at the input
position and append the new one to the left. There must always (lightning) an active
timeline section.
*/
bool ScreenPlayManager::addTimelineAt(const int index, const float reltiaveLinePosition, QString identifier)
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
// We always get the new endTime
const QString newStopPosition = getTimeString(reltiaveLinePosition);
QTime newStopPositionTime = QTime::fromString(newStopPosition, m_screenPlayTimeline.m_timelineTimeFormat);
if (!newStopPositionTime.isValid()) {
return false;
}
// IMPORTANT: The new element is always on the left. The first
// handle always persists because the
// user can never delete it. It only gets "pushed" further
// to the right, by increasing the size.
// | Insert here
// ˇ
// |-----------------------------------------------|
// 0 ID: AAA
//
//
// |-----------------------|-----------------------|
// 0 ID: BBB 1 - ID: AAA
// We directly get the new index of 0 in this example from qml
auto newTimelineSection = std::make_shared<WallpaperTimelineSection>();
newTimelineSection->index = index;
newTimelineSection->identifier = identifier;
newTimelineSection->endTime = newStopPositionTime;
// In case we do a full reset, we must set the start time manually
if (m_screenPlayTimeline.m_wallpaperTimelineSectionsList.empty()) {
newTimelineSection->startTime = QTime::fromString("00:00:00", m_screenPlayTimeline.m_timelineTimeFormat);
} else {
// We can use the given index here, because it points
// the the current item at that index, and we have not yet
// added our new timelineSection to our list.
newTimelineSection->startTime = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index)->startTime;
}
const bool isLast = (m_screenPlayTimeline.m_wallpaperTimelineSectionsList.length() - 1) == index;
if (isLast) {
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.last()->startTime = newTimelineSection->endTime;
}
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.append(newTimelineSection);
updateIndices();
printTimelines();
emit requestSaveProfiles();
return true;
const bool success = m_screenPlayTimelineManager.addTimelineAt(index, reltiaveLinePosition, identifier);
return success;
}
/*!
\brief Qml function that removes all Timeline sections. Qml then creates
a new default section.
*/
void ScreenPlayManager::removeAllTimlineSections()
QCoro::QmlTask ScreenPlayManager::removeAllTimlineSections()
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
// First check if there is any active wallpaper that we save in
// this shared ptr. We can have many timlines, but the current timeline
// can have no active wallpaper
auto activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
qCritical() << "There must always be (lightning) an active timline";
return;
}
if (activeTimelineSection) {
// Close the localsocket
activeTimelineSection->close();
// Reset all active wallpaper
activeTimelineSection.reset();
}
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.clear();
removeAllWallpapers();
removeAllWidgets();
// Do not call requestSaveProfiles, because qml will add
// the default timeline after this function
return QCoro::QmlTask([this]() -> QCoro::Task<Result> {
// call with coro
const bool success = co_await m_screenPlayTimelineManager.removeAllTimlineSections();
qDebug() << "Task: removeAllTimlineSections" << success;
// emit requestSaveProfiles();
// removeAllWallpapers();
co_return Result { success };
}());
}
/*!
\brief Removes a timeline at a given index. Expands the timeline next to it
to fill the space.
*/
bool ScreenPlayManager::removeTimelineAt(const int index)
{
printTimelines();
const auto timelineCount = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.size();
if (timelineCount == 0) {
qCritical() << "Timeline empty";
return false;
}
if (timelineCount == 1) {
qCritical() << "Timeline must always have at least one element, that span across the whole timeline from 00:00:00 to 23:59:59.";
return false;
}
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto& wallpapterTimelineSection = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index);
// When we have two timelines, we know that only the first
// timeline can be removed and the second one will then span
// across the whole timeline
// remove <- expand
// |-----------|-----------|
// 0 1
if (timelineCount == 2) {
if (index != 0) {
qCritical() << "Removing the last timeline is not allowed. This must always span the whole timeline";
return false;
}
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.removeAt(index);
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.first()->startTime = QTime::fromString("00:00:00", m_screenPlayTimeline.m_timelineTimeFormat);
updateIndices();
printTimelines();
return true;
}
// Now handle all states where we have more than two wallpaper
// There is no timeline before if we want to remove
// the timeline at index 0. We do not need to make the same
// check for the timelineAfter, because the last timeline
// cannot be deleted
QTime endTime;
if (index == 0) {
endTime = QTime::fromString("00:00:00", m_screenPlayTimeline.m_timelineTimeFormat);
} else {
endTime = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index - 1)->endTime;
}
auto timelineAfter = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.at(index + 1);
// before remove <- expand
// |-----------|-----------|-----------|
// 0 1 2
// Now when removing timeline at index 1, the next (after)
// wallpaper gets the remaining space
timelineAfter->startTime = endTime;
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.removeAt(index);
updateIndices();
printTimelines();
const bool success = m_screenPlayTimelineManager.removeTimelineAt(index);
emit requestSaveProfiles();
return true;
return success;
}
/*!
\brief Print to check if our qml data matches our c++.
*/
void ScreenPlayManager::printTimelines()
{
std::cout << "#############################\n";
for (auto& timeline : m_screenPlayTimeline.m_wallpaperTimelineSectionsList) {
std::cout << timeline->index << ": " << timeline->identifier.toStdString() << "\t"
<< timeline->relativePosition
<< " start: " << timeline->startTime.toString().toStdString()
<< " end: " << timeline->endTime.toString().toStdString() << std::endl;
}
}
/*!
\brief Qml function to build our timeline on creation in qml.
*/
QJsonArray ScreenPlayManager::initialSectionsList()
{
QJsonArray sectionPositions;
for (const auto& timelineSection : m_screenPlayTimeline.m_wallpaperTimelineSectionsList) {
sectionPositions.push_back(timelineSection->serialize());
}
return sectionPositions;
return m_screenPlayTimelineManager.initialSectionsList();
}
/*!
@ -775,7 +383,7 @@ void ScreenPlayManager::newConnection()
return;
}
auto activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
auto activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
return;
}
@ -829,7 +437,7 @@ void ScreenPlayManager::setActiveWidgetsCounter(int activeWidgetsCounter)
bool ScreenPlayManager::removeWallpaper(const QString& appID)
{
auto wallpaperSectionOpt = m_screenPlayTimeline.activeWallpaperSectionByAppID(appID);
auto wallpaperSectionOpt = m_screenPlayTimelineManager.activeWallpaperSectionByAppID(appID);
if (!wallpaperSectionOpt.has_value()) {
qCritical() << "No wallpaper found.";
return false;
@ -908,7 +516,7 @@ bool ScreenPlayManager::removeWidget(const QString& appID)
*/
bool ScreenPlayManager::setWallpaperValue(const QString& appID, const QString& key, const QString& value)
{
std::shared_ptr<WallpaperTimelineSection> activeTimelineSection = m_screenPlayTimeline.findActiveWallpaperTimelineSection();
std::shared_ptr<WallpaperTimelineSection> activeTimelineSection = m_screenPlayTimelineManager.findActiveWallpaperTimelineSection();
if (!activeTimelineSection) {
qCritical() << "setWallpaperValue failed, because no active timeline section was found";
return false;
@ -931,20 +539,7 @@ bool ScreenPlayManager::saveProfiles()
{
m_saveLimiter.stop();
QJsonArray timelineWallpaperList {};
for (const auto& activeTimelineWallpaper : std::as_const(m_screenPlayTimeline.m_wallpaperTimelineSectionsList)) {
QJsonObject timelineWallpaper;
timelineWallpaper.insert("startTime", activeTimelineWallpaper->startTime.toString());
timelineWallpaper.insert("endTime", activeTimelineWallpaper->endTime.toString());
QJsonArray wallpaper;
// Every timeline section can have multiple wallpaper
for (const auto& wallpaperData : activeTimelineWallpaper->wallpaperData) {
wallpaper.append(wallpaperData.serialize());
}
timelineWallpaper.insert("wallpaper", wallpaper);
timelineWallpaperList.append(timelineWallpaper);
}
QJsonArray timelineWallpaperList = m_screenPlayTimelineManager.timelineWallpaperList();
QJsonArray widgetList {};
for (const auto& activeWidget : std::as_const(m_screenPlayWidgets)) {
@ -1007,21 +602,10 @@ bool ScreenPlayManager::loadProfiles()
for (QJsonValueRef timelineWallpaper : wallpaper.toObject().value("timelineWallpaper").toArray()) {
QJsonObject wallpaperObj = timelineWallpaper.toObject();
std::optional<std::shared_ptr<WallpaperTimelineSection>> wallpaperDataOpt = m_screenPlayTimeline.loadTimelineWallpaperConfig(wallpaperObj);
if (!wallpaperDataOpt.has_value()) {
containsInvalidData = true;
break;
if (!m_screenPlayTimelineManager.addTimelineFromSettings(wallpaperObj)) {
qCritical() << "Unable to add wallpaper timeline";
continue;
}
std::shared_ptr<WallpaperTimelineSection> wallpaperData = wallpaperDataOpt.value();
wallpaperData->index = m_screenPlayTimeline.m_wallpaperTimelineSectionsList.length();
wallpaperData->identifier = m_util.generateRandomString(4);
qInfo() << wallpaperData->index
<< wallpaperData->startTime
<< wallpaperData->endTime;
m_screenPlayTimeline.m_wallpaperTimelineSectionsList.append(wallpaperDataOpt.value());
}
for (const QJsonValueRef widget : wallpaper.toObject().value("widgets").toArray()) {
@ -1037,30 +621,11 @@ bool ScreenPlayManager::loadProfiles()
if (containsInvalidData)
saveProfiles();
activateNewTimeline();
m_contentTimer.start();
m_screenPlayTimelineManager.startupFirstTimeline();
return true;
}
void ScreenPlayManager::updateQmlTimelines()
{
QVariantList data;
for (const auto& timelineSection : m_screenPlayTimeline.m_wallpaperTimelineSectionsList) {
if (timelineSection->wallpaperData.empty())
continue;
// We just use the frist one
const auto& wp = timelineSection->wallpaperData.front();
data.append(QVariant::fromValue(wp.previewImage));
}
}
/*!
\brief Loads a widget from C:\Users\XXX\AppData\Local\ScreenPlay\ScreenPlay\profiles.json
*/

View File

@ -1,106 +0,0 @@
#include "screenplaytimeline.h"
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlay/wallpaperdata.h"
namespace ScreenPlay {
/*!
* \brief Calculates the relative position of a given time within a day.
* * This function takes a QTime object representing the end time and calculates its
* position relative to a fixed start and end time on the same day. The relative position
* is a normalized value between 0 and 1, rounded to four decimal places.
* * \param endTime The time for which to calculate the relative position.
* \return A float representing the normalized relative position of endTime, rounded to four decimal places.
*/
float ScreenPlayTimeline::calculateRelativePosition(const QTime &endTime)
{
QTime startTime(0, 0, 0); // Start of the day
QTime maxTime(23, 59, 59); // End of the day range
// Total number of seconds from startTime to maxTime
int totalSeconds = startTime.secsTo(maxTime);
// Seconds from startTime to the given endTime
int endTimeSeconds = startTime.secsTo(endTime);
// Calculate the relative position
float relativePosition = static_cast<float>(endTimeSeconds) / totalSeconds;
// Round to four decimal places
return qRound(relativePosition * 10000.0) / 10000.0;
}
std::optional<std::shared_ptr<ScreenPlay::WallpaperTimelineSection> > ScreenPlay::ScreenPlayTimeline::loadTimelineWallpaperConfig(const QJsonObject &timelineObj)
{
const QTime startTime = QTime::fromString(timelineObj.value("startTime").toString(), m_timelineTimeFormat);
const QTime endTime = QTime::fromString(timelineObj.value("endTime").toString(), m_timelineTimeFormat);
if (startTime > endTime) {
qCritical() << "Invalid time, start time is later than end time: " << startTime.toString() << endTime.toString();
return std::nullopt;
}
// TODO check license
auto timelineSection = std::make_shared<WallpaperTimelineSection>();
timelineSection->startTime = startTime;
timelineSection->endTime = endTime;
timelineSection->relativePosition = calculateRelativePosition(endTime);
const auto wallpaperList = timelineObj.value("wallpaper").toArray();
for (auto& wallpaper : wallpaperList) {
std::optional<WallpaperData> wallpaperDataOpt = WallpaperData::loadWallpaperConfig(wallpaper.toObject());
if (!wallpaperDataOpt.has_value())
return std::nullopt;
timelineSection->wallpaperData.push_back(wallpaperDataOpt.value());
}
return timelineSection;
}
/*!
\brief Parses one timeline wallpaper:
"timelineWallpaper": [
{
"endTime": "08:32:00",
"startTime": "00:00:00",
"wallpaper": [
[...]
]
},
*/
std::shared_ptr<WallpaperTimelineSection> ScreenPlayTimeline::findActiveWallpaperTimelineSection()
{
for (const auto& section : m_wallpaperTimelineSectionsList) {
if (section->isActive) {
return section;
}
}
return nullptr;
}
/*!
\brief Returns the wallpaper timeline that has the isActive
flag enabled.
*/
std::optional<std::shared_ptr<WallpaperTimelineSection> > ScreenPlayTimeline::activeWallpaperSectionByAppID(const QString &appID)
{
for (const auto& section : m_wallpaperTimelineSectionsList) {
for (const auto& wallpaper : section->activeWallpaperList) {
if(wallpaper->appID() == appID)
return section;
}
}
qCritical() << "No matching appID for:"<< appID;
return nullptr;
}
/*!
\brief Returns the current active timline. There must always be an active timeline!
*/
std::shared_ptr<WallpaperTimelineSection> ScreenPlayTimeline::getCurrentTimeline()
{
const QTime currentTime = QTime::currentTime();
for (const auto& section : m_wallpaperTimelineSectionsList) {
if (section->containsTime(currentTime)) {
return section;
}
}
qCritical() << "No active timeline";
return nullptr;
}
}

View File

@ -0,0 +1,469 @@
#include "ScreenPlay/screenplaytimelinemanager.h"
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlay/wallpaperdata.h"
#include "ScreenPlayUtil/util.h"
#include <QString>
#include <QStringList>
#include <iostream>
namespace ScreenPlay {
/*!
* \brief Calculates the relative position of a given time within a day.
* * This function takes a QTime object representing the end time and calculates its
* position relative to a fixed start and end time on the same day. The relative position
* is a normalized value between 0 and 1, rounded to four decimal places.
* * \param endTime The time for which to calculate the relative position.
* \return A float representing the normalized relative position of endTime, rounded to four decimal places.
*/
ScreenPlayTimelineManager::ScreenPlayTimelineManager(
QObject* parent)
: QObject { parent }
{
// 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);
}
/*!
\brief Parses one timeline wallpaper:
"timelineWallpaper": [
{
"endTime": "08:32:00",
"startTime": "00:00:00",
"wallpaper": [
[...]
]
},
*/
std::shared_ptr<WallpaperTimelineSection> ScreenPlayTimelineManager::findActiveWallpaperTimelineSection()
{
for (const auto& section : m_wallpaperTimelineSectionsList) {
if (section->status == WallpaperTimelineSection::Status::Active) {
return section;
}
}
return nullptr;
}
/*!
\brief Returns the wallpaper timeline that has the isActive
flag enabled.
*/
std::optional<std::shared_ptr<WallpaperTimelineSection>> ScreenPlayTimelineManager::activeWallpaperSectionByAppID(const QString& appID)
{
for (const auto& section : m_wallpaperTimelineSectionsList) {
for (const auto& wallpaper : section->activeWallpaperList) {
if(wallpaper->appID() == appID)
return section;
}
}
qCritical() << "No matching appID for:"<< appID;
return nullptr;
}
/*!
\brief Returns the current active timline. There must always be an active timeline!
*/
std::shared_ptr<WallpaperTimelineSection> ScreenPlayTimelineManager::findTimelineForCurrentTime()
{
const QTime currentTime = QTime::currentTime();
for (const auto& section : m_wallpaperTimelineSectionsList) {
if (section->containsTime(currentTime)) {
return section;
}
}
qCritical() << "No active timeline";
return nullptr;
}
/*!
\brief Loads all timelines.
*/
bool ScreenPlayTimelineManager::addTimelineFromSettings(const QJsonObject& timelineObj)
{
const QTime startTime = QTime::fromString(timelineObj.value("startTime").toString(), m_timelineTimeFormat);
const QTime endTime = QTime::fromString(timelineObj.value("endTime").toString(), m_timelineTimeFormat);
if (startTime > endTime) {
qCritical() << "Invalid time, start time is later than end time: " << startTime.toString() << endTime.toString();
return false;
}
// TODO check license
auto timelineSection = std::make_shared<WallpaperTimelineSection>();
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);
const auto wallpaperList = timelineObj.value("wallpaper").toArray();
for (auto& wallpaper : wallpaperList) {
std::optional<WallpaperData> wallpaperDataOpt = WallpaperData::loadWallpaperConfig(wallpaper.toObject());
if (!wallpaperDataOpt.has_value())
return false;
timelineSection->wallpaperDataList.push_back(wallpaperDataOpt.value());
}
// Todo: Should we use addTimelineAt?
timelineSection->index = m_wallpaperTimelineSectionsList.length();
timelineSection->identifier = Util().generateRandomString(4);
qInfo() << timelineSection->index
<< timelineSection->startTime
<< timelineSection->endTime;
// Todo: Should we use addTimelineAt?
m_wallpaperTimelineSectionsList.append(timelineSection);
return false;
}
/*!
\brief We always handle the endTimeString, because it is the handle for the
timeline. The last, default, timeline does not have a handle.
*/
bool ScreenPlayTimelineManager::moveTimelineAt(const int index, const QString identifier, const float relativePosition, QString positionTimeString)
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto& wallpapterTimelineSection = m_wallpaperTimelineSectionsList.at(index);
QTime newPositionTime = QTime::fromString(positionTimeString, "hh:mm");
if (!newPositionTime.isValid()) {
qWarning() << "Unable to move with invalid time:" << positionTimeString;
return false;
}
wallpapterTimelineSection->endTime = newPositionTime;
wallpapterTimelineSection->relativePosition = relativePosition;
// We set the identifier here, because we generate it in qml
// The identiefier is only used for debugging
wallpapterTimelineSection->identifier = identifier;
const auto timelineCount = m_wallpaperTimelineSectionsList.size();
// Only update the next timeline startTime
// if we are not end last wallpaper, that always
// must end at 24:00
if (index <= timelineCount) {
auto& wallpapterTimelineSectionNext = m_wallpaperTimelineSectionsList.at(index + 1);
wallpapterTimelineSectionNext->startTime = newPositionTime;
}
// printTimelines();
emit requestSaveProfiles();
return true;
}
/*!
\brief Checks if we need to display a different wallpaper at the current time.
*/
void ScreenPlayTimelineManager::checkActiveWallpaperTimeline()
{
// Current timeline with the state active
std::shared_ptr<WallpaperTimelineSection> activeTimeline = findActiveWallpaperTimelineSection();
if (!activeTimeline) {
std::shared_ptr<WallpaperTimelineSection> currentTimeline = findTimelineForCurrentTime();
currentTimeline->activateTimeline();
return;
}
// Current timline that should be active, based on the current time
std::shared_ptr<WallpaperTimelineSection> currentTimeline = findTimelineForCurrentTime();
if (!currentTimeline) {
qCritical() << "No current timeline found. There must always be an active timeline.";
return;
}
if (currentTimeline != activeTimeline) {
activeTimeline->deactivateTimeline();
currentTimeline->activateTimeline();
}
}
void ScreenPlayTimelineManager::setSettings(const std::shared_ptr<Settings>& settings)
{
m_settings = settings;
}
void ScreenPlayTimelineManager::startupFirstTimeline()
{
std::shared_ptr<WallpaperTimelineSection> currentTimeline = findTimelineForCurrentTime();
if (!currentTimeline) {
qCritical() << "No current timeline found. There must always be an active timeline.";
return;
}
currentTimeline->activateTimeline();
}
void ScreenPlayTimelineManager::setGlobalVariables(const std::shared_ptr<GlobalVariables>& globalVariables)
{
m_globalVariables = globalVariables;
}
bool ScreenPlayTimelineManager::deactivateCurrentTimeline()
{
auto oldTimeline = findActiveWallpaperTimelineSection();
if (!oldTimeline) {
qCritical() << "No active timeline found. There must always be an active timeline.";
return false;
}
oldTimeline->deactivateTimeline();
return true;
}
/*!
\brief Change active timeline. We need an extra flags to know if our
timeline is active and out of time range.
*/
bool ScreenPlayTimelineManager::startTimeline()
{
std::shared_ptr<WallpaperTimelineSection> newTimelineSection = findTimelineForCurrentTime();
if (!newTimelineSection) {
qCritical() << "No new timeline found. There must always be an active timeline.";
return false;
}
if (!newTimelineSection->activateTimeline()) {
qCritical() << "Unable activate timeline.";
return false;
}
qInfo() << "Timeline switched successfully.";
return true;
}
/*!
\brief Update m_wallpaperTimelineSectionsList index based on the startTime;
*/
void ScreenPlayTimelineManager::updateIndices()
{
// Sort the vector based on startTime
std::sort(m_wallpaperTimelineSectionsList.begin(), m_wallpaperTimelineSectionsList.end(),
[](const auto& a, const auto& b) {
return a->startTime < b->startTime;
});
// Update the indices based on new order
for (int i = 0; i < m_wallpaperTimelineSectionsList.size(); ++i) {
m_wallpaperTimelineSectionsList[i]->index = i;
}
}
/*!
\brief Adds a new timeline at relative position. We always shrink the timeline at the input
position and append the new one to the left. There must always (lightning) an active
timeline section.
*/
bool ScreenPlayTimelineManager::addTimelineAt(const int index, const float reltiaveLinePosition, QString identifier)
{
// We always get the new endTime
const QString newStopPosition = Util().getTimeString(reltiaveLinePosition);
QTime newStopPositionTime = QTime::fromString(newStopPosition, m_timelineTimeFormat);
if (!newStopPositionTime.isValid()) {
return false;
}
// IMPORTANT: The new element is always on the left. The first
// handle always persists because the
// user can never delete it. It only gets "pushed" further
// to the right, by increasing the size.
// | Insert here
// ˇ
// |-----------------------------------------------|
// 0 ID: AAA
//
//
// |-----------------------|-----------------------|
// 0 ID: BBB 1 - ID: AAA
// We directly get the new index of 0 in this example from qml
auto newTimelineSection = std::make_shared<WallpaperTimelineSection>();
QObject::connect(newTimelineSection.get(), &WallpaperTimelineSection::requestSaveProfiles, this, &ScreenPlayTimelineManager::requestSaveProfiles);
newTimelineSection->settings = m_settings;
newTimelineSection->globalVariables = m_globalVariables;
newTimelineSection->index = index;
newTimelineSection->identifier = identifier;
newTimelineSection->endTime = newStopPositionTime;
// In case we do a full reset, we must set the start time manually
if (m_wallpaperTimelineSectionsList.empty()) {
newTimelineSection->startTime = QTime::fromString("00:00:00", m_timelineTimeFormat);
} else {
// We can use the given index here, because it points
// the the current item at that index, and we have not yet
// added our new timelineSection to our list.
newTimelineSection->startTime = m_wallpaperTimelineSectionsList.at(index)->startTime;
}
const bool isLast = (m_wallpaperTimelineSectionsList.length() - 1) == index;
if (isLast) {
m_wallpaperTimelineSectionsList.last()->startTime = newTimelineSection->endTime;
}
m_wallpaperTimelineSectionsList.append(newTimelineSection);
updateIndices();
printTimelines();
emit requestSaveProfiles();
return true;
}
/*!
\brief Qml function that removes all Timeline sections. Qml then creates
a new default section.
*/
QCoro::Task<bool> ScreenPlayTimelineManager::removeAllTimlineSections()
{
// 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
co_await activeTimelineSection->deactivateTimeline();
}
// After we have disconnected or timed out we remove the wallpaper
m_wallpaperTimelineSectionsList.clear();
// Do not call requestSaveProfiles, because qml will add
// the default timeline after this function
co_return true;
}
/*!
\brief Removes a timeline at a given index. Expands the timeline next to it
to fill the space.
*/
bool ScreenPlayTimelineManager::removeTimelineAt(const int index)
{
printTimelines();
const auto timelineCount = m_wallpaperTimelineSectionsList.size();
if (timelineCount == 0) {
qCritical() << "Timeline empty";
return false;
}
if (timelineCount == 1) {
qCritical() << "Timeline must always have at least one element, that span across the whole timeline from 00:00:00 to 23:59:59.";
return false;
}
auto& wallpapterTimelineSection = m_wallpaperTimelineSectionsList.at(index);
// When we have two timelines, we know that only the first
// timeline can be removed and the second one will then span
// across the whole timeline
// remove <- expand
// |-----------|-----------|
// 0 1
if (timelineCount == 2) {
if (index != 0) {
qCritical() << "Removing the last timeline is not allowed. This must always span the whole timeline";
return false;
}
m_wallpaperTimelineSectionsList.removeAt(index);
m_wallpaperTimelineSectionsList.first()->startTime = QTime::fromString("00:00:00", m_timelineTimeFormat);
updateIndices();
printTimelines();
return true;
}
// Now handle all states where we have more than two wallpaper
// There is no timeline before if we want to remove
// the timeline at index 0. We do not need to make the same
// check for the timelineAfter, because the last timeline
// cannot be deleted
QTime endTime;
if (index == 0) {
endTime = QTime::fromString("00:00:00", m_timelineTimeFormat);
} else {
endTime = m_wallpaperTimelineSectionsList.at(index - 1)->endTime;
}
auto timelineAfter = m_wallpaperTimelineSectionsList.at(index + 1);
// before remove <- expand
// |-----------|-----------|-----------|
// 0 1 2
// Now when removing timeline at index 1, the next (after)
// wallpaper gets the remaining space
timelineAfter->startTime = endTime;
m_wallpaperTimelineSectionsList.removeAt(index);
updateIndices();
printTimelines();
return true;
}
void ScreenPlayTimelineManager::printTimelines()
{
std::cout << "#############################\n";
for (auto& timeline : m_wallpaperTimelineSectionsList) {
std::cout << timeline->index << ": " << timeline->identifier.toStdString() << "\t"
<< timeline->relativePosition
<< " start: " << timeline->startTime.toString().toStdString()
<< " end: " << timeline->endTime.toString().toStdString() << std::endl;
}
}
bool ScreenPlayTimelineManager::setWallpaperAtTimelineIndex(WallpaperData wallpaperData, const int timelineIndex, const QString& identifier)
{
bool found = false;
for (auto& timelineSection : m_wallpaperTimelineSectionsList) {
const bool sameIndex = timelineSection->index == timelineIndex;
const bool sameIdentifier = timelineSection->identifier == identifier;
if (sameIndex && sameIdentifier) {
found = true;
// Check if we already have a wallpaper at the same position or on one or more of the specified monitors
auto it = std::find_if(timelineSection->wallpaperDataList.begin(), timelineSection->wallpaperDataList.end(),
[&wallpaperData](const WallpaperData& existingWallpaper) {
return std::any_of(wallpaperData.monitors.begin(), wallpaperData.monitors.end(),
[&existingWallpaper](int monitor) {
return existingWallpaper.monitors.contains(monitor);
});
});
if (it != timelineSection->wallpaperDataList.end()) {
// Overwrite the existing wallpaper
*it = wallpaperData;
} else {
// Append the new wallpaper data
timelineSection->wallpaperDataList.push_back(wallpaperData);
// Start all wallpaper
timelineSection->activateTimeline();
}
break;
}
}
return found;
}
QJsonArray ScreenPlayTimelineManager::initialSectionsList()
{
QJsonArray sectionPositions;
for (const auto& timelineSection : m_wallpaperTimelineSectionsList) {
sectionPositions.push_back(timelineSection->serialize());
}
return sectionPositions;
}
QJsonArray ScreenPlayTimelineManager::timelineWallpaperList()
{
QJsonArray timelineWallpaperList {};
for (const auto& activeTimelineWallpaper : std::as_const(m_wallpaperTimelineSectionsList)) {
QJsonObject timelineWallpaper;
timelineWallpaper.insert("startTime", activeTimelineWallpaper->startTime.toString());
timelineWallpaper.insert("endTime", activeTimelineWallpaper->endTime.toString());
QJsonArray wallpaper;
// Every timeline section can have multiple wallpaper
for (const auto& wallpaperData : activeTimelineWallpaper->wallpaperDataList) {
wallpaper.append(wallpaperData.serialize());
}
timelineWallpaper.insert("wallpaper", wallpaper);
timelineWallpaperList.append(timelineWallpaper);
}
return timelineWallpaperList;
}
}

View File

@ -1,7 +1,10 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlayUtil/util.h"
#include "util.h"
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
namespace ScreenPlay {
@ -259,7 +262,7 @@ void ScreenPlayWallpaper::setSDKConnection(std::unique_ptr<SDKConnection> connec
std::optional<bool> running = m_processManager.isRunning(m_processID);
if (running.has_value()) {
// qInfo() << "running:" << running.value();
qInfo() << "running:" << running.value();
} else {
qInfo() << "INVALID PID:" << m_processID;
}

View File

@ -1,5 +1,9 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/screenplaywidget.h"
#include "ScreenPlayUtil/util.h"
#include <QCoreApplication>
#include <QDebug>
namespace ScreenPlay {
@ -115,7 +119,7 @@ void ScreenPlayWidget::setSDKConnection(std::unique_ptr<SDKConnection> connectio
std::optional<bool> running = m_processManager.isRunning(m_processID);
if (running.has_value()) {
qInfo() << "running:" << running.value();
// qInfo() << "running:" << running.value();
} else {
qInfo() << "INVALID PID:" << m_processID;
}

View File

@ -1,5 +1,13 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/sdkconnection.h"
#include "ScreenPlay/globalvariables.h"
#include "ScreenPlayUtil/util.h"
#include <QJsonDocument>
#include <QJsonValue>
#include <QLocalServer>
#include <QTimer>
#include <QWebSocketServer>
namespace ScreenPlay {
@ -90,7 +98,7 @@ void ScreenPlay::SDKConnection::readyRead()
emit jsonMessageReceived(doc.object());
} else {
qInfo() << "### Message from: " << m_appID << ": " << msg;
// qInfo() << "### Message from: " << m_appID << ": " << msg;
}
}
}

View File

@ -1,10 +1,33 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/settings.h"
#include "ScreenPlayUtil/util.h"
#include <QFileInfo>
#include <QSysInfo>
#include "ScreenPlay/CMakeVariables.h"
#include "ScreenPlayUtil/util.h"
#include <QDebug>
#include <QDir>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNodeList>
#include <QFile>
#include <QFileInfo>
#include <QFontDatabase>
#include <QGuiApplication>
#include <QIODevice>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QPair>
#include <QProcess>
#include <QProcessEnvironment>
#include <QQmlPropertyMap>
#include <QStandardPaths>
#include <QSysInfo>
#include <QTextStream>
#include <QThread>
#include <QUrl>
#include <QVector>
#include <QtConcurrent/QtConcurrent>
#include <QtGlobal>
#ifdef Q_OS_WIN
#include <qt_windows.h>

View File

@ -1,10 +1,147 @@
#include "wallpapertimelinesection.h"
#include "ScreenPlay/wallpapertimelinesection.h"
#include "ScreenPlay/screenplaywallpaper.h"
#include "ScreenPlay/wallpaperdata.h"
#include "ScreenPlayUtil/util.h"
#include <QDebug>
#include <QDir>
#include <QFileInfoList>
#include <QGuiApplication>
#include <QObject>
bool ScreenPlay::WallpaperTimelineSection::close(){
for (auto& wallpaper : activeWallpaperList) {
wallpaper->close();
namespace ScreenPlay {
bool WallpaperTimelineSection::containsTime(const QTime& time) const
{
if (endTime < startTime) { // Timeline spans midnight
return (time >= startTime || time < endTime);
} else {
return (time >= startTime && time < endTime);
}
}
QJsonObject WallpaperTimelineSection::serialize() const
{
QJsonObject data;
data.insert("identifier", identifier);
data.insert("index", index);
data.insert("relativePosition", relativePosition);
data.insert("startTime", startTime.toString());
data.insert("endTime", endTime.toString());
// Serialize vector<WallpaperData>
QJsonArray wallpaperDataArray;
for (const auto& wallpaper : wallpaperDataList) {
QJsonObject wallpaperObject = wallpaper.serialize(); // Assuming WallpaperData has a serialize method
wallpaperDataArray.append(wallpaperObject);
}
data.insert("wallpaperData", wallpaperDataArray);
// Serialize vector<std::shared_ptr<ScreenPlayWallpaper>>
// QJsonArray activeWallpaperArray;
// for (const auto& wallpaper : activeWallpaperList) {
// QJsonObject wallpaperObject = wallpaper->serialize(); // Assuming ScreenPlayWallpaper has a serialize method
// activeWallpaperArray.append(wallpaperObject);
// }
// data.insert("activeWallpaperList", activeWallpaperArray);
return data;
}
// Start all ScreenPlayWallpaper processes of this current timeline
bool WallpaperTimelineSection::activateTimeline()
{
status = Status::Active;
// TODO: this can be called if the timeline is already active.
// Add a check to only launch new
for (auto& wallpaperData : wallpaperDataList) {
const int screenCount = QGuiApplication::screens().count();
QJsonArray monitors;
for (const int index : wallpaperData.monitors) {
monitors.append(index);
if (index > screenCount - 1) {
qWarning() << "Configuration contains invalid monitor with index: " << index << " screen count: " << screenCount;
return false;
}
}
// Remove file:///
wallpaperData.absolutePath = QUrl::fromUserInput(wallpaperData.absolutePath).toLocalFile();
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<ScreenPlayWallpaper>(
globalVariables,
appID,
wallpaperData,
settings);
QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestSave, this, [this]() {
// &ScreenPlayManager::requestSaveProfiles
emit requestSaveProfiles();
});
QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::requestClose, this, []() {
// , &ScreenPlayManager::removeWallpaper);
});
// QObject::connect(screenPlayWallpaper.get(), &ScreenPlayWallpaper::error, this, this, []() {
// // , &ScreenPlayManager::displayErrorPopup);
// });
if (!screenPlayWallpaper->start()) {
return false;
}
// m_monitorListModel->setWallpaperMonitor(wallpaper, wallpaperData.monitors);
activeWallpaperList.push_back(screenPlayWallpaper);
}
activeWallpaperList.clear();
return true;
}
QCoro::Task<bool> WallpaperTimelineSection::deactivateTimeline()
{
status = Status::Closing;
for (auto& activeWallpaper : activeWallpaperList) {
activeWallpaper->close();
}
QTimer timer;
timer.start(250);
const int maxRetries = 30;
for (int i = 1; i <= maxRetries; ++i) {
// Wait for the timer to tick
co_await timer;
bool wallpaperStillActive = false;
for (auto& activeWallpaper : activeWallpaperList) {
if (activeWallpaper->isConnected()) {
wallpaperStillActive = true;
break;
}
}
if (!wallpaperStillActive) {
status = Status::Inactive;
// Clear all active wallpaper connections, but do not
// clear WallpaperData in case we need it again later
activeWallpaperList.clear();
co_return true;
}
}
co_return false;
}
}

View File

@ -1,5 +1,23 @@
// SPDX-License-Identifier: LicenseRef-EliasSteurerTachiom OR AGPL-3.0-only
#include "ScreenPlay/wizards.h"
#include "ScreenPlay/CMakeVariables.h"
#include "ScreenPlayUtil/util.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QFont>
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QLinearGradient>
#include <QPainter>
#include <QTextOption>
#include <QTime>
#include <QTimer>
#include <QtMath>
namespace ScreenPlay {
/*!

View File

@ -134,6 +134,7 @@ public:
Q_INVOKABLE void requestAllLicenses();
Q_INVOKABLE void requestDataProtection();
Q_INVOKABLE bool fileExists(const QString& filePath) const;
Q_INVOKABLE float calculateRelativePosition(const QTime& endTime) const;
Q_INVOKABLE void setNavigation(QString nav)
{
@ -151,6 +152,11 @@ public:
emit requestToggleWallpaperConfiguration();
}
/*!
\brief Converts a range from 0.0f - 1.0f to 00:00:00 0 23:59:59
*/
Q_INVOKABLE QString getTimeString(double relativeLinePosition);
signals:
void extractionProgressChanged(QString file, int proc, int total, qint64 br, qint64 bt);
void extractionFinished();

View File

@ -608,6 +608,58 @@ bool Util::fileExists(const QString& filePath) const
return file.isFile();
}
float Util::calculateRelativePosition(const QTime& endTime) const
{
QTime startTime(0, 0, 0); // Start of the day
QTime maxTime(23, 59, 59); // End of the day range
// Total number of seconds from startTime to maxTime
int totalSeconds = startTime.secsTo(maxTime);
// Seconds from startTime to the given endTime
int endTimeSeconds = startTime.secsTo(endTime);
// Calculate the relative position
float relativePosition = static_cast<float>(endTimeSeconds) / totalSeconds;
// Round to four decimal places
return qRound(relativePosition * 10000.0) / 10000.0;
}
QString Util::getTimeString(double relativeLinePosition)
{
if (relativeLinePosition == 1.0) {
// We overwrite the endTime here
return "23:59:59";
}
const double totalHours = relativeLinePosition * 24;
int hours = static_cast<int>(std::floor(totalHours)); // Gets the whole hour part
double fractionalHours = totalHours - hours;
int minutes = static_cast<int>(std::floor(fractionalHours * 60)); // Calculates the minutes
double fractionalMinutes = fractionalHours * 60 - minutes;
int seconds = static_cast<int>(std::round(fractionalMinutes * 60)); // Calculates the seconds
// Adjust minutes and seconds if seconds rolled over to 60
if (seconds == 60) {
seconds = 0;
minutes += 1;
}
// Adjust hours and minutes if minutes rolled over to 60
if (minutes == 60) {
minutes = 0;
hours += 1;
}
// Ensure hours wrap correctly at 24
if (hours == 24) {
hours = 0;
}
// Format the output to "HH:MM:SS"
return QString("%1:%2:%3").arg(hours, 2, 10, QChar('0')).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
}
/*!
\brief Takes reference to \a obj. If the copy of the thumbnail is successful,
it adds the corresponding settings entry to the json object reference.

View File

@ -0,0 +1,124 @@
# File: Tools/pre-commit/ollama_code_review.py
import sys
import asyncio
import subprocess
import re
from typing import Optional, Sequence, List, Tuple
from ollama import AsyncClient
MODEL_NAME = 'glm4' # You can change this to any model you prefer
ANALYSIS_TIMEOUT = 60 # Timeout in seconds for each file analysis
def get_git_staged_diff() -> str:
"""Get the git diff of staged changes."""
try:
result = subprocess.run(['git', 'diff', '--cached'], capture_output=True, text=True, check=True)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error getting git diff: {e}")
sys.exit(1)
def parse_git_diff(diff: str) -> List[Tuple[str, str]]:
"""Parse git diff and return a list of (filename, file_diff) tuples."""
file_diffs = []
current_file = None
current_diff = []
for line in diff.split('\n'):
if line.startswith('diff --git'):
if current_file:
file_diffs.append((current_file, '\n'.join(current_diff)))
current_file = re.search(r'b/(.+)$', line).group(1)
current_diff = [line]
elif current_file:
current_diff.append(line)
if current_file:
file_diffs.append((current_file, '\n'.join(current_diff)))
return file_diffs
async def analyze_file_with_ollama(filename: str, file_diff: str) -> None:
"""Send a single file diff to Ollama for analysis and stream the response."""
prompt = f"""
Analyze the following git diff for the file '{filename}'.
Focus only on:
1. Copy-paste errors: Look for actual repeated code blocks or patterns that suggest copy-pasting, ignoring the '+' and '-' at the start of lines.
2. Spelling issues: Identify any misspelled words or typos in comments and strings. Ignore variable names, function names, and other code identifiers.
Use the following emoji system to indicate the severity of issues:
🟢 - No issues found
🟡 - Minor issues (e.g., a few typos or small repeated code segments)
🔴 - Significant issues (e.g., large blocks of copied code or numerous spelling errors)
Start your response with one of these emojis to indicate the overall status.
Then provide a concise summary of findings, if any. If no issues are found, just state that.
Git diff for {filename}:
{file_diff}
"""
try:
client = AsyncClient()
print(f"\nAnalyzing {file_diff}:")
full_response = []
async def process_stream():
async for response in await client.generate(model=MODEL_NAME, prompt=prompt, stream=True):
chunk = response['response']
full_response.append(chunk)
print(chunk, end='', flush=True)
await asyncio.wait_for(process_stream(), timeout=ANALYSIS_TIMEOUT)
print() # New line after complete response
except asyncio.TimeoutError:
print(f"\nAnalysis for {filename} timed out after {ANALYSIS_TIMEOUT} seconds.")
except Exception as e:
print(f"\nError communicating with Ollama for {filename}: {e}")
async def check_and_pull_model() -> bool:
"""Check if the model exists, and pull it if it doesn't."""
client = AsyncClient()
try:
# Check if the model exists
models = await client.list()
if MODEL_NAME not in [model['name'] for model in models['models']]:
print(f"Model '{MODEL_NAME}' not found. Pulling it now...")
await client.pull(model=MODEL_NAME)
print(f"Model '{MODEL_NAME}' has been pulled successfully.")
return True
except Exception as e:
print(f"Error checking or pulling the model: {e}")
return False
async def main(argv: Optional[Sequence[str]] = None) -> int:
# First, check and pull the model if necessary
if not await check_and_pull_model():
print("Failed to ensure the model is available. Exiting.")
return 1
full_diff = get_git_staged_diff()
if not full_diff:
print("No staged changes to analyze.")
return 0
file_diffs = parse_git_diff(full_diff)
for filename, file_diff in file_diffs:
await analyze_file_with_ollama(filename, file_diff)
# Ask user if they want to proceed with the commit
while True:
answer = input("\nDo you want to proceed with the commit? (y/n): ").lower()
if answer in ['y', 'yes']:
return 0 # Proceed with commit
elif answer in ['n', 'no']:
return 1 # Abort commit
else:
print("Please answer 'y' or 'n'.")
if __name__ == "__main__":
sys.exit(asyncio.run(main()))