diff --git a/ScreenPlay/CMakeLists.txt b/ScreenPlay/CMakeLists.txt index edcf9319..73391ae7 100644 --- a/ScreenPlay/CMakeLists.txt +++ b/ScreenPlay/CMakeLists.txt @@ -185,6 +185,7 @@ set(RESOURCES assets/icons/icon_widgets.svg assets/icons/icon_window.svg assets/icons/item_banner_new.svg + assets/icons/icon_edit.svg assets/icons/monitor_setup.svg assets/icons/steam_default_avatar.png assets/images/Early_Access.png diff --git a/ScreenPlay/inc/public/ScreenPlay/util.h b/ScreenPlay/inc/public/ScreenPlay/util.h index 2d2eaf59..2502d653 100644 --- a/ScreenPlay/inc/public/ScreenPlay/util.h +++ b/ScreenPlay/inc/public/ScreenPlay/util.h @@ -58,7 +58,8 @@ class Util : public QObject { Q_PROPERTY(QString debugMessages READ debugMessages NOTIFY debugMessagesChanged) public: - Util(); + Util( + const std::shared_ptr& globalVariables); ~Util(); QString debugMessages() const { return m_debugMessages; } @@ -82,6 +83,7 @@ public slots: void openFolderInExplorer(const QString& url) const; QString toLocal(const QString& url) const; bool exportProject(QString contentPath, QString exportFileName); + bool openGodotEditor(QString contentPath) const; bool importProject(QString archivePath, QString extractionPath); void requestAllLicenses(); void requestDataProtection(); @@ -124,6 +126,7 @@ private: QFuture m_requestAllLicensesFuture; std::unique_ptr m_compressor; std::unique_ptr m_extractor; + const std::shared_ptr& m_globalVariables; }; } diff --git a/ScreenPlay/inc/public/ScreenPlay/wizards.h b/ScreenPlay/inc/public/ScreenPlay/wizards.h index 93ba97c0..aff61fc6 100644 --- a/ScreenPlay/inc/public/ScreenPlay/wizards.h +++ b/ScreenPlay/inc/public/ScreenPlay/wizards.h @@ -34,9 +34,11 @@ namespace ScreenPlay { class Wizards : public QObject { Q_OBJECT QML_ELEMENT + QML_UNCREATABLE("CPP ONLY") public: - explicit Wizards(const std::shared_ptr& globalVariables, QObject* parent = nullptr); - Wizards() { } + explicit Wizards( + const std::shared_ptr& globalVariables, + QObject* parent = nullptr); enum class WizardResult { Ok, diff --git a/ScreenPlay/qml/Installed/Installed.qml b/ScreenPlay/qml/Installed/Installed.qml index d3b39704..1c4c9470 100644 --- a/ScreenPlay/qml/Installed/Installed.qml +++ b/ScreenPlay/qml/Installed/Installed.qml @@ -25,23 +25,23 @@ Item { function checkIsContentInstalled() { if (App.installedListModel.count === 0) { - loaderHelp.active = true; - gridView.footerItem.isVisible = true; - gridView.visible = false; - navWrapper.visible = false; + loaderHelp.active = true + gridView.footerItem.isVisible = true + gridView.visible = false + navWrapper.visible = false } else { - loaderHelp.active = false; - gridView.footerItem.isVisible = false; - refresh = false; - gridView.contentY = -82; - gridView.visible = true; - navWrapper.visible = true; + loaderHelp.active = false + gridView.footerItem.isVisible = false + refresh = false + gridView.contentY = -82 + gridView.visible = true + navWrapper.visible = true } } StackView.onActivated: { - navWrapper.state = "in"; - checkIsContentInstalled(); + navWrapper.state = "in" + checkIsContentInstalled() } Action { @@ -51,12 +51,12 @@ Item { Connections { function onInstalledLoadingFinished() { - checkIsContentInstalled(); + checkIsContentInstalled() } function onCountChanged(count) { if (count === 0) - checkIsContentInstalled(); + checkIsContentInstalled() } target: App.installedListModel @@ -71,7 +71,7 @@ Item { Connections { function onSortChanged() { - gridView.positionViewAtBeginning(); + gridView.positionViewAtBeginning() } target: App.installedListFilter @@ -128,12 +128,12 @@ Item { } onContentYChanged: { if (contentY <= -180) - gridView.headerItem.isVisible = true; + gridView.headerItem.isVisible = true else - gridView.headerItem.isVisible = false; + gridView.headerItem.isVisible = false //Pull to refresh if (contentY <= -180 && !refresh && !isDragging) - App.installedListModel.reset(); + App.installedListModel.reset() } anchors { @@ -150,11 +150,11 @@ Item { opacity: 0 onIsVisibleChanged: { if (isVisible) { - txtHeader.color = Material.accent; - txtHeader.text = qsTr("Refreshing!"); + txtHeader.color = Material.accent + txtHeader.text = qsTr("Refreshing!") } else { - txtHeader.color = "gray"; - txtHeader.text = qsTr("Pull to refresh!"); + txtHeader.color = "gray" + txtHeader.text = qsTr("Pull to refresh!") } } @@ -162,7 +162,7 @@ Item { interval: 150 running: true onTriggered: { - animFadeIn.start(); + animFadeIn.start() } } @@ -206,7 +206,7 @@ Item { interval: 400 running: true onTriggered: { - animFadeInTxtFooter.start(); + animFadeInTxtFooter.start() } } @@ -236,15 +236,23 @@ Item { isScrolling: gridView.isScrolling onOpenContextMenu: function (position) { // Set the menu to the current item informations - contextMenu.publishedFileID = delegate.publishedFileID; - contextMenu.absoluteStoragePath = delegate.absoluteStoragePath; - contextMenu.fileName = delegate.customTitle; - const pos = delegate.mapToItem(root, position.x, position.y); + contextMenu.publishedFileID = delegate.publishedFileID + contextMenu.absoluteStoragePath = delegate.absoluteStoragePath + contextMenu.fileName = delegate.customTitle + contextMenu.type = delegate.type + print(delegate.publishedFileID) + if(contextMenu.godotItem) + contextMenu.godotItem.destroy() + const pos = delegate.mapToItem(root, position.x, position.y) // Disable duplicate opening. The can happen if we // call popup when we are in the closing animtion. if (contextMenu.visible || contextMenu.opened) - return; - contextMenu.popup(pos.x, pos.y); + return + if (delegate.type === InstalledType.GodotWallpaper) { + contextMenu.godotItem = editGodotWallpaperComp.createObject() + contextMenu.insertItem(0, contextMenu.godotItem) + } + contextMenu.popup(pos.x, pos.y) } } @@ -252,21 +260,37 @@ Item { snapMode: ScrollBar.SnapOnRelease } } + Component { + id: editGodotWallpaperComp + MenuItem { + text: qsTr("Edit Wallpaper") + objectName: "editWallpaper" + enabled: contextMenu.type === InstalledType.GodotWallpaper + icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_edit.svg" + onClicked: { + App.util.openGodotEditor(contextMenu.absoluteStoragePath) + } + } + } Menu { id: contextMenu objectName: "installedItemContextMenu" // Must be var to support 64-bit size! property var publishedFileID: 0 + property var type: 0 property url absoluteStoragePath property string fileName + // We need to dynamically add this menu item + // if it is a Godot Wallpaper, see onOpenContextMenu + property var godotItem MenuItem { text: qsTr("Open containing folder") objectName: "openFolder" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_folder_open.svg" onClicked: { - App.util.openFolderInExplorer(contextMenu.absoluteStoragePath); + App.util.openFolderInExplorer(contextMenu.absoluteStoragePath) } } @@ -275,10 +299,12 @@ Item { objectName: enabled ? "removeItem" : "removeWorkshopItem" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_import_export_.svg" onClicked: { - exportFileDialog.absoluteStoragePath = contextMenu.absoluteStoragePath; - let urlFileName = QCore.StandardPaths.writableLocation(QCore.StandardPaths.DesktopLocation) + "/" + contextMenu.fileName + ".screenplay"; - exportFileDialog.currentFile = urlFileName; - exportFileDialog.open(); + exportFileDialog.absoluteStoragePath = contextMenu.absoluteStoragePath + let urlFileName = QCore.StandardPaths.writableLocation( + QCore.StandardPaths.DesktopLocation) + "/" + + contextMenu.fileName + ".screenplay" + exportFileDialog.currentFile = urlFileName + exportFileDialog.open() } } @@ -286,18 +312,21 @@ Item { text: enabled ? qsTr("Remove Item") : qsTr("Remove via Workshop") objectName: enabled ? "removeItem" : "removeWorkshopItem" icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_delete.svg" - enabled: contextMenu.publishedFileID === 0 || !App.settings.steamVersion + enabled: contextMenu.publishedFileID === 0 + || !App.settings.steamVersion onClicked: { - deleteDialog.open(); + deleteDialog.open() } } MenuItem { text: qsTr("Open Workshop Page") - enabled: contextMenu.publishedFileID !== 0 && App.settings.steamVersion + enabled: contextMenu.publishedFileID !== 0 + && App.settings.steamVersion icon.source: "qrc:/qml/ScreenPlayApp/assets/icons/icon_steam.svg" onClicked: { - Qt.openUrlExternally("steam://url/CommunityFilePage/" + contextMenu.publishedFileID); + Qt.openUrlExternally( + "steam://url/CommunityFilePage/" + contextMenu.publishedFileID) } } } @@ -310,8 +339,9 @@ Item { modalSource: root.modalSource anchors.centerIn: Overlay.overlay onAccepted: { - root.sidebar.clear(); - App.installedListModel.deinstallItemAt(contextMenu.absoluteStoragePath); + root.sidebar.clear() + App.installedListModel.deinstallItemAt( + contextMenu.absoluteStoragePath) } } @@ -320,7 +350,7 @@ Item { fileMode: FileDialog.SaveFile property string absoluteStoragePath onAccepted: { - exportFileProgressDialog.open(); + exportFileProgressDialog.open() } } @@ -333,7 +363,9 @@ Item { modalSource: root.modalSource closePolicy: Popup.NoAutoClose onOpened: { - const success = App.util.exportProject(exportFileDialog.absoluteStoragePath, exportFileDialog.currentFile); + const success = App.util.exportProject( + exportFileDialog.absoluteStoragePath, + exportFileDialog.currentFile) } onClosed: exportProgressBar.value = 0 ColumnLayout { @@ -358,10 +390,10 @@ Item { id: exportConnections target: App.util function onCompressionProgressChanged(file, proc, total, br, bt) { - exportProgressBar.value = (br * 100 / bt); + exportProgressBar.value = (br * 100 / bt) } function onCompressionFinished() { - exportFileProgressDialog.close(); + exportFileProgressDialog.close() } } } @@ -381,28 +413,31 @@ Item { anchors.fill: parent property string filePath onEntered: function (drag) { - dropPopup.open(); + dropPopup.open() } onDropped: function (drop) { - dropPopup.close(); - dropArea.enabled = false; + dropPopup.close() + dropArea.enabled = false if (drop.urls.length > 1) { - importProjectErrorDialog.title = qsTr("We only support adding one item at once."); - importProjectErrorDialog.open(); - return; + importProjectErrorDialog.title = qsTr( + "We only support adding one item at once.") + importProjectErrorDialog.open() + return } - var file = ""; // Convert url to string - file = "" + drop.urls[0]; + var file = "" + // Convert url to string + file = "" + drop.urls[0] if (!file.endsWith('.screenplay')) { - importProjectErrorDialog.title = qsTr("File type not supported. We only support '.screenplay' files."); - importProjectErrorDialog.open(); - return; + importProjectErrorDialog.title = qsTr( + "File type not supported. We only support '.screenplay' files.") + importProjectErrorDialog.open() + return } - importDialog.open(); - dropArea.filePath = file; + importDialog.open() + dropArea.filePath = file } onExited: { - dropPopup.close(); + dropPopup.close() } Util.Dialog { @@ -423,9 +458,11 @@ Item { closePolicy: Popup.NoAutoClose onClosed: importProgressBar.value = 0 onOpened: { - const success = App.util.importProject(dropArea.filePath, App.globalVariables.localStoragePath); - print("finished", success); - dropArea.filePath = ""; + const success = App.util.importProject( + dropArea.filePath, + App.globalVariables.localStoragePath) + print("finished", success) + dropArea.filePath = "" } ColumnLayout { width: parent.width @@ -447,10 +484,10 @@ Item { id: importConnections target: App.util function onExtractionProgressChanged(file, proc, total, br, bt) { - importProgressBar.value = (br * 100 / bt); + importProgressBar.value = (br * 100 / bt) } function onExtractionFinished() { - importDialog.close(); + importDialog.close() } } } @@ -466,8 +503,8 @@ Item { modal: true onOpened: fileDropAnimation.state = "fileDrop" onClosed: { - fileDropAnimation.state = ""; - dropArea.enabled = true; + fileDropAnimation.state = "" + dropArea.enabled = true } Util.FileDropAnimation { diff --git a/ScreenPlay/qml/Installed/ScreenPlayItem.qml b/ScreenPlay/qml/Installed/ScreenPlayItem.qml index e91f4167..79ef8247 100644 --- a/ScreenPlay/qml/Installed/ScreenPlayItem.qml +++ b/ScreenPlay/qml/Installed/ScreenPlayItem.qml @@ -14,6 +14,7 @@ Item { property string screenId property url absoluteStoragePath property int type: InstalledType.Unknown + // Must be var to make it work wit 64bit ints property var publishedFileID: 0 property int itemIndex property bool isScrolling: false diff --git a/ScreenPlay/src/app.cpp b/ScreenPlay/src/app.cpp index 06ac8417..3bd9cde9 100644 --- a/ScreenPlay/src/app.cpp +++ b/ScreenPlay/src/app.cpp @@ -138,10 +138,9 @@ void App::init() using std::make_shared, std::make_unique; - // Util should be created as first so we redirect qDebugs etc. into the log - m_util = make_unique(); m_globalVariables = make_shared(); m_monitorListModel = make_shared(); + m_util = make_unique(m_globalVariables); m_profileListModel = make_shared(m_globalVariables); m_settings = make_shared(m_globalVariables); m_installedListModel = make_shared(m_globalVariables, m_settings); diff --git a/ScreenPlay/src/profilelistmodel.cpp b/ScreenPlay/src/profilelistmodel.cpp index c92c6a1d..95252a25 100644 --- a/ScreenPlay/src/profilelistmodel.cpp +++ b/ScreenPlay/src/profilelistmodel.cpp @@ -22,7 +22,9 @@ namespace ScreenPlay { /*! Constructor */ -ProfileListModel::ProfileListModel(const std::shared_ptr& globalVariables, QObject* parent) +ProfileListModel::ProfileListModel( + const std::shared_ptr& globalVariables, + QObject* parent) : QAbstractListModel(parent) , m_globalVariables { globalVariables } { diff --git a/ScreenPlay/src/util.cpp b/ScreenPlay/src/util.cpp index 669a91d6..7743c8c9 100644 --- a/ScreenPlay/src/util.cpp +++ b/ScreenPlay/src/util.cpp @@ -23,8 +23,10 @@ namespace ScreenPlay { /*! \brief Constructor. */ -Util::Util() +Util::Util( + const std::shared_ptr& globalVariables) : QObject(nullptr) + , m_globalVariables { globalVariables } { m_extractor = std::make_unique(); m_compressor = std::make_unique(); @@ -144,6 +146,15 @@ bool Util::exportProject(QString contentPath, QString exportFileName) return true; } +bool Util::openGodotEditor(QString contentPath) const +{ + const QList godotCmd = { "--editor", "--path", toLocal(contentPath) }; + QProcess process; + process.setProgram(m_globalVariables->godotEditorExecutablePath().toString()); + process.setArguments(godotCmd); + return process.startDetached(); +} + /*! \brief Imports a given project from a .screenplay zip file. The argument extractionPath must be copied otherwise it will get reset in qml before extracting.