Close subwindows gracefully, prompt save on quit

This commit is contained in:
GriffinR 2024-02-08 16:00:55 -05:00
parent a960456c6e
commit b60e54c07c
8 changed files with 100 additions and 72 deletions

View file

@ -43,7 +43,7 @@ public:
public:
Ui::MainWindow* ui;
QObject *parent = nullptr;
Project *project = nullptr;
QPointer<Project> project = nullptr;
Map *map = nullptr;
Settings *settings;
void saveProject();

View file

@ -295,7 +295,7 @@ private slots:
public:
Ui::MainWindow *ui;
Editor *editor = nullptr;
QPointer<Editor> 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();

View file

@ -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() {

View file

@ -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);
}

View file

@ -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_;

View file

@ -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;

View file

@ -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();

View file

@ -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();
}