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

Fix adding, removing and moving of timelines

This commit is contained in:
Elias Steurer 2024-05-21 10:18:32 +02:00
parent 0bd9a47fbe
commit 99b0647f81
9 changed files with 238 additions and 134 deletions

View File

@ -4,10 +4,12 @@
#include <QObject>
#include <QPoint>
#include <QVariantMap>
#include <QProcess>
#include <QtWebSockets/QWebSocket>
#include "ScreenPlayUtil/projectfile.h"
#include "ScreenPlayUtil/util.h"
#include "globalvariables.h"
#include "installedlistmodel.h"
#include "monitorlistmodel.h"
@ -16,6 +18,7 @@
#include "screenplaywallpaper.h"
#include "screenplaywidget.h"
#include "settings.h"
#include <iostream>
#include <memory>
#include <optional>
@ -40,8 +43,6 @@ public:
int activeWallpaperCounter() const { return m_activeWallpaperCounter; }
int activeWidgetsCounter() const { return m_activeWidgetsCounter; }
std::shared_ptr<ScreenPlayWallpaper> startWallpaper(
WallpaperData wallpaperData,
const bool saveToProfilesConfigFile);
@ -50,7 +51,6 @@ public:
Q_INVOKABLE bool removeAllWidgets(bool saveToProfile = false);
Q_INVOKABLE bool removeWallpaperAt(const int index);
// Timeline wallpaper updates:
// 1. Disable timeline updates
// Optional: 1.1 Insert new timeline
@ -58,93 +58,151 @@ public:
Q_INVOKABLE ScreenPlayWallpaper* getWallpaperByAppID(const QString& appID);
void updateTimelineStartTime(const int index){
}
void updateTimelineEndTime(const int index){
}
// We always handle the endTimeString, because it is the handle for the
// timeline. The last, default, timeline does not have a handle.
Q_INVOKABLE bool moveTimelineAt(
const int index,
QString endTimeString){
const QString identifier,
const float relativePosition,
QString positionTimeString)
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto& wallpapterTimelineSection = m_wallpaperTimelineSectionsList.at(index);
QTime endTime = QTime::fromString(endTimeString, "hh:mm");
if (!endTime.isValid()){
QTime newPositionTime = QTime::fromString(positionTimeString, "hh:mm");
if (!newPositionTime.isValid()) {
qWarning() << "Unable to move with invalid time:" << positionTimeString;
return false;
}
wallpapterTimelineSection->endTime = endTime;
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){
if (index <= timelineCount) {
auto& wallpapterTimelineSectionNext = m_wallpaperTimelineSectionsList.at(index + 1);
wallpapterTimelineSectionNext->startTime =endTime;
wallpapterTimelineSectionNext->startTime = newPositionTime;
}
printTimelines();
return true;
}
Q_INVOKABLE QString getTimeString(double relativeLinePosition)
{
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'));
}
void updateIndices()
{
// Sort the vector based on relativePosition
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;
}
}
Q_INVOKABLE bool addTimelineAt(
const int index,
QString startTimeString,
QString endTimeString,
QString identitfier) {
const float reltiaveLinePosition,
QString identifier)
{
m_contentTimer.stop();
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
QTime startTime = QTime::fromString(startTimeString, m_timelineTimeFormat);
if (!startTime.isValid()){
// We always get the new endTime
const QString newStopPosition = getTimeString(reltiaveLinePosition);
QTime newStopPositionTime = QTime::fromString(newStopPosition, m_timelineTimeFormat);
if (!newStopPositionTime.isValid()) {
return false;
}
QTime endTime = QTime::fromString(endTimeString, m_timelineTimeFormat);
if (!endTime.isValid()){
return false;
}
auto timelineSection = std::make_shared<WallpaperTimelineSection>();
timelineSection->index = index;
timelineSection->startTime = startTime;
timelineSection->endTime = endTime;
m_wallpaperTimelineSectionsList.append(timelineSection);
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 =endTime;
// 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;
// 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;
}
// Only update the next timeline startTime
// if we are not end last wallpaper, that always
// must end at 24:00
if(index > 0){
auto& wallpapterTimelineSectionBefore = m_wallpaperTimelineSectionsList.at(index - 1);
wallpapterTimelineSectionBefore->endTime =startTime;
}
m_wallpaperTimelineSectionsList.append(newTimelineSection);
updateIndices();
printTimelines();
return true;
}
Q_INVOKABLE bool removeTimelineAt(const int index){
Q_INVOKABLE bool removeTimelineAt(const int index)
{
printTimelines();
const auto timelineCount = m_wallpaperTimelineSectionsList.size();
if(timelineCount == 0){
qCritical()<< "Timeline empty";
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.";
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 updateTimer = qScopeGuard([this] { m_contentTimer.start(); });
auto& wallpapterTimelineSection = m_wallpaperTimelineSectionsList.at(index);
@ -154,49 +212,62 @@ public:
// 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";
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);
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
// If we are the frist wallpaper, then the next in line
// wallpaper gets the remainging time
// remove <- expand
// 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
if(index == 0){
}
if(timelineCount > 2){
auto timelineBefore = m_wallpaperTimelineSectionsList.at(index - 1);
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 = timelineBefore->endTime;
m_wallpaperTimelineSectionsList.removeAt(index);
return true;
}
// 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;
printTimelines();
return true;
}
void printTimelines(){
qInfo() << "# Timlines:";
for (auto&timeline : m_wallpaperTimelineSectionsList) {
std::cout << timeline->index << "/" << m_wallpaperTimelineSectionsList.length() <<" start: " << timeline->startTime.toString().toStdString() << " end: "<<timeline->endTime.toString().toStdString() << std::endl ;
void 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;
}
}
Q_INVOKABLE QVariantMap initialStopPositions()
{
QVariantMap sectionPositions;
for (const auto& timelineSection : m_wallpaperTimelineSectionsList) {
sectionPositions.insert({timelineSection->identifier},{timelineSection->relativePosition});
}
return sectionPositions;
}
Q_INVOKABLE bool setWallpaperAtTimelineIndex(
const ScreenPlay::ContentTypes::InstalledType type,
const ScreenPlay::Video::FillMode fillMode,
@ -227,14 +298,6 @@ public:
Q_INVOKABLE bool setAllWallpaperValue(const QString& key, const QString& value);
Q_INVOKABLE bool setWallpaperValue(const QString& appID, const QString& key, const QString& value);
Q_INVOKABLE QVector<float> getRelativeSectionPositions(){
QVector<float> sectionPositions;
for (const auto &timelineSection : m_wallpaperTimelineSectionsList) {
sectionPositions.append(timelineSection->relativePosition);
}
return sectionPositions;
}
bool removeWallpaperAtTimelineIndex(const int timelineIndex, const int monitorIndex);
signals:
void activeWallpaperCounterChanged(int activeWallpaperCounter);

View File

@ -40,7 +40,7 @@ class ScreenPlayWallpaper;
struct WallpaperTimelineSection {
bool active = false;
QString m_identifier;
QString identifier;
int index = 0; // Needed to check
float relativePosition = 0.0f;
QTime startTime;

View File

@ -21,9 +21,11 @@ Item {
hours = 0;
}
return hours + ":" + minutes.toString().padStart(2, '0')
// Format hours and minutes to always have two digits
return hours.toString().padStart(2, '0') + ":" + minutes.toString().padStart(2, '0');
}
property real lineMinimum: .5
property real lineMaximum: .5
property bool isLast: false

View File

@ -4,6 +4,7 @@ import QtQuick.Controls
Rectangle {
id: root
property int index: 0
property string identifier
property bool selected: false
property bool isLast: false
property alias text: text.text
@ -61,6 +62,10 @@ Rectangle {
root.lineSelected(root.index)
}
}
Text {
anchors.centerIn: parent
text: root.index + " - "+ root.identifier
}
}
ToolButton {
text:"❌"

View File

@ -3,6 +3,8 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import ScreenPlayApp
import ScreenPlayUtil
Control {
id: root
@ -17,23 +19,13 @@ Control {
timeLine.removeAll()
}
function generateRandomString(length) {
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var result = '';
for (var i = 0; i < length; i++) {
var index = Math.floor(Math.random() * characters.length);
result += characters.charAt(index);
}
return result;
}
Component {
id: sectionComp
QtObject {
property string id
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
}
@ -47,7 +39,7 @@ Control {
onWidthChanged: timeLine.updatePositions()
Component.onCompleted: {
const initialStopPositions = App.screenPlayManager.getRelativeSectionPositions()
const initialStopPositions = App.screenPlayManager.initialStopPositions()
createAllSections(initialStopPositions)
}
@ -70,15 +62,20 @@ Control {
}
function createAllSections(initialStopPositions) {
for (let index in initialStopPositions) {
addSection(initialStopPositions[Number(index)])
for (let identifier in initialStopPositions) {
let stopPosition = initialStopPositions[identifier];
addSection(identifier, stopPosition);
}
}
function addSection(stopPosition) {
// 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.
function addSection(identifier, stopPosition) {
print("stopPosition", stopPosition)
//if (!isFloat(stopPosition))
// console.error(typeof (stopPosition), stopPosition)
// Make sure to limit float precision
const fixedStopPosition = stopPosition
print("addSection at: ", fixedStopPosition)
@ -86,9 +83,8 @@ Control {
console.error("Invalid position:", fixedStopPosition)
return
}
let sectionObject = sectionComp.createObject(timeLine, {
"id" : root.generateRandomString(8),
"identifier" : identifier,
"relativeLinePosition": fixedStopPosition
})
timeLine.sectionsList.push(sectionObject)
@ -98,13 +94,14 @@ Control {
})
const index = timeLine.sectionsList.indexOf(sectionObject)
console.log("Addsection:", index)
createSection(index, fixedStopPosition, sectionObject)
createSection(index, fixedStopPosition, sectionObject,identifier)
updatePositions()
return sectionObject
}
function createSection(index, stopPosition, section) {
console.log("Adding at:", index, stopPosition)
function createSection(index, stopPosition, section,identifier) {
console.log("Adding at:", index, stopPosition, identifier)
//console.assert(isFloat(stopPosition))
let haComponent = Qt.createComponent("LineHandle.qml")
@ -137,16 +134,32 @@ Control {
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 onHandleMoved(handle){
function sectionFromHandle(lineHandle){
for (let i = 0; i < timeLine.sectionsList.length; i++) {
if(timeLine.sectionsList[i].lineHandle === lineHandle)
return timeLine.sectionsList[i]
}
return null
}
function onHandleMoved(lineHandle){
updatePositions()
App.screenPlayManager.moveTimelineAt(handle.linePosition, handle.timeString)
const section = sectionFromHandle(lineHandle)
if (section === null){
print(lineHandle.linePosition)
console.error("Unable to match handle to section list")
return
}
App.screenPlayManager.moveTimelineAt(section.index, section.identifier, lineHandle.linePosition, lineHandle.timeString)
}
function lineIndicatorSelected(selectedIndicatorindex) {
@ -161,9 +174,15 @@ Control {
// We must update all indexes when removing/adding an element
function updateIndicatorIndexes() {
if(timeLine.sectionsList === null || timeLine.sectionsList === undefined)
return
timeLine.sectionsList.sort(function (a, b) {
return a.relativeLinePosition - b.relativeLinePosition
})
for (let i = 0; i < timeLine.sectionsList.length; i++) {
timeLine.sectionsList[i].index = i
timeLine.sectionsList[i].lineIndicator.index = i
//print("updateIndicatorIndexes:", timeLine.sectionsList[i].index, timeLine.sectionsList[i].relativeLinePosition)
}
}
@ -217,16 +236,16 @@ Control {
//timeLine.sectionsList[i].relativeLinePosition =prevPos / timeLine.width
print("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeLine.sectionsList[i].relativeLinePosition)
//print("sections: ", i, "prev minimum ",prevPos,"next maximum", nextPos, timeLine.sectionsList[i].relativeLinePosition)
}
for (let i = 0; i < timeLine.sectionsList.length; i++) {
let section = timeLine.sectionsList[i]
section.relativeLinePosition = section.lineHandle.linePosition
}
updateIndicatorIndexes()
updateIndicatorPositions()
updateLastHandle()
updateIndicatorColor()
updateIndicatorIndexes()
}
@ -257,7 +276,7 @@ Control {
function updateIndicatorPositions() {
for (let i = 0; i < timeLine.sectionsList.length; i++) {
const lineIndicator = timeLine.sectionsList[i].lineIndicator
print(i, lineIndicator.x, lineIndicator.width, timeLine.sectionsList[i].relativeLinePosition)
//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(
@ -318,7 +337,12 @@ Control {
onClicked: {
const p = this.x / timeLine.width
const position = p.toFixed(2)
timeLine.addSection(position)
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

@ -63,7 +63,7 @@ void ScreenPlayManager::checkActiveWallpaperTimeline()
{
auto activeTimelineSection = findActiveSection();
if (!activeTimelineSection) {
//qCritical() << "There must always be (lightning) an active timline";
// qCritical() << "There must always be (lightning) an active timline";
return;
}
@ -72,6 +72,8 @@ void ScreenPlayManager::checkActiveWallpaperTimeline()
if (activeTimelineSection == m_activeWallpaperTimeline) {
return;
}
// TODO
return;
// This means the timeline section has never been active
// and we must create new wallpaper
@ -677,6 +679,13 @@ bool ScreenPlayManager::loadProfiles()
containsInvalidData = true;
break;
}
wallpaperDataOpt.value()->index = m_wallpaperTimelineSectionsList.length();
wallpaperDataOpt.value()->identifier = util.generateRandomString(4);
qInfo() << wallpaperDataOpt.value()->index
<< wallpaperDataOpt.value()->startTime
<< wallpaperDataOpt.value()->endTime;
m_wallpaperTimelineSectionsList.append(wallpaperDataOpt.value());
}
@ -693,7 +702,6 @@ bool ScreenPlayManager::loadProfiles()
if (containsInvalidData)
saveProfiles();
checkActiveWallpaperTimeline();
m_contentTimer.start();
@ -709,20 +717,21 @@ bool ScreenPlayManager::loadProfiles()
* \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 calculateRelativePosition(const QTime& endTime) {
QTime startTime(0, 0, 0); // Start of the day
float 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
// Total number of seconds from startTime to maxTime
int totalSeconds = startTime.secsTo(maxTime);
// Seconds from startTime to the given endTime
// Seconds from startTime to the given endTime
int endTimeSeconds = startTime.secsTo(endTime);
// Calculate the relative position
// Calculate the relative position
float relativePosition = static_cast<float>(endTimeSeconds) / totalSeconds;
// Round to four decimal places
// Round to four decimal places
return qRound(relativePosition * 10000.0) / 10000.0;
}
std::optional<std::shared_ptr<WallpaperTimelineSection>> ScreenPlayManager::loadTimelineWallpaperConfig(const QJsonObject& timelineObj)

View File

@ -257,7 +257,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;
}
@ -295,6 +295,7 @@ bool ScreenPlayWallpaper::replace(
emit requestSave();
return success;
}
}
#include "moc_screenplaywallpaper.cpp"

View File

@ -17,11 +17,11 @@ public:
explicit QQmlSmartListWrapper(QObject* object, const int reserve = 0)
: QmlListPropertyType(object,
&m_items,
&SmartListWrapperType::callbackAppend,
&SmartListWrapperType::callbackCount,
&SmartListWrapperType::callbackAt,
&SmartListWrapperType::callbackClear)
&m_items,
&SmartListWrapperType::callbackAppend,
&SmartListWrapperType::callbackCount,
&SmartListWrapperType::callbackAt,
&SmartListWrapperType::callbackClear)
{
if (reserve > 0) {
m_items.reserve(reserve);

View File

@ -112,7 +112,7 @@ public:
QString toString(const QStringList& list) const;
std::optional<QVector<int>> parseStringToIntegerList(const QString& string) const;
float roundDecimalPlaces(const float number) const;
QString generateRandomString(quint32 length = 32);
Q_INVOKABLE QString generateRandomString(quint32 length = 32);
QString executableAppEnding();
QString executableBinEnding();
QStringList getAvailableWallpaper() const;