mirror of
https://gitlab.com/kelteseth/ScreenPlay.git
synced 2024-09-18 08:22:33 +02:00
Fix project deletion when content (preview.gif) was in use
This commit is contained in:
parent
2226ed1b7e
commit
c0d528048b
@ -34,11 +34,17 @@ ApplicationWindow {
|
|||||||
function switchPage(name) {
|
function switchPage(name) {
|
||||||
if (nav.currentNavigationName === name) {
|
if (nav.currentNavigationName === name) {
|
||||||
if (name === "Installed")
|
if (name === "Installed")
|
||||||
ScreenPlay.installedListModel.reset();
|
ScreenPlay.installedListModel.reset()
|
||||||
|
|
||||||
}
|
}
|
||||||
stackView.replace("qrc:/qml/" + name + "/" + name + ".qml");
|
|
||||||
sidebar.state = "inactive";
|
if (name === "Installed") {
|
||||||
|
stackView.replace("qrc:/qml/" + name + "/" + name + ".qml", {
|
||||||
|
"sidebar": sidebar
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stackView.replace("qrc:/qml/" + name + "/" + name + ".qml")
|
||||||
|
sidebar.state = "inactive"
|
||||||
}
|
}
|
||||||
|
|
||||||
color: Material.theme === Material.Dark ? Qt.darker(Material.background) : Material.background
|
color: Material.theme === Material.Dark ? Qt.darker(Material.background) : Material.background
|
||||||
|
@ -13,6 +13,7 @@ Item {
|
|||||||
|
|
||||||
property bool refresh: false
|
property bool refresh: false
|
||||||
property bool enabled: true
|
property bool enabled: true
|
||||||
|
property Sidebar sidebar
|
||||||
|
|
||||||
signal setNavigationItem(var pos)
|
signal setNavigationItem(var pos)
|
||||||
signal setSidebarActive(var active)
|
signal setSidebarActive(var active)
|
||||||
@ -102,6 +103,36 @@ Item {
|
|||||||
onDragStarted: isDragging = true
|
onDragStarted: isDragging = true
|
||||||
onDragEnded: isDragging = false
|
onDragEnded: isDragging = false
|
||||||
model: ScreenPlay.installedListFilter
|
model: ScreenPlay.installedListFilter
|
||||||
|
removeDisplaced: Transition {
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation {
|
||||||
|
duration: 150
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "x,y"
|
||||||
|
duration: 250
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove: Transition {
|
||||||
|
SequentialAnimation {
|
||||||
|
|
||||||
|
NumberAnimation {
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
NumberAnimation {
|
||||||
|
properties: "y"
|
||||||
|
to: 100
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.InOutQuart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
onContentYChanged: {
|
onContentYChanged: {
|
||||||
if (contentY <= -180)
|
if (contentY <= -180)
|
||||||
gridView.headerItem.isVisible = true;
|
gridView.headerItem.isVisible = true;
|
||||||
@ -262,8 +293,8 @@ Item {
|
|||||||
enabled: contextMenu.publishedFileID !== 0
|
enabled: contextMenu.publishedFileID !== 0
|
||||||
icon.source: "qrc:/assets/icons/icon_steam.svg"
|
icon.source: "qrc:/assets/icons/icon_steam.svg"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
print(contextMenu.publishedFileID)
|
Qt.openUrlExternally(
|
||||||
Qt.openUrlExternally("steam://url/CommunityFilePage/" + contextMenu.publishedFileID);
|
"steam://url/CommunityFilePage/" + contextMenu.publishedFileID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,15 +302,16 @@ Item {
|
|||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: deleteDialog
|
id: deleteDialog
|
||||||
|
|
||||||
property int currentItemIndex: 0
|
|
||||||
|
|
||||||
title: qsTr("Are you sure you want to delete this item?")
|
title: qsTr("Are you sure you want to delete this item?")
|
||||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
modal: true
|
modal: true
|
||||||
dim: true
|
dim: true
|
||||||
anchors.centerIn: Overlay.overlay
|
anchors.centerIn: Overlay.overlay
|
||||||
onAccepted: ScreenPlay.installedListModel.deinstallItemAt(currentItemIndex)
|
onAccepted: {
|
||||||
|
root.sidebar.clear()
|
||||||
|
ScreenPlay.installedListModel.deinstallItemAt(
|
||||||
|
contextMenu.absoluteStoragePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigation {
|
Navigation {
|
||||||
|
@ -29,6 +29,14 @@ Item {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is used for removing wallpaper. We need to clear
|
||||||
|
// the preview image/gif so we can release the file for deletion.
|
||||||
|
function clear(){
|
||||||
|
image.source = ""
|
||||||
|
txtHeadline.text = ""
|
||||||
|
root.state = "inactive"
|
||||||
|
}
|
||||||
|
|
||||||
width: 400
|
width: 400
|
||||||
state: "inactive"
|
state: "inactive"
|
||||||
onContentFolderNameChanged: {
|
onContentFolderNameChanged: {
|
||||||
@ -48,8 +56,10 @@ Item {
|
|||||||
btnSetWallpaper.enabled = false;
|
btnSetWallpaper.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onSetSidebarItem(folderName, type) {
|
function onSetSidebarItem(folderName, type) {
|
||||||
|
|
||||||
// Toggle sidebar if clicked on the same content twice
|
// Toggle sidebar if clicked on the same content twice
|
||||||
if (root.contentFolderName === folderName && root.state !== "inactive") {
|
if (root.contentFolderName === folderName && root.state !== "inactive") {
|
||||||
root.state = "inactive";
|
root.state = "inactive";
|
||||||
@ -152,10 +162,12 @@ Item {
|
|||||||
|
|
||||||
AnimatedImage {
|
AnimatedImage {
|
||||||
id: image
|
id: image
|
||||||
|
// Do NOT enable async image loading!
|
||||||
|
// Otherwhise it will still hold the file
|
||||||
|
// when calling InstalledListModel::deinstallItemAt
|
||||||
|
asynchronous: false
|
||||||
playing: true
|
playing: true
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
asynchronous: true
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (image.status === Image.Error)
|
if (image.status === Image.Error)
|
||||||
|
@ -45,27 +45,65 @@ void InstalledListModel::init()
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Deleted the item from the local storage and removes it from the
|
\brief Deleted the item from the local storage and removes it from the
|
||||||
installed list.
|
installed list. We wait for the qml engine to free all resources before
|
||||||
|
we proceed. This like the preview.gif will be in use when clicking on an item
|
||||||
*/
|
*/
|
||||||
bool InstalledListModel::deinstallItemAt(const int index)
|
void InstalledListModel::deinstallItemAt(const QString& absoluteStoragePath)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= m_screenPlayFiles.count()) {
|
QTimer::singleShot(1000, this, [this, absoluteStoragePath]() {
|
||||||
qWarning() << "remove folder error, invalid index " << index;
|
int index = -1;
|
||||||
return false;
|
for (int i = 0; i < m_screenPlayFiles.size(); ++i) {
|
||||||
}
|
if (m_screenPlayFiles.at(i).m_absoluteStoragePath.toString() == absoluteStoragePath) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beginRemoveRows(QModelIndex(), index, index);
|
if (index < 0 || index >= m_screenPlayFiles.count()) {
|
||||||
const QString path = QUrl::fromUserInput(m_screenPlayFiles.at(index).m_absoluteStoragePath.toString()).toLocalFile();
|
qWarning() << "Remove folder error, invalid index " << index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QDir dir(path);
|
beginRemoveRows(QModelIndex(), index, index);
|
||||||
const bool success = dir.removeRecursively();
|
m_screenPlayFiles.removeAt(index);
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
if (!success)
|
const QString path = ScreenPlayUtil::toLocal(absoluteStoragePath);
|
||||||
qWarning() << "Could not remove folder: " << m_screenPlayFiles.at(index).m_absoluteStoragePath.toString();
|
|
||||||
|
|
||||||
m_screenPlayFiles.removeAt(index);
|
QDir dir(path);
|
||||||
endRemoveRows();
|
bool success = true;
|
||||||
return success;
|
if (!dir.exists()) {
|
||||||
|
qWarning() << "Directory does not exist!" << dir;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must pause the QFileSystemWatcher to not trigger
|
||||||
|
// a reload for every removed file
|
||||||
|
m_fileSystemWatcher.blockSignals(true);
|
||||||
|
for (auto& item : dir.entryInfoList(QDir::Files)) {
|
||||||
|
if (!QFile::remove(item.absoluteFilePath())) {
|
||||||
|
qWarning() << "Unable to remove file:" << item;
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
qWarning() << "Could not remove folder content at: " << path;
|
||||||
|
loadInstalledContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.rmdir(path)) {
|
||||||
|
qWarning() << "Could not remove folder at: " << path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add delay to the watcher, because it was trigger by
|
||||||
|
// something when enabling after the removal.
|
||||||
|
QTimer::singleShot(3000, this, [this]() {
|
||||||
|
m_fileSystemWatcher.blockSignals(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -156,11 +194,17 @@ void InstalledListModel::append(const QJsonObject& obj, const QString& folderNam
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Loads all installed content. Skips projects.json without a "type" field.
|
\brief Loads all installed content.
|
||||||
|
- Skips if the loadContentFuture is already running.
|
||||||
|
- Skips projects.json without a "type" field.
|
||||||
*/
|
*/
|
||||||
void InstalledListModel::loadInstalledContent()
|
void InstalledListModel::loadInstalledContent()
|
||||||
{
|
{
|
||||||
QtConcurrent::run([this]() {
|
qInfo() << "loadInstalledContent";
|
||||||
|
if (m_loadContentFuture.isRunning())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_loadContentFuture = QtConcurrent::run([this]() {
|
||||||
QFileInfoList list = QDir(m_globalVariables->localStoragePath().toLocalFile()).entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs);
|
QFileInfoList list = QDir(m_globalVariables->localStoragePath().toLocalFile()).entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs);
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@ -198,27 +242,33 @@ void InstalledListModel::loadInstalledContent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief .
|
\brief Used for receiving values from qml. One must add all new fields
|
||||||
|
when adding new roles to this model.
|
||||||
*/
|
*/
|
||||||
QVariantMap InstalledListModel::get(const QString& folderId) const
|
QVariantMap InstalledListModel::get(const QString& folderName) const
|
||||||
{
|
{
|
||||||
|
|
||||||
if (m_screenPlayFiles.count() == 0)
|
if (m_screenPlayFiles.count() == 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
QVariantMap map;
|
const QString localInstalledPath = ScreenPlayUtil::toLocal(m_globalVariables->localStoragePath().toString());
|
||||||
for (int i = 0; i < m_screenPlayFiles.count(); i++) {
|
|
||||||
|
|
||||||
if (m_screenPlayFiles[i].m_folderId == folderId) {
|
if (!QDir(localInstalledPath + "/" + folderName).exists()) {
|
||||||
map.insert("m_title", m_screenPlayFiles[i].m_title);
|
return {};
|
||||||
map.insert("m_preview", m_screenPlayFiles[i].m_preview);
|
}
|
||||||
map.insert("m_previewGIF", m_screenPlayFiles[i].m_previewGIF);
|
|
||||||
map.insert("m_file", m_screenPlayFiles[i].m_file);
|
for (const auto& item : m_screenPlayFiles) {
|
||||||
map.insert("m_type", QVariant::fromValue(m_screenPlayFiles[i].m_type));
|
if (item.m_folderId == folderName) {
|
||||||
map.insert("m_absoluteStoragePath", m_screenPlayFiles[i].m_absoluteStoragePath);
|
QVariantMap map;
|
||||||
map.insert("m_publishedFileID", m_screenPlayFiles[i].m_publishedFileID);
|
map.insert("m_title", item.m_title);
|
||||||
map.insert("m_isNew", m_screenPlayFiles[i].m_isNew);
|
map.insert("m_preview", item.m_preview);
|
||||||
map.insert("m_lastModified", m_screenPlayFiles[i].m_lastModified);
|
map.insert("m_previewGIF", item.m_previewGIF);
|
||||||
|
map.insert("m_file", item.m_file);
|
||||||
|
map.insert("m_type", QVariant::fromValue(item.m_type));
|
||||||
|
map.insert("m_absoluteStoragePath", item.m_absoluteStoragePath);
|
||||||
|
map.insert("m_publishedFileID", item.m_publishedFileID);
|
||||||
|
map.insert("m_isNew", item.m_isNew);
|
||||||
|
map.insert("m_lastModified", item.m_lastModified);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +277,7 @@ QVariantMap InstalledListModel::get(const QString& folderId) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief .
|
\brief Removes all entires and loads it again.
|
||||||
*/
|
*/
|
||||||
void InstalledListModel::reset()
|
void InstalledListModel::reset()
|
||||||
{
|
{
|
||||||
|
@ -99,13 +99,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QVariantMap get(const QString& folderId) const;
|
QVariantMap get(const QString& folderName) const;
|
||||||
|
|
||||||
void loadInstalledContent();
|
void loadInstalledContent();
|
||||||
void append(const QJsonObject&, const QString&, const bool isNew, const QDateTime& lastModified);
|
void append(const QJsonObject&, const QString&, const bool isNew, const QDateTime& lastModified);
|
||||||
void reset();
|
void reset();
|
||||||
void init();
|
void init();
|
||||||
bool deinstallItemAt(const int index);
|
void deinstallItemAt(const QString& absoluteStoragePath);
|
||||||
|
|
||||||
void setCount(int count)
|
void setCount(int count)
|
||||||
{
|
{
|
||||||
@ -124,6 +124,7 @@ private:
|
|||||||
QFileSystemWatcher m_fileSystemWatcher;
|
QFileSystemWatcher m_fileSystemWatcher;
|
||||||
QVector<ProjectFile> m_screenPlayFiles;
|
QVector<ProjectFile> m_screenPlayFiles;
|
||||||
int m_count { 0 };
|
int m_count { 0 };
|
||||||
|
QFuture<void> m_loadContentFuture;
|
||||||
|
|
||||||
const std::shared_ptr<GlobalVariables>& m_globalVariables;
|
const std::shared_ptr<GlobalVariables>& m_globalVariables;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user