diff --git a/include/mainwindow.h b/include/mainwindow.h index 3fe187f3..2d065b37 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -367,7 +367,7 @@ private: void scrollMapListToCurrentMap(MapTree *list); void scrollMapListToCurrentLayout(MapTree *list); void resetMapListFilters(); - void showFileWatcherWarning(QString filepath); + void showFileWatcherWarning(); QString getExistingDirectory(QString); bool openProject(QString dir, bool initial = false); bool closeProject(); diff --git a/include/project.h b/include/project.h index fcdde462..f3a1093b 100644 --- a/include/project.h +++ b/include/project.h @@ -71,7 +71,7 @@ public: QMap facingDirections; ParseUtil parser; QFileSystemWatcher fileWatcher; - QMap modifiedFileTimestamps; + QSet modifiedFiles; bool usingAsmTilesets; QSet disabledSettingsNames; QSet topLevelMapFields; @@ -252,6 +252,7 @@ public: private: QMap mapSectionDisplayNames; + QMap modifiedFileTimestamps; void updateLayout(Layout *); @@ -259,6 +260,7 @@ private: void setNewLayoutBorder(Layout *layout); void ignoreWatchedFileTemporarily(QString filepath); + void recordFileChange(const QString &filepath); static int num_tiles_primary; static int num_tiles_total; @@ -270,7 +272,7 @@ private: static int max_object_events; signals: - void fileChanged(QString filepath); + void fileChanged(const QString &filepath); void mapLoaded(Map *map); void mapCreated(Map *newMap, const QString &groupName); void layoutCreated(Layout *newLayout); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 277aaf97..9e59758e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -69,6 +69,8 @@ MainWindow::MainWindow(QWidget *parent) : QCoreApplication::setApplicationVersion(PORYMAP_VERSION); QApplication::setApplicationDisplayName(QApplication::applicationName()); QApplication::setWindowIcon(QIcon(":/icons/porymap-icon-2.ico")); + connect(qApp, &QApplication::applicationStateChanged, this, &MainWindow::showFileWatcherWarning); + ui->setupUi(this); cleanupLargeLog(); @@ -791,26 +793,46 @@ void MainWindow::openSubWindow(QWidget * window) { } } -void MainWindow::showFileWatcherWarning(QString filepath) { - if (!porymapConfig.monitorFiles || !isProjectOpen()) +void MainWindow::showFileWatcherWarning() { + if (!porymapConfig.monitorFiles || !isProjectOpen()) + return; + + // Only show the file watcher warning if Porymap is the currently active application. + // This stops Porymap from bugging users when they switch to their projects to make edits; + // we'll warn them about the need to reload when they return to Porymap. + if (QGuiApplication::applicationState() != Qt::ApplicationActive) return; Project *project = this->editor->project; - if (project->modifiedFileTimestamps.contains(filepath)) { - if (QDateTime::currentMSecsSinceEpoch() < project->modifiedFileTimestamps[filepath]) { - return; - } - project->modifiedFileTimestamps.remove(filepath); + QStringList modifiedFiles(project->modifiedFiles.constBegin(), project->modifiedFiles.constEnd()); + if (modifiedFiles.isEmpty()) + return; + project->modifiedFiles.clear(); + + // Only allow one of these warnings at a single time. + // Additional file changes are ignored while the warning is already active. + static bool showing = false; + if (showing) + return; + showing = true; + + // Strip project root from filepaths + const QString root = project->root + "/"; + for (auto &path : modifiedFiles) { + path.remove(root); } - static bool showing = false; - if (showing) return; + QuestionMessage msgBox("", this); + if (modifiedFiles.count() == 1) { + msgBox.setText(QString("The file %1 has changed on disk. Would you like to reload the project?").arg(modifiedFiles.first())); + } else { + msgBox.setText(QStringLiteral("Some project files have changed on disk. Would you like to reload the project?")); + msgBox.setDetailedText(QStringLiteral("The following files have changed:\n") + modifiedFiles.join("\n")); + } - QuestionMessage msgBox(QString("The file %1 has changed on disk. Would you like to reload the project?").arg(filepath.remove(project->root + "/")), this); QCheckBox showAgainCheck("Do not ask again."); msgBox.setCheckBox(&showAgainCheck); - showing = true; auto reply = msgBox.exec(); if (reply == QMessageBox::Yes) { on_action_Reload_Project_triggered(); diff --git a/src/project.cpp b/src/project.cpp index fb2d3d2b..3f510e6b 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -35,7 +35,7 @@ int Project::max_object_events = 64; Project::Project(QObject *parent) : QObject(parent) { - QObject::connect(&this->fileWatcher, &QFileSystemWatcher::fileChanged, this, &Project::fileChanged); + QObject::connect(&this->fileWatcher, &QFileSystemWatcher::fileChanged, this, &Project::recordFileChange); } Project::~Project() @@ -660,7 +660,25 @@ void Project::saveMapLayouts() { void Project::ignoreWatchedFileTemporarily(QString filepath) { // Ignore any file-change events for this filepath for the next 5 seconds. - modifiedFileTimestamps.insert(filepath, QDateTime::currentMSecsSinceEpoch() + 5000); + this->modifiedFileTimestamps.insert(filepath, QDateTime::currentMSecsSinceEpoch() + 5000); +} + +void Project::recordFileChange(const QString &filepath) { + if (this->modifiedFiles.contains(filepath)) { + // We already recorded a change to this file + return; + } + + if (this->modifiedFileTimestamps.contains(filepath)) { + if (QDateTime::currentMSecsSinceEpoch() < this->modifiedFileTimestamps[filepath]) { + // We're still ignoring changes to this file + return; + } + this->modifiedFileTimestamps.remove(filepath); + } + + this->modifiedFiles.insert(filepath); + emit fileChanged(filepath); } void Project::saveMapGroups() {