diff --git a/forms/newlayoutform.ui b/forms/newlayoutform.ui index f51fb742..bc3e7502 100644 --- a/forms/newlayoutform.ui +++ b/forms/newlayoutform.ui @@ -170,14 +170,30 @@ - + + + + false + + + color: rgb(255, 0, 0) + + + + + + true + + + + Secondary - + <html><head/><body><p>The secondary tileset for the new map.</p></body></html> @@ -190,8 +206,8 @@ - - + + false diff --git a/forms/newnamedialog.ui b/forms/newnamedialog.ui new file mode 100644 index 00000000..6fa6b56d --- /dev/null +++ b/forms/newnamedialog.ui @@ -0,0 +1,82 @@ + + + NewNameDialog + + + + 0 + 0 + 252 + 87 + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Name + + + + + + + true + + + + + + + false + + + color: rgb(255, 0, 0) + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + false + + + + + + + + diff --git a/include/ui/newlayoutform.h b/include/ui/newlayoutform.h index 6f8d9905..3e77af18 100644 --- a/include/ui/newlayoutform.h +++ b/include/ui/newlayoutform.h @@ -33,7 +33,8 @@ private: Project *m_project; bool validateMapDimensions(); - bool validateTilesets(); + bool validatePrimaryTileset(bool allowEmpty = false); + bool validateSecondaryTileset(bool allowEmpty = false); }; #endif // NEWLAYOUTFORM_H diff --git a/include/ui/newnamedialog.h b/include/ui/newnamedialog.h new file mode 100644 index 00000000..23979fc5 --- /dev/null +++ b/include/ui/newnamedialog.h @@ -0,0 +1,42 @@ +#ifndef NEWNAMEDIALOG_H +#define NEWNAMEDIALOG_H + +/* + This is a generic dialog for requesting a new unique name from the user. +*/ + +#include +#include + +class Project; + +namespace Ui { +class NewNameDialog; +} + +class NewNameDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NewNameDialog(const QString &label, Project *project, QWidget *parent = nullptr); + ~NewNameDialog(); + + void setNamePrefix(const QString &prefix); + + virtual void accept() override; + +signals: + void applied(const QString &newName); + +private: + Ui::NewNameDialog *ui; + Project *project = nullptr; + const QString symbolPrefix; + + bool validateName(bool allowEmpty = false); + void onNameChanged(const QString &name); + void dialogButtonClicked(QAbstractButton *button); +}; + +#endif // NEWNAMEDIALOG_H diff --git a/porymap.pro b/porymap.pro index 1dfaf4b1..171ea48d 100644 --- a/porymap.pro +++ b/porymap.pro @@ -91,6 +91,7 @@ SOURCES += src/core/advancemapparser.cpp \ src/ui/neweventtoolbutton.cpp \ src/ui/newlayoutdialog.cpp \ src/ui/newlayoutform.cpp \ + src/ui/newnamedialog.cpp \ src/ui/noscrollcombobox.cpp \ src/ui/noscrollspinbox.cpp \ src/ui/montabwidget.cpp \ @@ -197,6 +198,7 @@ HEADERS += include/core/advancemapparser.h \ include/ui/neweventtoolbutton.h \ include/ui/newlayoutdialog.h \ include/ui/newlayoutform.h \ + include/ui/newnamedialog.h \ include/ui/noscrollcombobox.h \ include/ui/noscrollspinbox.h \ include/ui/montabwidget.h \ @@ -243,6 +245,7 @@ FORMS += forms/mainwindow.ui \ forms/maplisttoolbar.ui \ forms/newlayoutdialog.ui \ forms/newlayoutform.ui \ + forms/newnamedialog.ui \ forms/newmapconnectiondialog.ui \ forms/prefabcreationdialog.ui \ forms/prefabframe.ui \ diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e006ed9a..629a582f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -26,6 +26,7 @@ #include "newmapdialog.h" #include "newlayoutdialog.h" #include "newtilesetdialog.h" +#include "newnamedialog.h" #include #include @@ -1310,90 +1311,16 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { } void MainWindow::mapListAddGroup() { - QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - dialog.setWindowModality(Qt::ApplicationModal); - QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); - connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - - QLineEdit *newNameEdit = new QLineEdit(&dialog); - newNameEdit->setClearButtonEnabled(true); - - static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*"); - newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit)); - - QLabel *errorMessageLabel = new QLabel(&dialog); - errorMessageLabel->setVisible(false); - errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }"); - - connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ - const QString mapGroupName = newNameEdit->text(); - if (!this->editor->project->isIdentifierUnique(mapGroupName)) { - errorMessageLabel->setText(QString("The name '%1' is not unique.").arg(mapGroupName)); - errorMessageLabel->setVisible(true); - } else { - dialog.accept(); - } - }); - - QFormLayout form(&dialog); - - form.addRow("New Group Name", newNameEdit); - form.addRow("", errorMessageLabel); - form.addRow(&newItemButtonBox); - - if (dialog.exec() == QDialog::Accepted) { - QString newFieldName = newNameEdit->text(); - if (newFieldName.isEmpty()) return; - this->editor->project->addNewMapGroup(newFieldName); - } + auto dialog = new NewNameDialog("New Group Name", this->editor->project, this); + connect(dialog, &NewNameDialog::applied, this->editor->project, &Project::addNewMapGroup); + dialog->open(); } void MainWindow::mapListAddArea() { - QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint); - dialog.setWindowModality(Qt::ApplicationModal); - QDialogButtonBox newItemButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); - connect(&newItemButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - - // TODO: This would be a little more seamless with a single line edit that enforces the MAPSEC prefix, rather than a separate label for the actual name. - const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix); - auto newNameEdit = new QLineEdit(&dialog); - auto newNameDisplay = new QLabel(&dialog); - newNameDisplay->setText(prefix); - connect(newNameEdit, &QLineEdit::textEdited, [newNameDisplay, prefix] (const QString &text) { - // As the user types a name, update the label to show the name with the prefix. - newNameDisplay->setText(prefix + text); - }); - - QLabel *errorMessageLabel = new QLabel(&dialog); - errorMessageLabel->setVisible(false); - errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }"); - - static const QRegularExpression re_validChars("[A-Za-z_]+[\\w]*"); - newNameEdit->setValidator(new QRegularExpressionValidator(re_validChars, newNameEdit)); - - connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){ - const QString newAreaName = newNameDisplay->text(); - if (!this->editor->project->isIdentifierUnique(newAreaName)) { - errorMessageLabel->setText(QString("The name '%1' is not unique.").arg(newAreaName)); - errorMessageLabel->setVisible(true); - } else { - dialog.accept(); - } - }); - - QLabel *newNameEditLabel = new QLabel("New Area Name", &dialog); - QLabel *newNameDisplayLabel = new QLabel("Constant Name", &dialog); - - QFormLayout form(&dialog); - form.addRow(newNameEditLabel, newNameEdit); - form.addRow(newNameDisplayLabel, newNameDisplay); - form.addRow("", errorMessageLabel); - form.addRow(&newItemButtonBox); - - if (dialog.exec() == QDialog::Accepted) { - if (newNameEdit->text().isEmpty()) return; - this->editor->project->addNewMapsec(newNameDisplay->text()); - } + auto dialog = new NewNameDialog("New Area Name", this->editor->project, this); + dialog->setNamePrefix(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)); + connect(dialog, &NewNameDialog::applied, this->editor->project, &Project::addNewMapsec); + dialog->open(); } void MainWindow::onNewMapCreated(Map *newMap, const QString &groupName) { diff --git a/src/ui/newlayoutform.cpp b/src/ui/newlayoutform.cpp index 64c2fdfb..aa8178ce 100644 --- a/src/ui/newlayoutform.cpp +++ b/src/ui/newlayoutform.cpp @@ -12,12 +12,14 @@ NewLayoutForm::NewLayoutForm(QWidget *parent) ui->groupBox_BorderDimensions->setVisible(projectConfig.useCustomBorderSize); - // TODO: Read from project? ui->spinBox_BorderWidth->setMaximum(MAX_BORDER_WIDTH); ui->spinBox_BorderHeight->setMaximum(MAX_BORDER_HEIGHT); - connect(ui->spinBox_MapWidth, QOverload::of(&QSpinBox::valueChanged), [=](int){validateMapDimensions();}); - connect(ui->spinBox_MapHeight, QOverload::of(&QSpinBox::valueChanged), [=](int){validateMapDimensions();}); + connect(ui->spinBox_MapWidth, QOverload::of(&QSpinBox::valueChanged), [=](int){ validateMapDimensions(); }); + connect(ui->spinBox_MapHeight, QOverload::of(&QSpinBox::valueChanged), [=](int){ validateMapDimensions(); }); + + connect(ui->comboBox_PrimaryTileset->lineEdit(), &QLineEdit::editingFinished, [this]{ validatePrimaryTileset(true); }); + connect(ui->comboBox_SecondaryTileset->lineEdit(), &QLineEdit::editingFinished, [this]{ validateSecondaryTileset(true); }); } NewLayoutForm::~NewLayoutForm() @@ -71,12 +73,12 @@ 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; if (!validateMapDimensions()) valid = false; - if (!validateTilesets()) valid = false; + if (!validatePrimaryTileset()) valid = false; + if (!validateSecondaryTileset()) valid = false; return valid; } @@ -84,11 +86,17 @@ bool NewLayoutForm::validateMapDimensions() { int size = m_project->getMapDataSize(ui->spinBox_MapWidth->value(), ui->spinBox_MapHeight->value()); int maxSize = m_project->getMaxMapDataSize(); + // TODO: Get from project + const int additionalWidth = 15; + const int additionalHeight = 14; + QString errorText; if (size > maxSize) { errorText = QString("The specified width and height are too large.\n" - "The maximum map width and height is the following: (width + 15) * (height + 14) <= %1\n" - "The specified map width and height was: (%2 + 15) * (%3 + 14) = %4") + "The maximum map width and height is the following: (width + %1) * (height + %2) <= %3\n" + "The specified map width and height was: (%4 + %1) * (%5 + %2) = %6") + .arg(additionalWidth) + .arg(additionalHeight) .arg(maxSize) .arg(ui->spinBox_MapWidth->value()) .arg(ui->spinBox_MapHeight->value()) @@ -101,33 +109,36 @@ bool NewLayoutForm::validateMapDimensions() { return isValid; } -bool NewLayoutForm::validateTilesets() { - QString primaryTileset = ui->comboBox_PrimaryTileset->currentText(); - QString secondaryTileset = ui->comboBox_SecondaryTileset->currentText(); +bool NewLayoutForm::validatePrimaryTileset(bool allowEmpty) { + const QString name = ui->comboBox_PrimaryTileset->currentText(); - QString primaryErrorText; - if (primaryTileset.isEmpty()) { - primaryErrorText = QString("The primary tileset cannot be empty."); - } else if (ui->comboBox_PrimaryTileset->findText(primaryTileset) < 0) { - primaryErrorText = QString("The specified primary tileset '%1' does not exist.").arg(primaryTileset); + QString errorText; + if (name.isEmpty()) { + if (!allowEmpty) errorText = QString("The Primary Tileset cannot be empty."); + } else if (ui->comboBox_PrimaryTileset->findText(name) < 0) { + errorText = QString("The Primary Tileset '%1' does not exist.").arg(ui->label_PrimaryTileset->text()).arg(name); } - QString secondaryErrorText; - if (secondaryTileset.isEmpty()) { - secondaryErrorText = QString("The secondary tileset cannot be empty."); - } else if (ui->comboBox_SecondaryTileset->findText(secondaryTileset) < 0) { - secondaryErrorText = QString("The specified secondary tileset '%2' does not exist.").arg(secondaryTileset); - } - - QString errorText = QString("%1%2%3") - .arg(primaryErrorText) - .arg(!primaryErrorText.isEmpty() ? "\n" : "") - .arg(secondaryErrorText); - bool isValid = errorText.isEmpty(); - ui->label_TilesetsError->setText(errorText); - ui->label_TilesetsError->setVisible(!isValid); - ui->comboBox_PrimaryTileset->lineEdit()->setStyleSheet(!primaryErrorText.isEmpty() ? lineEdit_ErrorStylesheet : ""); - ui->comboBox_SecondaryTileset->lineEdit()->setStyleSheet(!secondaryErrorText.isEmpty() ? lineEdit_ErrorStylesheet : ""); + ui->label_PrimaryTilesetError->setText(errorText); + ui->label_PrimaryTilesetError->setVisible(!isValid); + ui->comboBox_PrimaryTileset->lineEdit()->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : ""); + return isValid; +} + +bool NewLayoutForm::validateSecondaryTileset(bool allowEmpty) { + const QString name = ui->comboBox_SecondaryTileset->currentText(); + + QString errorText; + if (name.isEmpty()) { + if (!allowEmpty) errorText = QString("The Secondary Tileset cannot be empty."); + } else if (ui->comboBox_SecondaryTileset->findText(name) < 0) { + errorText = QString("The Secondary Tileset '%1' does not exist.").arg(name); + } + + bool isValid = errorText.isEmpty(); + ui->label_SecondaryTilesetError->setText(errorText); + ui->label_SecondaryTilesetError->setVisible(!isValid); + ui->comboBox_SecondaryTileset->lineEdit()->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : ""); return isValid; } diff --git a/src/ui/newnamedialog.cpp b/src/ui/newnamedialog.cpp new file mode 100644 index 00000000..d5800daa --- /dev/null +++ b/src/ui/newnamedialog.cpp @@ -0,0 +1,76 @@ +#include "newnamedialog.h" +#include "ui_newnamedialog.h" +#include "project.h" +#include "imageexport.h" + +const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }"; + +NewNameDialog::NewNameDialog(const QString &label, Project* project, QWidget *parent) : + QDialog(parent), + ui(new Ui::NewNameDialog) +{ + setAttribute(Qt::WA_DeleteOnClose); + setModal(true); + ui->setupUi(this); + this->project = project; + + if (!label.isEmpty()) + ui->label_Name->setText(label); + + // Identifiers must only contain word characters, and cannot start with a digit. + static const QRegularExpression expression("[A-Za-z_]+[\\w]*"); + QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression, this); + ui->lineEdit_Name->setValidator(validator); + + connect(ui->lineEdit_Name, &QLineEdit::textChanged, this, &NewNameDialog::onNameChanged); + connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewNameDialog::dialogButtonClicked); + + adjustSize(); +} + +NewNameDialog::~NewNameDialog() +{ + delete ui; +} + +void NewNameDialog::setNamePrefix(const QString &) { + //TODO +} + +void NewNameDialog::onNameChanged(const QString &) { + validateName(true); +} + +bool NewNameDialog::validateName(bool allowEmpty) { + const QString name = ui->lineEdit_Name->text(); + + QString errorText; + if (name.isEmpty()) { + if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_Name->text()); + } else if (!this->project->isIdentifierUnique(name)) { + errorText = QString("%1 '%2' is not unique.").arg(ui->label_Name->text()).arg(name); + } + + bool isValid = errorText.isEmpty(); + ui->label_NameError->setText(errorText); + ui->label_NameError->setVisible(!isValid); + ui->lineEdit_Name->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : ""); + return isValid; +} + +void NewNameDialog::dialogButtonClicked(QAbstractButton *button) { + auto role = ui->buttonBox->buttonRole(button); + if (role == QDialogButtonBox::RejectRole){ + reject(); + } else if (role == QDialogButtonBox::AcceptRole) { + accept(); + } +} + +void NewNameDialog::accept() { + if (!validateName()) + return; + + emit applied(ui->lineEdit_Name->text()); + QDialog::accept(); +}