create 'add layout' button

This commit is contained in:
garak 2024-02-15 22:19:49 -05:00
parent 963b09c866
commit 22b4108a7f
7 changed files with 213 additions and 22 deletions

View file

@ -69,6 +69,17 @@ public:
QUndoStack editHistory;
// to simplify new layout settings transfer between functions
struct SimpleSettings {
QString id;
QString name;
int width;
int height;
QString tileset_primary_label;
QString tileset_secondary_label;
QString from_id = QString();
};
public:
Layout *copy();
void copyFrom(Layout *other);

View file

@ -139,6 +139,7 @@ public:
bool loadMapData(Map*);
bool readMapLayouts();
Layout *loadLayout(QString layoutId);
Layout *createNewLayout(Layout::SimpleSettings &layoutSettings);
bool loadLayout(Layout *);
bool loadMapLayout(Map*);
bool loadLayoutTilesets(Layout *);
@ -235,8 +236,8 @@ public:
private:
void updateLayout(Layout *);
void setNewMapBlockdata(Map* map);
void setNewMapBorder(Map *map);
void setNewLayoutBlockdata(Layout *layout);
void setNewLayoutBorder(Layout *layout);
void setNewMapEvents(Map *map);
void setNewMapConnections(Map *map);

View file

@ -155,6 +155,7 @@ public:
QStandardItem *createLayoutItem(QString layoutId);
QStandardItem *createMapItem(QString mapName);
QStandardItem *insertLayoutItem(QString layoutId);
QStandardItem *insertMapItem(QString mapName, QString layoutId);
QStandardItem *getItem(const QModelIndex &index) const;

View file

@ -74,6 +74,7 @@ void Editor::save() {
}
else if (this->project && this->layout) {
this->project->saveLayout(this->layout);
this->project->saveAllDataStructures();
}
}

View file

@ -1309,7 +1309,121 @@ void MainWindow::mapListAddGroup() {
}
void MainWindow::mapListAddLayout() {
// this->layoutTreeModel->insertMapItem(newMapName, newMap->layout->id);
if (!editor || !editor->project) return;
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-z0-9]*$");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(re_validChars);
newNameEdit->setValidator(validator);
QLabel *newId = new QLabel("LAYOUT_", &dialog);
connect(newNameEdit, &QLineEdit::textChanged, [&](QString text){
newId->setText(Layout::layoutConstantFromName(text.remove("_Layout")));
});
NoScrollComboBox *useExistingCombo = new NoScrollComboBox(&dialog);
useExistingCombo->addItems(this->editor->project->mapLayoutsTable);
useExistingCombo->setEnabled(false);
QCheckBox *useExistingCheck = new QCheckBox(&dialog);
QLabel *errorMessageLabel = new QLabel(&dialog);
errorMessageLabel->setVisible(false);
errorMessageLabel->setStyleSheet("QLabel { background-color: rgba(255, 0, 0, 25%) }");
QString errorMessage;
QComboBox *primaryCombo = new QComboBox(&dialog);
primaryCombo->addItems(this->editor->project->primaryTilesetLabels);
QComboBox *secondaryCombo = new QComboBox(&dialog);
secondaryCombo->addItems(this->editor->project->secondaryTilesetLabels);
QSpinBox *widthSpin = new QSpinBox(&dialog);
QSpinBox *heightSpin = new QSpinBox(&dialog);
widthSpin->setMinimum(1);
heightSpin->setMinimum(1);
widthSpin->setMaximum(this->editor->project->getMaxMapWidth());
heightSpin->setMaximum(this->editor->project->getMaxMapHeight());
connect(useExistingCheck, &QCheckBox::stateChanged, [&](int state){
bool useExisting = (state == Qt::Checked);
useExistingCombo->setEnabled(useExisting);
primaryCombo->setEnabled(!useExisting);
secondaryCombo->setEnabled(!useExisting);
widthSpin->setEnabled(!useExisting);
heightSpin->setEnabled(!useExisting);
});
QFormLayout form(&dialog);
form.addRow("New Layout Name", newNameEdit);
form.addRow("New Layout ID", newId);
form.addRow("Copy Existing Layout", useExistingCheck);
form.addRow("", useExistingCombo);
form.addRow("Primary Tileset", primaryCombo);
form.addRow("Secondary Tileset", secondaryCombo);
form.addRow("Layout Width", widthSpin);
form.addRow("Layout Height", heightSpin);
form.addRow("", errorMessageLabel);
connect(&newItemButtonBox, &QDialogButtonBox::accepted, [&](){
// verify some things
bool issue = false;
QString tryLayoutName = newNameEdit->text();
// name not empty
if (tryLayoutName.isEmpty()) {
errorMessage = "Name cannot be empty";
issue = true;
}
// unique layout name & id
else if (this->editor->project->mapLayoutsTable.contains(newId->text())
|| this->editor->project->layoutIdsToNames.find(tryLayoutName) != this->editor->project->layoutIdsToNames.end()) {
errorMessage = "Layout Name / ID is not unique";
issue = true;
}
// from id is existing value
else if (useExistingCheck->isChecked()) {
if (!this->editor->project->mapLayoutsTable.contains(useExistingCombo->currentText())) {
errorMessage = "Existing layout ID is not valid";
issue = true;
}
}
if (issue) {
// show error
errorMessageLabel->setText(errorMessage);
errorMessageLabel->setVisible(true);
}
else {
dialog.accept();
}
});
form.addRow(&newItemButtonBox);
if (dialog.exec() == QDialog::Accepted) {
Layout::SimpleSettings layoutSettings;
QString layoutName = newNameEdit->text();
layoutSettings.name = layoutName;
layoutSettings.id = Layout::layoutConstantFromName(layoutName.remove("_Layout"));
if (useExistingCheck->isChecked()) {
layoutSettings.from_id = useExistingCombo->currentText();
} else {
layoutSettings.width = widthSpin->value();
layoutSettings.height = heightSpin->value();
layoutSettings.tileset_primary_label = primaryCombo->currentText();
layoutSettings.tileset_secondary_label = secondaryCombo->currentText();
}
Layout *newLayout = this->editor->project->createNewLayout(layoutSettings);
QStandardItem *item = this->layoutTreeModel->insertLayoutItem(newLayout->id);
setLayout(newLayout->id);
}
}
void MainWindow::mapListAddArea() {
@ -1322,10 +1436,10 @@ void MainWindow::mapListAddItem() {
this->mapListAddGroup();
break;
case 1:
this->mapListAddLayout();
this->mapListAddArea();
break;
case 2:
this->mapListAddArea();
this->mapListAddLayout();
break;
}
}

View file

@ -385,6 +385,60 @@ QString Project::readMapLocation(QString map_name) {
return ParseUtil::jsonToQString(mapObj["region_map_section"]);
}
Layout *Project::createNewLayout(Layout::SimpleSettings &layoutSettings) {
QString basePath = projectConfig.getFilePath(ProjectFilePath::data_layouts_folders);
Layout *layout;
// Handle the case where we are copying from an existing layout first.
if (!layoutSettings.from_id.isEmpty()) {
// load from layout
loadLayout(mapLayouts[layoutSettings.from_id]);
layout = mapLayouts[layoutSettings.from_id]->copy();
layout->name = layoutSettings.name;
layout->id = layoutSettings.id;
layout->border_path = QString("%1%2/border.bin").arg(basePath, layoutSettings.name);
layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layoutSettings.name);
}
else {
layout = new Layout;
layout->name = layoutSettings.name;
layout->id = layoutSettings.id;
layout->width = layoutSettings.width;
layout->height = layoutSettings.height;
layout->border_width = DEFAULT_BORDER_WIDTH;
layout->border_height = DEFAULT_BORDER_HEIGHT;
layout->tileset_primary_label = layoutSettings.tileset_primary_label;
layout->tileset_secondary_label = layoutSettings.tileset_secondary_label;
layout->border_path = QString("%1%2/border.bin").arg(basePath, layoutSettings.name);
layout->blockdata_path = QString("%1%2/map.bin").arg(basePath, layoutSettings.name);
setNewLayoutBlockdata(layout);
setNewLayoutBorder(layout);
}
// 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));
delete layout;
return nullptr;
}
mapLayouts.insert(layout->id, layout);
mapLayoutsMaster.insert(layout->id, layout->copy());
mapLayoutsTable.append(layout->id);
mapLayoutsTableMaster.append(layout->id);
layoutIdsToNames.insert(layout->id, layout->name);
saveLayout(layout);
this->loadLayout(layout);
return layout;
}
bool Project::loadLayout(Layout *layout) {
// !TODO: make sure this doesn't break anything, maybe do something better. new layouts work too?
if (!layout->loaded) {
@ -1119,16 +1173,16 @@ bool Project::loadBlockdata(Layout *layout) {
return true;
}
void Project::setNewMapBlockdata(Map *map) {
map->layout->blockdata.clear();
int width = map->getWidth();
int height = map->getHeight();
void Project::setNewLayoutBlockdata(Layout *layout) {
layout->blockdata.clear();
int width = layout->getWidth();
int height = layout->getHeight();
Block block(projectConfig.getDefaultMetatileId(), projectConfig.getDefaultCollision(), projectConfig.getDefaultElevation());
for (int i = 0; i < width * height; i++) {
map->layout->blockdata.append(block);
layout->blockdata.append(block);
}
map->layout->lastCommitBlocks.blocks = map->layout->blockdata;
map->layout->lastCommitBlocks.layoutDimensions = QSize(width, height);
layout->lastCommitBlocks.blocks = layout->blockdata;
layout->lastCommitBlocks.layoutDimensions = QSize(width, height);
}
bool Project::loadLayoutBorder(Layout *layout) {
@ -1147,27 +1201,27 @@ bool Project::loadLayoutBorder(Layout *layout) {
return true;
}
void Project::setNewMapBorder(Map *map) {
map->layout->border.clear();
int width = map->getBorderWidth();
int height = map->getBorderHeight();
void Project::setNewLayoutBorder(Layout *layout) {
layout->border.clear();
int width = layout->getBorderWidth();
int height = layout->getBorderHeight();
const QList<uint16_t> configMetatileIds = projectConfig.getNewMapBorderMetatileIds();
if (configMetatileIds.length() != width * height) {
// Border size doesn't match the number of default border metatiles.
// Fill the border with empty metatiles.
for (int i = 0; i < width * height; i++) {
map->layout->border.append(0);
layout->border.append(0);
}
} else {
// Fill the border with the default metatiles from the config.
for (int i = 0; i < width * height; i++) {
map->layout->border.append(configMetatileIds.at(i));
layout->border.append(configMetatileIds.at(i));
}
}
map->layout->lastCommitBlocks.border = map->layout->border;
map->layout->lastCommitBlocks.borderDimensions = QSize(width, height);
layout->lastCommitBlocks.border = layout->border;
layout->lastCommitBlocks.borderDimensions = QSize(width, height);
}
void Project::saveLayoutBorder(Layout *layout) {
@ -1809,10 +1863,10 @@ Map* Project::addNewMapToGroup(QString mapName, int groupNum, Map *newMap, bool
mapLayoutsTable.append(newMap->layoutId);
layoutIdsToNames.insert(newMap->layout->id, newMap->layout->name);
if (!importedMap) {
setNewMapBlockdata(newMap);
setNewLayoutBlockdata(newMap->layout);
}
if (newMap->layout->border.isEmpty()) {
setNewMapBorder(newMap);
setNewLayoutBorder(newMap->layout);
}
}

View file

@ -535,6 +535,11 @@ QStandardItem *LayoutTreeModel::createMapItem(QString mapName) {
return map;
}
QStandardItem *LayoutTreeModel::insertLayoutItem(QString layoutId) {
QStandardItem *layoutItem = this->createLayoutItem(layoutId);
this->root->appendRow(layoutItem);
}
QStandardItem *LayoutTreeModel::insertMapItem(QString mapName, QString layoutId) {
QStandardItem *layout = nullptr;
if (this->layoutItems.contains(layoutId)) {
@ -591,6 +596,7 @@ QVariant LayoutTreeModel::data(const QModelIndex &index, int role) const {
int col = index.column();
if (role == Qt::DecorationRole) {
static QIcon mapGrayIcon = QIcon(QStringLiteral(":/icons/map_grayed.ico"));
static QIcon mapIcon = QIcon(QStringLiteral(":/icons/map.ico"));
static QIcon mapEditedIcon = QIcon(QStringLiteral(":/icons/map_edited.ico"));
static QIcon mapOpenedIcon = QIcon(QStringLiteral(":/icons/map_opened.ico"));
@ -607,6 +613,9 @@ QVariant LayoutTreeModel::data(const QModelIndex &index, int role) const {
if (this->project->mapLayouts.value(layoutId)->hasUnsavedChanges()) {
return mapEditedIcon;
}
else if (!this->project->mapLayouts[layoutId]->loaded) {
return mapGrayIcon;
}
}
return mapIcon;
}