Redesign new tileset dialog

This commit is contained in:
GriffinR 2024-12-03 22:27:29 -05:00
parent b7c34a67e5
commit 9c40b04ad5
13 changed files with 530 additions and 582 deletions

View File

@ -2928,6 +2928,7 @@
<addaction name="action_NewMap"/>
<addaction name="action_NewLayout"/>
<addaction name="actionNew_Tileset"/>
<addaction name="separator"/>
<addaction name="actionTileset_Editor"/>
<addaction name="actionRegion_Map_Editor"/>
<addaction name="separator"/>

View File

@ -6,238 +6,152 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>216</height>
<width>450</width>
<height>209</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Add new Tileset</string>
</property>
<widget class="QWidget" name="gridWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>216</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>380</width>
<height>161</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QWidget" name="formLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>155</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>6</number>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="form">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_FriendlyName">
<property name="text">
<string>Name</string>
</property>
<property name="leftMargin">
<number>10</number>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_FriendlyName">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
<property name="topMargin">
<number>10</number>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_NameError">
<property name="visible">
<bool>false</bool>
</property>
<property name="rightMargin">
<number>10</number>
<property name="styleSheet">
<string notr="true">color: rgb(255, 0, 0)</string>
</property>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name</string>
</property>
</widget>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_SymbolName">
<property name="text">
<string>Symbol Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_SymbolNameDisplay">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_Type">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="NoScrollComboBox" name="comboBox_Type">
<property name="editable">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>Primary</string>
</property>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameLineEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
<item>
<property name="text">
<string>Secondary</string>
</property>
</item>
<item row="1" column="0">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="typeComboBox">
<item>
<property name="text">
<string>Primary</string>
</property>
</item>
<item>
<property name="text">
<string>Secondary</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="pathLabel">
<property name="text">
<string>Path</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="pathLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="symbolNameLabel">
<property name="text">
<string>Symbol Name</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="symbolNameLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="fillLabel">
<property name="text">
<string>Checkerboard Fill</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="fillCheckBox">
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="5" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_CheckerboardFill">
<property name="text">
<string>Checkerboard Fill</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="checkBox_CheckerboardFill"/>
</item>
<item row="5" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_GenericError">
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 0, 0)</string>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>NoScrollComboBox</class>
<extends>QComboBox</extends>
<header>noscrollcombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NewTilesetDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NewTilesetDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

View File

@ -38,8 +38,6 @@ public:
QList<QList<QRgb>> palettes;
QList<QList<QRgb>> palettePreviews;
bool hasUnsavedTilesImage;
static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
static Tileset* getTileTileset(int, Tileset*, Tileset*);
static Metatile* getMetatile(int, Tileset*, Tileset*);
@ -56,10 +54,25 @@ public:
static QHash<int, QString> getHeaderMemberMap(bool usingAsm);
static QString getExpectedDir(QString tilesetName, bool isSecondary);
QString getExpectedDir();
void load();
void loadMetatiles();
void loadMetatileAttributes();
void loadTilesImage(QImage *importedImage = nullptr);
void loadPalettes();
void save();
void saveMetatileAttributes();
void saveMetatiles();
void saveTilesImage();
void savePalettes();
bool appendToHeaders(QString root, QString friendlyName, bool usingAsm);
bool appendToGraphics(QString root, QString friendlyName, bool usingAsm);
bool appendToMetatiles(QString root, QString friendlyName, bool usingAsm);
void setTilesImage(const QImage &image);
void setMetatiles(const QList<Metatile*> &metatiles);
void addMetatile(Metatile* metatile);
@ -72,6 +85,7 @@ public:
private:
QList<Metatile*> m_metatiles;
bool m_hasUnsavedTilesImage = false;
};
#endif // TILESET_H

View File

@ -22,7 +22,6 @@
#include "mapimageexporter.h"
#include "filterchildrenproxymodel.h"
#include "maplistmodels.h"
#include "newtilesetdialog.h"
#include "shortcutseditor.h"
#include "preferenceeditor.h"
#include "projectsettingseditor.h"
@ -191,6 +190,7 @@ private slots:
void onNewMapGroupCreated(const QString &groupName);
void onNewMapSectionCreated(const QString &idName);
void onNewLayoutCreated(Layout *layout);
void onNewTilesetCreated(Tileset *tileset);
void onMapLoaded(Map *map);
void onMapRulerStatusChanged(const QString &);
void applyUserShortcuts();
@ -412,7 +412,6 @@ private:
QObjectList shortcutableObjects() const;
void addCustomHeaderValue(QString key, QJsonValue value, bool isNew = false);
int insertTilesetLabel(QStringList * list, QString label);
void checkForUpdates(bool requestedByUser);
void setDivingMapsVisible(bool visible);

View File

@ -139,6 +139,7 @@ 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);
bool isIdentifierUnique(const QString &identifier) const;
QString getProjectTitle();
@ -167,10 +168,7 @@ public:
bool loadMapLayout(Map*);
bool loadLayoutTilesets(Layout *);
void loadTilesetAssets(Tileset*);
void loadTilesetTiles(Tileset*, QImage);
void loadTilesetMetatiles(Tileset*);
void loadTilesetMetatileLabels(Tileset*);
void loadTilesetPalettes(Tileset*);
void readTilesetPaths(Tileset* tileset);
void saveLayout(Layout *);
@ -188,10 +186,6 @@ public:
void saveHealLocations(Map*);
void saveTilesets(Tileset*, Tileset*);
void saveTilesetMetatileLabels(Tileset*, Tileset*);
void saveTilesetMetatileAttributes(Tileset*);
void saveTilesetMetatiles(Tileset*);
void saveTilesetTilesImage(Tileset*);
void saveTilesetPalettes(Tileset*);
void appendTilesetLabel(const QString &label, const QString &isSecondaryStr);
bool readTilesetLabels();
bool readTilesetMetatileLabels();
@ -282,6 +276,7 @@ signals:
void mapLoaded(Map *map);
void mapCreated(Map *newMap, const QString &groupName);
void layoutCreated(Layout *newLayout);
void tilesetCreated(Tileset *newTileset);
void mapGroupAdded(const QString &groupName);
void mapSectionAdded(const QString &idName);
void mapSectionIdNamesChanged(const QStringList &idNames);

View File

@ -39,7 +39,6 @@ private:
void refresh();
void saveSettings();
bool isExistingLayout() const;
private slots:
void dialogButtonClicked(QAbstractButton *button);

View File

@ -2,7 +2,10 @@
#define NEWTILESETDIALOG_H
#include <QDialog>
#include "project.h"
#include <QAbstractButton>
class Project;
class Tileset;
namespace Ui {
class NewTilesetDialog;
@ -15,20 +18,17 @@ class NewTilesetDialog : public QDialog
public:
explicit NewTilesetDialog(Project *project, QWidget *parent = nullptr);
~NewTilesetDialog();
QString path;
QString fullSymbolName;
QString friendlyName;
bool isSecondary;
bool checkerboardFill;
private slots:
void NameOrSecondaryChanged();
void SecondaryChanged();
void FillChanged();
virtual void accept() override;
private:
Ui::NewTilesetDialog *ui;
Project *project = nullptr;
const QString symbolPrefix;
bool validateName(bool allowEmpty = false);
void onFriendlyNameChanged(const QString &friendlyName);
void dialogButtonClicked(QAbstractButton *button);
};
#endif // NEWTILESETDIALOG_H

View File

@ -3,6 +3,7 @@
#include "project.h"
#include "log.h"
#include "config.h"
#include "imageproviders.h"
#include <QPainter>
#include <QImage>
@ -23,7 +24,7 @@ Tileset::Tileset(const Tileset &other)
metatileLabels(other.metatileLabels),
palettes(other.palettes),
palettePreviews(other.palettePreviews),
hasUnsavedTilesImage(false)
m_hasUnsavedTilesImage(other.m_hasUnsavedTilesImage)
{
for (auto tile : other.tiles) {
tiles.append(tile.copy());
@ -397,3 +398,184 @@ QHash<int, QString> Tileset::getHeaderMemberMap(bool usingAsm)
map.insert(metatileAttrPosition, "metatileAttributes");
return map;
}
void Tileset::loadMetatiles() {
clearMetatiles();
QFile metatiles_file(this->metatiles_path);
if (!metatiles_file.open(QIODevice::ReadOnly)) {
logError(QString("Could not open '%1' for reading.").arg(this->metatiles_path));
return;
}
QByteArray data = metatiles_file.readAll();
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
int bytesPerMetatile = 2 * tilesPerMetatile;
int num_metatiles = data.length() / bytesPerMetatile;
for (int i = 0; i < num_metatiles; i++) {
auto metatile = new Metatile;
int index = i * bytesPerMetatile;
for (int j = 0; j < tilesPerMetatile; j++) {
uint16_t tileRaw = static_cast<unsigned char>(data[index++]);
tileRaw |= static_cast<unsigned char>(data[index++]) << 8;
metatile->tiles.append(Tile(tileRaw));
}
m_metatiles.append(metatile);
}
}
void Tileset::saveMetatiles() {
QFile metatiles_file(this->metatiles_path);
if (!metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
logError(QString("Could not open '%1' for writing.").arg(this->metatiles_path));
return;
}
QByteArray data;
int numTiles = projectConfig.getNumTilesInMetatile();
for (const auto &metatile : m_metatiles) {
for (int i = 0; i < numTiles; i++) {
uint16_t tile = metatile->tiles.at(i).rawValue();
data.append(static_cast<char>(tile));
data.append(static_cast<char>(tile >> 8));
}
}
metatiles_file.write(data);
}
void Tileset::loadMetatileAttributes() {
QFile attrs_file(this->metatile_attrs_path);
if (!attrs_file.open(QIODevice::ReadOnly)) {
logError(QString("Could not open '%1' for reading.").arg(this->metatile_attrs_path));
return;
}
QByteArray data = attrs_file.readAll();
int attrSize = projectConfig.metatileAttributesSize;
int numMetatiles = m_metatiles.length();
int numMetatileAttrs = data.length() / attrSize;
if (numMetatiles != numMetatileAttrs) {
logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(numMetatiles).arg(numMetatileAttrs).arg(this->name));
}
for (int i = 0; i < qMin(numMetatiles, numMetatileAttrs); i++) {
uint32_t attributes = 0;
for (int j = 0; j < attrSize; j++)
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
m_metatiles.at(i)->setAttributes(attributes);
}
}
void Tileset::saveMetatileAttributes() {
QFile attrs_file(this->metatile_attrs_path);
if (!attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
logError(QString("Could not open '%1' for writing.").arg(this->metatile_attrs_path));
return;
}
QByteArray data;
for (const auto &metatile : m_metatiles) {
uint32_t attributes = metatile->getAttributes();
for (int i = 0; i < projectConfig.metatileAttributesSize; i++)
data.append(static_cast<char>(attributes >> (8 * i)));
}
attrs_file.write(data);
}
void Tileset::loadTilesImage(QImage *importedImage) {
QImage image;
if (importedImage) {
image = *importedImage;
m_hasUnsavedTilesImage = true;
} else if (QFile::exists(this->tilesImagePath)) {
// No image provided, load from file path.
image = QImage(this->tilesImagePath).convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither);
} else {
// Use default image
image = QImage(8, 8, QImage::Format_Indexed8);
}
// Validate image contains 16 colors.
int colorCount = image.colorCount();
if (colorCount > 16) {
flattenTo4bppImage(&image);
} else if (colorCount < 16) {
QVector<QRgb> colorTable = image.colorTable();
for (int i = colorTable.length(); i < 16; i++) {
colorTable.append(Qt::black);
}
image.setColorTable(colorTable);
}
QList<QImage> tiles;
int w = 8;
int h = 8;
for (int y = 0; y < image.height(); y += h)
for (int x = 0; x < image.width(); x += w) {
QImage tile = image.copy(x, y, w, h);
tiles.append(tile);
}
this->tilesImage = image;
this->tiles = tiles;
}
void Tileset::saveTilesImage() {
// Only write the tiles image if it was changed.
// Porymap will only ever change an existing tiles image by importing a new one.
if (!m_hasUnsavedTilesImage)
return;
if (!this->tilesImage.save(this->tilesImagePath, "PNG")) {
logError(QString("Failed to save tiles image '%1'").arg(this->tilesImagePath));
return;
}
m_hasUnsavedTilesImage = false;
}
void Tileset::loadPalettes() {
this->palettes.clear();
this->palettePreviews.clear();
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
QList<QRgb> palette;
QString path = this->palettePaths.value(i);
if (!path.isEmpty()) {
bool error = false;
palette = PaletteUtil::parse(path, &error);
if (error) palette.clear();
}
if (palette.isEmpty()) {
// Either the palette failed to load, or no palette exists.
// We expect tilesets to have a certain number of palettes,
// so fill this palette with dummy colors.
for (int j = 0; j < 16; j++) {
palette.append(qRgb(j * 16, j * 16, j * 16));
}
}
this->palettes.append(palette);
this->palettePreviews.append(palette);
}
}
void Tileset::savePalettes() {
int numPalettes = qMin(this->palettePaths.length(), this->palettes.length());
for (int i = 0; i < numPalettes; i++) {
PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, 16);
}
}
void Tileset::load() {
loadMetatiles();
loadMetatileAttributes();
loadTilesImage();
loadPalettes();
}
// Because metatile labels are global (and handled by the project) we don't save them here.
void Tileset::save() {
saveMetatiles();
saveMetatileAttributes();
saveTilesImage();
savePalettes();
}

View File

@ -25,6 +25,7 @@
#include "filedialog.h"
#include "newmapdialog.h"
#include "newlayoutdialog.h"
#include "newtilesetdialog.h"
#include <QClipboard>
#include <QDirIterator>
@ -407,8 +408,8 @@ void MainWindow::initMapList() {
layout->setContentsMargins(0, 0, 0, 0);
// Create add map/layout button
// TODO: Tool tip
QPushButton *buttonAdd = new QPushButton(QIcon(":/icons/add.ico"), "");
buttonAdd->setToolTip("Create New Map");
connect(buttonAdd, &QPushButton::clicked, this, &MainWindow::openNewMapDialog);
layout->addWidget(buttonAdd);
@ -623,6 +624,7 @@ bool MainWindow::openProject(QString dir, bool initial) {
connect(project, &Project::mapLoaded, this, &MainWindow::onMapLoaded);
connect(project, &Project::mapCreated, this, &MainWindow::onNewMapCreated);
connect(project, &Project::layoutCreated, this, &MainWindow::onNewLayoutCreated);
connect(project, &Project::tilesetCreated, this, &MainWindow::onNewTilesetCreated);
connect(project, &Project::mapGroupAdded, this, &MainWindow::onNewMapGroupCreated);
connect(project, &Project::mapSectionAdded, this, &MainWindow::onNewMapSectionCreated);
connect(project, &Project::mapSectionIdNamesChanged, this->mapHeaderForm, &MapHeaderForm::setLocations);
@ -925,7 +927,6 @@ void MainWindow::setLayoutOnlyMode(bool layoutOnly) {
// setLayout, but with a visible error message in case of failure.
// Use when the user is specifically requesting a layout to open.
// TODO: Update the various functions taking layout IDs to take layout names (to mirror the equivalent map functions, this discrepancy is confusing atm)
bool MainWindow::userSetLayout(QString layoutId) {
if (!setLayout(layoutId)) {
QMessageBox msgBox(this);
@ -1439,6 +1440,21 @@ void MainWindow::onNewMapSectionCreated(const QString &idName) {
// TODO: Refresh Region Map Editor's map section dropdown, if it's open
}
void MainWindow::onNewTilesetCreated(Tileset *tileset) {
QString message = QString("Created a new tileset named %1.").arg(tileset->name);
logInfo(message);
statusBar()->showMessage(message);
// Refresh tileset combo boxes
if (!tileset->is_secondary) {
int index = this->editor->project->primaryTilesetLabels.indexOf(tileset->name);
ui->comboBox_PrimaryTileset->insertItem(index, tileset->name);
} else {
int index = this->editor->project->secondaryTilesetLabels.indexOf(tileset->name);
ui->comboBox_SecondaryTileset->insertItem(index, tileset->name);
}
}
void MainWindow::openNewMapDialog() {
auto dialog = new NewMapDialog(this->editor->project, this);
dialog->open();
@ -1450,119 +1466,9 @@ void MainWindow::openNewLayoutDialog() {
dialog->open();
}
// Insert label for newly-created tileset into sorted list of existing labels
int MainWindow::insertTilesetLabel(QStringList * list, QString label) {
int i = 0;
for (; i < list->length(); i++)
if (list->at(i) > label) break;
list->insert(i, label);
return i;
}
void MainWindow::on_actionNew_Tileset_triggered() {
NewTilesetDialog *createTilesetDialog = new NewTilesetDialog(editor->project, this);
if(createTilesetDialog->exec() == QDialog::Accepted){
if(createTilesetDialog->friendlyName.isEmpty()) {
logError(QString("Tried to create a directory with an empty name."));
QMessageBox msgBox(this);
msgBox.setText("Failed to add new tileset.");
QString message = QString("The given name was empty.");
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
QString fullDirectoryPath = editor->project->root + "/" + createTilesetDialog->path;
QDir directory;
if(directory.exists(fullDirectoryPath)) {
logError(QString("Could not create tileset \"%1\", the folder \"%2\" already exists.").arg(createTilesetDialog->friendlyName, fullDirectoryPath));
QMessageBox msgBox(this);
msgBox.setText("Failed to add new tileset.");
QString message = QString("The folder for tileset \"%1\" already exists. View porymap.log for specific errors.").arg(createTilesetDialog->friendlyName);
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
if (editor->project->tilesetLabelsOrdered.contains(createTilesetDialog->fullSymbolName)) {
logError(QString("Could not create tileset \"%1\", the symbol \"%2\" already exists.").arg(createTilesetDialog->friendlyName, createTilesetDialog->fullSymbolName));
QMessageBox msgBox(this);
msgBox.setText("Failed to add new tileset.");
QString message = QString("The symbol for tileset \"%1\" (\"%2\") already exists.").arg(createTilesetDialog->friendlyName, createTilesetDialog->fullSymbolName);
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.exec();
return;
}
directory.mkdir(fullDirectoryPath);
directory.mkdir(fullDirectoryPath + "/palettes");
Tileset newSet;
newSet.name = createTilesetDialog->fullSymbolName;
newSet.tilesImagePath = fullDirectoryPath + "/tiles.png";
newSet.metatiles_path = fullDirectoryPath + "/metatiles.bin";
newSet.metatile_attrs_path = fullDirectoryPath + "/metatile_attributes.bin";
newSet.is_secondary = createTilesetDialog->isSecondary;
int numMetatiles = createTilesetDialog->isSecondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary();
QImage tilesImage(":/images/blank_tileset.png");
editor->project->loadTilesetTiles(&newSet, tilesImage);
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
for(int i = 0; i < numMetatiles; ++i) {
Metatile *mt = new Metatile();
for(int j = 0; j < tilesPerMetatile; ++j){
Tile tile = Tile();
if (createTilesetDialog->checkerboardFill) {
// Create a checkerboard-style dummy tileset
if (((i / 8) % 2) == 0)
tile.tileId = ((i % 2) == 0) ? 1 : 2;
else
tile.tileId = ((i % 2) == 1) ? 1 : 2;
}
mt->tiles.append(tile);
}
newSet.addMetatile(mt);
}
for(int i = 0; i < 16; ++i) {
QList<QRgb> currentPal;
for(int i = 0; i < 16;++i) {
currentPal.append(qRgb(0,0,0));
}
newSet.palettes.append(currentPal);
newSet.palettePreviews.append(currentPal);
QString fileName = QString("%1.pal").arg(i, 2, 10, QLatin1Char('0'));
newSet.palettePaths.append(fullDirectoryPath+"/palettes/" + fileName);
}
newSet.palettes[0][1] = qRgb(255,0,255);
newSet.palettePreviews[0][1] = qRgb(255,0,255);
exportIndexed4BPPPng(newSet.tilesImage, newSet.tilesImagePath);
editor->project->saveTilesetMetatiles(&newSet);
editor->project->saveTilesetMetatileAttributes(&newSet);
editor->project->saveTilesetPalettes(&newSet);
//append to tileset specific files
newSet.appendToHeaders(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
newSet.appendToGraphics(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
newSet.appendToMetatiles(editor->project->root, createTilesetDialog->friendlyName, editor->project->usingAsmTilesets);
if (!createTilesetDialog->isSecondary) {
int index = insertTilesetLabel(&editor->project->primaryTilesetLabels, createTilesetDialog->fullSymbolName);
this->ui->comboBox_PrimaryTileset->insertItem(index, createTilesetDialog->fullSymbolName);
} else {
int index = insertTilesetLabel(&editor->project->secondaryTilesetLabels, createTilesetDialog->fullSymbolName);
this->ui->comboBox_SecondaryTileset->insertItem(index, createTilesetDialog->fullSymbolName);
}
editor->project->tilesetLabelsOrdered.append(createTilesetDialog->fullSymbolName);
QMessageBox msgBox(this);
msgBox.setText("Successfully created tileset.");
QString message = QString("Tileset \"%1\" was created successfully.").arg(createTilesetDialog->friendlyName);
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Information);
msgBox.exec();
}
auto dialog = new NewTilesetDialog(editor->project, this);
dialog->open();
}
void MainWindow::updateTilesetEditor() {

View File

@ -122,19 +122,13 @@ QString Project::getProjectTitle() {
}
void Project::clearMapCache() {
for (auto *map : mapCache.values()) {
if (map)
delete map;
}
mapCache.clear();
qDeleteAll(this->mapCache);
this->mapCache.clear();
}
void Project::clearTilesetCache() {
for (auto *tileset : tilesetCache.values()) {
if (tileset)
delete tileset;
}
tilesetCache.clear();
qDeleteAll(this->tilesetCache);
this->tilesetCache.clear();
}
Map* Project::loadMap(QString mapName) {
@ -1005,25 +999,21 @@ void Project::saveHealLocationsConstants() {
void Project::saveTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
saveTilesetMetatileLabels(primaryTileset, secondaryTileset);
saveTilesetMetatileAttributes(primaryTileset);
saveTilesetMetatileAttributes(secondaryTileset);
saveTilesetMetatiles(primaryTileset);
saveTilesetMetatiles(secondaryTileset);
saveTilesetTilesImage(primaryTileset);
saveTilesetTilesImage(secondaryTileset);
saveTilesetPalettes(primaryTileset);
saveTilesetPalettes(secondaryTileset);
if (primaryTileset)
primaryTileset->save();
if (secondaryTileset)
secondaryTileset->save();
}
void Project::updateTilesetMetatileLabels(Tileset *tileset) {
// Erase old labels, then repopulate with new labels
const QString prefix = tileset->getMetatileLabelPrefix();
metatileLabelsMap[tileset->name].clear();
this->metatileLabelsMap[tileset->name].clear();
for (int metatileId : tileset->metatileLabels.keys()) {
if (tileset->metatileLabels[metatileId].isEmpty())
continue;
QString label = prefix + tileset->metatileLabels[metatileId];
metatileLabelsMap[tileset->name][label] = metatileId;
this->metatileLabelsMap[tileset->name][label] = metatileId;
}
}
@ -1082,59 +1072,6 @@ void Project::saveTilesetMetatileLabels(Tileset *primaryTileset, Tileset *second
saveTextFile(root + "/" + filename, outputText);
}
void Project::saveTilesetMetatileAttributes(Tileset *tileset) {
QFile attrs_file(tileset->metatile_attrs_path);
if (attrs_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QByteArray data;
for (const auto &metatile : tileset->metatiles()) {
uint32_t attributes = metatile->getAttributes();
for (int i = 0; i < projectConfig.metatileAttributesSize; i++)
data.append(static_cast<char>(attributes >> (8 * i)));
}
attrs_file.write(data);
} else {
logError(QString("Could not save tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path));
}
}
void Project::saveTilesetMetatiles(Tileset *tileset) {
QFile metatiles_file(tileset->metatiles_path);
if (metatiles_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QByteArray data;
int numTiles = projectConfig.getNumTilesInMetatile();
for (const auto &metatile : tileset->metatiles()) {
for (int i = 0; i < numTiles; i++) {
uint16_t tile = metatile->tiles.at(i).rawValue();
data.append(static_cast<char>(tile));
data.append(static_cast<char>(tile >> 8));
}
}
metatiles_file.write(data);
} else {
tileset->clearMetatiles();
logError(QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path));
}
}
void Project::saveTilesetTilesImage(Tileset *tileset) {
// Only write the tiles image if it was changed.
// Porymap will only ever change an existing tiles image by importing a new one.
if (tileset->hasUnsavedTilesImage) {
if (!tileset->tilesImage.save(tileset->tilesImagePath, "PNG")) {
logError(QString("Failed to save tiles image '%1'").arg(tileset->tilesImagePath));
return;
}
tileset->hasUnsavedTilesImage = false;
}
}
void Project::saveTilesetPalettes(Tileset *tileset) {
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
QString filepath = tileset->palettePaths.at(i);
PaletteUtil::writeJASC(filepath, tileset->palettes.at(i).toVector(), 0, 16);
}
}
bool Project::loadLayoutTilesets(Layout *layout) {
layout->tileset_primary = getTileset(layout->tileset_primary_label);
if (!layout->tileset_primary) {
@ -1465,18 +1402,9 @@ void Project::loadTilesetAssets(Tileset* tileset) {
if (tileset->name.isNull()) {
return;
}
this->readTilesetPaths(tileset);
QImage image;
if (QFile::exists(tileset->tilesImagePath)) {
image = QImage(tileset->tilesImagePath).convertToFormat(QImage::Format_Indexed8, Qt::ThresholdDither);
flattenTo4bppImage(&image);
} else {
image = QImage(8, 8, QImage::Format_Indexed8);
}
this->loadTilesetTiles(tileset, image);
this->loadTilesetMetatiles(tileset);
this->loadTilesetMetatileLabels(tileset);
this->loadTilesetPalettes(tileset);
readTilesetPaths(tileset);
loadTilesetMetatileLabels(tileset);
tileset->load();
}
void Project::readTilesetPaths(Tileset* tileset) {
@ -1536,84 +1464,89 @@ void Project::readTilesetPaths(Tileset* tileset) {
}
}
void Project::loadTilesetPalettes(Tileset* tileset) {
QList<QList<QRgb>> palettes;
QList<QList<QRgb>> palettePreviews;
for (int i = 0; i < tileset->palettePaths.length(); i++) {
QString path = tileset->palettePaths.value(i);
bool error = false;
QList<QRgb> palette = PaletteUtil::parse(path, &error);
if (error) {
for (int j = 0; j < 16; j++) {
palette.append(qRgb(j * 16, j * 16, j * 16));
Tileset *Project::createNewTileset(const QString &friendlyName, bool secondary, bool checkerboardFill) {
auto tileset = new Tileset();
tileset->name = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix) + friendlyName;
tileset->is_secondary = secondary;
// Create tileset directories
const QString fullDirectoryPath = QString("%1/%2").arg(this->root).arg(tileset->getExpectedDir());
QDir directory;
if (!directory.mkpath(fullDirectoryPath)) {
logError(QString("Failed to create directory '%1' for new tileset '%2'").arg(fullDirectoryPath).arg(tileset->name));
delete tileset;
return nullptr;
}
const QString palettesPath = fullDirectoryPath + "/palettes";
if (!directory.mkpath(palettesPath)) {
logError(QString("Failed to create palettes directory '%1' for new tileset '%2'").arg(palettesPath).arg(tileset->name));
delete tileset;
return nullptr;
}
tileset->tilesImagePath = fullDirectoryPath + "/tiles.png";
tileset->metatiles_path = fullDirectoryPath + "/metatiles.bin";
tileset->metatile_attrs_path = fullDirectoryPath + "/metatile_attributes.bin";
// Set default tiles image
QImage tilesImage(":/images/blank_tileset.png");
tileset->loadTilesImage(&tilesImage);
//exportIndexed4BPPPng(tileset->tilesImage, tileset->tilesImagePath); // TODO: Make sure we can now properly handle the 8bpp images that get written without this.
// Create default metatiles
const int numMetatiles = tileset->is_secondary ? (Project::getNumMetatilesTotal() - Project::getNumMetatilesPrimary()) : Project::getNumMetatilesPrimary();
const int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
for (int i = 0; i < numMetatiles; ++i) {
auto metatile = new Metatile();
for(int j = 0; j < tilesPerMetatile; ++j){
Tile tile = Tile();
if (checkerboardFill) {
// Create a checkerboard-style dummy tileset
if (((i / 8) % 2) == 0)
tile.tileId = ((i % 2) == 0) ? 1 : 2;
else
tile.tileId = ((i % 2) == 1) ? 1 : 2;
}
metatile->tiles.append(tile);
}
palettes.append(palette);
palettePreviews.append(palette);
}
tileset->palettes = palettes;
tileset->palettePreviews = palettePreviews;
}
void Project::loadTilesetTiles(Tileset *tileset, QImage image) {
QList<QImage> tiles;
int w = 8;
int h = 8;
for (int y = 0; y < image.height(); y += h)
for (int x = 0; x < image.width(); x += w) {
QImage tile = image.copy(x, y, w, h);
tiles.append(tile);
}
tileset->tilesImage = image;
tileset->tiles = tiles;
}
void Project::loadTilesetMetatiles(Tileset* tileset) {
QFile metatiles_file(tileset->metatiles_path);
if (metatiles_file.open(QIODevice::ReadOnly)) {
QByteArray data = metatiles_file.readAll();
int tilesPerMetatile = projectConfig.getNumTilesInMetatile();
int bytesPerMetatile = 2 * tilesPerMetatile;
int num_metatiles = data.length() / bytesPerMetatile;
QList<Metatile*> metatiles;
for (int i = 0; i < num_metatiles; i++) {
Metatile *metatile = new Metatile;
int index = i * bytesPerMetatile;
for (int j = 0; j < tilesPerMetatile; j++) {
uint16_t tileRaw = static_cast<unsigned char>(data[index++]);
tileRaw |= static_cast<unsigned char>(data[index++]) << 8;
metatile->tiles.append(Tile(tileRaw));
}
metatiles.append(metatile);
}
tileset->setMetatiles(metatiles);
} else {
tileset->clearMetatiles();
logError(QString("Could not open tileset metatiles file '%1'").arg(tileset->metatiles_path));
tileset->addMetatile(metatile);
}
QFile attrs_file(tileset->metatile_attrs_path);
if (attrs_file.open(QIODevice::ReadOnly)) {
QByteArray data = attrs_file.readAll();
int num_metatiles = tileset->numMetatiles();
int attrSize = projectConfig.metatileAttributesSize;
int num_metatileAttrs = data.length() / attrSize;
if (num_metatiles != num_metatileAttrs) {
logWarn(QString("Metatile count %1 does not match metatile attribute count %2 in %3").arg(num_metatiles).arg(num_metatileAttrs).arg(tileset->name));
if (num_metatileAttrs > num_metatiles)
num_metatileAttrs = num_metatiles;
// Create default palettes
for(int i = 0; i < 16; ++i) {
QList<QRgb> currentPal;
for(int i = 0; i < 16;++i) {
currentPal.append(qRgb(0,0,0));
}
for (int i = 0; i < num_metatileAttrs; i++) {
uint32_t attributes = 0;
for (int j = 0; j < attrSize; j++)
attributes |= static_cast<unsigned char>(data.at(i * attrSize + j)) << (8 * j);
tileset->metatileAt(i)->setAttributes(attributes);
}
} else {
logError(QString("Could not open tileset metatile attributes file '%1'").arg(tileset->metatile_attrs_path));
tileset->palettes.append(currentPal);
tileset->palettePreviews.append(currentPal);
tileset->palettePaths.append(QString("%1/%2.pal").arg(palettesPath).arg(i, 2, 10, QLatin1Char('0')));
}
tileset->palettes[0][1] = qRgb(255,0,255);
tileset->palettePreviews[0][1] = qRgb(255,0,255);
// Update tileset label arrays
QStringList *labelList = tileset->is_secondary ? &this->secondaryTilesetLabels : &this->primaryTilesetLabels;
for (int i = 0; i < labelList->length(); i++) {
if (labelList->at(i) > tileset->name) {
labelList->insert(i, tileset->name);
break;
}
}
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);
tileset->save();
this->tilesetCache.insert(tileset->name, tileset);
emit tilesetCreated(tileset);
return tileset;
}
QString Project::findMetatileLabelsTileset(QString label) {
@ -1658,9 +1591,9 @@ void Project::loadTilesetMetatileLabels(Tileset* tileset) {
QString metatileLabelPrefix = tileset->getMetatileLabelPrefix();
// Reverse map for faster lookup by metatile id
for (QString labelName : metatileLabelsMap[tileset->name].keys()) {
auto metatileId = metatileLabelsMap[tileset->name][labelName];
tileset->metatileLabels[metatileId] = labelName.replace(metatileLabelPrefix, "");
for (auto it = this->metatileLabelsMap[tileset->name].constBegin(); it != this->metatileLabelsMap[tileset->name].constEnd(); it++) {
QString labelName = it.key();
tileset->metatileLabels[it.value()] = labelName.replace(metatileLabelPrefix, "");
}
}

View File

@ -337,7 +337,7 @@ void MainWindow::refreshAfterPaletteChange(Tileset *tileset) {
this->editor->map_item->draw(true);
this->editor->updateMapBorder();
this->editor->updateMapConnections();
this->editor->project->saveTilesetPalettes(tileset);
tileset->savePalettes();
}
void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList<QList<int>> colors) {
@ -576,14 +576,14 @@ void MainWindow::setSecondaryTileset(QString tileset) {
void MainWindow::saveMetatilesByMetatileId(int metatileId) {
Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
if (this->editor->project && tileset)
this->editor->project->saveTilesetMetatiles(tileset);
if (tileset)
tileset->saveMetatiles();
}
void MainWindow::saveMetatileAttributesByMetatileId(int metatileId) {
Tileset * tileset = Tileset::getMetatileTileset(metatileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
if (this->editor->project && tileset)
this->editor->project->saveTilesetMetatileAttributes(tileset);
if (tileset)
tileset->saveMetatileAttributes();
// If the tileset editor is open it needs to be refreshed with the new changes.
// Rather than do a full refresh (which is costly) we tell the editor it will need
@ -811,9 +811,6 @@ QJSValue MainWindow::getTilePixels(int tileId) {
// Editing map header
//=====================
// TODO: Connect signals from new function calls to update UI
// TODO: Is the error-checking for known constant names still reasonable / needed? (you can type anything after all)
QString MainWindow::getSong() {
if (!this->editor || !this->editor->map)
return QString();
@ -823,10 +820,6 @@ QString MainWindow::getSong() {
void MainWindow::setSong(QString song) {
if (!this->editor || !this->editor->map || !this->editor->project)
return;
if (!this->editor->project->songNames.contains(song)) {
logError(QString("Unknown song '%1'").arg(song));
return;
}
this->editor->map->header()->setSong(song);
}
@ -867,10 +860,6 @@ QString MainWindow::getWeather() {
void MainWindow::setWeather(QString weather) {
if (!this->editor || !this->editor->map || !this->editor->project)
return;
if (!this->editor->project->weatherNames.contains(weather)) {
logError(QString("Unknown weather '%1'").arg(weather));
return;
}
this->editor->map->header()->setWeather(weather);
}
@ -883,10 +872,6 @@ QString MainWindow::getType() {
void MainWindow::setType(QString type) {
if (!this->editor || !this->editor->map || !this->editor->project)
return;
if (!this->editor->project->mapTypes.contains(type)) {
logError(QString("Unknown map type '%1'").arg(type));
return;
}
this->editor->map->header()->setType(type);
}
@ -899,10 +884,6 @@ QString MainWindow::getBattleScene() {
void MainWindow::setBattleScene(QString battleScene) {
if (!this->editor || !this->editor->map || !this->editor->project)
return;
if (!this->editor->project->mapBattleScenes.contains(battleScene)) {
logError(QString("Unknown battle scene '%1'").arg(battleScene));
return;
}
this->editor->map->header()->setBattleScene(battleScene);
}

View File

@ -1,50 +1,87 @@
#include "newtilesetdialog.h"
#include "ui_newtilesetdialog.h"
#include "project.h"
#include "imageexport.h"
const QString lineEdit_ErrorStylesheet = "QLineEdit { background-color: rgba(255, 0, 0, 25%) }";
NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) :
QDialog(parent),
ui(new Ui::NewTilesetDialog)
ui(new Ui::NewTilesetDialog),
symbolPrefix(projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix))
{
setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
ui->setupUi(this);
this->setFixedSize(this->width(), this->height());
this->project = project;
ui->checkBox_CheckerboardFill->setChecked(porymapConfig.tilesetCheckerboardFill);
ui->label_SymbolNameDisplay->setText(this->symbolPrefix);
ui->comboBox_Type->setMinimumContentsLength(12);
//only allow characters valid for a symbol
static const QRegularExpression expression("[_A-Za-z0-9]+$"); // TODO: Incorrect, allows digits at beginning
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression);
this->ui->nameLineEdit->setValidator(validator);
static const QRegularExpression expression("[A-Za-z_]+[\\w]*");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(expression, this);
ui->lineEdit_FriendlyName->setValidator(validator);
bool checkerboard = porymapConfig.tilesetCheckerboardFill;
this->ui->fillCheckBox->setChecked(checkerboard);
this->checkerboardFill = checkerboard;
connect(ui->lineEdit_FriendlyName, &QLineEdit::textChanged, this, &NewTilesetDialog::onFriendlyNameChanged);
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &NewTilesetDialog::dialogButtonClicked);
connect(this->ui->nameLineEdit, &QLineEdit::textChanged, this, &NewTilesetDialog::NameOrSecondaryChanged);
connect(this->ui->typeComboBox, &QComboBox::currentTextChanged, this, &NewTilesetDialog::SecondaryChanged);
connect(this->ui->fillCheckBox, &QCheckBox::stateChanged, this, &NewTilesetDialog::FillChanged);
//connect(this->ui->toolButton, &QToolButton::clicked, this, &NewTilesetDialog::ChangeFilePath);
this->SecondaryChanged();
adjustSize();
}
NewTilesetDialog::~NewTilesetDialog()
{
porymapConfig.tilesetCheckerboardFill = ui->checkBox_CheckerboardFill->isChecked();
delete ui;
}
void NewTilesetDialog::SecondaryChanged(){
this->isSecondary = (this->ui->typeComboBox->currentIndex() == 1);
NameOrSecondaryChanged();
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);
validateName(true);
}
// TODO: No validation
void NewTilesetDialog::NameOrSecondaryChanged() {
this->friendlyName = this->ui->nameLineEdit->text();
this->fullSymbolName = projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix) + this->friendlyName;
this->ui->symbolNameLineEdit->setText(this->fullSymbolName);
this->path = Tileset::getExpectedDir(this->fullSymbolName, this->isSecondary);
this->ui->pathLineEdit->setText(this->path);
bool NewTilesetDialog::validateName(bool allowEmpty) {
const QString friendlyName = ui->lineEdit_FriendlyName->text();
const QString symbolName = ui->label_SymbolNameDisplay->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);
}
bool isValid = errorText.isEmpty();
ui->label_NameError->setText(errorText);
ui->label_NameError->setVisible(!isValid);
ui->lineEdit_FriendlyName->setStyleSheet(!isValid ? lineEdit_ErrorStylesheet : "");
return isValid;
}
void NewTilesetDialog::FillChanged() {
this->checkerboardFill = this->ui->fillCheckBox->isChecked();
porymapConfig.tilesetCheckerboardFill = this->checkerboardFill;
void NewTilesetDialog::dialogButtonClicked(QAbstractButton *button) {
auto role = ui->buttonBox->buttonRole(button);
if (role == QDialogButtonBox::RejectRole){
reject();
} else if (role == QDialogButtonBox::AcceptRole) {
accept();
}
}
void NewTilesetDialog::accept() {
if (!validateName())
return;
bool secondary = ui->comboBox_Type->currentIndex() == 1;
Tileset *tileset = this->project->createNewTileset(ui->lineEdit_FriendlyName->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);
return;
}
ui->label_GenericError->setVisible(false);
QDialog::accept();
}

View File

@ -716,22 +716,9 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
image = image.convertToFormat(QImage::Format::Format_Indexed8, colorTable);
}
// Validate image is properly indexed to 16 colors.
int colorCount = image.colorCount();
if (colorCount > 16) {
flattenTo4bppImage(&image);
} else if (colorCount < 16) {
QVector<QRgb> colorTable = image.colorTable();
for (int i = colorTable.length(); i < 16; i++) {
colorTable.append(Qt::black);
}
image.setColorTable(colorTable);
}
this->project->loadTilesetTiles(tileset, image);
tileset->loadTilesImage(&image);
this->refresh();
this->hasUnsavedChanges = true;
tileset->hasUnsavedTilesImage = true;
}
void TilesetEditor::closeEvent(QCloseEvent *event)