mirror of
https://github.com/huderlem/porymap.git
synced 2026-03-21 17:45:44 -05:00
Merge pull request #760 from GriffinRichards/color-search
Add color usage features to the Palette Editor
This commit is contained in:
commit
2e93e78a73
152
forms/palettecolorsearch.ui
Normal file
152
forms/palettecolorsearch.ui
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PaletteColorSearch</class>
|
||||
<widget class="QDialog" name="PaletteColorSearch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>547</width>
|
||||
<height>329</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Palette Color Search</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_Title">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="searchBar">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_Color">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_Color">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="NoScrollSpinBox" name="spinBox_ColorId">
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_Palette">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Palette</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBox_PaletteId"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="table_Results">
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>NoScrollSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>noscrollspinbox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
@ -38,6 +38,27 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton_ColorSearch">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Opens a search dialog to find which tilesets/metatiles are using certain colors.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources/images.qrc">
|
||||
<normaloff>:/icons/magnifier.ico</normaloff>:/icons/magnifier.ico</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_AllColorsUsed">
|
||||
<property name="text">
|
||||
<string>(All colors used)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
@ -89,7 +110,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>883</width>
|
||||
<height>784</height>
|
||||
<height>779</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="layout_Colors">
|
||||
|
|
@ -133,8 +154,22 @@
|
|||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
<addaction name="actionShow_Unused_Colors"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTools">
|
||||
<property name="title">
|
||||
<string>Tools</string>
|
||||
</property>
|
||||
<addaction name="actionFind_Color_Usage"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuTools"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionUndo">
|
||||
|
|
@ -164,7 +199,22 @@
|
|||
<string>Import Palette</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShow_Unused_Colors">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Unused Colors</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFind_Color_Usage">
|
||||
<property name="text">
|
||||
<string>Find Color Usage...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ public:
|
|||
bool showTilesetEditorLayerGrid;
|
||||
bool showTilesetEditorDivider;
|
||||
bool showTilesetEditorRawAttributes;
|
||||
bool showPaletteEditorUnusedColors;
|
||||
bool monitorFiles;
|
||||
bool tilesetCheckerboardFill;
|
||||
bool newMapHeaderSectionExpanded;
|
||||
|
|
|
|||
|
|
@ -122,8 +122,8 @@ public:
|
|||
int getZ() const { return this->elevation; }
|
||||
int getElevation() const { return this->elevation; }
|
||||
|
||||
int getPixelX() const { return (this->x * 16) - qMax(0, (pixmap.width() - 16) / 2); }
|
||||
int getPixelY() const { return (this->y * 16) - qMax(0, pixmap.height() - 16); }
|
||||
int getPixelX() const;
|
||||
int getPixelY() const;
|
||||
|
||||
virtual EventFrame *getEventFrame();
|
||||
virtual EventFrame *createEventFrame() = 0;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public:
|
|||
static int getDefaultAttributesSize(BaseGameVersion version);
|
||||
static void setLayout(Project*);
|
||||
static QString getMetatileIdString(uint16_t metatileId);
|
||||
static QString getMetatileIdStrings(const QList<uint16_t> metatileIds);
|
||||
static QString getMetatileIdStrings(const QList<uint16_t> &metatileIds);
|
||||
static QString getLayerName(int layerNum);
|
||||
|
||||
static constexpr int tileWidth() { return 2; }
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public:
|
|||
static constexpr int pixelWidth() { return 8; }
|
||||
static constexpr int pixelHeight() { return 8; }
|
||||
static constexpr QSize pixelSize() { return QSize(Tile::pixelWidth(), Tile::pixelHeight()); }
|
||||
static constexpr int numPixels() { return Tile::pixelWidth() * Tile::pixelHeight(); }
|
||||
static constexpr int sizeInBytes() { return sizeof(uint16_t); }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,14 @@ public:
|
|||
QList<QList<QRgb>> palettePreviews;
|
||||
|
||||
static QString stripPrefix(const QString &fullName);
|
||||
static Tileset* getPaletteTileset(int, Tileset*, Tileset*);
|
||||
static const Tileset* getPaletteTileset(int, const Tileset*, const Tileset*);
|
||||
static Tileset* getMetatileTileset(int, Tileset*, Tileset*);
|
||||
static const Tileset* getMetatileTileset(int, const Tileset*, const Tileset*);
|
||||
static Tileset* getTileTileset(int, Tileset*, Tileset*);
|
||||
static const Tileset* getTileTileset(int, const Tileset*, const Tileset*);
|
||||
static Metatile* getMetatile(int, Tileset*, Tileset*);
|
||||
static const Metatile* getMetatile(int, const Tileset*, const Tileset*);
|
||||
static Tileset* getMetatileLabelTileset(int, Tileset*, Tileset*);
|
||||
static QString getMetatileLabel(int, Tileset *, Tileset *);
|
||||
static QString getOwnedMetatileLabel(int, Tileset *, Tileset *);
|
||||
|
|
@ -47,9 +52,9 @@ public:
|
|||
static bool setMetatileLabel(int, QString, Tileset *, Tileset *);
|
||||
QString getMetatileLabelPrefix();
|
||||
static QString getMetatileLabelPrefix(const QString &name);
|
||||
static QList<QList<QRgb>> getBlockPalettes(Tileset*, Tileset*, bool useTruePalettes = false);
|
||||
static QList<QRgb> getPalette(int, Tileset*, Tileset*, bool useTruePalettes = false);
|
||||
static bool metatileIsValid(uint16_t metatileId, Tileset *, Tileset *);
|
||||
static QList<QList<QRgb>> getBlockPalettes(const Tileset*, const Tileset*, bool useTruePalettes = false);
|
||||
static QList<QRgb> getPalette(int, const Tileset*, const Tileset*, bool useTruePalettes = false);
|
||||
static bool metatileIsValid(uint16_t metatileId, const Tileset*, const Tileset*);
|
||||
static QHash<int, QString> getHeaderMemberMap(bool usingAsm);
|
||||
static QString getExpectedDir(QString tilesetName, bool isSecondary);
|
||||
QString getExpectedDir();
|
||||
|
|
@ -92,6 +97,12 @@ public:
|
|||
|
||||
QImage tileImage(uint16_t tileId) const { return m_tiles.value(Tile::getIndexInTileset(tileId)); }
|
||||
|
||||
QSet<int> getUnusedColorIds(int paletteId, const Tileset *pairedTileset, const QSet<int> &searchColors = {}) const;
|
||||
QList<uint16_t> findMetatilesUsingColor(int paletteId, int colorId, const Tileset *pairedTileset) const;
|
||||
|
||||
static constexpr int maxPalettes() { return 16; }
|
||||
static constexpr int numColorsPerPalette() { return 16; }
|
||||
|
||||
private:
|
||||
QList<Metatile*> m_metatiles;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace Util {
|
|||
QString replaceExtension(const QString &path, const QString &newExtension);
|
||||
void setErrorStylesheet(QLineEdit *lineEdit, bool isError);
|
||||
QString toStylesheetString(const QFont &font);
|
||||
void show(QWidget *w);
|
||||
}
|
||||
|
||||
#endif // UTILITY_H
|
||||
|
|
|
|||
|
|
@ -67,8 +67,6 @@ public:
|
|||
bool setLayout(QString layoutName);
|
||||
void unsetMap();
|
||||
|
||||
Tileset *getCurrentMapPrimaryTileset();
|
||||
|
||||
bool displayMap();
|
||||
bool displayLayout();
|
||||
|
||||
|
|
|
|||
|
|
@ -385,7 +385,6 @@ private:
|
|||
void openDuplicateMapOrLayoutDialog();
|
||||
void openNewMapGroupDialog();
|
||||
void openNewLocationDialog();
|
||||
void openSubWindow(QWidget * window);
|
||||
void scrollMapList(MapTree *list, const QString &itemName);
|
||||
void scrollMapListToCurrentMap(MapTree *list);
|
||||
void scrollMapListToCurrentLayout(MapTree *list);
|
||||
|
|
|
|||
|
|
@ -104,11 +104,11 @@ public:
|
|||
bool load();
|
||||
|
||||
QMap<QString, Tileset*> tilesetCache;
|
||||
Tileset* loadTileset(QString, Tileset *tileset = nullptr);
|
||||
Tileset* getTileset(QString, bool forceLoad = false);
|
||||
Tileset* getTileset(const QString&, bool forceLoad = false);
|
||||
QStringList primaryTilesetLabels;
|
||||
QStringList secondaryTilesetLabels;
|
||||
QStringList tilesetLabelsOrdered;
|
||||
QSet<QString> getPairedTilesetLabels(const Tileset *tileset) const;
|
||||
|
||||
bool readMapGroups();
|
||||
void addNewMapGroup(const QString &groupName);
|
||||
|
|
@ -342,6 +342,7 @@ private:
|
|||
void resetFileCache();
|
||||
void resetFileWatcher();
|
||||
void logFileWatchStatus();
|
||||
void cacheTileset(const QString &label, Tileset *tileset);
|
||||
|
||||
bool saveMapLayouts();
|
||||
bool saveMapGroups();
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ class Layout;
|
|||
QImage getCollisionMetatileImage(Block);
|
||||
QImage getCollisionMetatileImage(int, int);
|
||||
|
||||
QImage getMetatileImage(uint16_t, Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(Metatile*, Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(uint16_t, Tileset*, Tileset*, const QList<int>&, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(Metatile*, Tileset*, Tileset*, const QList<int>&, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(uint16_t, const Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(const Metatile*, const Layout*, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(uint16_t, const Tileset*, const Tileset*, const QList<int>& = {0,1,2}, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
QImage getMetatileImage(const Metatile*, const Tileset*, const Tileset*, const QList<int>& = {0,1,2}, const QList<float>& = {}, bool useTruePalettes = false);
|
||||
|
||||
QImage getMetatileSheetImage(Layout *layout, int numMetatilesWIde, bool useTruePalettes = false);
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
QImage getMetatileSheetImage(const Layout *layout, int numMetatilesWIde, bool useTruePalettes = false);
|
||||
QImage getMetatileSheetImage(const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
uint16_t metatileIdStart,
|
||||
uint16_t metatileIdEnd,
|
||||
int numMetatilesWIde,
|
||||
|
|
@ -26,8 +26,8 @@ QImage getMetatileSheetImage(Tileset *primaryTileset,
|
|||
const QList<float> &layerOpacity = {},
|
||||
const QSize &metatileSize = Metatile::pixelSize(),
|
||||
bool useTruePalettes = false);
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
QImage getMetatileSheetImage(const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
int numMetatilesWide,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity = {},
|
||||
|
|
@ -35,10 +35,10 @@ QImage getMetatileSheetImage(Tileset *primaryTileset,
|
|||
bool useTruePalettes = false);
|
||||
|
||||
|
||||
QImage getTileImage(uint16_t, Tileset*, Tileset*);
|
||||
QImage getPalettedTileImage(uint16_t, Tileset*, Tileset*, int, bool useTruePalettes = false);
|
||||
QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList<QRgb> &palette);
|
||||
QImage getGreyscaleTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset);
|
||||
QImage getTileImage(uint16_t, const Tileset*, const Tileset*);
|
||||
QImage getPalettedTileImage(uint16_t, const Tileset*, const Tileset*, int, bool useTruePalettes = false);
|
||||
QImage getColoredTileImage(uint16_t tileId, const Tileset *, const Tileset *, const QList<QRgb> &palette);
|
||||
QImage getGreyscaleTileImage(uint16_t tileId, const Tileset *, const Tileset *);
|
||||
|
||||
void flattenTo4bppImage(QImage * image);
|
||||
|
||||
|
|
|
|||
20
include/ui/numericsorttableitem.h
Normal file
20
include/ui/numericsorttableitem.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef NUMERICSORTTABLEITEM_H
|
||||
#define NUMERICSORTTABLEITEM_H
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QCollator>
|
||||
|
||||
class NumericSortTableItem : public QTableWidgetItem
|
||||
{
|
||||
public:
|
||||
explicit NumericSortTableItem(const QString &text) : QTableWidgetItem(text) {};
|
||||
|
||||
protected:
|
||||
virtual bool operator<(const QTableWidgetItem &other) const override {
|
||||
QCollator collator;
|
||||
collator.setNumericMode(true);
|
||||
return collator.compare(text(), other.text()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // NUMERICSORTTABLEITEM_H
|
||||
67
include/ui/palettecolorsearch.h
Normal file
67
include/ui/palettecolorsearch.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef PALETTECOLORSEARCH_H
|
||||
#define PALETTECOLORSEARCH_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QIcon>
|
||||
#include <QMap>
|
||||
|
||||
class Tileset;
|
||||
class Project;
|
||||
|
||||
namespace Ui {
|
||||
class PaletteColorSearch;
|
||||
}
|
||||
|
||||
class PaletteColorSearch : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PaletteColorSearch(Project *project,
|
||||
const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
QWidget *parent = nullptr);
|
||||
~PaletteColorSearch();
|
||||
|
||||
void setPaletteId(int paletteId);
|
||||
int currentPaletteId() const;
|
||||
|
||||
void setColorId(int colorId);
|
||||
int currentColorId() const;
|
||||
|
||||
void setTilesets(const Tileset *primaryTileset, const Tileset *secondaryTileset);
|
||||
const Tileset* currentTileset() const;
|
||||
|
||||
signals:
|
||||
void metatileSelected(uint16_t metatileId);
|
||||
void paletteIdChanged(int paletteId);
|
||||
|
||||
private:
|
||||
struct RowData {
|
||||
QString tilesetName;
|
||||
QString pairedTilesetName;
|
||||
QString metatileId;
|
||||
QIcon metatileIcon;
|
||||
};
|
||||
|
||||
enum ResultsColumn {
|
||||
TilesetName,
|
||||
Metatile,
|
||||
};
|
||||
|
||||
Ui::PaletteColorSearch *ui;
|
||||
Project *m_project;
|
||||
const Tileset *m_primaryTileset;
|
||||
const Tileset *m_secondaryTileset;
|
||||
|
||||
QMap<QString,QList<RowData>> m_resultsCache;
|
||||
|
||||
void addTableEntry(const RowData &rowData);
|
||||
QList<RowData> search(int colorId) const;
|
||||
QList<RowData> search(int colorId, const Tileset *tileset, const Tileset *pairedTileset) const;
|
||||
void refresh();
|
||||
void updateResults();
|
||||
void cellDoubleClicked(int row, int col);
|
||||
};
|
||||
|
||||
#endif // PALETTECOLORSEARCH_H
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
#define PALETTEEDITOR_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QPointer>
|
||||
|
||||
#include "colorinputwidget.h"
|
||||
#include "project.h"
|
||||
#include "history.h"
|
||||
#include "palettecolorsearch.h"
|
||||
|
||||
namespace Ui {
|
||||
class PaletteEditor;
|
||||
|
|
@ -24,25 +26,39 @@ class PaletteEditor : public QMainWindow {
|
|||
public:
|
||||
explicit PaletteEditor(Project*, Tileset*, Tileset*, int paletteId, QWidget *parent = nullptr);
|
||||
~PaletteEditor();
|
||||
|
||||
void setPaletteId(int);
|
||||
int currentPaletteId() const;
|
||||
|
||||
void setTilesets(Tileset*, Tileset*);
|
||||
|
||||
bool showingUnusedColors() const;
|
||||
|
||||
signals:
|
||||
void metatileSelected(uint16_t metatileId);
|
||||
|
||||
private:
|
||||
Ui::PaletteEditor *ui;
|
||||
Project *project = nullptr;
|
||||
QList<ColorInputWidget*> colorInputs;
|
||||
|
||||
Project *project;
|
||||
Tileset *primaryTileset;
|
||||
Tileset *secondaryTileset;
|
||||
|
||||
QList<ColorInputWidget*> colorInputs;
|
||||
QList<History<PaletteHistoryItem*>> palettesHistory;
|
||||
QMap<int,QSet<int>> unusedColorCache;
|
||||
QPointer<PaletteColorSearch> colorSearchWindow;
|
||||
|
||||
Tileset* getTileset(int paletteId);
|
||||
Tileset* getTileset(int paletteId) const;
|
||||
void refreshColorInputs();
|
||||
void refreshPaletteId();
|
||||
void commitEditHistory();
|
||||
void commitEditHistory(int paletteId);
|
||||
void restoreWindowState();
|
||||
void invalidateCache();
|
||||
void closeEvent(QCloseEvent*);
|
||||
void setColorInputTitles(bool show);
|
||||
QSet<int> getUnusedColorIds();
|
||||
void openColorSearch();
|
||||
|
||||
void setRgb(int index, QRgb rgb);
|
||||
void setPalette(int paletteId, const QList<QRgb> &palette);
|
||||
|
|
@ -50,14 +66,13 @@ private:
|
|||
void setBitDepth(int bits);
|
||||
int bitDepth = 24;
|
||||
|
||||
static const int numColors = 16;
|
||||
static const int numColors = Tileset::numColorsPerPalette();
|
||||
|
||||
signals:
|
||||
void closed();
|
||||
void changedPaletteColor();
|
||||
void changedPalette(int);
|
||||
private slots:
|
||||
void on_spinBox_PaletteId_valueChanged(int arg1);
|
||||
void on_actionUndo_triggered();
|
||||
void on_actionRedo_triggered();
|
||||
void on_actionImport_Palette_triggered();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define TILESETEDITOR_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QPointer>
|
||||
#include "project.h"
|
||||
#include "history.h"
|
||||
#include "paletteeditor.h"
|
||||
|
|
@ -54,6 +55,9 @@ public:
|
|||
|
||||
QObjectList shortcutableObjects() const;
|
||||
|
||||
void setPaletteId(int paletteId);
|
||||
int paletteId() const;
|
||||
|
||||
public slots:
|
||||
void applyUserShortcuts();
|
||||
void onSelectedMetatileChanged(uint16_t);
|
||||
|
|
@ -65,13 +69,9 @@ private slots:
|
|||
void onHoveredTileChanged(const Tile&);
|
||||
void onHoveredTileChanged(uint16_t);
|
||||
void onHoveredTileCleared();
|
||||
void onSelectedTilesChanged();
|
||||
void onMetatileLayerTileChanged(int, int);
|
||||
void onMetatileLayerSelectionChanged(QPoint, int, int);
|
||||
void onPaletteEditorChangedPaletteColor();
|
||||
void onPaletteEditorChangedPalette(int);
|
||||
|
||||
void on_spinBox_paletteSelector_valueChanged(int arg1);
|
||||
|
||||
void on_actionChange_Metatiles_Count_triggered();
|
||||
|
||||
|
|
@ -136,23 +136,20 @@ private:
|
|||
void commitTerrainType();
|
||||
void commitLayerType();
|
||||
void setRawAttributesVisible(bool visible);
|
||||
void setXFlip(bool enabled);
|
||||
void setYFlip(bool enabled);
|
||||
void refreshTileFlips();
|
||||
void refreshPaletteId();
|
||||
|
||||
Ui::TilesetEditor *ui;
|
||||
History<MetatileHistoryItem*> metatileHistory;
|
||||
TilesetEditorMetatileSelector *metatileSelector = nullptr;
|
||||
TilesetEditorTileSelector *tileSelector = nullptr;
|
||||
MetatileLayersItem *metatileLayersItem = nullptr;
|
||||
PaletteEditor *paletteEditor = nullptr;
|
||||
QPointer<PaletteEditor> paletteEditor = nullptr;
|
||||
Project *project = nullptr;
|
||||
Layout *layout = nullptr;
|
||||
Metatile *metatile = nullptr;
|
||||
Metatile *copiedMetatile = nullptr;
|
||||
QString copiedMetatileLabel;
|
||||
int paletteId;
|
||||
bool tileXFlip;
|
||||
bool tileYFlip;
|
||||
bool hasUnsavedChanges;
|
||||
Tileset *primaryTileset = nullptr;
|
||||
Tileset *secondaryTileset = nullptr;
|
||||
|
|
|
|||
|
|
@ -2,24 +2,11 @@
|
|||
#define WILDMONSEARCH_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QCollator>
|
||||
|
||||
#include "numericsorttableitem.h"
|
||||
|
||||
class Project;
|
||||
|
||||
class NumericSortTableItem : public QTableWidgetItem
|
||||
{
|
||||
public:
|
||||
explicit NumericSortTableItem(const QString &text) : QTableWidgetItem(text) {};
|
||||
|
||||
protected:
|
||||
virtual bool operator<(const QTableWidgetItem &other) const override {
|
||||
QCollator collator;
|
||||
collator.setNumericMode(true);
|
||||
return collator.compare(text(), other.text()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Ui {
|
||||
class WildMonSearch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ SOURCES += src/core/advancemapparser.cpp \
|
|||
src/ui/montabwidget.cpp \
|
||||
src/ui/encountertablemodel.cpp \
|
||||
src/ui/encountertabledelegates.cpp \
|
||||
src/ui/palettecolorsearch.cpp \
|
||||
src/ui/paletteeditor.cpp \
|
||||
src/ui/selectablepixmapitem.cpp \
|
||||
src/ui/tileseteditor.cpp \
|
||||
|
|
@ -234,6 +235,7 @@ HEADERS += include/core/advancemapparser.h \
|
|||
include/ui/encountertablemodel.h \
|
||||
include/ui/encountertabledelegates.h \
|
||||
include/ui/adjustingstackedwidget.h \
|
||||
include/ui/palettecolorsearch.h \
|
||||
include/ui/paletteeditor.h \
|
||||
include/ui/selectablepixmapitem.h \
|
||||
include/ui/tileseteditor.h \
|
||||
|
|
@ -287,6 +289,7 @@ FORMS += forms/mainwindow.ui \
|
|||
forms/prefabcreationdialog.ui \
|
||||
forms/prefabframe.ui \
|
||||
forms/tileseteditor.ui \
|
||||
forms/palettecolorsearch.ui \
|
||||
forms/paletteeditor.ui \
|
||||
forms/regionmapeditor.ui \
|
||||
forms/newmapdialog.ui \
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ void PorymapConfig::reset() {
|
|||
this->showTilesetEditorLayerGrid = true;
|
||||
this->showTilesetEditorDivider = false;
|
||||
this->showTilesetEditorRawAttributes = false;
|
||||
this->showPaletteEditorUnusedColors = false;
|
||||
this->monitorFiles = true;
|
||||
this->tilesetCheckerboardFill = true;
|
||||
this->newMapHeaderSectionExpanded = false;
|
||||
|
|
@ -469,6 +470,8 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
|
|||
this->showTilesetEditorDivider = getConfigBool(key, value);
|
||||
} else if (key == "show_tileset_editor_raw_attributes") {
|
||||
this->showTilesetEditorRawAttributes = getConfigBool(key, value);
|
||||
} else if (key == "show_palette_editor_unused_colors") {
|
||||
this->showPaletteEditorUnusedColors = getConfigBool(key, value);
|
||||
} else if (key == "monitor_files") {
|
||||
this->monitorFiles = getConfigBool(key, value);
|
||||
} else if (key == "tileset_checkerboard_fill") {
|
||||
|
|
@ -609,6 +612,7 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
|
|||
map.insert("show_tileset_editor_layer_grid", this->showTilesetEditorLayerGrid ? "1" : "0");
|
||||
map.insert("show_tileset_editor_divider", this->showTilesetEditorDivider ? "1" : "0");
|
||||
map.insert("show_tileset_editor_raw_attributes", this->showTilesetEditorRawAttributes ? "1" : "0");
|
||||
map.insert("show_palette_editor_unused_colors", this->showPaletteEditorUnusedColors ? "1" : "0");
|
||||
map.insert("monitor_files", this->monitorFiles ? "1" : "0");
|
||||
map.insert("tileset_checkerboard_fill", this->tilesetCheckerboardFill ? "1" : "0");
|
||||
map.insert("new_map_header_section_expanded", this->newMapHeaderSectionExpanded ? "1" : "0");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "eventframes.h"
|
||||
#include "project.h"
|
||||
#include "config.h"
|
||||
#include "metatile.h"
|
||||
|
||||
Event* Event::create(Event::Type type) {
|
||||
switch (type) {
|
||||
|
|
@ -23,6 +24,14 @@ Event::~Event() {
|
|||
delete this->eventFrame;
|
||||
}
|
||||
|
||||
int Event::getPixelX() const {
|
||||
return (this->x * Metatile::pixelWidth()) - qMax(0, (this->pixmap.width() - Metatile::pixelWidth()) / 2);
|
||||
}
|
||||
|
||||
int Event::getPixelY() const {
|
||||
return (this->y * Metatile::pixelHeight()) - qMax(0, this->pixmap.height() - Metatile::pixelHeight());
|
||||
}
|
||||
|
||||
EventFrame *Event::getEventFrame() {
|
||||
if (!this->eventFrame) createEventFrame();
|
||||
return this->eventFrame;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ QString Metatile::getMetatileIdString(uint16_t metatileId) {
|
|||
return Util::toHexString(metatileId, numMetatileIdChars);
|
||||
};
|
||||
|
||||
QString Metatile::getMetatileIdStrings(const QList<uint16_t> metatileIds) {
|
||||
QString Metatile::getMetatileIdStrings(const QList<uint16_t> &metatileIds) {
|
||||
QStringList metatiles;
|
||||
for (auto metatileId : metatileIds)
|
||||
metatiles << Metatile::getMetatileIdString(metatileId);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
Tileset::Tileset(const Tileset &other)
|
||||
|
|
@ -110,7 +111,26 @@ int Tileset::maxTiles() const {
|
|||
return this->is_secondary ? Project::getNumTilesSecondary() : Project::getNumTilesPrimary();
|
||||
}
|
||||
|
||||
Tileset* Tileset::getPaletteTileset(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
return const_cast<Tileset*>(getPaletteTileset(paletteId, static_cast<const Tileset*>(primaryTileset), static_cast<const Tileset*>(secondaryTileset)));
|
||||
}
|
||||
|
||||
const Tileset* Tileset::getPaletteTileset(int paletteId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
if (paletteId < Project::getNumPalettesPrimary()) {
|
||||
return primaryTileset;
|
||||
} else if (paletteId < Project::getNumPalettesTotal()) {
|
||||
return secondaryTileset;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Tileset* Tileset::getTileTileset(int tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
return const_cast<Tileset*>(getTileTileset(tileId, static_cast<const Tileset*>(primaryTileset), static_cast<const Tileset*>(secondaryTileset)));
|
||||
}
|
||||
|
||||
// Get the tileset *expected* to contain the given 'tileId'. Note that this does not mean the tile actually exists in that tileset.
|
||||
const Tileset* Tileset::getTileTileset(int tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
if (tileId < Project::getNumTilesPrimary()) {
|
||||
return primaryTileset;
|
||||
} else if (tileId < Project::getNumTilesTotal()) {
|
||||
|
|
@ -121,6 +141,11 @@ Tileset* Tileset::getTileTileset(int tileId, Tileset *primaryTileset, Tileset *s
|
|||
}
|
||||
|
||||
Tileset* Tileset::getMetatileTileset(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
return const_cast<Tileset*>(getMetatileTileset(metatileId, static_cast<const Tileset*>(primaryTileset), static_cast<const Tileset*>(secondaryTileset)));
|
||||
}
|
||||
|
||||
// Get the tileset *expected* to contain the given 'metatileId'. Note that this does not mean the metatile actually exists in that tileset.
|
||||
const Tileset* Tileset::getMetatileTileset(int metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
if (metatileId < Project::getNumMetatilesPrimary()) {
|
||||
return primaryTileset;
|
||||
} else if (metatileId < Project::getNumMetatilesTotal()) {
|
||||
|
|
@ -131,7 +156,11 @@ Tileset* Tileset::getMetatileTileset(int metatileId, Tileset *primaryTileset, Ti
|
|||
}
|
||||
|
||||
Metatile* Tileset::getMetatile(int metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
|
||||
return const_cast<Metatile*>(getMetatile(metatileId, static_cast<const Tileset*>(primaryTileset), static_cast<const Tileset*>(secondaryTileset)));
|
||||
}
|
||||
|
||||
const Metatile* Tileset::getMetatile(int metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
const Tileset *tileset = Tileset::getMetatileTileset(metatileId, primaryTileset, secondaryTileset);
|
||||
if (!tileset) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -220,12 +249,12 @@ QString Tileset::getMetatileLabelPrefix(const QString &name)
|
|||
return QString("%1%2_").arg(labelPrefix).arg(Tileset::stripPrefix(name));
|
||||
}
|
||||
|
||||
bool Tileset::metatileIsValid(uint16_t metatileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
bool Tileset::metatileIsValid(uint16_t metatileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
return (primaryTileset && primaryTileset->contains(metatileId))
|
||||
|| (secondaryTileset && secondaryTileset->contains(metatileId));
|
||||
}
|
||||
|
||||
QList<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
|
||||
QList<QList<QRgb>> Tileset::getBlockPalettes(const Tileset *primaryTileset, const Tileset *secondaryTileset, bool useTruePalettes) {
|
||||
QList<QList<QRgb>> palettes;
|
||||
|
||||
QList<QList<QRgb>> primaryPalettes;
|
||||
|
|
@ -247,9 +276,9 @@ QList<QList<QRgb>> Tileset::getBlockPalettes(Tileset *primaryTileset, Tileset *s
|
|||
return palettes;
|
||||
}
|
||||
|
||||
QList<QRgb> Tileset::getPalette(int paletteId, Tileset *primaryTileset, Tileset *secondaryTileset, bool useTruePalettes) {
|
||||
QList<QRgb> Tileset::getPalette(int paletteId, const Tileset *primaryTileset, const Tileset *secondaryTileset, bool useTruePalettes) {
|
||||
QList<QRgb> paletteTable;
|
||||
Tileset *tileset = paletteId < Project::getNumPalettesPrimary()
|
||||
const Tileset *tileset = paletteId < Project::getNumPalettesPrimary()
|
||||
? primaryTileset
|
||||
: secondaryTileset;
|
||||
if (!tileset) {
|
||||
|
|
@ -334,7 +363,7 @@ bool Tileset::appendToGraphics(const QString &filepath, const QString &friendlyN
|
|||
dataString.append(QString("\t.incbin \"%1\"\n").arg(tilesPath));
|
||||
} else {
|
||||
// Append to C file
|
||||
dataString.append(QString("const u16 gTilesetPalettes_%1[][16] =\n{\n").arg(friendlyName));
|
||||
dataString.append(QString("const u16 gTilesetPalettes_%1[][%2] =\n{\n").arg(friendlyName).arg(Tileset::numColorsPerPalette()));
|
||||
for (int i = 0; i < Project::getNumPalettesTotal(); i++)
|
||||
dataString.append(QString(" INCBIN_U16(\"%1%2%3\"),\n").arg(palettesPath).arg(i, 2, 10, QLatin1Char('0')).arg(palettesExt));
|
||||
dataString.append("};\n");
|
||||
|
|
@ -546,13 +575,13 @@ bool Tileset::loadTilesImage(QImage *importedImage) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Validate image contains 16 colors.
|
||||
// Validate the number of colors in the image.
|
||||
int colorCount = image.colorCount();
|
||||
if (colorCount > 16) {
|
||||
if (colorCount > Tileset::numColorsPerPalette()) {
|
||||
flattenTo4bppImage(&image);
|
||||
} else if (colorCount < 16) {
|
||||
} else if (colorCount < Tileset::numColorsPerPalette()) {
|
||||
QVector<QRgb> colorTable = image.colorTable();
|
||||
for (int i = colorTable.length(); i < 16; i++) {
|
||||
for (int i = colorTable.length(); i < Tileset::numColorsPerPalette(); i++) {
|
||||
colorTable.append(0);
|
||||
}
|
||||
image.setColorTable(colorTable);
|
||||
|
|
@ -616,8 +645,9 @@ bool Tileset::loadPalettes() {
|
|||
// 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));
|
||||
for (int j = 0; j < Tileset::numColorsPerPalette(); j++) {
|
||||
int colorComponent = j * (256 / Tileset::numColorsPerPalette());
|
||||
palette.append(qRgb(colorComponent, colorComponent, colorComponent));
|
||||
}
|
||||
}
|
||||
this->palettes.append(palette);
|
||||
|
|
@ -630,7 +660,7 @@ bool Tileset::savePalettes() {
|
|||
bool success = true;
|
||||
int numPalettes = qMin(this->palettePaths.length(), this->palettes.length());
|
||||
for (int i = 0; i < numPalettes; i++) {
|
||||
if (!PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, 16))
|
||||
if (!PaletteUtil::writeJASC(this->palettePaths.at(i), this->palettes.at(i).toVector(), 0, Tileset::numColorsPerPalette()))
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
|
|
@ -658,3 +688,85 @@ bool Tileset::save() {
|
|||
QString Tileset::stripPrefix(const QString &fullName) {
|
||||
return QString(fullName).replace(projectConfig.getIdentifier(ProjectIdentifier::symbol_tilesets_prefix), "");
|
||||
}
|
||||
|
||||
// Find which of the specified color IDs in 'searchColors' are not used by any of this tileset's metatiles.
|
||||
// The 'pairedTileset' may be used to get the tile images for any tiles that don't belong to this tileset.
|
||||
// If 'searchColors' is empty, it will for search for all unused colors.
|
||||
QSet<int> Tileset::getUnusedColorIds(int paletteId, const Tileset *pairedTileset, const QSet<int> &searchColors) const {
|
||||
QSet<int> unusedColors = searchColors;
|
||||
if (unusedColors.isEmpty()) {
|
||||
// Search for all colors
|
||||
for (int i = 0; i < Tileset::numColorsPerPalette(); i++) {
|
||||
unusedColors.insert(i);
|
||||
}
|
||||
}
|
||||
const Tileset *primaryTileset = this->is_secondary ? pairedTileset : this;
|
||||
const Tileset *secondaryTileset = this->is_secondary ? this : pairedTileset;
|
||||
QSet<uint16_t> seenTileIds;
|
||||
for (const auto &metatile : m_metatiles)
|
||||
for (const auto &tile : metatile->tiles) {
|
||||
if (tile.palette != paletteId)
|
||||
continue;
|
||||
|
||||
// Save time by ignoring tiles we've already inspected.
|
||||
if (seenTileIds.contains(tile.tileId))
|
||||
continue;
|
||||
seenTileIds.insert(tile.tileId);
|
||||
|
||||
QImage image = getTileImage(tile.tileId, primaryTileset, secondaryTileset);
|
||||
if (image.isNull() || image.sizeInBytes() < Tile::numPixels())
|
||||
continue;
|
||||
|
||||
const uchar * pixels = image.constBits();
|
||||
for (int i = 0; i < Tile::numPixels(); i++) {
|
||||
auto it = unusedColors.constFind(pixels[i]);
|
||||
if (it != unusedColors.constEnd()) {
|
||||
unusedColors.erase(it);
|
||||
if (unusedColors.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return unusedColors;
|
||||
}
|
||||
|
||||
// Returns the list of metatile IDs representing all the metatiles in this tileset that use the specified color ID.
|
||||
QList<uint16_t> Tileset::findMetatilesUsingColor(int paletteId, int colorId, const Tileset *pairedTileset) const {
|
||||
const Tileset *primaryTileset = this->is_secondary ? pairedTileset : this;
|
||||
const Tileset *secondaryTileset = this->is_secondary ? this : pairedTileset;
|
||||
QSet<uint16_t> metatileIdSet;
|
||||
QHash<uint16_t, bool> tileContainsColor;
|
||||
uint16_t metatileIdBase = firstMetatileId();
|
||||
for (int i = 0; i < m_metatiles.length(); i++) {
|
||||
uint16_t metatileId = i + metatileIdBase;
|
||||
for (const auto &tile : m_metatiles.at(i)->tiles) {
|
||||
if (tile.palette != paletteId)
|
||||
continue;
|
||||
|
||||
// Save time on tiles we've already inspected by getting the cached result.
|
||||
auto tileIt = tileContainsColor.constFind(tile.tileId);
|
||||
if (tileIt != tileContainsColor.constEnd()) {
|
||||
if (tileIt.value()) metatileIdSet.insert(metatileId);
|
||||
continue;
|
||||
}
|
||||
tileContainsColor[tile.tileId] = false;
|
||||
|
||||
QImage image = getTileImage(tile.tileId, primaryTileset, secondaryTileset);
|
||||
if (image.isNull() || image.sizeInBytes() < Tile::numPixels())
|
||||
continue;
|
||||
|
||||
const uchar * pixels = image.constBits();
|
||||
for (int j = 0; j < Tile::numPixels(); j++) {
|
||||
if (pixels[j] == colorId) {
|
||||
metatileIdSet.insert(metatileId);
|
||||
tileContainsColor[tile.tileId] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QList<uint16_t> metatileIds(metatileIdSet.constBegin(), metatileIdSet.constEnd());
|
||||
std::sort(metatileIds.begin(), metatileIds.end());
|
||||
return metatileIds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,3 +112,16 @@ void Util::setErrorStylesheet(QLineEdit *lineEdit, bool isError) {
|
|||
static const QString stylesheet = QStringLiteral("QLineEdit { background-color: rgba(255, 0, 0, 25%) }");
|
||||
lineEdit->setStyleSheet(isError ? stylesheet : "");
|
||||
}
|
||||
|
||||
void Util::show(QWidget *w) {
|
||||
if (!w) return;
|
||||
|
||||
if (!w->isVisible()) {
|
||||
w->show();
|
||||
} else if (w->isMinimized()) {
|
||||
w->showNormal();
|
||||
} else {
|
||||
w->raise();
|
||||
w->activateWindow();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2060,12 +2060,6 @@ void Editor::updateCustomMapAttributes()
|
|||
map->modify();
|
||||
}
|
||||
|
||||
Tileset* Editor::getCurrentMapPrimaryTileset()
|
||||
{
|
||||
QString tilesetLabel = this->layout->tileset_primary_label;
|
||||
return project->getTileset(tilesetLabel);
|
||||
}
|
||||
|
||||
void Editor::redrawAllEvents() {
|
||||
if (this->map) redrawEvents(this->map->getEvents());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ void MainWindow::checkForUpdates(bool requestedByUser) {
|
|||
|
||||
|
||||
if (requestedByUser) {
|
||||
openSubWindow(this->updatePromoter);
|
||||
Util::show(this->updatePromoter);
|
||||
} else {
|
||||
// This is an automatic update check. Only run if we haven't done one in the last 5 minutes
|
||||
QDateTime lastCheck = porymapConfig.lastUpdateCheckTime;
|
||||
|
|
@ -419,7 +419,7 @@ void MainWindow::initEditor() {
|
|||
}
|
||||
|
||||
void MainWindow::openEditHistory() {
|
||||
openSubWindow(this->undoView);
|
||||
Util::show(this->undoView);
|
||||
}
|
||||
|
||||
void MainWindow::initMiscHeapObjects() {
|
||||
|
|
@ -936,19 +936,6 @@ void MainWindow::refreshRecentProjectsMenu() {
|
|||
clearAction->setEnabled(!recentProjects.isEmpty());
|
||||
}
|
||||
|
||||
void MainWindow::openSubWindow(QWidget * window) {
|
||||
if (!window) return;
|
||||
|
||||
if (!window->isVisible()) {
|
||||
window->show();
|
||||
} else if (window->isMinimized()) {
|
||||
window->showNormal();
|
||||
} else {
|
||||
window->raise();
|
||||
window->activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showFileWatcherWarning() {
|
||||
if (!porymapConfig.monitorFiles || !isProjectOpen())
|
||||
return;
|
||||
|
|
@ -2266,7 +2253,7 @@ void MainWindow::on_actionGrid_Settings_triggered() {
|
|||
connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::updateMapGrid);
|
||||
connect(this->gridSettingsDialog, &GridSettingsDialog::accepted, [this] { porymapConfig.gridSettings = this->editor->gridSettings; });
|
||||
}
|
||||
openSubWindow(this->gridSettingsDialog);
|
||||
Util::show(this->gridSettingsDialog);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionShortcuts_triggered()
|
||||
|
|
@ -2274,7 +2261,7 @@ void MainWindow::on_actionShortcuts_triggered()
|
|||
if (!shortcutsEditor)
|
||||
initShortcutsEditor();
|
||||
|
||||
openSubWindow(shortcutsEditor);
|
||||
Util::show(shortcutsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::initShortcutsEditor() {
|
||||
|
|
@ -2698,7 +2685,7 @@ void MainWindow::showExportMapImageWindow(ImageExporterMode mode) {
|
|||
}
|
||||
}
|
||||
|
||||
openSubWindow(this->mapImageExporter);
|
||||
Util::show(this->mapImageExporter);
|
||||
}
|
||||
|
||||
void MainWindow::on_pushButton_AddConnection_clicked() {
|
||||
|
|
@ -2726,7 +2713,7 @@ void MainWindow::on_pushButton_SummaryChart_clicked() {
|
|||
connect(this->editor, &Editor::wildMonTableClosed, this->wildMonChart, &WildMonChart::clearTable);
|
||||
connect(this->editor, &Editor::wildMonTableEdited, this->wildMonChart, &WildMonChart::refresh);
|
||||
}
|
||||
openSubWindow(this->wildMonChart);
|
||||
Util::show(this->wildMonChart);
|
||||
}
|
||||
|
||||
void MainWindow::on_toolButton_WildMonSearch_clicked() {
|
||||
|
|
@ -2735,7 +2722,7 @@ void MainWindow::on_toolButton_WildMonSearch_clicked() {
|
|||
connect(this->wildMonSearch, &WildMonSearch::openWildMonTableRequested, this, &MainWindow::openWildMonTable);
|
||||
connect(this->editor, &Editor::wildMonTableEdited, this->wildMonSearch, &WildMonSearch::refresh);
|
||||
}
|
||||
openSubWindow(this->wildMonSearch);
|
||||
Util::show(this->wildMonSearch);
|
||||
}
|
||||
|
||||
void MainWindow::openWildMonTable(const QString &mapName, const QString &groupName, const QString &fieldName) {
|
||||
|
|
@ -2843,7 +2830,7 @@ void MainWindow::on_actionTileset_Editor_triggered()
|
|||
initTilesetEditor();
|
||||
}
|
||||
|
||||
openSubWindow(this->tilesetEditor);
|
||||
Util::show(this->tilesetEditor);
|
||||
|
||||
MetatileSelection selection = this->editor->metatile_selector_item->getMetatileSelection();
|
||||
if (!selection.metatileItems.isEmpty()) {
|
||||
|
|
@ -2895,7 +2882,7 @@ void MainWindow::on_actionAbout_Porymap_triggered()
|
|||
{
|
||||
if (!this->aboutWindow)
|
||||
this->aboutWindow = new AboutPorymap(this);
|
||||
openSubWindow(this->aboutWindow);
|
||||
Util::show(this->aboutWindow);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionOpen_Log_File_triggered() {
|
||||
|
|
@ -2927,7 +2914,7 @@ void MainWindow::on_actionPreferences_triggered() {
|
|||
connect(preferenceEditor, &PreferenceEditor::reloadProjectRequested, this, &MainWindow::on_action_Reload_Project_triggered);
|
||||
}
|
||||
|
||||
openSubWindow(preferenceEditor);
|
||||
Util::show(preferenceEditor);
|
||||
}
|
||||
|
||||
void MainWindow::togglePreferenceSpecificUi() {
|
||||
|
|
@ -2944,7 +2931,7 @@ void MainWindow::openProjectSettingsEditor(int tab) {
|
|||
this, &MainWindow::on_action_Reload_Project_triggered);
|
||||
}
|
||||
this->projectSettingsEditor->setTab(tab);
|
||||
openSubWindow(this->projectSettingsEditor);
|
||||
Util::show(this->projectSettingsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionProject_Settings_triggered() {
|
||||
|
|
@ -2982,7 +2969,7 @@ void MainWindow::on_actionCustom_Scripts_triggered() {
|
|||
if (!this->customScriptsEditor)
|
||||
initCustomScriptsEditor();
|
||||
|
||||
openSubWindow(this->customScriptsEditor);
|
||||
Util::show(this->customScriptsEditor);
|
||||
}
|
||||
|
||||
void MainWindow::initCustomScriptsEditor() {
|
||||
|
|
@ -3062,7 +3049,7 @@ void MainWindow::on_actionRegion_Map_Editor_triggered() {
|
|||
}
|
||||
}
|
||||
|
||||
openSubWindow(this->regionMapEditor);
|
||||
Util::show(this->regionMapEditor);
|
||||
}
|
||||
|
||||
void MainWindow::on_pushButton_CreatePrefab_clicked() {
|
||||
|
|
@ -3120,7 +3107,7 @@ bool MainWindow::closeSupplementaryWindows() {
|
|||
if (widget != this && widget->isWindow()) {
|
||||
// Make sure the window is raised and activated before closing in case it has a confirmation prompt.
|
||||
if (widget->isVisible()) {
|
||||
openSubWindow(widget);
|
||||
Util::show(widget);
|
||||
}
|
||||
if (!widget->close()) {
|
||||
QString message = QStringLiteral("Aborted project close");
|
||||
|
|
|
|||
|
|
@ -186,6 +186,8 @@ bool Project::load() {
|
|||
this->parser.setUpdatesSplashScreen(true);
|
||||
resetFileWatcher();
|
||||
resetFileCache();
|
||||
QPixmapCache::clear();
|
||||
|
||||
this->disabledSettingsNames.clear();
|
||||
bool success = readGlobalConstants()
|
||||
&& readMapLayouts()
|
||||
|
|
@ -273,6 +275,17 @@ void Project::clearTilesetCache() {
|
|||
this->tilesetCache.clear();
|
||||
}
|
||||
|
||||
void Project::cacheTileset(const QString &name, Tileset *tileset) {
|
||||
auto it = this->tilesetCache.constFind(name);
|
||||
if (it != this->tilesetCache.constEnd() && it.value() && tileset != it.value()) {
|
||||
// Callers of this function should ensure this doesn't happen,
|
||||
// but in case it does we should avoid leaking memory.
|
||||
logWarn(QString("New tileset %1 overwrote existing tileset.").arg(name));
|
||||
delete it.value();
|
||||
}
|
||||
this->tilesetCache.insert(name, tileset);
|
||||
}
|
||||
|
||||
Map* Project::loadMap(const QString &mapName) {
|
||||
if (mapName == getDynamicMapName()) {
|
||||
// Silently ignored, caller is expected to handle this if they want this to be an error.
|
||||
|
|
@ -1180,7 +1193,21 @@ bool Project::loadLayoutTilesets(Layout *layout) {
|
|||
return layout->tileset_primary && layout->tileset_secondary;
|
||||
}
|
||||
|
||||
Tileset* Project::loadTileset(QString label, Tileset *tileset) {
|
||||
Tileset* Project::getTileset(const QString &label, bool forceLoad) {
|
||||
Tileset *tileset = nullptr;
|
||||
|
||||
auto it = this->tilesetCache.constFind(label);
|
||||
if (it != this->tilesetCache.constEnd()) {
|
||||
tileset = it.value();
|
||||
if (!forceLoad) {
|
||||
return tileset;
|
||||
}
|
||||
} else {
|
||||
// Create a cache entry even if we don't end up loading the tileset successfully.
|
||||
// This will prevent repeated file reads if the tileset fails to load.
|
||||
cacheTileset(label, nullptr);
|
||||
}
|
||||
|
||||
auto memberMap = Tileset::getHeaderMemberMap(this->usingAsmTilesets);
|
||||
if (this->usingAsmTilesets) {
|
||||
// Read asm tileset header. Backwards compatibility
|
||||
|
|
@ -1225,7 +1252,7 @@ Tileset* Project::loadTileset(QString label, Tileset *tileset) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
tilesetCache.insert(label, tileset);
|
||||
cacheTileset(tileset->name, tileset);
|
||||
return tileset;
|
||||
}
|
||||
|
||||
|
|
@ -1517,7 +1544,7 @@ void Project::readTilesetPaths(Tileset* tileset) {
|
|||
tileset->metatile_attrs_path = defaultPath + "/metatile_attributes.bin";
|
||||
if (tileset->palettePaths.isEmpty()) {
|
||||
QString palettes_dir_path = defaultPath + "/palettes/";
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < Tileset::maxPalettes(); i++) {
|
||||
tileset->palettePaths.append(palettes_dir_path + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".pal");
|
||||
}
|
||||
}
|
||||
|
|
@ -1579,9 +1606,9 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa
|
|||
}
|
||||
|
||||
// Create default palettes
|
||||
for(int i = 0; i < 16; ++i) {
|
||||
for(int i = 0; i < Tileset::maxPalettes(); ++i) {
|
||||
QList<QRgb> currentPal;
|
||||
for(int i = 0; i < 16;++i) {
|
||||
for(int i = 0; i < Tileset::numColorsPerPalette();++i) {
|
||||
currentPal.append(qRgb(0,0,0));
|
||||
}
|
||||
tileset->palettes.append(currentPal);
|
||||
|
|
@ -1617,15 +1644,14 @@ Tileset *Project::createNewTileset(QString name, bool secondary, bool checkerboa
|
|||
metatilesFilepath.append(projectConfig.getFilePath(ProjectFilePath::tilesets_metatiles));
|
||||
}
|
||||
ignoreWatchedFilesTemporarily({headersFilepath, graphicsFilepath, metatilesFilepath});
|
||||
name = Tileset::stripPrefix(name);
|
||||
tileset->appendToHeaders(headersFilepath, name, this->usingAsmTilesets);
|
||||
tileset->appendToGraphics(graphicsFilepath, name, this->usingAsmTilesets);
|
||||
tileset->appendToMetatiles(metatilesFilepath, name, this->usingAsmTilesets);
|
||||
QString baseName = Tileset::stripPrefix(name);
|
||||
tileset->appendToHeaders(headersFilepath, baseName, this->usingAsmTilesets);
|
||||
tileset->appendToGraphics(graphicsFilepath, baseName, this->usingAsmTilesets);
|
||||
tileset->appendToMetatiles(metatilesFilepath, baseName, this->usingAsmTilesets);
|
||||
|
||||
tileset->save();
|
||||
|
||||
this->tilesetCache.insert(tileset->name, tileset);
|
||||
|
||||
cacheTileset(tileset->name, tileset);
|
||||
emit tilesetCreated(tileset);
|
||||
return tileset;
|
||||
}
|
||||
|
|
@ -1680,20 +1706,6 @@ void Project::loadTilesetMetatileLabels(Tileset* tileset) {
|
|||
}
|
||||
}
|
||||
|
||||
Tileset* Project::getTileset(QString label, bool forceLoad) {
|
||||
Tileset *existingTileset = nullptr;
|
||||
if (tilesetCache.contains(label)) {
|
||||
existingTileset = tilesetCache.value(label);
|
||||
}
|
||||
|
||||
if (existingTileset && !forceLoad) {
|
||||
return existingTileset;
|
||||
} else {
|
||||
Tileset *tileset = loadTileset(label, existingTileset);
|
||||
return tileset;
|
||||
}
|
||||
}
|
||||
|
||||
bool Project::saveTextFile(const QString &path, const QString &text) {
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
|
|
@ -2266,6 +2278,7 @@ bool Project::readTilesetLabels() {
|
|||
this->primaryTilesetLabels.clear();
|
||||
this->secondaryTilesetLabels.clear();
|
||||
this->tilesetLabelsOrdered.clear();
|
||||
clearTilesetCache();
|
||||
|
||||
QString filename = projectConfig.getFilePath(ProjectFilePath::tilesets_headers);
|
||||
QFileInfo fileInfo(this->root + "/" + filename);
|
||||
|
|
@ -2347,7 +2360,7 @@ bool Project::readFieldmapProperties() {
|
|||
logWarn(QString("Value for '%1' not found. Using default (%2) instead.").arg(name).arg(*dest));
|
||||
}
|
||||
};
|
||||
loadDefine(numPalsTotalName, &Project::num_pals_total, 2, INT_MAX); // In reality the max would be 16, but as far as Porymap is concerned it doesn't matter.
|
||||
loadDefine(numPalsTotalName, &Project::num_pals_total, 2, Tileset::maxPalettes());
|
||||
loadDefine(numTilesTotalName, &Project::num_tiles_total, 2, 1024); // 1024 is fixed because we store tile IDs in a 10-bit field.
|
||||
loadDefine(numPalsPrimaryName, &Project::num_pals_primary, 1, Project::num_pals_total - 1);
|
||||
loadDefine(numTilesPrimaryName, &Project::num_tiles_primary, 1, Project::num_tiles_total - 1);
|
||||
|
|
@ -3518,3 +3531,18 @@ bool Project::hasUnsavedChanges() {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Searches the project's map layouts to find the names of the tilesets that the provided tileset gets paired with.
|
||||
QSet<QString> Project::getPairedTilesetLabels(const Tileset *tileset) const {
|
||||
QSet<QString> pairedLabels;
|
||||
for (const auto &layout : this->mapLayouts) {
|
||||
if (tileset->is_secondary) {
|
||||
if (layout->tileset_secondary_label == tileset->name) {
|
||||
pairedLabels.insert(layout->tileset_primary_label);
|
||||
}
|
||||
} else if (layout->tileset_primary_label == tileset->name) {
|
||||
pairedLabels.insert(layout->tileset_secondary_label);
|
||||
}
|
||||
}
|
||||
return pairedLabels;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,10 +346,7 @@ void MainWindow::setTilesetPalette(Tileset *tileset, int paletteIndex, QList<QLi
|
|||
return;
|
||||
if (paletteIndex >= tileset->palettes.size())
|
||||
return;
|
||||
if (colors.size() != 16)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < qMin(colors.length(), Tileset::numColorsPerPalette()); i++) {
|
||||
if (colors[i].size() != 3)
|
||||
continue;
|
||||
tileset->palettes[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]);
|
||||
|
|
@ -457,10 +454,7 @@ void MainWindow::setTilesetPalettePreview(Tileset *tileset, int paletteIndex, QL
|
|||
return;
|
||||
if (paletteIndex >= tileset->palettePreviews.size())
|
||||
return;
|
||||
if (colors.size() != 16)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < qMin(colors.length(), Tileset::numColorsPerPalette()); i++) {
|
||||
if (colors[i].size() != 3)
|
||||
continue;
|
||||
tileset->palettePreviews[paletteIndex][i] = qRgb(colors[i][0], colors[i][1], colors[i][2]);
|
||||
|
|
@ -798,14 +792,13 @@ QJSValue MainWindow::getTilePixels(int tileId) {
|
|||
if (tileId < 0 || !this->editor || !this->editor->layout)
|
||||
return QJSValue();
|
||||
|
||||
const int numPixels = Tile::pixelWidth() * Tile::pixelHeight();
|
||||
QImage tileImage = getTileImage(tileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
|
||||
if (tileImage.isNull() || tileImage.sizeInBytes() < numPixels)
|
||||
if (tileImage.isNull() || tileImage.sizeInBytes() < Tile::numPixels())
|
||||
return QJSValue();
|
||||
|
||||
const uchar * pixels = tileImage.constBits();
|
||||
QJSValue pixelArray = Scripting::getEngine()->newArray(numPixels);
|
||||
for (int i = 0; i < numPixels; i++) {
|
||||
QJSValue pixelArray = Scripting::getEngine()->newArray(Tile::numPixels());
|
||||
for (int i = 0; i < Tile::numPixels(); i++) {
|
||||
pixelArray.setProperty(i, pixels[i]);
|
||||
}
|
||||
return pixelArray;
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ QImage getCollisionMetatileImage(int collision, int elevation) {
|
|||
return image ? *image : QImage();
|
||||
}
|
||||
|
||||
QImage getMetatileImage(uint16_t metatileId, Layout *layout, bool useTruePalettes) {
|
||||
QImage getMetatileImage(uint16_t metatileId, const Layout *layout, bool useTruePalettes) {
|
||||
Metatile* metatile = Tileset::getMetatile(metatileId,
|
||||
layout ? layout->tileset_primary : nullptr,
|
||||
layout ? layout->tileset_secondary : nullptr);
|
||||
return getMetatileImage(metatile, layout, useTruePalettes);
|
||||
}
|
||||
|
||||
QImage getMetatileImage(Metatile *metatile, Layout *layout, bool useTruePalettes) {
|
||||
QImage getMetatileImage(const Metatile *metatile, const Layout *layout, bool useTruePalettes) {
|
||||
if (!layout) {
|
||||
return getMetatileImage(metatile, nullptr, nullptr, {}, {}, useTruePalettes);
|
||||
}
|
||||
|
|
@ -33,8 +33,8 @@ QImage getMetatileImage(Metatile *metatile, Layout *layout, bool useTruePalettes
|
|||
|
||||
QImage getMetatileImage(
|
||||
uint16_t metatileId,
|
||||
Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity,
|
||||
bool useTruePalettes)
|
||||
|
|
@ -54,9 +54,9 @@ QColor getInvalidImageColor() {
|
|||
}
|
||||
|
||||
QImage getMetatileImage(
|
||||
Metatile *metatile,
|
||||
Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
const Metatile *metatile,
|
||||
const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity,
|
||||
bool useTruePalettes)
|
||||
|
|
@ -141,12 +141,12 @@ QImage getMetatileImage(
|
|||
return metatileImage;
|
||||
}
|
||||
|
||||
QImage getTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
Tileset *tileset = Tileset::getTileTileset(tileId, primaryTileset, secondaryTileset);
|
||||
QImage getTileImage(uint16_t tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
const Tileset *tileset = Tileset::getTileTileset(tileId, primaryTileset, secondaryTileset);
|
||||
return tileset ? tileset->tileImage(tileId) : QImage();
|
||||
}
|
||||
|
||||
QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, const QList<QRgb> &palette) {
|
||||
QImage getColoredTileImage(uint16_t tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset, const QList<QRgb> &palette) {
|
||||
QImage tileImage = getTileImage(tileId, primaryTileset, secondaryTileset);
|
||||
if (tileImage.isNull()) {
|
||||
// Some tiles specify tile IDs or palette IDs that are outside the valid range.
|
||||
|
|
@ -155,19 +155,19 @@ QImage getColoredTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *se
|
|||
tileImage = QImage(Tile::pixelSize(), QImage::Format_RGBA8888);
|
||||
tileImage.fill(getInvalidImageColor());
|
||||
} else {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int i = 0; i < Tileset::numColorsPerPalette(); i++) {
|
||||
tileImage.setColor(i, palette.value(i, getInvalidImageColor().rgb()));
|
||||
}
|
||||
}
|
||||
return tileImage;
|
||||
}
|
||||
|
||||
QImage getPalettedTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, bool useTruePalettes) {
|
||||
QImage getPalettedTileImage(uint16_t tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset, int paletteId, bool useTruePalettes) {
|
||||
QList<QRgb> palette = Tileset::getPalette(paletteId, primaryTileset, secondaryTileset, useTruePalettes);
|
||||
return getColoredTileImage(tileId, primaryTileset, secondaryTileset, palette);
|
||||
}
|
||||
|
||||
QImage getGreyscaleTileImage(uint16_t tileId, Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
QImage getGreyscaleTileImage(uint16_t tileId, const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
return getColoredTileImage(tileId, primaryTileset, secondaryTileset, greyscalePalette);
|
||||
}
|
||||
|
||||
|
|
@ -181,8 +181,8 @@ void flattenTo4bppImage(QImage * image) {
|
|||
}
|
||||
|
||||
// Constructs a grid image of the metatiles in the specified ID range.
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
QImage getMetatileSheetImage(const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
uint16_t metatileIdStart,
|
||||
uint16_t metatileIdEnd,
|
||||
int numMetatilesWide,
|
||||
|
|
@ -219,15 +219,15 @@ QImage getMetatileSheetImage(Tileset *primaryTileset,
|
|||
// Constructs a grid image of the metatiles in the primary and secondary tileset,
|
||||
// rounding as necessary to keep the two tilesets on separate rows.
|
||||
// The unused metatiles (if any) between the primary and secondary tilesets are skipped.
|
||||
QImage getMetatileSheetImage(Tileset *primaryTileset,
|
||||
Tileset *secondaryTileset,
|
||||
QImage getMetatileSheetImage(const Tileset *primaryTileset,
|
||||
const Tileset *secondaryTileset,
|
||||
int numMetatilesWide,
|
||||
const QList<int> &layerOrder,
|
||||
const QList<float> &layerOpacity,
|
||||
const QSize &metatileSize,
|
||||
bool useTruePalettes)
|
||||
{
|
||||
auto createSheetImage = [=](uint16_t start, Tileset *tileset) {
|
||||
auto createSheetImage = [=](uint16_t start, const Tileset *tileset) {
|
||||
uint16_t end = start;
|
||||
if (tileset) {
|
||||
if (tileset->numMetatiles() == 0)
|
||||
|
|
@ -259,7 +259,7 @@ QImage getMetatileSheetImage(Tileset *primaryTileset,
|
|||
return image;
|
||||
}
|
||||
|
||||
QImage getMetatileSheetImage(Layout *layout, int numMetatilesWide, bool useTruePalettes) {
|
||||
QImage getMetatileSheetImage(const Layout *layout, int numMetatilesWide, bool useTruePalettes) {
|
||||
if (!layout)
|
||||
return QImage();
|
||||
return getMetatileSheetImage(layout->tileset_primary,
|
||||
|
|
|
|||
207
src/ui/palettecolorsearch.cpp
Normal file
207
src/ui/palettecolorsearch.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#include "palettecolorsearch.h"
|
||||
#include "ui_palettecolorsearch.h"
|
||||
#include "project.h"
|
||||
#include "tileset.h"
|
||||
#include "imageproviders.h"
|
||||
#include "eventfilters.h"
|
||||
#include "log.h"
|
||||
#include "numericsorttableitem.h"
|
||||
|
||||
enum ResultsDataRole {
|
||||
PairedTilesetName = Qt::UserRole,
|
||||
};
|
||||
|
||||
PaletteColorSearch::PaletteColorSearch(Project *project, const Tileset *primaryTileset, const Tileset *secondaryTileset, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PaletteColorSearch),
|
||||
m_project(project),
|
||||
m_primaryTileset(primaryTileset),
|
||||
m_secondaryTileset(secondaryTileset)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->buttonBox->setVisible(isModal());
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::close);
|
||||
|
||||
// Rather than try to keep track of metatile/tile changes that affect which colors are used,
|
||||
// we'll just refresh when the window is activated.
|
||||
ActiveWindowFilter *filter = new ActiveWindowFilter(this);
|
||||
connect(filter, &ActiveWindowFilter::activated, this, &PaletteColorSearch::refresh);
|
||||
this->installEventFilter(filter);
|
||||
|
||||
ui->spinBox_ColorId->setRange(0, Tileset::numColorsPerPalette() - 1);
|
||||
connect(ui->spinBox_ColorId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteColorSearch::updateResults);
|
||||
|
||||
ui->spinBox_PaletteId->setRange(0, Project::getNumPalettesTotal() - 1);
|
||||
connect(ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteColorSearch::updateResults);
|
||||
connect(ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteColorSearch::paletteIdChanged);
|
||||
|
||||
// Set up table header
|
||||
static const QStringList labels = {"Tileset", "Metatile"};
|
||||
ui->table_Results->setHorizontalHeaderLabels(labels);
|
||||
ui->table_Results->horizontalHeader()->setSectionResizeMode(ResultsColumn::TilesetName, QHeaderView::ResizeToContents);
|
||||
ui->table_Results->horizontalHeader()->setSectionResizeMode(ResultsColumn::Metatile, QHeaderView::Stretch);
|
||||
|
||||
// Table is read-only
|
||||
ui->table_Results->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ui->table_Results->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
|
||||
connect(ui->table_Results, &QTableWidget::cellDoubleClicked, this, &PaletteColorSearch::cellDoubleClicked);
|
||||
}
|
||||
|
||||
PaletteColorSearch::~PaletteColorSearch() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PaletteColorSearch::setPaletteId(int paletteId) {
|
||||
ui->spinBox_PaletteId->setValue(paletteId);
|
||||
}
|
||||
|
||||
int PaletteColorSearch::currentPaletteId() const {
|
||||
return ui->spinBox_PaletteId->value();
|
||||
}
|
||||
|
||||
void PaletteColorSearch::setColorId(int colorId) {
|
||||
ui->spinBox_ColorId->setValue(colorId);
|
||||
}
|
||||
|
||||
int PaletteColorSearch::currentColorId() const {
|
||||
return ui->spinBox_ColorId->value();
|
||||
}
|
||||
|
||||
void PaletteColorSearch::setTilesets(const Tileset *primaryTileset, const Tileset *secondaryTileset) {
|
||||
m_primaryTileset = primaryTileset;
|
||||
m_secondaryTileset = secondaryTileset;
|
||||
refresh();
|
||||
}
|
||||
|
||||
const Tileset* PaletteColorSearch::currentTileset() const {
|
||||
return Tileset::getPaletteTileset(currentPaletteId(), m_primaryTileset, m_secondaryTileset);
|
||||
}
|
||||
|
||||
void PaletteColorSearch::addTableEntry(const RowData &rowData) {
|
||||
int row = ui->table_Results->rowCount();
|
||||
ui->table_Results->insertRow(row);
|
||||
|
||||
auto tilesetNameItem = new NumericSortTableItem(rowData.tilesetName);
|
||||
tilesetNameItem->setData(ResultsDataRole::PairedTilesetName, rowData.pairedTilesetName);
|
||||
|
||||
ui->table_Results->setItem(row, ResultsColumn::TilesetName, tilesetNameItem);
|
||||
ui->table_Results->setItem(row, ResultsColumn::Metatile, new QTableWidgetItem(rowData.metatileIcon, rowData.metatileId));
|
||||
}
|
||||
|
||||
QList<PaletteColorSearch::RowData> PaletteColorSearch::search(int colorId) const {
|
||||
QList<RowData> results;
|
||||
|
||||
// Check our current tilesets for color usage.
|
||||
results.append(search(colorId, m_primaryTileset, m_secondaryTileset));
|
||||
results.append(search(colorId, m_secondaryTileset, m_primaryTileset));
|
||||
|
||||
// The current palette comes from either the primary or secondary tileset.
|
||||
// We need to check all the other tilesets that are paired with the tileset that owns this palette.
|
||||
const Tileset *paletteTileset = currentTileset();
|
||||
QSet<QString> tilesetsToSearch = m_project->getPairedTilesetLabels(paletteTileset);
|
||||
|
||||
// We exclude the currently-loaded pair (we already checked them, and because they're being
|
||||
// edited in the Tileset Editor they may differ from their copies saved in the layout).
|
||||
tilesetsToSearch.remove(m_primaryTileset->name);
|
||||
tilesetsToSearch.remove(m_secondaryTileset->name);
|
||||
|
||||
for (const auto &label : tilesetsToSearch) {
|
||||
Tileset *searchTileset = m_project->getTileset(label);
|
||||
if (searchTileset) {
|
||||
results.append(search(colorId, searchTileset, paletteTileset));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<PaletteColorSearch::RowData> PaletteColorSearch::search(int colorId, const Tileset *tileset, const Tileset *pairedTileset) const {
|
||||
QList<RowData> results;
|
||||
QList<uint16_t> metatileIds = tileset->findMetatilesUsingColor(currentPaletteId(), colorId, pairedTileset);
|
||||
auto primaryTileset = tileset->is_secondary ? pairedTileset : tileset;
|
||||
auto secondaryTileset = tileset->is_secondary ? tileset : pairedTileset;
|
||||
for (const auto &metatileId : metatileIds) {
|
||||
QImage metatileImage = getMetatileImage(metatileId, primaryTileset, secondaryTileset);
|
||||
RowData rowData = {
|
||||
.tilesetName = tileset->name,
|
||||
.pairedTilesetName = pairedTileset->name,
|
||||
.metatileId = Metatile::getMetatileIdString(metatileId),
|
||||
.metatileIcon = QIcon(QPixmap::fromImage(metatileImage)),
|
||||
};
|
||||
results.append(rowData);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void PaletteColorSearch::refresh() {
|
||||
m_resultsCache.clear();
|
||||
updateResults();
|
||||
}
|
||||
|
||||
void PaletteColorSearch::updateResults() {
|
||||
const Tileset *tileset = currentTileset();
|
||||
int paletteId = currentPaletteId();
|
||||
int colorId = currentColorId();
|
||||
|
||||
// Update color icon
|
||||
QRgb color = tileset->palettePreviews.value(paletteId).value(colorId);
|
||||
ui->frame_Color->setStyleSheet(QString("background-color: rgb(%1, %2, %3);").arg(qRed(color)).arg(qGreen(color)).arg(qBlue(color)));
|
||||
|
||||
// Update title
|
||||
ui->label_Title->setText(QString("Searching for usage of %1's palette %2.").arg(tileset->name).arg(paletteId));
|
||||
|
||||
// Update table
|
||||
ui->table_Results->clearContents();
|
||||
ui->table_Results->setRowCount(0);
|
||||
|
||||
QString cacheKey = QString("%1#%2").arg(paletteId).arg(colorId);
|
||||
auto it = m_resultsCache.constFind(cacheKey);
|
||||
bool inCache = (it != m_resultsCache.constEnd());
|
||||
const QList<RowData> results = inCache ? it.value() : search(colorId);
|
||||
|
||||
if (results.isEmpty()) {
|
||||
static const RowData noResults = {
|
||||
.tilesetName = QStringLiteral("This color is unused."),
|
||||
.pairedTilesetName = "",
|
||||
.metatileId = QStringLiteral("--"),
|
||||
.metatileIcon = QIcon(),
|
||||
};
|
||||
addTableEntry(noResults);
|
||||
} else {
|
||||
for (const auto &entry : results) {
|
||||
addTableEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
ui->table_Results->sortByColumn(ResultsColumn::TilesetName, Qt::AscendingOrder);
|
||||
|
||||
if (!inCache) m_resultsCache.insert(cacheKey, results);
|
||||
}
|
||||
|
||||
// Double-clicking row data selects the corresponding metatile in the Tileset Editor.
|
||||
void PaletteColorSearch::cellDoubleClicked(int row, int) {
|
||||
auto tilesetNameItem = ui->table_Results->item(row, ResultsColumn::TilesetName);
|
||||
auto metatileItem = ui->table_Results->item(row, ResultsColumn::Metatile);
|
||||
if (!tilesetNameItem || !metatileItem)
|
||||
return;
|
||||
|
||||
// The Tileset Editor (as of writing) has no way to change the selected tilesets independently of
|
||||
// the main editor's layout, so if the metatile is not in the current tileset we do nothing.
|
||||
// To compare the tileset names, rather than sort out which was the primary or secondary we
|
||||
// just make sure it's the same set of names.
|
||||
QSet<QString> currentTilesets;
|
||||
currentTilesets.insert(m_primaryTileset->name);
|
||||
currentTilesets.insert(m_secondaryTileset->name);
|
||||
|
||||
QSet<QString> metatileTilesets;
|
||||
metatileTilesets.insert(tilesetNameItem->text());
|
||||
metatileTilesets.insert(tilesetNameItem->data(ResultsDataRole::PairedTilesetName).toString());
|
||||
if (currentTilesets != metatileTilesets)
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
uint16_t metatileId = metatileItem->text().toUInt(&ok, 0);
|
||||
if (ok) emit metatileSelected(metatileId);
|
||||
}
|
||||
|
|
@ -5,23 +5,23 @@
|
|||
#include "log.h"
|
||||
#include "filedialog.h"
|
||||
#include "message.h"
|
||||
#include "eventfilters.h"
|
||||
#include "utility.h"
|
||||
|
||||
|
||||
PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::PaletteEditor)
|
||||
ui(new Ui::PaletteEditor),
|
||||
project(project),
|
||||
primaryTileset(primaryTileset),
|
||||
secondaryTileset(secondaryTileset)
|
||||
{
|
||||
this->project = project;
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
this->ui->setupUi(this);
|
||||
this->ui->spinBox_PaletteId->setMinimum(0);
|
||||
this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
|
||||
|
||||
this->colorInputs.clear();
|
||||
const int numColorsPerRow = 4;
|
||||
for (int i = 0; i < this->numColors; i++) {
|
||||
auto colorInput = new ColorInputWidget(QString("Color %1").arg(i));
|
||||
auto colorInput = new ColorInputWidget;
|
||||
connect(colorInput, &ColorInputWidget::colorChanged, [this, i](QRgb color) { setRgb(i, color); });
|
||||
connect(colorInput, &ColorInputWidget::editingFinished, [this] { commitEditHistory(); });
|
||||
this->colorInputs.append(colorInput);
|
||||
|
|
@ -42,23 +42,48 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
|
|||
setBitDepth(bitDepth);
|
||||
|
||||
// Connect bit depth buttons
|
||||
connect(this->ui->bit_depth_15, &QRadioButton::toggled, [this](bool checked){ if (checked) this->setBitDepth(15); });
|
||||
connect(this->ui->bit_depth_24, &QRadioButton::toggled, [this](bool checked){ if (checked) this->setBitDepth(24); });
|
||||
connect(this->ui->bit_depth_15, &QRadioButton::toggled, [this](bool checked){ if (checked) setBitDepth(15); });
|
||||
connect(this->ui->bit_depth_24, &QRadioButton::toggled, [this](bool checked){ if (checked) setBitDepth(24); });
|
||||
|
||||
this->setPaletteId(paletteId);
|
||||
this->commitEditHistory();
|
||||
this->restoreWindowState();
|
||||
this->ui->actionShow_Unused_Colors->setChecked(porymapConfig.showPaletteEditorUnusedColors);
|
||||
connect(this->ui->actionShow_Unused_Colors, &QAction::toggled, this, &PaletteEditor::setColorInputTitles);
|
||||
|
||||
connect(this->ui->toolButton_ColorSearch, &QToolButton::clicked, this, &PaletteEditor::openColorSearch);
|
||||
connect(this->ui->actionFind_Color_Usage, &QAction::triggered, this, &PaletteEditor::openColorSearch);
|
||||
|
||||
// Rather than try to keep track of metatile/tile changes that affect which colors are used,
|
||||
// we'll just refresh when the window is activated.
|
||||
ActiveWindowFilter *filter = new ActiveWindowFilter(this);
|
||||
connect(filter, &ActiveWindowFilter::activated, this, &PaletteEditor::invalidateCache);
|
||||
this->installEventFilter(filter);
|
||||
|
||||
this->ui->spinBox_PaletteId->setRange(0, Project::getNumPalettesTotal() - 1);
|
||||
this->ui->spinBox_PaletteId->setValue(paletteId);
|
||||
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::refreshPaletteId);
|
||||
connect(this->ui->spinBox_PaletteId, QOverload<int>::of(&QSpinBox::valueChanged), this, &PaletteEditor::changedPalette);
|
||||
|
||||
refreshPaletteId();
|
||||
restoreWindowState();
|
||||
}
|
||||
|
||||
PaletteEditor::~PaletteEditor()
|
||||
{
|
||||
PaletteEditor::~PaletteEditor() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
Tileset* PaletteEditor::getTileset(int paletteId) {
|
||||
return (paletteId < Project::getNumPalettesPrimary())
|
||||
? this->primaryTileset
|
||||
: this->secondaryTileset;
|
||||
int PaletteEditor::currentPaletteId() const {
|
||||
return ui->spinBox_PaletteId->value();
|
||||
}
|
||||
|
||||
void PaletteEditor::setPaletteId(int paletteId) {
|
||||
ui->spinBox_PaletteId->setValue(paletteId);
|
||||
}
|
||||
|
||||
bool PaletteEditor::showingUnusedColors() const {
|
||||
return ui->actionShow_Unused_Colors->isChecked();
|
||||
}
|
||||
|
||||
Tileset* PaletteEditor::getTileset(int paletteId) const {
|
||||
return Tileset::getPaletteTileset(paletteId, this->primaryTileset, this->secondaryTileset);
|
||||
}
|
||||
|
||||
void PaletteEditor::setBitDepth(int bits) {
|
||||
|
|
@ -70,62 +95,63 @@ void PaletteEditor::setBitDepth(int bits) {
|
|||
}
|
||||
|
||||
void PaletteEditor::setRgb(int colorIndex, QRgb rgb) {
|
||||
const int paletteId = this->ui->spinBox_PaletteId->value();
|
||||
|
||||
const int paletteId = currentPaletteId();
|
||||
Tileset *tileset = getTileset(paletteId);
|
||||
tileset->palettes[paletteId][colorIndex] = rgb;
|
||||
tileset->palettePreviews[paletteId][colorIndex] = rgb;
|
||||
|
||||
emit changedPaletteColor();
|
||||
}
|
||||
|
||||
void PaletteEditor::setPalette(int paletteId, const QList<QRgb> &palette) {
|
||||
Tileset *tileset = getTileset(paletteId);
|
||||
for (int i = 0; i < this->numColors; i++) {
|
||||
tileset->palettes[paletteId][i] = palette.at(i);
|
||||
tileset->palettePreviews[paletteId][i] = palette.at(i);
|
||||
tileset->palettes[paletteId][i] = palette.value(i);
|
||||
tileset->palettePreviews[paletteId][i] = palette.value(i);
|
||||
}
|
||||
refreshColorInputs();
|
||||
emit changedPaletteColor();
|
||||
}
|
||||
|
||||
void PaletteEditor::refreshColorInputs() {
|
||||
const int paletteId = ui->spinBox_PaletteId->value();
|
||||
const int paletteId = currentPaletteId();
|
||||
Tileset *tileset = getTileset(paletteId);
|
||||
for (int i = 0; i < this->numColors; i++) {
|
||||
for (int i = 0; i < this->colorInputs.length(); i++) {
|
||||
auto colorInput = this->colorInputs.at(i);
|
||||
const QSignalBlocker b(colorInput);
|
||||
colorInput->setColor(tileset->palettes.at(paletteId).at(i));
|
||||
colorInput->setColor(tileset->palettes.value(paletteId).value(i));
|
||||
}
|
||||
setColorInputTitles(showingUnusedColors());
|
||||
}
|
||||
|
||||
void PaletteEditor::setPaletteId(int paletteId) {
|
||||
const QSignalBlocker b(ui->spinBox_PaletteId);
|
||||
this->ui->spinBox_PaletteId->setValue(paletteId);
|
||||
this->refreshColorInputs();
|
||||
void PaletteEditor::refreshPaletteId() {
|
||||
refreshColorInputs();
|
||||
|
||||
int paletteId = currentPaletteId();
|
||||
if (!this->palettesHistory[paletteId].current()) {
|
||||
commitEditHistory(paletteId);
|
||||
}
|
||||
if (this->colorSearchWindow) {
|
||||
this->colorSearchWindow->setPaletteId(paletteId);
|
||||
}
|
||||
}
|
||||
|
||||
void PaletteEditor::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
|
||||
this->primaryTileset = primaryTileset;
|
||||
this->secondaryTileset = secondaryTileset;
|
||||
this->refreshColorInputs();
|
||||
}
|
||||
|
||||
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
|
||||
this->refreshColorInputs();
|
||||
if (!this->palettesHistory[paletteId].current()) {
|
||||
this->commitEditHistory(paletteId);
|
||||
invalidateCache();
|
||||
if (this->colorSearchWindow) {
|
||||
this->colorSearchWindow->setTilesets(primaryTileset, secondaryTileset);
|
||||
}
|
||||
emit this->changedPalette(paletteId);
|
||||
refreshColorInputs();
|
||||
}
|
||||
|
||||
void PaletteEditor::commitEditHistory() {
|
||||
commitEditHistory(ui->spinBox_PaletteId->value());
|
||||
commitEditHistory(currentPaletteId());
|
||||
}
|
||||
|
||||
void PaletteEditor::commitEditHistory(int paletteId) {
|
||||
QList<QRgb> colors;
|
||||
for (int i = 0; i < this->numColors; i++) {
|
||||
for (int i = 0; i < this->colorInputs.length(); i++) {
|
||||
colors.append(this->colorInputs.at(i)->color());
|
||||
}
|
||||
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
|
||||
|
|
@ -135,28 +161,25 @@ void PaletteEditor::commitEditHistory(int paletteId) {
|
|||
void PaletteEditor::restoreWindowState() {
|
||||
logInfo("Restoring palette editor geometry from previous session.");
|
||||
QMap<QString, QByteArray> geometry = porymapConfig.getPaletteEditorGeometry();
|
||||
this->restoreGeometry(geometry.value("palette_editor_geometry"));
|
||||
this->restoreState(geometry.value("palette_editor_state"));
|
||||
restoreGeometry(geometry.value("palette_editor_geometry"));
|
||||
restoreState(geometry.value("palette_editor_state"));
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionUndo_triggered()
|
||||
{
|
||||
int paletteId = this->ui->spinBox_PaletteId->value();
|
||||
void PaletteEditor::on_actionUndo_triggered() {
|
||||
int paletteId = currentPaletteId();
|
||||
PaletteHistoryItem *prev = this->palettesHistory[paletteId].back();
|
||||
if (prev)
|
||||
setPalette(paletteId, prev->colors);
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionRedo_triggered()
|
||||
{
|
||||
int paletteId = this->ui->spinBox_PaletteId->value();
|
||||
void PaletteEditor::on_actionRedo_triggered() {
|
||||
int paletteId = currentPaletteId();
|
||||
PaletteHistoryItem *next = this->palettesHistory[paletteId].next();
|
||||
if (next)
|
||||
setPalette(paletteId, next->colors);
|
||||
}
|
||||
|
||||
void PaletteEditor::on_actionImport_Palette_triggered()
|
||||
{
|
||||
void PaletteEditor::on_actionImport_Palette_triggered() {
|
||||
QString filepath = FileDialog::getOpenFileName(this, "Import Tileset Palette", "", "Palette Files (*.pal *.act *tpl *gpl)");
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
|
|
@ -171,14 +194,83 @@ void PaletteEditor::on_actionImport_Palette_triggered()
|
|||
palette.append(0);
|
||||
}
|
||||
|
||||
const int paletteId = ui->spinBox_PaletteId->value();
|
||||
const int paletteId = currentPaletteId();
|
||||
setPalette(paletteId, palette);
|
||||
commitEditHistory(paletteId);
|
||||
}
|
||||
|
||||
void PaletteEditor::openColorSearch() {
|
||||
if (!this->colorSearchWindow) {
|
||||
this->colorSearchWindow = new PaletteColorSearch(this->project, this->primaryTileset, this->secondaryTileset, this);
|
||||
this->colorSearchWindow->setPaletteId(currentPaletteId());
|
||||
connect(this->colorSearchWindow, &PaletteColorSearch::metatileSelected, this, &PaletteEditor::metatileSelected);
|
||||
connect(this->colorSearchWindow, &PaletteColorSearch::paletteIdChanged, this, &PaletteEditor::setPaletteId);
|
||||
}
|
||||
Util::show(this->colorSearchWindow);
|
||||
}
|
||||
|
||||
void PaletteEditor::invalidateCache() {
|
||||
this->unusedColorCache.clear();
|
||||
if (showingUnusedColors()) {
|
||||
setColorInputTitles(true);
|
||||
}
|
||||
}
|
||||
|
||||
QSet<int> PaletteEditor::getUnusedColorIds() {
|
||||
const int paletteId = currentPaletteId();
|
||||
|
||||
if (this->unusedColorCache.contains(paletteId)) {
|
||||
return this->unusedColorCache.value(paletteId);
|
||||
}
|
||||
this->unusedColorCache[paletteId] = {};
|
||||
|
||||
// Check our current tilesets for color usage.
|
||||
QSet<int> unusedColorIds = this->primaryTileset->getUnusedColorIds(paletteId, this->secondaryTileset);
|
||||
if (unusedColorIds.isEmpty())
|
||||
return {};
|
||||
unusedColorIds = this->secondaryTileset->getUnusedColorIds(paletteId, this->primaryTileset, unusedColorIds);
|
||||
if (unusedColorIds.isEmpty())
|
||||
return {};
|
||||
|
||||
// The current palette comes from either the primary or secondary tileset.
|
||||
// We need to check all the other tilesets that are paired with the tileset that owns this palette.
|
||||
Tileset *paletteTileset = getTileset(paletteId);
|
||||
QSet<QString> tilesetsToSearch = this->project->getPairedTilesetLabels(paletteTileset);
|
||||
|
||||
// We exclude the currently-loaded pair (we already checked them, and because they're being
|
||||
// edited in the Tileset Editor they may differ from their copies saved in the layout).
|
||||
tilesetsToSearch.remove(this->primaryTileset->name);
|
||||
tilesetsToSearch.remove(this->secondaryTileset->name);
|
||||
|
||||
for (const auto &label : tilesetsToSearch) {
|
||||
Tileset *searchTileset = this->project->getTileset(label);
|
||||
if (!searchTileset) continue;
|
||||
unusedColorIds = searchTileset->getUnusedColorIds(paletteId, paletteTileset, unusedColorIds);
|
||||
if (unusedColorIds.isEmpty())
|
||||
return {};
|
||||
}
|
||||
|
||||
this->unusedColorCache[paletteId] = unusedColorIds;
|
||||
return unusedColorIds;
|
||||
}
|
||||
|
||||
void PaletteEditor::setColorInputTitles(bool showUnused) {
|
||||
porymapConfig.showPaletteEditorUnusedColors = showUnused;
|
||||
|
||||
QSet<int> unusedColorIds = showUnused ? getUnusedColorIds() : QSet<int>();
|
||||
ui->label_AllColorsUsed->setVisible(showUnused && unusedColorIds.isEmpty());
|
||||
for (int i = 0; i < this->colorInputs.length(); i++) {
|
||||
QString title = QString("Color %1").arg(i);
|
||||
if (unusedColorIds.contains(i)) {
|
||||
title.append(QStringLiteral(" (Unused)"));
|
||||
}
|
||||
this->colorInputs.at(i)->setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
void PaletteEditor::closeEvent(QCloseEvent*) {
|
||||
porymapConfig.setPaletteEditorGeometry(
|
||||
this->saveGeometry(),
|
||||
this->saveState()
|
||||
saveGeometry(),
|
||||
saveState()
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,14 +26,23 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
|||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->spinBox_paletteSelector->setRange(0, Project::getNumPalettesTotal() - 1);
|
||||
|
||||
auto validator = new IdentifierValidator(this);
|
||||
validator->setAllowEmpty(true);
|
||||
ui->lineEdit_metatileLabel->setValidator(validator);
|
||||
|
||||
ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
|
||||
ui->actionShow_Raw_Metatile_Attributes->setChecked(porymapConfig.showTilesetEditorRawAttributes);
|
||||
|
||||
ActiveWindowFilter *filter = new ActiveWindowFilter(this);
|
||||
connect(filter, &ActiveWindowFilter::activated, this, &TilesetEditor::onWindowActivated);
|
||||
this->installEventFilter(filter);
|
||||
|
||||
setTilesets(this->layout->tileset_primary_label, this->layout->tileset_secondary_label);
|
||||
|
||||
connect(ui->checkBox_xFlip, &QCheckBox::toggled, this, &TilesetEditor::setXFlip);
|
||||
connect(ui->checkBox_yFlip, &QCheckBox::toggled, this, &TilesetEditor::setYFlip);
|
||||
|
||||
this->tileXFlip = ui->checkBox_xFlip->isChecked();
|
||||
this->tileYFlip = ui->checkBox_yFlip->isChecked();
|
||||
this->paletteId = ui->spinBox_paletteSelector->value();
|
||||
connect(ui->checkBox_xFlip, &QCheckBox::toggled, this, &TilesetEditor::refreshTileFlips);
|
||||
connect(ui->checkBox_yFlip, &QCheckBox::toggled, this, &TilesetEditor::refreshTileFlips);
|
||||
|
||||
connect(ui->actionSave_Tileset, &QAction::triggered, this, &TilesetEditor::save);
|
||||
|
||||
|
|
@ -51,19 +60,7 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
|
|||
|
||||
connect(ui->actionExport_Metatiles_Image, &QAction::triggered, [this] { exportMetatilesImage(); });
|
||||
|
||||
ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
|
||||
ui->actionShow_Raw_Metatile_Attributes->setChecked(porymapConfig.showTilesetEditorRawAttributes);
|
||||
|
||||
ui->spinBox_paletteSelector->setMinimum(0);
|
||||
ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
|
||||
|
||||
auto validator = new IdentifierValidator(this);
|
||||
validator->setAllowEmpty(true);
|
||||
ui->lineEdit_metatileLabel->setValidator(validator);
|
||||
|
||||
ActiveWindowFilter *filter = new ActiveWindowFilter(this);
|
||||
connect(filter, &ActiveWindowFilter::activated, this, &TilesetEditor::onWindowActivated);
|
||||
this->installEventFilter(filter);
|
||||
connect(ui->spinBox_paletteSelector, QOverload<int>::of(&QSpinBox::valueChanged), this, &TilesetEditor::refreshPaletteId);
|
||||
|
||||
initAttributesUi();
|
||||
initMetatileSelector();
|
||||
|
|
@ -131,11 +128,11 @@ void TilesetEditor::setTilesets(QString primaryTilesetLabel, QString secondaryTi
|
|||
this->metatileReloadQueue.clear();
|
||||
Tileset *primaryTileset = project->getTileset(primaryTilesetLabel);
|
||||
Tileset *secondaryTileset = project->getTileset(secondaryTilesetLabel);
|
||||
if (this->primaryTileset) delete this->primaryTileset;
|
||||
if (this->secondaryTileset) delete this->secondaryTileset;
|
||||
delete this->primaryTileset;
|
||||
delete this->secondaryTileset;
|
||||
this->primaryTileset = new Tileset(*primaryTileset);
|
||||
this->secondaryTileset = new Tileset(*secondaryTileset);
|
||||
if (paletteEditor) paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||
if (this->paletteEditor) this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
|
||||
this->initMetatileHistory();
|
||||
}
|
||||
|
||||
|
|
@ -265,10 +262,8 @@ void TilesetEditor::initTileSelector()
|
|||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileChanged, [this](uint16_t tileId) {
|
||||
onHoveredTileChanged(tileId);
|
||||
});
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileCleared,
|
||||
this, &TilesetEditor::onHoveredTileCleared);
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::selectedTilesChanged,
|
||||
this, &TilesetEditor::onSelectedTilesChanged);
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::hoveredTileCleared, this, &TilesetEditor::onHoveredTileCleared);
|
||||
connect(this->tileSelector, &TilesetEditorTileSelector::selectedTilesChanged, this, &TilesetEditor::drawSelectedTiles);
|
||||
|
||||
this->tileSelector->showDivider = this->ui->actionShow_Tileset_Divider->isChecked();
|
||||
|
||||
|
|
@ -475,10 +470,6 @@ void TilesetEditor::onHoveredTileCleared() {
|
|||
this->ui->statusbar->clearMessage();
|
||||
}
|
||||
|
||||
void TilesetEditor::onSelectedTilesChanged() {
|
||||
this->drawSelectedTiles();
|
||||
}
|
||||
|
||||
void TilesetEditor::onMetatileLayerTileChanged(int x, int y) {
|
||||
static const QList<QPoint> tileCoords = QList<QPoint>{
|
||||
QPoint(0, 0),
|
||||
|
|
@ -543,39 +534,32 @@ void TilesetEditor::onMetatileLayerSelectionChanged(QPoint selectionOrigin, int
|
|||
|
||||
this->tileSelector->setExternalSelection(width, height, tiles, tileIdxs);
|
||||
if (width == 1 && height == 1) {
|
||||
ui->spinBox_paletteSelector->setValue(tiles[0].palette);
|
||||
setPaletteId(tiles[0].palette);
|
||||
this->tileSelector->highlight(static_cast<uint16_t>(tiles[0].tileId));
|
||||
this->redrawTileSelector();
|
||||
}
|
||||
this->metatileLayersItem->clearLastModifiedCoords();
|
||||
}
|
||||
|
||||
void TilesetEditor::on_spinBox_paletteSelector_valueChanged(int paletteId)
|
||||
{
|
||||
this->ui->spinBox_paletteSelector->blockSignals(true);
|
||||
this->ui->spinBox_paletteSelector->setValue(paletteId);
|
||||
this->ui->spinBox_paletteSelector->blockSignals(false);
|
||||
this->paletteId = paletteId;
|
||||
this->tileSelector->setPaletteId(paletteId);
|
||||
void TilesetEditor::setPaletteId(int paletteId) {
|
||||
ui->spinBox_paletteSelector->setValue(paletteId);
|
||||
}
|
||||
|
||||
int TilesetEditor::paletteId() const {
|
||||
return ui->spinBox_paletteSelector->value();
|
||||
}
|
||||
|
||||
void TilesetEditor::refreshPaletteId() {
|
||||
this->tileSelector->setPaletteId(paletteId());
|
||||
this->drawSelectedTiles();
|
||||
if (this->paletteEditor) {
|
||||
this->paletteEditor->setPaletteId(paletteId);
|
||||
this->paletteEditor->setPaletteId(paletteId());
|
||||
}
|
||||
this->metatileLayersItem->clearLastModifiedCoords();
|
||||
}
|
||||
|
||||
void TilesetEditor::setXFlip(bool enabled)
|
||||
{
|
||||
this->tileXFlip = enabled;
|
||||
this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
|
||||
this->drawSelectedTiles();
|
||||
this->metatileLayersItem->clearLastModifiedCoords();
|
||||
}
|
||||
|
||||
void TilesetEditor::setYFlip(bool enabled)
|
||||
{
|
||||
this->tileYFlip = enabled;
|
||||
this->tileSelector->setTileFlips(this->tileXFlip, this->tileYFlip);
|
||||
void TilesetEditor::refreshTileFlips() {
|
||||
this->tileSelector->setTileFlips(ui->checkBox_xFlip->isChecked(), ui->checkBox_yFlip->isChecked());
|
||||
this->drawSelectedTiles();
|
||||
this->metatileLayersItem->clearLastModifiedCoords();
|
||||
}
|
||||
|
|
@ -872,21 +856,12 @@ void TilesetEditor::on_actionChange_Palettes_triggered()
|
|||
{
|
||||
if (!this->paletteEditor) {
|
||||
this->paletteEditor = new PaletteEditor(this->project, this->primaryTileset,
|
||||
this->secondaryTileset, this->paletteId, this);
|
||||
connect(this->paletteEditor, &PaletteEditor::changedPaletteColor,
|
||||
this, &TilesetEditor::onPaletteEditorChangedPaletteColor);
|
||||
connect(this->paletteEditor, &PaletteEditor::changedPalette,
|
||||
this, &TilesetEditor::onPaletteEditorChangedPalette);
|
||||
}
|
||||
|
||||
if (!this->paletteEditor->isVisible()) {
|
||||
this->paletteEditor->show();
|
||||
} else if (this->paletteEditor->isMinimized()) {
|
||||
this->paletteEditor->showNormal();
|
||||
} else {
|
||||
this->paletteEditor->raise();
|
||||
this->paletteEditor->activateWindow();
|
||||
this->secondaryTileset, this->paletteId(), this);
|
||||
connect(this->paletteEditor, &PaletteEditor::changedPaletteColor, this, &TilesetEditor::onPaletteEditorChangedPaletteColor);
|
||||
connect(this->paletteEditor, &PaletteEditor::changedPalette, this, &TilesetEditor::setPaletteId);
|
||||
connect(this->paletteEditor, &PaletteEditor::metatileSelected, this, &TilesetEditor::selectMetatile);
|
||||
}
|
||||
Util::show(this->paletteEditor);
|
||||
}
|
||||
|
||||
void TilesetEditor::onPaletteEditorChangedPaletteColor() {
|
||||
|
|
@ -894,10 +869,6 @@ void TilesetEditor::onPaletteEditorChangedPaletteColor() {
|
|||
this->hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
void TilesetEditor::onPaletteEditorChangedPalette(int paletteId) {
|
||||
this->on_spinBox_paletteSelector_valueChanged(paletteId);
|
||||
}
|
||||
|
||||
bool TilesetEditor::replaceMetatile(uint16_t metatileId, const Metatile * src, QString newLabel)
|
||||
{
|
||||
Metatile * dest = Tileset::getMetatile(metatileId, this->primaryTileset, this->secondaryTileset);
|
||||
|
|
@ -996,7 +967,7 @@ void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel)
|
|||
|
||||
void TilesetEditor::exportTilesImage(Tileset *tileset) {
|
||||
bool primary = !tileset->is_secondary;
|
||||
QString defaultFilepath = QString("%1/%2_Tiles_Pal%3.png").arg(FileDialog::getDirectory()).arg(tileset->name).arg(this->paletteId);
|
||||
QString defaultFilepath = QString("%1/%2_Tiles_Pal%3.png").arg(FileDialog::getDirectory()).arg(tileset->name).arg(this->paletteId());
|
||||
QString filepath = FileDialog::getSaveFileName(this, QString("Export %1 Tiles Image").arg(primary ? "Primary" : "Secondary"), defaultFilepath, "Image Files (*.png)");
|
||||
if (!filepath.isEmpty()) {
|
||||
QImage image = primary ? this->tileSelector->buildPrimaryTilesIndexedImage() : this->tileSelector->buildSecondaryTilesIndexedImage();
|
||||
|
|
|
|||
|
|
@ -122,7 +122,10 @@ void WildMonSearch::updateResults(const QString &species) {
|
|||
if (ui->comboBox_Search->findText(species) < 0)
|
||||
return; // Not a species name, no need to search wild encounter data.
|
||||
|
||||
const QList<RowData> results = this->resultsCache.value(species, search(species));
|
||||
auto it = this->resultsCache.constFind(species);
|
||||
bool inCache = (it != this->resultsCache.constEnd());
|
||||
const QList<RowData> results = inCache ? it.value() : search(species);
|
||||
|
||||
if (results.isEmpty()) {
|
||||
static const RowData noResults = {
|
||||
.mapName = "",
|
||||
|
|
@ -140,7 +143,7 @@ void WildMonSearch::updateResults(const QString &species) {
|
|||
|
||||
ui->table_Results->setSortingEnabled(true);
|
||||
|
||||
this->resultsCache.insert(species, results);
|
||||
if (!inCache) this->resultsCache.insert(species, results);
|
||||
}
|
||||
|
||||
// Double-clicking row data opens the corresponding map/table on the Wild Pokémon tab.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user