From b60e54c07c3c34eea6f911882720c05869f3e8e2 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Thu, 8 Feb 2024 16:00:55 -0500 Subject: [PATCH] Close subwindows gracefully, prompt save on quit --- include/editor.h | 2 +- include/mainwindow.h | 7 +- src/editor.cpp | 9 ++- src/mainwindow.cpp | 150 +++++++++++++++++++++--------------- src/ui/mapimageexporter.cpp | 1 + src/ui/newmappopup.cpp | 1 + src/ui/regionmapeditor.cpp | 1 + src/ui/tileseteditor.cpp | 1 + 8 files changed, 100 insertions(+), 72 deletions(-) diff --git a/include/editor.h b/include/editor.h index f684b491..837b267c 100644 --- a/include/editor.h +++ b/include/editor.h @@ -43,7 +43,7 @@ public: public: Ui::MainWindow* ui; QObject *parent = nullptr; - Project *project = nullptr; + QPointer project = nullptr; Map *map = nullptr; Settings *settings; void saveProject(); diff --git a/include/mainwindow.h b/include/mainwindow.h index 6f969eba..5dc6b124 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -295,7 +295,7 @@ private slots: public: Ui::MainWindow *ui; - Editor *editor = nullptr; + QPointer editor = nullptr; private: QLabel *label_MapRulerStatus = nullptr; @@ -331,7 +331,6 @@ private: bool isProgrammaticEventTabChange; bool projectHasUnsavedChanges; - bool projectOpenFailure = false; bool newMapDefaultsSet = false; MapSortOrder mapSortOrder; @@ -348,7 +347,9 @@ private: void openSubWindow(QWidget * window); QString getExistingDirectory(QString); bool openProject(const QString &dir, bool initial = false); + bool closeProject(); void showProjectOpenFailure(); + void saveGlobalConfigs(); bool setInitialMap(); void setRecentMap(QString map_name); QStandardItem* createMapItem(QString mapName, int groupNum, int inGroupNum); @@ -379,7 +380,7 @@ private: void setTheme(QString); void updateTilesetEditor(); Event::Group getEventGroupFromTabWidget(QWidget *tab); - void closeSupplementaryWindows(); + bool closeSupplementaryWindows(); void setWindowDisabled(bool); void initTilesetEditor(); diff --git a/src/editor.cpp b/src/editor.cpp index 014fa489..a2e4f3f9 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -79,10 +79,11 @@ void Editor::saveUiFields() { } void Editor::closeProject() { - if (this->project) { - delete this->project; - this->project = nullptr; - } + if (!this->project) + return; + + Scripting::cb_ProjectClosed(this->project->root); + delete this->project; } void Editor::setEditingMap() { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f7e0dee1..beddb7ec 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -71,6 +71,7 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { delete label_MapRulerStatus; + delete editor; delete ui; } @@ -477,8 +478,12 @@ void MainWindow::setTheme(QString theme) { } bool MainWindow::openProject(const QString &dir, bool initial) { + if (!this->closeProject()) { + logInfo("Aborted project open."); + return false; + } + if (dir.isNull() || dir.length() <= 0) { - projectOpenFailure = true; if (!initial) setWindowDisabled(true); return false; } @@ -486,7 +491,6 @@ bool MainWindow::openProject(const QString &dir, bool initial) { const QString projectString = QString("%1project '%2'").arg(initial ? "recent " : "").arg(QDir::toNativeSeparators(dir)); if (!QDir(dir).exists()) { - projectOpenFailure = true; const QString errorMsg = QString("Failed to open %1: No such directory").arg(projectString); this->statusBar()->showMessage(errorMsg); if (initial) { @@ -503,43 +507,30 @@ bool MainWindow::openProject(const QString &dir, bool initial) { this->statusBar()->showMessage(openMessage); logInfo(openMessage); + // TODO: Don't save these yet userConfig.setProjectDir(dir); userConfig.load(); projectConfig.setProjectDir(dir); projectConfig.load(); - this->closeSupplementaryWindows(); this->newMapDefaultsSet = false; - if (isProjectOpen()) - Scripting::cb_ProjectClosed(editor->project->root); Scripting::init(this); - bool already_open = isProjectOpen() && (editor->project->root == dir); - if (!already_open) { - editor->closeProject(); - editor->project = new Project(this); - QObject::connect(editor->project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered); - QObject::connect(editor->project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared); - QObject::connect(editor->project, &Project::uncheckMonitorFilesAction, [this]() { - porymapConfig.setMonitorFiles(false); - if (this->preferenceEditor) - this->preferenceEditor->updateFields(); - }); - editor->project->set_root(dir); - } else { - editor->project->fileWatcher.removePaths(editor->project->fileWatcher.files()); - editor->project->clearMapCache(); - editor->project->clearTilesetCache(); - } + this->editor->project = new Project(this); + QObject::connect(this->editor->project, &Project::reloadProject, this, &MainWindow::on_action_Reload_Project_triggered); + QObject::connect(this->editor->project, &Project::mapCacheCleared, this, &MainWindow::onMapCacheCleared); + QObject::connect(this->editor->project, &Project::uncheckMonitorFilesAction, [this]() { + porymapConfig.setMonitorFiles(false); + if (this->preferenceEditor) + this->preferenceEditor->updateFields(); + }); + this->editor->project->set_root(dir); - this->projectOpenFailure = !(loadDataStructures() - && populateMapList() - && setInitialMap()); - - if (this->projectOpenFailure) { + if (!(loadDataStructures() && populateMapList() && setInitialMap())) { this->statusBar()->showMessage(QString("Failed to open %1").arg(projectString)); showProjectOpenFailure(); + delete this->editor->project; return false; } @@ -568,7 +559,7 @@ void MainWindow::showProjectOpenFailure() { } bool MainWindow::isProjectOpen() { - return !projectOpenFailure && editor && editor->project; + return editor && editor->project; } bool MainWindow::setInitialMap() { @@ -1228,12 +1219,10 @@ void MainWindow::openNewMapPopupWindow() { } if (!this->newMapPrompt) { this->newMapPrompt = new NewMapPopup(this, this->editor->project); + connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated); } openSubWindow(this->newMapPrompt); - - connect(this->newMapPrompt, &NewMapPopup::applied, this, &MainWindow::onNewMapCreated); - this->newMapPrompt->setAttribute(Qt::WA_DeleteOnClose); } void MainWindow::on_action_NewMap_triggered() { @@ -1751,11 +1740,6 @@ void MainWindow::on_mapViewTab_tabBarClicked(int index) editor->setCursorRectVisible(false); } -void MainWindow::on_action_Exit_triggered() -{ - QApplication::quit(); -} - void MainWindow::on_mainTabBar_tabBarClicked(int index) { int oldIndex = ui->mainTabBar->currentIndex(); @@ -2486,11 +2470,8 @@ void MainWindow::importMapFromAdvanceMap1_92() void MainWindow::showExportMapImageWindow(ImageExporterMode mode) { if (!editor->project) return; - if (this->mapImageExporter) - delete this->mapImageExporter; - - this->mapImageExporter = new MapImageExporter(this, this->editor, mode); - this->mapImageExporter->setAttribute(Qt::WA_DeleteOnClose); + if (!this->mapImageExporter) + this->mapImageExporter = new MapImageExporter(this, this->editor, mode); openSubWindow(this->mapImageExporter); } @@ -2923,36 +2904,59 @@ bool MainWindow::initRegionMapEditor(bool silent) { return true; } -void MainWindow::closeSupplementaryWindows() { - delete this->tilesetEditor; - delete this->regionMapEditor; - delete this->mapImageExporter; - delete this->newMapPrompt; - delete this->shortcutsEditor; - delete this->customScriptsEditor; +// Attempt to close any open sub-windows of the main window, giving each a chance to abort the process. +// Each of these are expected to be a QPointer to a widget with WA_DeleteOnClose set, so manually deleting +// and nullifying the pointer members is not necessary here. +// TODO: Testing +bool MainWindow::closeSupplementaryWindows() { + if (this->tilesetEditor && !this->tilesetEditor->close()) + return false; + if (this->regionMapEditor && !this->regionMapEditor->close()) + return false; + if (this->mapImageExporter && !this->mapImageExporter->close()) + return false; + if (this->newMapPrompt && !this->newMapPrompt->close()) + return false; + if (this->shortcutsEditor && !this->shortcutsEditor->close()) + return false; + if (this->preferenceEditor && !this->preferenceEditor->close()) + return false; + if (this->customScriptsEditor && !this->customScriptsEditor->close()) + return false; if (this->projectSettingsEditor) this->projectSettingsEditor->closeQuietly(); + + return true; } -void MainWindow::closeEvent(QCloseEvent *event) { - if (isProjectOpen()) { - if (projectHasUnsavedChanges || (editor->map && editor->map->hasUnsavedChanges())) { - QMessageBox::StandardButton result = QMessageBox::question( - this, "porymap", "The project has been modified, save changes?", - QMessageBox::No | QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); +bool MainWindow::closeProject() { + if (!closeSupplementaryWindows()) + return false; - if (result == QMessageBox::Yes) { - editor->saveProject(); - } else if (result == QMessageBox::No) { - logWarn("Closing porymap with unsaved changes."); - } else if (result == QMessageBox::Cancel) { - event->ignore(); - return; - } + if (!isProjectOpen()) + return true; + + if (projectHasUnsavedChanges || (editor->map && editor->map->hasUnsavedChanges())) { + QMessageBox::StandardButton result = QMessageBox::question( + this, "porymap", "The project has been modified, save changes?", + QMessageBox::No | QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (result == QMessageBox::Yes) { + editor->saveProject(); + } else if (result == QMessageBox::No) { + logWarn("Closing project with unsaved changes."); + } else if (result == QMessageBox::Cancel) { + return false; } - projectConfig.save(); - userConfig.save(); } + projectConfig.save(); + userConfig.save(); + editor->closeProject(); + + return true; +} + +void MainWindow::saveGlobalConfigs() { porymapConfig.setMainGeometry( this->saveGeometry(), this->saveState(), @@ -2962,6 +2966,24 @@ void MainWindow::closeEvent(QCloseEvent *event) { ); porymapConfig.save(); shortcutsConfig.save(); +} + +void MainWindow::on_action_Exit_triggered() { + if (!closeProject()) + return; + + saveGlobalConfigs(); + + QApplication::quit(); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + if (!closeProject()) { + event->ignore(); + return; + } + + saveGlobalConfigs(); QMainWindow::closeEvent(event); } diff --git a/src/ui/mapimageexporter.cpp b/src/ui/mapimageexporter.cpp index 099f2288..e36d9606 100644 --- a/src/ui/mapimageexporter.cpp +++ b/src/ui/mapimageexporter.cpp @@ -27,6 +27,7 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor QDialog(parent_), ui(new Ui::MapImageExporter) { + this->setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); this->map = editor_->map; this->editor = editor_; diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index 2c4141bc..fdc3f79c 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -14,6 +14,7 @@ NewMapPopup::NewMapPopup(QWidget *parent, Project *project) : QMainWindow(parent), ui(new Ui::NewMapPopup) { + this->setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); this->project = project; this->existingLayout = false; diff --git a/src/ui/regionmapeditor.cpp b/src/ui/regionmapeditor.cpp index 220f9cca..318bc517 100644 --- a/src/ui/regionmapeditor.cpp +++ b/src/ui/regionmapeditor.cpp @@ -26,6 +26,7 @@ RegionMapEditor::RegionMapEditor(QWidget *parent, Project *project) : QMainWindow(parent), ui(new Ui::RegionMapEditor) { + this->setAttribute(Qt::WA_DeleteOnClose); this->ui->setupUi(this); this->project = project; this->initShortcuts(); diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 4813790a..bd233974 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -20,6 +20,7 @@ TilesetEditor::TilesetEditor(Project *project, Map *map, QWidget *parent) : map(map), hasUnsavedChanges(false) { + this->setAttribute(Qt::WA_DeleteOnClose); this->setTilesets(this->map->layout->tileset_primary_label, this->map->layout->tileset_secondary_label); this->initUi(); }