Refactor palette editor to use new color input widget

This commit is contained in:
GriffinR 2024-10-01 02:20:26 -04:00
parent 44642c347f
commit 932c299935
5 changed files with 104 additions and 5362 deletions

View file

@ -10,9 +10,6 @@
<height>212</height>
</rect>
</property>
<property name="title">
<string>Color</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="frame_InputMain">

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,8 @@
#define PALETTEEDITOR_H
#include <QMainWindow>
#include <QValidator>
#include <QSlider>
#include <QFrame>
#include <QLabel>
#include "colorinputwidget.h"
#include "project.h"
#include "history.h"
@ -32,43 +30,26 @@ public:
private:
Ui::PaletteEditor *ui;
Project *project = nullptr;
QList<QList<QSlider*>> sliders;
QList<QList<QSpinBox *>> spinners;
QList<QFrame*> frames;
QList<QToolButton *> pickButtons;
QList<QLineEdit *> hexEdits;
QList<ColorInputWidget*> colorInputs;
Tileset *primaryTileset;
Tileset *secondaryTileset;
QList<History<PaletteHistoryItem*>> palettesHistory;
void refreshColorUis();
void updateColorUi(int index, QRgb color);
void commitEditHistory(int paletteid);
Tileset* getTileset(int paletteId);
void refreshColorInputs();
void commitEditHistory(int paletteId);
void restoreWindowState();
void setSignalsEnabled(bool enabled);
void setColorsFromHistory(PaletteHistoryItem*, int);
void closeEvent(QCloseEvent*);
void pickColor(int i);
void setRgb(int index, QRgb rgb);
void setRgbFromSliders(int colorIndex);
void setRgbFromHexEdit(int colorIndex);
void setRgbFromSpinners(int colorIndex);
void setPalette(int paletteId, const QList<QRgb> &palette);
void setBitDepth(int bits);
int bitDepth = 24;
class HexCodeValidator : public QValidator {
virtual QValidator::State validate(QString &input, int &) const override {
input = input.toUpper();
return QValidator::Acceptable;
}
};
HexCodeValidator *hexValidator = nullptr;
static const int numColors = 16;
signals:
void closed();

View file

@ -4,8 +4,6 @@
#include <cmath>
// TODO: Refactor palette editor to make use of this class
class HexCodeValidator : public QValidator {
virtual QValidator::State validate(QString &input, int &) const override {
input = input.toUpper();

View file

@ -1,19 +1,12 @@
#include "paletteeditor.h"
#include "ui_paletteeditor.h"
#include "colorpicker.h"
#include "paletteutil.h"
#include "config.h"
#include "log.h"
#include <cmath>
#include <QFileDialog>
#include <QMessageBox>
static inline int rgb5(int rgb) { return round(static_cast<double>(rgb * 31) / 255.0); }
static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); }
static inline int gbaRed(int rgb) { return rgb & 0x1f; }
static inline int gbaGreen(int rgb) { return (rgb >> 5) & 0x1f; }
static inline int gbaBlue(int rgb) { return (rgb >> 10) & 0x1f; }
PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, QWidget *parent) :
QMainWindow(parent),
@ -26,55 +19,13 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
this->ui->spinBox_PaletteId->setMinimum(0);
this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
this->sliders.clear();
for (int i = 0; i < 16; i++) {
QList<QSlider *> rgbSliders;
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_red_" + QString::number(i)));
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_green_" + QString::number(i)));
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_blue_" + QString::number(i)));
this->sliders.append(rgbSliders);
connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
}
this->spinners.clear();
for (int i = 0; i < 16; i++) {
QList<QSpinBox *> rgbSpinners;
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_red_" + QString::number(i)));
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_green_" + QString::number(i)));
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_blue_" + QString::number(i)));
this->spinners.append(rgbSpinners);
connect(this->spinners[i][0], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
connect(this->spinners[i][1], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
connect(this->spinners[i][2], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
}
this->frames.clear();
for (int i = 0; i < 16; i++) {
this->frames.append(this->ui->container->findChild<QFrame *>("colorFrame_" + QString::number(i)));
this->frames[i]->setFrameStyle(QFrame::NoFrame);
}
this->pickButtons.clear();
for (int i = 0; i < 16; i++) {
this->pickButtons.append(this->ui->container->findChild<QToolButton *>("pick_" + QString::number(i)));
}
this->hexValidator = new HexCodeValidator;
this->hexEdits.clear();
for (int i = 0; i < 16; i++) {
this->hexEdits.append(this->ui->container->findChild<QLineEdit *>("hex_" + QString::number(i)));
this->hexEdits[i]->setValidator(hexValidator);
}
// Connect to function that will update color when hex edit is changed
for (int i = 0; i < this->hexEdits.length(); i++) {
connect(this->hexEdits[i], &QLineEdit::textEdited, [this, i](QString text){
if ((this->bitDepth == 24 && text.length() == 6) || (this->bitDepth == 15 && text.length() == 4)) setRgbFromHexEdit(i);
});
this->colorInputs.clear();
const int numColorsPerRow = 4;
for (int i = 0; i < this->numColors; i++) {
auto colorInput = new ColorInputWidget(QString("Color %1").arg(i));
connect(colorInput, &ColorInputWidget::colorChanged, [this, i](QRgb color) { setRgb(i, color); });
this->colorInputs.append(colorInput);
ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow);
}
// Setup edit-undo history for each of the palettes.
@ -82,11 +33,6 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
this->palettesHistory.append(History<PaletteHistoryItem*>());
}
// Connect the color picker's selection to the correct color index
for (int i = 0; i < 16; i++) {
connect(this->pickButtons[i], &QToolButton::clicked, [this, i](){ this->pickColor(i); });
}
int bitDepth = porymapConfig.paletteEditorBitDepth;
if (bitDepth == 15) {
this->ui->bit_depth_15->setChecked(true);
@ -107,229 +53,67 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
PaletteEditor::~PaletteEditor()
{
delete ui;
delete this->hexValidator;
}
void PaletteEditor::updateColorUi(int colorIndex, QRgb rgb) {
setSignalsEnabled(false);
int red = qRed(rgb);
int green = qGreen(rgb);
int blue = qBlue(rgb);
if (this->bitDepth == 15) {
// sliders
this->sliders[colorIndex][0]->setValue(rgb5(red));
this->sliders[colorIndex][1]->setValue(rgb5(green));
this->sliders[colorIndex][2]->setValue(rgb5(blue));
// hex
int hex15 = (rgb5(blue) << 10) | (rgb5(green) << 5) | rgb5(red);
QString hexcode = QString("%1").arg(hex15, 4, 16, QLatin1Char('0')).toUpper();
this->hexEdits[colorIndex]->setText(hexcode);
// spinners
this->spinners[colorIndex][0]->setValue(rgb5(red));
this->spinners[colorIndex][1]->setValue(rgb5(green));
this->spinners[colorIndex][2]->setValue(rgb5(blue));
} else {
// sliders
this->sliders[colorIndex][0]->setValue(red);
this->sliders[colorIndex][1]->setValue(green);
this->sliders[colorIndex][2]->setValue(blue);
// hex
QColor color(red, green, blue);
QString hexcode = color.name().remove(0, 1).toUpper();
this->hexEdits[colorIndex]->setText(hexcode);
// spinners
this->spinners[colorIndex][0]->setValue(red);
this->spinners[colorIndex][1]->setValue(green);
this->spinners[colorIndex][2]->setValue(blue);
}
// frame
QString stylesheet = QString("background-color: rgb(%1, %2, %3);").arg(red).arg(green).arg(blue);
this->frames[colorIndex]->setStyleSheet(stylesheet);
setSignalsEnabled(true);
}
void PaletteEditor::setSignalsEnabled(bool enabled) {
// spinners, sliders, hexbox
for (int i = 0; i < this->sliders.length(); i++) {
this->sliders.at(i).at(0)->blockSignals(!enabled);
this->sliders.at(i).at(1)->blockSignals(!enabled);
this->sliders.at(i).at(2)->blockSignals(!enabled);
}
for (int i = 0; i < this->spinners.length(); i++) {
this->spinners.at(i).at(0)->blockSignals(!enabled);
this->spinners.at(i).at(1)->blockSignals(!enabled);
this->spinners.at(i).at(2)->blockSignals(!enabled);
}
for (int i = 0; i < this->hexEdits.length(); i++) {
this->hexEdits.at(i)->blockSignals(!enabled);
}
Tileset* PaletteEditor::getTileset(int paletteId) {
return (paletteId < Project::getNumPalettesPrimary())
? this->primaryTileset
: this->secondaryTileset;
}
void PaletteEditor::setBitDepth(int bits) {
setSignalsEnabled(false);
switch (bits) {
case 15:
for (int i = 0; i < 16; i++) {
// sliders ranged [0, 31] with 1 single step and 4 page step
this->sliders[i][0]->setSingleStep(1);
this->sliders[i][1]->setSingleStep(1);
this->sliders[i][2]->setSingleStep(1);
this->sliders[i][0]->setPageStep(4);
this->sliders[i][1]->setPageStep(4);
this->sliders[i][2]->setPageStep(4);
this->sliders[i][0]->setMaximum(31);
this->sliders[i][1]->setMaximum(31);
this->sliders[i][2]->setMaximum(31);
// spinners limited [0, 31] with 1 step
this->spinners[i][0]->setSingleStep(1);
this->spinners[i][1]->setSingleStep(1);
this->spinners[i][2]->setSingleStep(1);
this->spinners[i][0]->setMaximum(31);
this->spinners[i][1]->setMaximum(31);
this->spinners[i][2]->setMaximum(31);
// hex box now 4 digits
this->hexEdits[i]->setInputMask("HHHH");
this->hexEdits[i]->setMaxLength(4);
}
break;
case 24:
default:
for (int i = 0; i < 16; i++) {
// sliders ranged [0, 31] with 1 single step and 4 page step
this->sliders[i][0]->setSingleStep(8);
this->sliders[i][1]->setSingleStep(8);
this->sliders[i][2]->setSingleStep(8);
this->sliders[i][0]->setPageStep(24);
this->sliders[i][1]->setPageStep(24);
this->sliders[i][2]->setPageStep(24);
this->sliders[i][0]->setMaximum(255);
this->sliders[i][1]->setMaximum(255);
this->sliders[i][2]->setMaximum(255);
// spinners limited [0, 31] with 1 step
this->spinners[i][0]->setSingleStep(8);
this->spinners[i][1]->setSingleStep(8);
this->spinners[i][2]->setSingleStep(8);
this->spinners[i][0]->setMaximum(255);
this->spinners[i][1]->setMaximum(255);
this->spinners[i][2]->setMaximum(255);
// hex box now 4 digits
this->hexEdits[i]->setInputMask("HHHHHH");
this->hexEdits[i]->setMaxLength(6);
}
break;
}
this->bitDepth = bits;
porymapConfig.paletteEditorBitDepth = bits;
refreshColorUis();
setSignalsEnabled(true);
for (const auto &colorInput : this->colorInputs) {
colorInput->setBitDepth(bits);
}
}
void PaletteEditor::setRgb(int colorIndex, QRgb rgb) {
int paletteNum = this->ui->spinBox_PaletteId->value();
const int paletteId = this->ui->spinBox_PaletteId->value();
Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
? this->primaryTileset
: this->secondaryTileset;
tileset->palettes[paletteNum][colorIndex] = rgb;
tileset->palettePreviews[paletteNum][colorIndex] = rgb;
Tileset *tileset = getTileset(paletteId);
tileset->palettes[paletteId][colorIndex] = rgb;
tileset->palettePreviews[paletteId][colorIndex] = rgb;
this->updateColorUi(colorIndex, rgb);
this->commitEditHistory(paletteNum);
emit this->changedPaletteColor();
commitEditHistory(paletteId);
emit changedPaletteColor();
}
void PaletteEditor::setRgbFromSliders(int colorIndex) {
if (this->bitDepth == 15) {
setRgb(colorIndex, qRgb(rgb8(this->sliders[colorIndex][0]->value()),
rgb8(this->sliders[colorIndex][1]->value()),
rgb8(this->sliders[colorIndex][2]->value())));
} else {
setRgb(colorIndex, qRgb(this->sliders[colorIndex][0]->value(),
this->sliders[colorIndex][1]->value(),
this->sliders[colorIndex][2]->value()));
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);
}
refreshColorInputs();
emit changedPaletteColor();
}
void PaletteEditor::setRgbFromHexEdit(int colorIndex) {
QString text = this->hexEdits[colorIndex]->text();
bool ok = false;
int rgb = text.toInt(&ok, 16);
if (!ok) rgb = 0xFFFFFFFF;
if (this->bitDepth == 15) {
int rc = gbaRed(rgb);
int gc = gbaGreen(rgb);
int bc = gbaBlue(rgb);
setRgb(colorIndex, qRgb(rgb8(rc), rgb8(gc), rgb8(bc)));
} else {
setRgb(colorIndex, qRgb(qRed(rgb), qGreen(rgb), qBlue(rgb)));
}
}
void PaletteEditor::setRgbFromSpinners(int colorIndex) {
if (this->bitDepth == 15) {
setRgb(colorIndex, qRgb(rgb8(this->spinners[colorIndex][0]->value()),
rgb8(this->spinners[colorIndex][1]->value()),
rgb8(this->spinners[colorIndex][2]->value())));
} else {
setRgb(colorIndex, qRgb(this->spinners[colorIndex][0]->value(),
this->spinners[colorIndex][1]->value(),
this->spinners[colorIndex][2]->value()));
}
}
void PaletteEditor::refreshColorUis() {
int paletteNum = this->ui->spinBox_PaletteId->value();
for (int i = 0; i < 16; i++) {
QRgb color;
if (paletteNum < Project::getNumPalettesPrimary()) {
color = this->primaryTileset->palettes.at(paletteNum).at(i);
} else {
color = this->secondaryTileset->palettes.at(paletteNum).at(i);
}
this->updateColorUi(i, color);
void PaletteEditor::refreshColorInputs() {
const int paletteId = ui->spinBox_PaletteId->value();
Tileset *tileset = getTileset(paletteId);
for (int i = 0; i < this->numColors; i++) {
auto colorInput = this->colorInputs.at(i);
const QSignalBlocker b(colorInput);
colorInput->setColor(tileset->palettes.at(paletteId).at(i));
}
}
void PaletteEditor::setPaletteId(int paletteId) {
this->ui->spinBox_PaletteId->blockSignals(true);
const QSignalBlocker b(ui->spinBox_PaletteId);
this->ui->spinBox_PaletteId->setValue(paletteId);
this->refreshColorUis();
this->ui->spinBox_PaletteId->blockSignals(false);
this->refreshColorInputs();
}
void PaletteEditor::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->refreshColorUis();
}
void PaletteEditor::pickColor(int index) {
ColorPicker picker(this);
if (picker.exec() == QDialog::Accepted) {
QColor c = picker.getColor();
this->setRgb(index, c.rgb());
}
return;
this->refreshColorInputs();
}
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
this->refreshColorUis();
this->refreshColorInputs();
if (!this->palettesHistory[paletteId].current()) {
this->commitEditHistory(paletteId);
}
@ -338,8 +122,8 @@ void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
void PaletteEditor::commitEditHistory(int paletteId) {
QList<QRgb> colors;
for (int i = 0; i < 16; i++) {
colors.append(qRgb(this->spinners[i][0]->value(), this->spinners[i][1]->value(), this->spinners[i][2]->value()));
for (int i = 0; i < this->numColors; i++) {
colors.append(this->colorInputs.at(i)->color());
}
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
this->palettesHistory[paletteId].push(commit);
@ -356,31 +140,16 @@ void PaletteEditor::on_actionUndo_triggered()
{
int paletteId = this->ui->spinBox_PaletteId->value();
PaletteHistoryItem *prev = this->palettesHistory[paletteId].back();
this->setColorsFromHistory(prev, paletteId);
if (prev)
setPalette(paletteId, prev->colors);
}
void PaletteEditor::on_actionRedo_triggered()
{
int paletteId = this->ui->spinBox_PaletteId->value();
PaletteHistoryItem *next = this->palettesHistory[paletteId].next();
this->setColorsFromHistory(next, paletteId);
}
void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int paletteId) {
if (!history) return;
for (int i = 0; i < 16; i++) {
if (paletteId < Project::getNumPalettesPrimary()) {
this->primaryTileset->palettes[paletteId][i] = history->colors.at(i);
this->primaryTileset->palettePreviews[paletteId][i] = history->colors.at(i);
} else {
this->secondaryTileset->palettes[paletteId][i] = history->colors.at(i);
this->secondaryTileset->palettePreviews[paletteId][i] = history->colors.at(i);
}
}
this->refreshColorUis();
emit this->changedPaletteColor();
if (next)
setPalette(paletteId, next->colors);
}
void PaletteEditor::on_actionImport_Palette_triggered()
@ -407,10 +176,12 @@ void PaletteEditor::on_actionImport_Palette_triggered()
return;
}
if (palette.length() < 16) {
if (palette.length() < this->numColors) {
QMessageBox msgBox(this);
msgBox.setText("Failed to import palette.");
QString message = QString("The palette file has %1 colors, but it must have 16 colors.").arg(palette.length());
QString message = QString("The palette file has %1 colors, but it must have %2 colors.")
.arg(palette.length())
.arg(this->numColors);
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
@ -418,20 +189,9 @@ void PaletteEditor::on_actionImport_Palette_triggered()
return;
}
int paletteId = this->ui->spinBox_PaletteId->value();
for (int i = 0; i < 16; i++) {
if (paletteId < Project::getNumPalettesPrimary()) {
this->primaryTileset->palettes[paletteId][i] = palette.at(i);
this->primaryTileset->palettePreviews[paletteId][i] = palette.at(i);
} else {
this->secondaryTileset->palettes[paletteId][i] = palette.at(i);
this->secondaryTileset->palettePreviews[paletteId][i] = palette.at(i);
}
}
this->refreshColorUis();
this->commitEditHistory(paletteId);
emit this->changedPaletteColor();
const int paletteId = ui->spinBox_PaletteId->value();
setPalette(paletteId, palette);
commitEditHistory(paletteId);
}
void PaletteEditor::closeEvent(QCloseEvent*) {