From bf3820745a1870d6c0395c14de5febc608b315a6 Mon Sep 17 00:00:00 2001 From: GriffinR Date: Sat, 14 Dec 2024 16:25:11 -0500 Subject: [PATCH] Add new QValidator classes --- forms/newtilesetdialog.ui | 26 +++---------- include/core/validator.h | 66 ++++++++++++++++++++++++++++++++ include/project.h | 4 +- include/ui/aboutporymap.h | 1 - include/ui/colorinputwidget.h | 1 - include/ui/maplistmodels.h | 3 -- include/ui/newnamedialog.h | 6 +-- include/ui/newtilesetdialog.h | 2 +- include/ui/tileseteditor.h | 2 - porymap.pro | 2 + src/config.cpp | 7 ++-- src/core/tileset.cpp | 7 ++-- src/core/validator.cpp | 30 +++++++++++++++ src/editor.cpp | 5 +-- src/mainwindow.cpp | 5 +-- src/project.cpp | 31 ++++++++------- src/ui/colorinputwidget.cpp | 12 ++---- src/ui/maplistmodels.cpp | 9 ++--- src/ui/newlayoutdialog.cpp | 5 +-- src/ui/newmapdialog.cpp | 5 +-- src/ui/newnamedialog.cpp | 20 ++++------ src/ui/newtilesetdialog.cpp | 33 +++++++--------- src/ui/projectsettingseditor.cpp | 5 --- src/ui/tileseteditor.cpp | 50 ++++++++++-------------- 24 files changed, 189 insertions(+), 148 deletions(-) create mode 100644 include/core/validator.h create mode 100644 src/core/validator.cpp diff --git a/forms/newtilesetdialog.ui b/forms/newtilesetdialog.ui index 3b898761..13996c73 100644 --- a/forms/newtilesetdialog.ui +++ b/forms/newtilesetdialog.ui @@ -33,14 +33,14 @@ 0 - + Name - + true @@ -60,27 +60,13 @@ - - - Symbol Name - - - - - - - - - - - Type - + false @@ -97,17 +83,17 @@ - + Checkerboard Fill - + - + Qt::Orientation::Vertical diff --git a/include/core/validator.h b/include/core/validator.h new file mode 100644 index 00000000..f5f2ef91 --- /dev/null +++ b/include/core/validator.h @@ -0,0 +1,66 @@ +#ifndef VALIDATOR_H +#define VALIDATOR_H + +/* + This file contains our subclasses of QValidator. + + - PrefixValidator is for input widgets that want to enforce a particular prefix. + It differs from a QRegularExpressionValidator with a prefix in the regex because + it will automatically enforce the prefix in fixup() if it isn't present. + It's preferable to QLineEdit's input mask because it won't affect cursor behavior. + + - IdentifierValidator is for validating that input text can be used for the name of an identifier in the project. + (i.e., starts with a letter or underscore, then may continue with letters, numbers, or underscores). + Unless a prefix is specified this is a normal QRegularExpressionValidator, we only have a subclass because we use it so often. + + - UppercaseValidator is just a validator that uppercases input text. +*/ + +#include + +class PrefixValidator : public QRegularExpressionValidator { + Q_OBJECT + +public: + explicit PrefixValidator(const QString &prefix, QObject *parent = nullptr) + : QRegularExpressionValidator(parent), m_prefix(prefix) {}; + explicit PrefixValidator(const QString &prefix, const QRegularExpression &re, QObject *parent = nullptr) + : QRegularExpressionValidator(re, parent), m_prefix(prefix) {}; + ~PrefixValidator() {}; + + virtual QValidator::State validate(QString &input, int &) const override; + virtual void fixup(QString &input) const override; + + QString prefix() const { return m_prefix; } + void setPrefix(const QString &prefix); + + bool isValid(QString &input) const; + +private: + QString m_prefix; + + bool missingPrefix(const QString &input) const; +}; + +class IdentifierValidator : public PrefixValidator { + Q_OBJECT + +public: + explicit IdentifierValidator(QObject *parent = nullptr) + : PrefixValidator("", re_identifier, parent) {}; + explicit IdentifierValidator(const QString &prefix, QObject *parent = nullptr) + : PrefixValidator(prefix, re_identifier, parent) {}; + ~IdentifierValidator() {}; + +private: + static const QRegularExpression re_identifier; +}; + +class UppercaseValidator : public QValidator { + virtual QValidator::State validate(QString &input, int &) const override { + input = input.toUpper(); + return QValidator::Acceptable; + } +}; + +#endif // VALIDATOR_H diff --git a/include/project.h b/include/project.h index 9c9fdf33..fefa1f34 100644 --- a/include/project.h +++ b/include/project.h @@ -140,9 +140,9 @@ public: Map *createNewMap(const Project::NewMapSettings &mapSettings, const Map* toDuplicate = nullptr); Layout *createNewLayout(const Layout::Settings &layoutSettings, const Layout* toDuplicate = nullptr); - Tileset *createNewTileset(const QString &friendlyName, bool secondary, bool checkerboardFill); + Tileset *createNewTileset(QString name, bool secondary, bool checkerboardFill); bool isIdentifierUnique(const QString &identifier) const; - bool isValidNewIdentifier(const QString &identifier) const; + bool isValidNewIdentifier(QString identifier) const; QString toUniqueIdentifier(const QString &identifier) const; QString getProjectTitle(); diff --git a/include/ui/aboutporymap.h b/include/ui/aboutporymap.h index 28b06249..f960ab8c 100644 --- a/include/ui/aboutporymap.h +++ b/include/ui/aboutporymap.h @@ -2,7 +2,6 @@ #define ABOUTPORYMAP_H #include -#include #include namespace Ui { diff --git a/include/ui/colorinputwidget.h b/include/ui/colorinputwidget.h index cd871e0b..527794d9 100644 --- a/include/ui/colorinputwidget.h +++ b/include/ui/colorinputwidget.h @@ -2,7 +2,6 @@ #define COLORINPUTWIDGET_H #include -#include namespace Ui { class ColorInputWidget; diff --git a/include/ui/maplistmodels.h b/include/ui/maplistmodels.h index 920bf9ba..53a79f4d 100644 --- a/include/ui/maplistmodels.h +++ b/include/ui/maplistmodels.h @@ -54,9 +54,6 @@ private: }; - -class QRegularExpressionValidator; - class MapListModel : public QStandardItemModel { Q_OBJECT diff --git a/include/ui/newnamedialog.h b/include/ui/newnamedialog.h index 23979fc5..4abe02df 100644 --- a/include/ui/newnamedialog.h +++ b/include/ui/newnamedialog.h @@ -19,11 +19,9 @@ class NewNameDialog : public QDialog Q_OBJECT public: - explicit NewNameDialog(const QString &label, Project *project, QWidget *parent = nullptr); + explicit NewNameDialog(const QString &label, const QString &prefix = "", Project *project = nullptr, QWidget *parent = nullptr); ~NewNameDialog(); - void setNamePrefix(const QString &prefix); - virtual void accept() override; signals: @@ -32,7 +30,7 @@ signals: private: Ui::NewNameDialog *ui; Project *project = nullptr; - const QString symbolPrefix; + const QString namePrefix; bool validateName(bool allowEmpty = false); void onNameChanged(const QString &name); diff --git a/include/ui/newtilesetdialog.h b/include/ui/newtilesetdialog.h index 06012708..374c85a7 100644 --- a/include/ui/newtilesetdialog.h +++ b/include/ui/newtilesetdialog.h @@ -27,7 +27,7 @@ private: const QString symbolPrefix; bool validateName(bool allowEmpty = false); - void onFriendlyNameChanged(const QString &friendlyName); + void onNameChanged(const QString &name); void dialogButtonClicked(QAbstractButton *button); }; diff --git a/include/ui/tileseteditor.h b/include/ui/tileseteditor.h index 32bdc97f..5cd6205e 100644 --- a/include/ui/tileseteditor.h +++ b/include/ui/tileseteditor.h @@ -120,9 +120,7 @@ private slots: void on_horizontalSlider_TilesZoom_valueChanged(int value); private: - void initUi(); void setAttributesUi(); - void setMetatileLabelValidator(); void initMetatileSelector(); void initTileSelector(); void initSelectedTileItem(); diff --git a/porymap.pro b/porymap.pro index 171ea48d..5ba1e6e0 100644 --- a/porymap.pro +++ b/porymap.pro @@ -39,6 +39,7 @@ SOURCES += src/core/advancemapparser.cpp \ src/core/parseutil.cpp \ src/core/tile.cpp \ src/core/tileset.cpp \ + src/core/validator.cpp \ src/core/regionmap.cpp \ src/core/wildmoninfo.cpp \ src/core/editcommands.cpp \ @@ -146,6 +147,7 @@ HEADERS += include/core/advancemapparser.h \ include/core/parseutil.h \ include/core/tile.h \ include/core/tileset.h \ + include/core/validator.h \ include/core/regionmap.h \ include/core/wildmoninfo.h \ include/core/editcommands.h \ diff --git a/src/config.cpp b/src/config.cpp index 234a0a33..bcfa3489 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -2,6 +2,7 @@ #include "log.h" #include "shortcut.h" #include "map.h" +#include "validator.h" #include #include #include @@ -952,10 +953,8 @@ void ProjectConfig::setIdentifier(ProjectIdentifier id, QString text) { const QString idName = defaultIdentifiers.value(id).first; if (idName.startsWith("define_") || idName.startsWith("symbol_")) { // Validate the input for the identifier, depending on the type. - static const QRegularExpression re("[A-Za-z_]+[\\w]*"); - auto validator = QRegularExpressionValidator(re); - int temp = 0; - if (validator.validate(text, temp) != QValidator::Acceptable) { + IdentifierValidator validator; + if (!validator.isValid(text)) { logError(QString("The name '%1' for project identifier '%2' is invalid. It must only contain word characters, and cannot start with a digit.").arg(text).arg(idName)); return; } diff --git a/src/core/tileset.cpp b/src/core/tileset.cpp index cf21a675..e79e7706 100644 --- a/src/core/tileset.cpp +++ b/src/core/tileset.cpp @@ -4,6 +4,7 @@ #include "log.h" #include "config.h" #include "imageproviders.h" +#include "validator.h" #include #include @@ -180,10 +181,8 @@ bool Tileset::setMetatileLabel(int metatileId, QString label, Tileset *primaryTi if (!tileset) return false; - static const QRegularExpression expression("[_A-Za-z0-9]*$"); - QRegularExpressionValidator validator(expression); - int pos = 0; - if (validator.validate(label, pos) != QValidator::Acceptable) + IdentifierValidator validator; + if (!validator.isValid(label)) return false; tileset->metatileLabels[metatileId] = label; diff --git a/src/core/validator.cpp b/src/core/validator.cpp new file mode 100644 index 00000000..b3b36589 --- /dev/null +++ b/src/core/validator.cpp @@ -0,0 +1,30 @@ +#include "validator.h" + +// Identifiers must only contain word characters, and cannot start with a digit. +const QRegularExpression IdentifierValidator::re_identifier = QRegularExpression("[A-Za-z_]+[\\w]*"); + + +bool PrefixValidator::missingPrefix(const QString &input) const { + return !m_prefix.isEmpty() && !input.startsWith(m_prefix); +} + +QValidator::State PrefixValidator::validate(QString &input, int &pos) const { + auto state = QRegularExpressionValidator::validate(input, pos); + if (state == QValidator::Acceptable) { + // This input could be valid. If there's a prefix we should require it now. + if (missingPrefix(input)) + state = QValidator::Intermediate; + } + return state; +} + +void PrefixValidator::fixup(QString &input) const { + QRegularExpressionValidator::fixup(input); + if (missingPrefix(input)) + input.prepend(m_prefix); +} + +bool PrefixValidator::isValid(QString &input) const { + int pos = 0; + return validate(input, pos) == QValidator::Acceptable; +} diff --git a/src/editor.cpp b/src/editor.cpp index e13f76eb..56914c30 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -11,6 +11,7 @@ #include "config.h" #include "scripting.h" #include "customattributestable.h" +#include "validator.h" #include #include #include @@ -299,9 +300,7 @@ void Editor::addNewWildMonGroup(QWidget *window) { QLineEdit *lineEdit = new QLineEdit(); lineEdit->setClearButtonEnabled(true); form.addRow(new QLabel("Group Base Label:"), lineEdit); - static const QRegularExpression re_validChars("[_A-Za-z0-9]*"); - QRegularExpressionValidator *validator = new QRegularExpressionValidator(re_validChars); - lineEdit->setValidator(validator); + lineEdit->setValidator(new IdentifierValidator(lineEdit)); connect(lineEdit, &QLineEdit::textChanged, [this, &lineEdit, &buttonBox](QString text){ if (this->project->encounterGroupLabels.contains(text)) { lineEdit->setStyleSheet("QLineEdit { background-color: rgba(255, 0, 0, 25%) }"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 65a323e2..50c8beae 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1310,14 +1310,13 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point) { } void MainWindow::openNewMapGroupDialog() { - auto dialog = new NewNameDialog("New Group Name", this->editor->project, this); + auto dialog = new NewNameDialog("New Group Name", "", this->editor->project, this); connect(dialog, &NewNameDialog::applied, this->editor->project, &Project::addNewMapGroup); dialog->open(); } void MainWindow::openNewAreaDialog() { - auto dialog = new NewNameDialog("New Area Name", this->editor->project, this); - dialog->setNamePrefix(projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix)); + auto dialog = new NewNameDialog("New Area Name", projectConfig.getIdentifier(ProjectIdentifier::define_map_section_prefix), this->editor->project, this); connect(dialog, &NewNameDialog::applied, this->editor->project, &Project::addNewMapsec); dialog->open(); } diff --git a/src/project.cpp b/src/project.cpp index 0a66c0e8..efc591e4 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -8,7 +8,7 @@ #include "tileset.h" #include "map.h" #include "filedialog.h" - +#include "validator.h" #include "orderedjson.h" #include @@ -1453,9 +1453,15 @@ void Project::readTilesetPaths(Tileset* tileset) { } } -Tileset *Project::createNewTileset(const QString &friendlyName, bool secondary, bool checkerboardFill) { +Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboardFill) { + const QString prefix = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix); + if (!name.startsWith(prefix)) { + logError(QString("Tileset name '%1' doesn't begin with the prefix '%2'.").arg(name).arg(prefix)); + return nullptr; + } + auto tileset = new Tileset(); - tileset->name = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix) + friendlyName; + tileset->name = name; tileset->is_secondary = secondary; // Create tileset directories @@ -1527,10 +1533,11 @@ Tileset *Project::createNewTileset(const QString &friendlyName, bool secondary, this->tilesetLabelsOrdered.append(tileset->name); // TODO: Ideally we wouldn't save new Tilesets immediately - // Append to tileset specific files - tileset->appendToHeaders(this->root, friendlyName, this->usingAsmTilesets); - tileset->appendToGraphics(this->root, friendlyName, this->usingAsmTilesets); - tileset->appendToMetatiles(this->root, friendlyName, this->usingAsmTilesets); + // Append to tileset specific files. Strip prefix from name to get base name for use in other symbols. + name.remove(0, prefix.length()); + tileset->appendToHeaders(this->root, name, this->usingAsmTilesets); + tileset->appendToGraphics(this->root, name, this->usingAsmTilesets); + tileset->appendToMetatiles(this->root, name, this->usingAsmTilesets); tileset->save(); @@ -1966,12 +1973,10 @@ bool Project::isIdentifierUnique(const QString &identifier) const { return true; } -// For some arbitrary string, return true if it's both a valid identifier name -// and not one that's already in-use. -bool Project::isValidNewIdentifier(const QString &identifier) const { - static const QRegularExpression re_identifier("[A-Za-z_]+[\\w]*"); - QRegularExpressionMatch match = re_identifier.match(identifier); - return match.hasMatch() && isIdentifierUnique(identifier); +// For some arbitrary string, return true if it's both a valid identifier name and not one that's already in-use. +bool Project::isValidNewIdentifier(QString identifier) const { + IdentifierValidator validator; + return validator.isValid(identifier) && isIdentifierUnique(identifier); } // Assumes 'identifier' is a valid name. If 'identifier' is unique, returns 'identifier'. diff --git a/src/ui/colorinputwidget.cpp b/src/ui/colorinputwidget.cpp index 8b40be27..6ed587aa 100644 --- a/src/ui/colorinputwidget.cpp +++ b/src/ui/colorinputwidget.cpp @@ -1,16 +1,10 @@ #include "colorinputwidget.h" #include "ui_colorinputwidget.h" #include "colorpicker.h" +#include "validator.h" #include -class HexCodeValidator : public QValidator { - virtual QValidator::State validate(QString &input, int &) const override { - input = input.toUpper(); - return QValidator::Acceptable; - } -}; - static inline int rgb5(int rgb) { return round(static_cast(rgb * 31) / 255.0); } static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); } static inline int gbaRed(int rgb) { return rgb & 0x1f; } @@ -43,8 +37,8 @@ void ColorInputWidget::init() { connect(ui->spinBox_Green, QOverload::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners); connect(ui->spinBox_Blue, QOverload::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners); - static const HexCodeValidator hexValidator; - ui->lineEdit_Hex->setValidator(&hexValidator); + static const UppercaseValidator uppercaseValidator; + ui->lineEdit_Hex->setValidator(&uppercaseValidator); connect(ui->lineEdit_Hex, &QLineEdit::textEdited, this, &ColorInputWidget::setRgbFromHexString); // We have separate signals for when color input editing finishes. diff --git a/src/ui/maplistmodels.cpp b/src/ui/maplistmodels.cpp index dc1e3d7e..a46dfc58 100644 --- a/src/ui/maplistmodels.cpp +++ b/src/ui/maplistmodels.cpp @@ -1,11 +1,11 @@ #include "maplistmodels.h" +#include "validator.h" +#include "project.h" +#include "filterchildrenproxymodel.h" #include #include -#include "project.h" -#include "filterchildrenproxymodel.h" - void MapTree::removeSelected() { @@ -179,9 +179,8 @@ QVariant MapListModel::data(const QModelIndex &index, int role) const { QWidget *GroupNameDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const { QLineEdit *editor = new QLineEdit(parent); - static const QRegularExpression expression("[A-Za-z_]+[\\w]*"); editor->setPlaceholderText("gMapGroup_"); - editor->setValidator(new QRegularExpressionValidator(expression, parent)); + editor->setValidator(new IdentifierValidator(parent)); editor->setFrame(false); return editor; } diff --git a/src/ui/newlayoutdialog.cpp b/src/ui/newlayoutdialog.cpp index 086bc346..226df897 100644 --- a/src/ui/newlayoutdialog.cpp +++ b/src/ui/newlayoutdialog.cpp @@ -2,6 +2,7 @@ #include "maplayout.h" #include "ui_newlayoutdialog.h" #include "config.h" +#include "validator.h" #include #include @@ -34,9 +35,7 @@ NewLayoutDialog::NewLayoutDialog(Project *project, const Layout *layoutToCopy, Q ui->newLayoutForm->initUi(project); - // Identifiers can only contain word characters, and cannot start with a digit. - static const QRegularExpression re("[A-Za-z_]+[\\w]*"); - auto validator = new QRegularExpressionValidator(re, this); + auto validator = new IdentifierValidator(this); ui->lineEdit_Name->setValidator(validator); ui->lineEdit_LayoutID->setValidator(validator); diff --git a/src/ui/newmapdialog.cpp b/src/ui/newmapdialog.cpp index caf98060..2d1fa46f 100644 --- a/src/ui/newmapdialog.cpp +++ b/src/ui/newmapdialog.cpp @@ -3,6 +3,7 @@ #include "mainwindow.h" #include "ui_newmapdialog.h" #include "config.h" +#include "validator.h" #include #include @@ -45,9 +46,7 @@ NewMapDialog::NewMapDialog(Project *project, const Map *mapToCopy, QWidget *pare ui->comboBox_Group->addItems(project->groupNames); ui->comboBox_LayoutID->addItems(project->layoutIds); - // Identifiers can only contain word characters, and cannot start with a digit. - static const QRegularExpression re("[A-Za-z_]+[\\w]*"); - auto validator = new QRegularExpressionValidator(re, this); + auto validator = new IdentifierValidator(this); ui->lineEdit_Name->setValidator(validator); ui->comboBox_Group->setValidator(validator); ui->comboBox_LayoutID->setValidator(validator); diff --git a/src/ui/newnamedialog.cpp b/src/ui/newnamedialog.cpp index 8f9cdc42..842bf4b7 100644 --- a/src/ui/newnamedialog.cpp +++ b/src/ui/newnamedialog.cpp @@ -2,12 +2,14 @@ #include "ui_newnamedialog.h" #include "project.h" #include "imageexport.h" +#include "validator.h" const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }"; -NewNameDialog::NewNameDialog(const QString &label, Project* project, QWidget *parent) : +NewNameDialog::NewNameDialog(const QString &label, const QString &prefix, Project* project, QWidget *parent) : QDialog(parent), - ui(new Ui::NewNameDialog) + ui(new Ui::NewNameDialog), + namePrefix(prefix) { setAttribute(Qt::WA_DeleteOnClose); ui->setupUi(this); @@ -16,10 +18,8 @@ NewNameDialog::NewNameDialog(const QString &label, Project* project, QWidget *pa 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); + ui->lineEdit_Name->setValidator(new IdentifierValidator(namePrefix, this)); + ui->lineEdit_Name->setText(namePrefix); connect(ui->lineEdit_Name, &QLineEdit::textChanged, this, &NewNameDialog::onNameChanged); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewNameDialog::dialogButtonClicked); @@ -32,10 +32,6 @@ NewNameDialog::~NewNameDialog() delete ui; } -void NewNameDialog::setNamePrefix(const QString &) { - //TODO -} - void NewNameDialog::onNameChanged(const QString &) { validateName(true); } @@ -44,9 +40,9 @@ bool NewNameDialog::validateName(bool allowEmpty) { const QString name = ui->lineEdit_Name->text(); QString errorText; - if (name.isEmpty()) { + if (name.isEmpty() || name == namePrefix) { if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_Name->text()); - } else if (!this->project->isIdentifierUnique(name)) { + } else if (this->project && !this->project->isIdentifierUnique(name)) { errorText = QString("%1 '%2' is not unique.").arg(ui->label_Name->text()).arg(name); } diff --git a/src/ui/newtilesetdialog.cpp b/src/ui/newtilesetdialog.cpp index 1e8c569f..049b317d 100644 --- a/src/ui/newtilesetdialog.cpp +++ b/src/ui/newtilesetdialog.cpp @@ -2,6 +2,7 @@ #include "ui_newtilesetdialog.h" #include "project.h" #include "imageexport.h" +#include "validator.h" const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }"; @@ -15,15 +16,11 @@ NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) : this->project = project; ui->checkBox_CheckerboardFill->setChecked(porymapConfig.tilesetCheckerboardFill); - ui->label_SymbolNameDisplay->setText(this->symbolPrefix); - ui->comboBox_Type->setMinimumContentsLength(12); + ui->comboBox_Type->setMinimumContentsLength(12 + this->symbolPrefix.length()); + ui->lineEdit_Name->setValidator(new IdentifierValidator(this->symbolPrefix, this)); + ui->lineEdit_Name->setText(this->symbolPrefix); - //only allow characters valid for a symbol - static const QRegularExpression expression("[A-Za-z_]+[\\w]*"); - QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression, this); - ui->lineEdit_FriendlyName->setValidator(validator); - - connect(ui->lineEdit_FriendlyName, &QLineEdit::textChanged, this, &NewTilesetDialog::onFriendlyNameChanged); + connect(ui->lineEdit_Name, &QLineEdit::textChanged, this, &NewTilesetDialog::onNameChanged); connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewTilesetDialog::dialogButtonClicked); adjustSize(); @@ -35,28 +32,24 @@ NewTilesetDialog::~NewTilesetDialog() delete ui; } -void NewTilesetDialog::onFriendlyNameChanged(const QString &friendlyName) { - // When the tileset name is changed, update this label to display the full symbol name. - ui->label_SymbolNameDisplay->setText(this->symbolPrefix + friendlyName); - +void NewTilesetDialog::onNameChanged(const QString &) { validateName(true); } bool NewTilesetDialog::validateName(bool allowEmpty) { - const QString friendlyName = ui->lineEdit_FriendlyName->text(); - const QString symbolName = ui->label_SymbolNameDisplay->text(); + const QString name = ui->lineEdit_Name->text(); QString errorText; - if (friendlyName.isEmpty()) { - if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_FriendlyName->text()); - } else if (!this->project->isIdentifierUnique(symbolName)) { - errorText = QString("%1 '%2' is not unique.").arg(ui->label_SymbolName->text()).arg(symbolName); + if (name.isEmpty() || name == symbolPrefix) { + 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_FriendlyName->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : ""); + ui->lineEdit_Name->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : ""); return isValid; } @@ -74,7 +67,7 @@ void NewTilesetDialog::accept() { return; bool secondary = ui->comboBox_Type->currentIndex() == 1; - Tileset *tileset = this->project->createNewTileset(ui->lineEdit_FriendlyName->text(), secondary, ui->checkBox_CheckerboardFill->isChecked()); + Tileset *tileset = this->project->createNewTileset(ui->lineEdit_Name->text(), secondary, ui->checkBox_CheckerboardFill->isChecked()); if (!tileset) { ui->label_GenericError->setText(QString("Failed to create tileset. See %1 for details.").arg(getLogPath())); ui->label_GenericError->setVisible(true); diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index 58d808ca..d01a3351 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -114,11 +114,6 @@ void ProjectSettingsEditor::initUi() { ui->lineEdit_BorderMetatiles->setValidator(validator_HexList); this->setBorderMetatilesUi(projectConfig.useCustomBorderSize); - // Validate that the text added to the warp behavior list could be a valid define - // (we don't care whether it actually is a metatile behavior define) - static const QRegularExpression expression_Word("^[A-Za-z0-9_]*$"); - QRegularExpressionValidator *validator_Word = new QRegularExpressionValidator(expression_Word); - ui->comboBox_WarpBehaviors->setValidator(validator_Word); ui->textEdit_WarpBehaviors->setTextColor(Qt::gray); // Set spin box limits diff --git a/src/ui/tileseteditor.cpp b/src/ui/tileseteditor.cpp index 0603e8cb..fab59928 100644 --- a/src/ui/tileseteditor.cpp +++ b/src/ui/tileseteditor.cpp @@ -8,6 +8,7 @@ #include "config.h" #include "shortcut.h" #include "filedialog.h" +#include "validator.h" #include #include #include @@ -20,9 +21,25 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent) layout(layout), hasUnsavedChanges(false) { - this->setAttribute(Qt::WA_DeleteOnClose); - this->setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label); - this->initUi(); + setAttribute(Qt::WA_DeleteOnClose); + setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label); + ui->setupUi(this); + + this->tileXFlip = ui->checkBox_xFlip->isChecked(); + this->tileYFlip = ui->checkBox_yFlip->isChecked(); + this->paletteId = ui->spinBox_paletteSelector->value(); + ui->spinBox_paletteSelector->setMinimum(0); + ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1); + ui->lineEdit_metatileLabel->setValidator(new IdentifierValidator(this)); + + setAttributesUi(); + initMetatileSelector(); + initMetatileLayersItem(); + initTileSelector(); + initSelectedTileItem(); + initShortcuts(); + this->metatileSelector->select(0); + restoreWindowState(); } TilesetEditor::~TilesetEditor() @@ -92,26 +109,6 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi this->initMetatileHistory(); } -void TilesetEditor::initUi() { - ui->setupUi(this); - this->tileXFlip = ui->checkBox_xFlip->isChecked(); - this->tileYFlip = ui->checkBox_yFlip->isChecked(); - this->paletteId = ui->spinBox_paletteSelector->value(); - this->ui->spinBox_paletteSelector->setMinimum(0); - this->ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1); - - this->setAttributesUi(); - this->setMetatileLabelValidator(); - - this->initMetatileSelector(); - this->initMetatileLayersItem(); - this->initTileSelector(); - this->initSelectedTileItem(); - this->initShortcuts(); - this->metatileSelector->select(0); - this->restoreWindowState(); -} - void TilesetEditor::setAttributesUi() { // Behavior if (projectConfig.metatileBehaviorMask) { @@ -171,13 +168,6 @@ void TilesetEditor::setAttributesUi() { this->ui->frame_Properties->adjustSize(); } -void TilesetEditor::setMetatileLabelValidator() { - //only allow characters valid for a symbol - static const QRegularExpression expression("[_A-Za-z0-9]*$"); - QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression); - this->ui->lineEdit_metatileLabel->setValidator(validator); -} - void TilesetEditor::initMetatileSelector() { this->metatileSelector = new TilesetEditorMetatileSelector(this->primaryTileset, this->secondaryTileset, this->layout);