Write config with project settings editor

This commit is contained in:
GriffinR
2023-08-23 21:07:13 -04:00
parent 4d8f5ef150
commit 9bed66ecfc
12 changed files with 423 additions and 280 deletions

View File

@@ -22,8 +22,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project)
ui->setupUi(this);
initUi();
setAttribute(Qt::WA_DeleteOnClose);
connect(ui->buttonBox, &QDialogButtonBox::clicked,
this, &ProjectSettingsEditor::dialogButtonClicked);
connectSignals();
refresh();
}
ProjectSettingsEditor::~ProjectSettingsEditor()
@@ -31,8 +31,76 @@ ProjectSettingsEditor::~ProjectSettingsEditor()
delete ui;
}
void ProjectSettingsEditor::connectSignals() {
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &ProjectSettingsEditor::dialogButtonClicked);
// Connect combo boxes
QList<NoScrollComboBox *> combos = ui->centralwidget->findChildren<NoScrollComboBox *>();
foreach(auto i, combos)
connect(i, &QComboBox::currentTextChanged, this, &ProjectSettingsEditor::markEdited);
// Connect check boxes
QList<QCheckBox *> checkboxes = ui->centralwidget->findChildren<QCheckBox *>();
foreach(auto i, checkboxes)
connect(i, &QCheckBox::stateChanged, this, &ProjectSettingsEditor::markEdited);
// Connect spin boxes
QList<QSpinBox *> spinBoxes = ui->centralwidget->findChildren<QSpinBox *>();
foreach(auto i, spinBoxes)
connect(i, QOverload<int>::of(&QSpinBox::valueChanged), [this](int) { this->markEdited(); });
// Connect line edits
QList<QLineEdit *> lineEdits = ui->centralwidget->findChildren<QLineEdit *>();
foreach(auto i, lineEdits)
connect(i, &QLineEdit::textEdited, this, &ProjectSettingsEditor::markEdited);
}
void ProjectSettingsEditor::markEdited() {
this->hasUnsavedChanges = true;
}
void ProjectSettingsEditor::initUi() {
// Block signals while setting initial UI states
// Create Default Tilesets combo boxes
auto *defaultTilesetsLayout = new QFormLayout(ui->groupBox_DefaultTilesets);
combo_defaultPrimaryTileset = new NoScrollComboBox(ui->groupBox_DefaultTilesets);
combo_defaultSecondaryTileset = new NoScrollComboBox(ui->groupBox_DefaultTilesets);
if (project) combo_defaultPrimaryTileset->addItems(project->primaryTilesetLabels);
if (project) combo_defaultSecondaryTileset->addItems(project->secondaryTilesetLabels);
defaultTilesetsLayout->addRow("Primary Tileset", combo_defaultPrimaryTileset);
defaultTilesetsLayout->addRow("Secondary Tileset", combo_defaultSecondaryTileset);
// Create Base game version combo box
combo_baseGameVersion = new NoScrollComboBox(ui->widget_BaseGameVersion);
combo_baseGameVersion->addItems(ProjectConfig::versionStrings);
combo_baseGameVersion->setEditable(false);
ui->layout_BaseGameVersion->insertRow(0, "Base game version", combo_baseGameVersion);
// Create Attributes size combo box
auto *attributesSizeLayout = new QFormLayout(ui->widget_SizeDropdown);
combo_attributesSize = new NoScrollComboBox(ui->widget_SizeDropdown);
combo_attributesSize->addItems({"1", "2", "4"});
combo_attributesSize->setEditable(false);
attributesSizeLayout->addRow("", combo_attributesSize);
// Validate that the border metatiles text is a comma-separated list of hex values
static const QRegularExpression expression("^((0[xX])?[A-Fa-f0-9]+,)*(0[xX])?[A-Fa-f0-9]$");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression);
ui->lineEdit_BorderMetatiles->setValidator(validator);
ui->spinBox_Elevation->setMaximum(15);
ui->spinBox_FillMetatile->setMaximum(Project::getNumMetatilesTotal() - 1);
// TODO: These need to be subclassed to handle larger values
ui->spinBox_BehaviorMask->setMaximum(INT_MAX);
ui->spinBox_EncounterTypeMask->setMaximum(INT_MAX);
ui->spinBox_LayerTypeMask->setMaximum(INT_MAX);
ui->spinBox_TerrainTypeMask->setMaximum(INT_MAX);
// TODO: File picker for prefabs?
}
// Set UI states using config data
void ProjectSettingsEditor::refresh() {
const QSignalBlocker blocker0(combo_defaultPrimaryTileset);
const QSignalBlocker blocker1(combo_defaultSecondaryTileset);
const QSignalBlocker blocker2(combo_baseGameVersion);
@@ -54,41 +122,21 @@ void ProjectSettingsEditor::initUi() {
const QSignalBlocker blocker12(ui->checkBox_OutputCallback);
const QSignalBlocker blocker13(ui->checkBox_OutputIsCompressed);
const QSignalBlocker blocker14(ui->spinBox_Elevation);
const QSignalBlocker blocker15(ui->lineEdit_BorderMetatiles);
const QSignalBlocker blocker16(ui->lineEdit_FillMetatile);
const QSignalBlocker blocker17(ui->lineEdit_PrefabsPath);
const QSignalBlocker blocker18(ui->lineEdit_BehaviorMask);
const QSignalBlocker blocker19(ui->lineEdit_EncounterTypeMask);
const QSignalBlocker blocker1A(ui->lineEdit_LayerTypeMask);
const QSignalBlocker blocker1B(ui->lineEdit_TerrainTypeMask);
const QSignalBlocker blocker15(ui->spinBox_FillMetatile);
const QSignalBlocker blocker16(ui->spinBox_BehaviorMask);
const QSignalBlocker blocker17(ui->spinBox_EncounterTypeMask);
const QSignalBlocker blocker18(ui->spinBox_LayerTypeMask);
const QSignalBlocker blocker19(ui->spinBox_TerrainTypeMask);
const QSignalBlocker blocker1A(ui->lineEdit_BorderMetatiles);
const QSignalBlocker blocker1B(ui->lineEdit_PrefabsPath);
// Create Default Tilesets combo boxes
auto *defaultTilesetsLayout = new QFormLayout(ui->groupBox_DefaultTilesets);
combo_defaultPrimaryTileset = new NoScrollComboBox(ui->groupBox_DefaultTilesets);
combo_defaultSecondaryTileset = new NoScrollComboBox(ui->groupBox_DefaultTilesets);
if (project) combo_defaultPrimaryTileset->addItems(project->primaryTilesetLabels);
if (project) combo_defaultSecondaryTileset->addItems(project->secondaryTilesetLabels);
// Set combo box texts
combo_defaultPrimaryTileset->setTextItem(projectConfig.getDefaultPrimaryTileset());
combo_defaultSecondaryTileset->setTextItem(projectConfig.getDefaultSecondaryTileset());
defaultTilesetsLayout->addRow("Primary Tileset", combo_defaultPrimaryTileset);
defaultTilesetsLayout->addRow("Secondary Tileset", combo_defaultSecondaryTileset);
// Create Base game version combo box
combo_baseGameVersion = new NoScrollComboBox(ui->widget_BaseGameVersion);
combo_baseGameVersion->addItems(ProjectConfig::baseGameVersions);
combo_baseGameVersion->setTextItem(projectConfig.getBaseGameVersionString());
combo_baseGameVersion->setEditable(false);
ui->layout_BaseGameVersion->insertRow(0, "Base game version", combo_baseGameVersion);
// Create Attributes size combo box
auto *attributesSizeLayout = new QFormLayout(ui->widget_SizeDropdown);
combo_attributesSize = new NoScrollComboBox(ui->widget_SizeDropdown);
combo_attributesSize->addItems({"1", "2", "4"});
combo_attributesSize->setTextItem(QString::number(projectConfig.getMetatileAttributesSize()));
combo_attributesSize->setEditable(false);
attributesSizeLayout->addRow("", combo_attributesSize);
// Init check boxes
// Set check box states
ui->checkBox_UsePoryscript->setChecked(projectConfig.getUsePoryScript());
ui->checkBox_ShowWildEncounterTables->setChecked(userConfig.getEncounterJsonActive());
ui->checkBox_CreateTextFile->setChecked(projectConfig.getCreateMapTextFileEnabled());
@@ -106,71 +154,107 @@ void ProjectSettingsEditor::initUi() {
ui->checkBox_OutputCallback->setChecked(projectConfig.getTilesetsHaveCallback());
ui->checkBox_OutputIsCompressed->setChecked(projectConfig.getTilesetsHaveIsCompressed());
// Init spinners
ui->spinBox_Elevation->setRange(0, 15);
// Set spin box values
ui->spinBox_Elevation->setValue(projectConfig.getNewMapElevation());
ui->spinBox_FillMetatile->setValue(projectConfig.getNewMapMetatileId());
ui->spinBox_BehaviorMask->setValue(projectConfig.getMetatileBehaviorMask());
ui->spinBox_EncounterTypeMask->setValue(projectConfig.getMetatileEncounterTypeMask());
ui->spinBox_LayerTypeMask->setValue(projectConfig.getMetatileLayerTypeMask());
ui->spinBox_TerrainTypeMask->setValue(projectConfig.getMetatileTerrainTypeMask());
// Init text boxes
// TODO: Validator for Border Metatiles and Fill Metatile
// Set line edit texts
ui->lineEdit_BorderMetatiles->setText(projectConfig.getNewMapBorderMetatileIdsString());
ui->lineEdit_FillMetatile->setText(projectConfig.getNewMapMetatileIdString());
ui->lineEdit_PrefabsPath->setText(projectConfig.getPrefabFilepath(false));
QString mask = ProjectConfig::getMaskString(projectConfig.getMetatileBehaviorMask());
ui->lineEdit_BehaviorMask->setText(mask);
mask = ProjectConfig::getMaskString(projectConfig.getMetatileEncounterTypeMask());
ui->lineEdit_EncounterTypeMask->setText(mask);
mask = ProjectConfig::getMaskString(projectConfig.getMetatileLayerTypeMask());
ui->lineEdit_LayerTypeMask->setText(mask);
mask = ProjectConfig::getMaskString(projectConfig.getMetatileTerrainTypeMask());
ui->lineEdit_TerrainTypeMask->setText(mask);
}
// TODO: Certain setting changes may require project reload
void ProjectSettingsEditor::saveFields() {
// TODO
/*
TODO combo_defaultPrimaryTileset
TODO combo_defaultSecondaryTileset
setBaseGameVersion combo_baseGameVersion
TODO combo_attributesSize
setUsePoryScript ui->checkBox_UsePoryscript
userConfig.setEncounterJsonActive ui->checkBox_ShowWildEncounterTables
setCreateMapTextFileEnabled ui->checkBox_CreateTextFile
setPrefabImportPrompted ui->checkBox_PrefabImportPrompted
setTripleLayerMetatilesEnabled ui->checkBox_EnableTripleLayerMetatiles
setHiddenItemRequiresItemfinderEnabled ui->checkBox_EnableRequiresItemfinder
setHiddenItemQuantityEnabled ui->checkBox_EnableQuantity
setEventCloneObjectEnabled ui->checkBox_EnableCloneObjects
setEventWeatherTriggerEnabled ui->checkBox_EnableWeatherTriggers
setEventSecretBaseEnabled ui->checkBox_EnableSecretBases
setHealLocationRespawnDataEnabled ui->checkBox_EnableRespawn
TODO ui->checkBox_EnableAllowFlags
setFloorNumberEnabled ui->checkBox_EnableFloorNumber
setUseCustomBorderSize ui->checkBox_EnableCustomBorderSize
setTilesetsHaveCallback ui->checkBox_OutputCallback
setTilesetsHaveIsCompressed ui->checkBox_OutputIsCompressed
setNewMapElevation ui->spinBox_Elevation
setPrefabFilepath ui->lineEdit_PrefabsPath
TODO ui->lineEdit_BehaviorMask
TODO ui->lineEdit_EncounterTypeMask
TODO ui->lineEdit_LayerTypeMask
TODO ui->lineEdit_TerrainTypeMask
setNewMapMetatileId ui->lineEdit_FillMetatile
setNewMapBorderMetatileIds ui->lineEdit_BorderMetatiles
*/
if (!this->hasUnsavedChanges)
return;
// Prevent a call to save() for each of the config settings
projectConfig.setSaveDisabled(true);
userConfig.setSaveDisabled(true);
projectConfig.setDefaultPrimaryTileset(combo_defaultPrimaryTileset->currentText());
projectConfig.setDefaultSecondaryTileset(combo_defaultSecondaryTileset->currentText());
projectConfig.setBaseGameVersion(projectConfig.stringToBaseGameVersion(combo_baseGameVersion->currentText()));
projectConfig.setMetatileAttributesSize(combo_attributesSize->currentText().toInt());
projectConfig.setUsePoryScript(ui->checkBox_UsePoryscript->isChecked());
userConfig.setEncounterJsonActive(ui->checkBox_ShowWildEncounterTables->isChecked());
projectConfig.setCreateMapTextFileEnabled(ui->checkBox_CreateTextFile->isChecked());
projectConfig.setPrefabImportPrompted(ui->checkBox_PrefabImportPrompted->isChecked());
projectConfig.setTripleLayerMetatilesEnabled(ui->checkBox_EnableTripleLayerMetatiles->isChecked());
projectConfig.setHiddenItemRequiresItemfinderEnabled(ui->checkBox_EnableRequiresItemfinder->isChecked());
projectConfig.setHiddenItemQuantityEnabled(ui->checkBox_EnableQuantity->isChecked());
projectConfig.setEventCloneObjectEnabled(ui->checkBox_EnableCloneObjects->isChecked());
projectConfig.setEventWeatherTriggerEnabled(ui->checkBox_EnableWeatherTriggers->isChecked());
projectConfig.setEventSecretBaseEnabled(ui->checkBox_EnableSecretBases->isChecked());
projectConfig.setHealLocationRespawnDataEnabled(ui->checkBox_EnableRespawn->isChecked());
projectConfig.setMapAllowFlagsEnabled(ui->checkBox_EnableAllowFlags->isChecked());
projectConfig.setFloorNumberEnabled(ui->checkBox_EnableFloorNumber->isChecked());
projectConfig.setUseCustomBorderSize(ui->checkBox_EnableCustomBorderSize->isChecked());
projectConfig.setTilesetsHaveCallback(ui->checkBox_OutputCallback->isChecked());
projectConfig.setTilesetsHaveIsCompressed(ui->checkBox_OutputIsCompressed->isChecked());
projectConfig.setNewMapElevation(ui->spinBox_Elevation->value());
projectConfig.setNewMapMetatileId(ui->spinBox_FillMetatile->value());
projectConfig.setMetatileBehaviorMask(ui->spinBox_BehaviorMask->value());
projectConfig.setMetatileTerrainTypeMask(ui->spinBox_EncounterTypeMask->value());
projectConfig.setMetatileEncounterTypeMask(ui->spinBox_LayerTypeMask->value());
projectConfig.setMetatileLayerTypeMask(ui->spinBox_TerrainTypeMask->value());
projectConfig.setPrefabFilepath(ui->lineEdit_PrefabsPath->text());
// Parse border metatile list
QList<QString> metatileIdStrings = ui->lineEdit_BorderMetatiles->text().split(",");
QList<uint16_t> metatileIds;
for (auto s : metatileIdStrings) {
uint16_t metatileId = s.toUInt(nullptr, 0);
metatileIds.append(qMin(metatileId, static_cast<uint16_t>(Project::getNumMetatilesTotal() - 1)));
}
projectConfig.setNewMapBorderMetatileIds(metatileIds);
projectConfig.setSaveDisabled(false);
projectConfig.save();
userConfig.setSaveDisabled(false);
userConfig.save();
this->hasUnsavedChanges = false;
emit saved();
}
// TODO: Standard prompt replacement?
bool ProjectSettingsEditor::prompt(const QString &text) {
QMessageBox messageBox(this);
messageBox.setText(text);
messageBox.setIcon(QMessageBox::Question);
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); // TODO: Cancel
return messageBox.exec() == QMessageBox::Yes;
}
void ProjectSettingsEditor::dialogButtonClicked(QAbstractButton *button) {
auto buttonRole = ui->buttonBox->buttonRole(button);
if (buttonRole == QDialogButtonBox::AcceptRole) {
// TODO: Prompt for unsaved changes
saveFields();
close();
} else if (buttonRole == QDialogButtonBox::ResetRole) {
// TODO
} else if (buttonRole == QDialogButtonBox::ApplyRole) {
saveFields();
} else if (buttonRole == QDialogButtonBox::ResetRole) {
// Restore Defaults
// TODO: Confirm dialogue?
const QString versionText = combo_baseGameVersion->currentText();
if (!prompt(QString("Restore default config settings for %1?").arg(versionText)))
return;
projectConfig.reset(projectConfig.stringToBaseGameVersion(versionText));
projectConfig.save();
userConfig.reset();
userConfig.save();
refresh();
} else if (buttonRole == QDialogButtonBox::RejectRole) {
if (this->hasUnsavedChanges && !prompt(QString("Discard unsaved changes?"))) {
// TODO:
// Unsaved changes prompt
}
close();
}
// TODO: Save geometry on close