Fix some problems with layout directory creation

This commit is contained in:
GriffinR
2024-11-27 02:42:14 -05:00
parent 06a263c689
commit 83ef14a242
9 changed files with 76 additions and 83 deletions

View File

@@ -67,12 +67,10 @@ public:
QString sharedEventsMap() const { return m_sharedEventsMap; }
QString sharedScriptsMap() const { return m_sharedScriptsMap; }
void setNeedsLayoutDir(bool needsLayoutDir) { m_needsLayoutDir = needsLayoutDir; }
void setNeedsHealLocation(bool needsHealLocation) { m_needsHealLocation = needsHealLocation; }
void setIsPersistedToFile(bool persistedToFile) { m_isPersistedToFile = persistedToFile; }
void setHasUnsavedDataChanges(bool unsavedDataChanges) { m_hasUnsavedDataChanges = unsavedDataChanges; }
bool needsLayoutDir() const { return m_needsLayoutDir; }
bool needsHealLocation() const { return m_needsHealLocation; }
bool isPersistedToFile() const { return m_isPersistedToFile; }
bool hasUnsavedDataChanges() const { return m_hasUnsavedDataChanges; }
@@ -121,7 +119,6 @@ private:
bool m_isPersistedToFile = true;
bool m_hasUnsavedDataChanges = false;
bool m_needsLayoutDir = true;
bool m_needsHealLocation = false;
bool m_scriptsLoaded = false;

View File

@@ -22,8 +22,9 @@ public:
Layout() {}
Layout(const Layout &other);
static QString layoutNameFromMapName(const QString &mapName);
static QString layoutConstantFromName(QString mapName);
static QString defaultSuffix();
bool loaded = false;
@@ -77,6 +78,9 @@ public:
struct Settings {
QString id;
QString name;
// The name of a new layout's folder in `data/layouts/` is not always the same as the layout's name
// (e.g. the majority of the default layouts use the name of their associated map).
QString folderName;
int width;
int height;
int borderWidth;

View File

@@ -40,7 +40,6 @@ private:
void refresh();
void saveSettings();
void setLayout(const Layout *mapLayout);
private slots:
void dialogButtonClicked(QAbstractButton *button);

View File

@@ -32,8 +32,8 @@ void Layout::copyFrom(const Layout *other) {
this->border = other->border;
}
QString Layout::layoutNameFromMapName(const QString &mapName) {
return QString("%1_Layout").arg(mapName);
QString Layout::defaultSuffix() {
return "_Layout";
}
QString Layout::layoutConstantFromName(QString mapName) {

View File

@@ -1382,7 +1382,9 @@ void MainWindow::mapListAddArea() {
void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) {
logInfo(QString("Created a new map named %1.").arg(newMap->name()));
// TODO: Creating a new map shouldn't be automatically saved
// TODO: Creating a new map shouldn't be automatically saved.
// For one, it takes away the option to discard the new map.
// For two, if the new map uses an existing layout, any unsaved changes to that layout will also be saved.
editor->project->saveMap(newMap);
editor->project->saveAllDataStructures();

View File

@@ -382,15 +382,17 @@ Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* t
map->setConstantName(mapConstant);
Layout *layout = this->mapLayouts.value(settings.layout.id);
if (layout) {
// Layout already exists
map->setNeedsLayoutDir(false); // TODO: Remove this member?
} else {
layout = createNewLayout(settings.layout, toDuplicate ? toDuplicate->layout() : nullptr);
}
if (!layout) {
delete map;
return nullptr;
// Layout doesn't already exist, create it.
layout = createNewLayout(settings.layout, toDuplicate ? toDuplicate->layout() : nullptr);
if (!layout) {
// Layout creation failed.
delete map;
return nullptr;
}
} else {
// This layout already exists. Make sure it's loaded.
loadLayout(layout);
}
map->setLayout(layout);
@@ -444,14 +446,17 @@ Layout *Project::createNewLayout(const Layout::Settings &settings, const Layout
layout->tileset_primary_label = settings.primaryTilesetLabel;
layout->tileset_secondary_label = settings.secondaryTilesetLabel;
const QString basePath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders);
layout->border_path = QString("%1%2/border.bin").arg(basePath, layout->name);
layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layout->name);
// If a special folder name was specified (as in the case when we're creating a layout for a new map) then use that name.
// Otherwise the new layout's folder name will just be the layout's name.
const QString folderName = !settings.folderName.isEmpty() ? settings.folderName : layout->name;
const QString folderPath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders) + folderName;
layout->border_path = folderPath + "/border.bin";
layout->blockdata_path = folderPath + "/map.bin";
// Create a new directory for the layout
QString newLayoutDir = QString(root + "/%1%2").arg(projectConfig.getFilePath(ProjectFilePath::data_layouts_folders), layout->name);
if (!QDir::root().mkdir(newLayoutDir)) {
logError(QString("Error: failed to create directory for new layout: '%1'").arg(newLayoutDir));
// Create a new directory for the layout, if it doesn't already exist.
const QString fullPath = QString("%1/%2").arg(this->root).arg(folderPath);
if (!QDir::root().mkpath(fullPath)) {
logError(QString("Failed to create directory for new layout: '%1'").arg(fullPath));
delete layout;
return nullptr;
}
@@ -493,14 +498,14 @@ bool Project::loadLayout(Layout *layout) {
}
Layout *Project::loadLayout(QString layoutId) {
if (mapLayouts.contains(layoutId)) {
Layout *layout = mapLayouts[layoutId];
if (this->mapLayouts.contains(layoutId)) {
Layout *layout = this->mapLayouts[layoutId];
if (loadLayout(layout)) {
return layout;
}
}
logError(QString("Error: Failed to load layout '%1'").arg(layoutId));
logError(QString("Failed to load layout '%1'").arg(layoutId));
return nullptr;
}
@@ -509,10 +514,10 @@ bool Project::loadMapLayout(Map* map) {
return true;
}
if (mapLayouts.contains(map->layoutId())) {
map->setLayout(mapLayouts[map->layoutId()]);
if (this->mapLayouts.contains(map->layoutId())) {
map->setLayout(this->mapLayouts[map->layoutId()]);
} else {
logError(QString("Error: Map '%1' has an unknown layout '%2'").arg(map->name()).arg(map->layoutId()));
logError(QString("Map '%1' has an unknown layout '%2'").arg(map->name()).arg(map->layoutId()));
return false;
}
@@ -535,8 +540,8 @@ void Project::clearMapLayouts() {
bool Project::readMapLayouts() {
clearMapLayouts();
QString layoutsFilepath = projectConfig.getFilePath(ProjectFilePath::json_layouts);
QString fullFilepath = QString("%1/%2").arg(root).arg(layoutsFilepath);
const QString layoutsFilepath = projectConfig.getFilePath(ProjectFilePath::json_layouts);
const QString fullFilepath = QString("%1/%2").arg(this->root).arg(layoutsFilepath);
fileWatcher.addPath(fullFilepath);
QJsonDocument layoutsDoc;
if (!parser.tryParseJsonFile(&layoutsDoc, fullFilepath)) {
@@ -1295,40 +1300,32 @@ void Project::saveAllMaps() {
void Project::saveMap(Map *map) {
// Create/Modify a few collateral files for brand new maps.
QString basePath = projectConfig.getFilePath(ProjectFilePath::data_map_folders);
QString mapDataDir = root + "/" + basePath + map->name();
const QString folderPath = projectConfig.getFilePath(ProjectFilePath::data_map_folders) + map->name();
const QString fullPath = QString("%1/%2").arg(this->root).arg(folderPath);
if (!map->isPersistedToFile()) {
if (!QDir::root().mkdir(mapDataDir)) {
logError(QString("Error: failed to create directory for new map: '%1'").arg(mapDataDir));
if (!QDir::root().mkpath(fullPath)) {
logError(QString("Failed to create directory for new map: '%1'").arg(fullPath));
}
// Create file data/maps/<map_name>/scripts.inc
QString text = this->getScriptDefaultString(projectConfig.usePoryScript, map->name());
saveTextFile(mapDataDir + "/scripts" + this->getScriptFileExtension(projectConfig.usePoryScript), text);
saveTextFile(fullPath + "/scripts" + this->getScriptFileExtension(projectConfig.usePoryScript), text);
if (projectConfig.createMapTextFileEnabled) {
// Create file data/maps/<map_name>/text.inc
saveTextFile(mapDataDir + "/text" + this->getScriptFileExtension(projectConfig.usePoryScript), "\n");
saveTextFile(fullPath + "/text" + this->getScriptFileExtension(projectConfig.usePoryScript), "\n");
}
// Simply append to data/event_scripts.s.
text = QString("\n\t.include \"%1%2/scripts.inc\"\n").arg(basePath, map->name());
text = QString("\n\t.include \"%1/scripts.inc\"\n").arg(folderPath);
if (projectConfig.createMapTextFileEnabled) {
text += QString("\t.include \"%1%2/text.inc\"\n").arg(basePath, map->name());
text += QString("\t.include \"%1/text.inc\"\n").arg(folderPath);
}
appendTextFile(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_event_scripts), text);
// TODO: Either simplify this redundancy or explain why we need it (to create folders without the _Layout suffix)
if (map->needsLayoutDir()) {
QString newLayoutDir = QString(root + "/%1%2").arg(projectConfig.getFilePath(ProjectFilePath::data_layouts_folders), map->name());
if (!QDir::root().mkdir(newLayoutDir)) {
logError(QString("Error: failed to create directory for new layout: '%1'").arg(newLayoutDir));
}
}
}
// Create map.json for map data.
QString mapFilepath = QString("%1/map.json").arg(mapDataDir);
QString mapFilepath = fullPath + "/map.json";
QFile mapFile(mapFilepath);
if (!mapFile.open(QIODevice::WriteOnly)) {
logError(QString("Error: Could not open %1 for writing").arg(mapFilepath));
@@ -1428,7 +1425,6 @@ void Project::saveMap(Map *map) {
}
void Project::saveLayout(Layout *layout) {
//
saveLayoutBorder(layout);
saveLayoutBlockdata(layout);
@@ -2022,7 +2018,8 @@ void Project::initNewMapSettings() {
this->newMapSettings.group = this->groupNames.at(0);
this->newMapSettings.canFlyTo = false;
this->newMapSettings.layout.name = Layout::layoutNameFromMapName(this->newMapSettings.name);
this->newMapSettings.layout.folderName = this->newMapSettings.name;
this->newMapSettings.layout.name = QString("%1%2").arg(this->newMapSettings.name).arg(Layout::defaultSuffix());
this->newMapSettings.layout.id = Layout::layoutConstantFromName(this->newMapSettings.name);
this->newMapSettings.layout.width = getDefaultMapDimension();
this->newMapSettings.layout.height = getDefaultMapDimension();

View File

@@ -28,26 +28,12 @@ NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layoutToCopy, Q
if (this->layoutToCopy && !this->layoutToCopy->name.isEmpty()) {
// Duplicating a layout, the initial name will be the base layout's name
// with a numbered suffix to make it unique.
// Note: Layouts imported with AdvanceMap have no name, so they'll use the default new layout name instead.
// If the layout name ends with the default '_Layout' suffix we'll ignore it.
// This is because (normally) the ID for these layouts will not have this suffix,
// so you can end up in a situation where you might have Map_Layout and Map_2_Layout,
// and if you try to duplicate Map_Layout the next available name (because of ID collisions)
// would be Map_Layout_3 instead of Map_3_Layout.
QString baseName = this->layoutToCopy->name;
QString suffix = "_Layout";
if (baseName.length() > suffix.length() && baseName.endsWith(suffix)) {
baseName.truncate(baseName.length() - suffix.length());
} else {
suffix = "";
}
// Note: If 'layoutToCopy' is an imported AdvanceMap layout it won't have
// a name, so it uses the default new layout name instead.
int i = 2;
do {
newName = QString("%1_%2%3").arg(baseName).arg(i).arg(suffix);
newId = QString("%1_%2").arg(this->layoutToCopy->id).arg(i);
i++;
newName = QString("%1_%2").arg(this->layoutToCopy->name).arg(i++);
newId = Layout::layoutConstantFromName(newName);
} while (!project->isIdentifierUnique(newName) || !project->isIdentifierUnique(newId));
} else {
newName = project->getNewLayoutName();

View File

@@ -71,6 +71,7 @@ Layout::Settings NewLayoutForm::settings() const {
return settings;
}
// TODO: Validate while typing
bool NewLayoutForm::validate() {
// Make sure to call each validation function so that all errors are shown at once.
bool valid = true;

View File

@@ -71,12 +71,12 @@ NewMapDialog::NewMapDialog(Project *project, const Map *mapToCopy, QWidget *pare
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewMapDialog::dialogButtonClicked);
refresh();
adjustSize(); // TODO: Save geometry?
adjustSize();
}
// Adding new map to existing map list folder. Initialize settings accordingly.
// Adding new map to an existing map list folder. Initialize settings accordingly.
// Even if we initialize settings like this we'll allow users to change them afterwards,
// because nothing is expecting them to stay at these values.
// because nothing is expecting them to stay at these values (with exception to layouts).
NewMapDialog::NewMapDialog(Project *project, int mapListTab, const QString &mapListItem, QWidget *parent) :
NewMapDialog(project, parent)
{
@@ -132,21 +132,18 @@ void NewMapDialog::saveSettings() {
settings->group = ui->comboBox_Group->currentText();
settings->layout = ui->newLayoutForm->settings();
settings->layout.id = ui->comboBox_LayoutID->currentText();
settings->layout.name = Layout::layoutNameFromMapName(settings->name); // TODO: Verify uniqueness
settings->canFlyTo = ui->checkBox_CanFlyTo->isChecked();
settings->header = this->headerForm->headerData();
porymapConfig.newMapHeaderSectionExpanded = this->headerSection->isExpanded();
}
// TODO: Verify uniqueness. If the layout ID belongs to an existing layout we don't need to do this at all.
settings->layout.name = QString("%1%2").arg(settings->name).arg(Layout::defaultSuffix());
void NewMapDialog::setLayout(const Layout *layout) {
if (layout) {
ui->comboBox_LayoutID->setTextItem(layout->id);
ui->newLayoutForm->setSettings(layout->settings());
ui->newLayoutForm->setDisabled(true);
} else {
ui->newLayoutForm->setDisabled(false);
}
// Folders for new layouts created for new maps use the map name, rather than the layout name.
// There's no real reason for this, aside from maintaining consistency with the default layout
// folder names that do this (which would otherwise all have a '_Layout' suffix in the name).
settings->layout.folderName = settings->name;
porymapConfig.newMapHeaderSectionExpanded = this->headerSection->isExpanded();
}
bool NewMapDialog::validateName(bool allowEmpty) {
@@ -168,6 +165,8 @@ bool NewMapDialog::validateName(bool allowEmpty) {
void NewMapDialog::on_lineEdit_Name_textChanged(const QString &text) {
validateName(true);
// Changing the map name updates the layout ID field to match.
if (ui->comboBox_LayoutID->isEnabled()) {
ui->comboBox_LayoutID->setCurrentText(Layout::layoutConstantFromName(text));
}
@@ -219,7 +218,15 @@ bool NewMapDialog::validateLayoutID(bool allowEmpty) {
void NewMapDialog::on_comboBox_LayoutID_currentTextChanged(const QString &text) {
validateLayoutID(true);
setLayout(this->project->mapLayouts.value(text));
// Changing the layout ID to an existing layout updates the layout settings to match.
const Layout *layout = this->project->mapLayouts.value(text);
if (layout) {
ui->newLayoutForm->setSettings(layout->settings());
ui->newLayoutForm->setDisabled(true);
} else {
ui->newLayoutForm->setDisabled(false);
}
}
void NewMapDialog::dialogButtonClicked(QAbstractButton *button) {