diff --git a/src/nostalgia/core/studio/consts.hpp b/src/nostalgia/core/studio/consts.hpp index 185cf465..c44476b7 100644 --- a/src/nostalgia/core/studio/consts.hpp +++ b/src/nostalgia/core/studio/consts.hpp @@ -17,8 +17,9 @@ constexpr auto PaletteDir = "/Palettes/"; // Command IDs to use with QUndoCommand::id() enum class CommandId { UpdatePixel = 1, - UpdateDimension = 2, - InsertTile = 3, + ModPixel = 2, + UpdateDimension = 3, + InsertTile = 4, }; } diff --git a/src/nostalgia/core/studio/tilesheeteditor.cpp b/src/nostalgia/core/studio/tilesheeteditor.cpp index 50275576..89a36f91 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.cpp +++ b/src/nostalgia/core/studio/tilesheeteditor.cpp @@ -9,10 +9,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -28,6 +31,7 @@ #include #include +#include #include "consts.hpp" #include "util.hpp" @@ -35,6 +39,40 @@ namespace nostalgia::core { +class ModAfterDialog: public QDialog { + + private: + QComboBox *const m_afterColor = new QComboBox(this); + QSpinBox *const m_mod = new QSpinBox(this); + + public: + ModAfterDialog(const QStringList &palette, QWidget *parent = nullptr): QDialog(parent) { + auto range = palette.size(); + auto okBtn = new QPushButton(tr("&OK"), this); + setWindowTitle(tr("Mod Colors After")); + setWindowModality(Qt::WindowModality::ApplicationModal); + for (int i = 0; i < palette.size(); ++i) { + const auto c = palette[i]; + m_afterColor->addItem(QString("%1: %2").arg(i + 1).arg(c)); + } + m_mod->setMinimum(-range + 1); + m_mod->setMaximum(range - 1); + auto lyt = new QFormLayout(this); lyt->addRow(tr("&Greater/Equal To:"), m_afterColor); + lyt->addRow(tr("&Mod By:"), m_mod); + lyt->addWidget(okBtn); + connect(okBtn, &QPushButton::clicked, this, &ModAfterDialog::accept); + } + + int color() { + return m_afterColor->currentIndex() - 1; + } + + int mod() { + return m_mod->value(); + } + +}; + struct LabeledSpinner: public QWidget { QSpinBox *const spinBox = new QSpinBox(this); @@ -107,6 +145,36 @@ class UpdateDimensionsCommand: public QUndoCommand { }; +class ModPixelsCommand: public QUndoCommand { + private: + SheetData *m_sheetData = nullptr; + QHash m_pixelUpdates; + int m_mod = 0; + + public: + ModPixelsCommand(SheetData *sheetData, int mod): m_sheetData(sheetData), m_mod(mod) { + } + + virtual ~ModPixelsCommand() = default; + + void addPixel(int index, int mod) { + m_pixelUpdates[index] = mod; + } + + int id() const override { + return static_cast(CommandId::ModPixel); + } + + void redo() override { + m_sheetData->modPixels(m_pixelUpdates, 1); + } + + void undo() override { + m_sheetData->modPixels(m_pixelUpdates, -1); + } + +}; + class UpdatePixelsCommand: public QUndoCommand { private: struct PixelUpdate { @@ -354,6 +422,27 @@ void SheetData::setSelectedColor(int index) { m_selectedColor = index; } +void SheetData::modGteCmd(int color, int mod) { + auto cmd = new ModPixelsCommand(this, mod); + for (int i = 0; i < m_pixels.size(); ++i) { + const auto p = m_pixels[i]; + if (p >= color) { + const auto mx = (m_palette.size() - p) - 1; + cmd->addPixel(i, std::clamp(mod, -p, mx)); + } + } + m_cmdStack->push(cmd); +} + +void SheetData::modPixels(const QHash &mods, int inversion) { + for (const auto index : mods.keys()) { + const auto mod = mods[index]; + m_pixels[index] += mod * inversion; + } + emit pixelsChanged(); + emit changeOccurred(); +} + std::unique_ptr SheetData::toNostalgiaGraphic() const { auto ng = std::make_unique(); const auto highestColorIdx = static_cast(*std::max_element(m_pixels.begin(), m_pixels.end())); @@ -433,8 +522,10 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg auto tb = new QToolBar(tr("Tile Sheet Options")); m_tilesX = new LabeledSpinner(tr("Tiles &X:"), 1, m_sheetData.columns()); m_tilesY = new LabeledSpinner(tr("Tiles &Y:"), 1, m_sheetData.rows()); + m_updateAfterBtn = new QPushButton(tr("&Mod After"), tb); tb->addWidget(m_tilesX); tb->addWidget(m_tilesY); + tb->addWidget(m_updateAfterBtn); canvasLyt->setMenuBar(tb); lyt->addWidget(m_splitter); m_splitter->addWidget(canvasParent); @@ -453,6 +544,7 @@ TileSheetEditor::TileSheetEditor(QString path, const studio::Context *ctx, QWidg m_sheetData.load(m_ctx, m_itemPath); connect(m_tilesX->spinBox, QOverload::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateColumns); connect(m_tilesY->spinBox, QOverload::of(&QSpinBox::valueChanged), &m_sheetData, &SheetData::updateRows); + connect(m_updateAfterBtn, &QPushButton::clicked, this, &TileSheetEditor::updateAfterClicked); m_canvas->rootContext()->setContextProperty("sheetData", &m_sheetData); m_canvas->setSource(QUrl::fromLocalFile(":/qml/TileSheetEditor.qml")); m_canvas->setResizeMode(QQuickWidget::SizeRootObjectToView); @@ -507,7 +599,6 @@ QWidget *TileSheetEditor::setupColorPicker(QWidget *parent) { m_colorPicker.colorTable->setSelectionMode(QAbstractItemView::SingleSelection); m_colorPicker.colorTable->setHorizontalHeaderLabels(QStringList() << tr("Hex Code") << tr("Color")); m_colorPicker.colorTable->horizontalHeader()->setStretchLastSection(true); - m_colorPicker.colorTable->verticalHeader()->hide(); connect(m_colorPicker.colorTable, &QTableWidget::itemSelectionChanged, this, &TileSheetEditor::colorSelected); connect(m_colorPicker.palette, &QComboBox::currentTextChanged, this, [this](QString name) { m_sheetData.setPalette(m_ctx, palettePath(name)); @@ -636,4 +727,13 @@ void TileSheetEditor::setColorTable() { } } +void TileSheetEditor::updateAfterClicked() { + auto d = new ModAfterDialog(m_sheetData.palette(), this); + connect(d, &ModAfterDialog::accepted, [this, d] { + m_sheetData.modGteCmd(d->color(), d->mod()); + }); + connect(d, &ModAfterDialog::finished, d, &ModAfterDialog::deleteLater); + d->open(); +} + } diff --git a/src/nostalgia/core/studio/tilesheeteditor.hpp b/src/nostalgia/core/studio/tilesheeteditor.hpp index e6d29d58..9df34e42 100644 --- a/src/nostalgia/core/studio/tilesheeteditor.hpp +++ b/src/nostalgia/core/studio/tilesheeteditor.hpp @@ -82,6 +82,10 @@ class SheetData: public QObject { void setSelectedColor(int index); + void modGteCmd(int val, int mod); + + void modPixels(const QHash &pixels, int inversion); + [[nodiscard]] std::unique_ptr toNostalgiaGraphic() const; public slots: @@ -128,6 +132,7 @@ class TileSheetEditor: public studio::Editor { class QSplitter *m_splitter = nullptr; struct LabeledSpinner *m_tilesX = nullptr; struct LabeledSpinner *m_tilesY = nullptr; + class QPushButton *m_updateAfterBtn = nullptr; class QQuickWidget* m_canvas = nullptr; struct { QComboBox *palette = nullptr; @@ -166,6 +171,9 @@ class TileSheetEditor: public studio::Editor { void setColorTable(); + private slots: + void updateAfterClicked(); + }; }