[nostalgia] Add PaletteEditor

This commit is contained in:
Gary Talent 2022-04-02 16:38:38 -05:00
parent 99987ee423
commit 71e0f181ea
14 changed files with 251 additions and 334 deletions

View File

@ -129,12 +129,13 @@ constexpr float bluef(Color32 c) noexcept {
[[nodiscard]] [[nodiscard]]
constexpr Color16 color16(uint8_t r, uint8_t g, uint8_t b) noexcept { constexpr Color16 color16(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0) noexcept {
return r | (g << 5) | (b << 10); return ox::min<uint8_t>(r, 31) | (ox::min<uint8_t>(g, 31) << 5) | (ox::min<uint8_t>(b, 31) << 10) | (a << 15);
} }
static_assert(color16(0, 31, 0) == 992); static_assert(color16(0, 31, 0) == 992);
static_assert(color16(16, 31, 0) == 1008); static_assert(color16(16, 31, 0) == 1008);
static_assert(color16(16, 31, 8) == 9200); static_assert(color16(16, 31, 8) == 9200);
static_assert(color16(16, 32, 8) == 9200);
} }

View File

@ -36,7 +36,6 @@ class ClipboardObject: public BaseClipboardObject {
} }
}; };
class Context;
class Drawer; class Drawer;
// User Input Output // User Input Output

View File

@ -4,7 +4,7 @@ add_library(
module.cpp module.cpp
#new_tilesheet_wizard.cpp #new_tilesheet_wizard.cpp
#newpalettewizard.cpp #newpalettewizard.cpp
#paletteeditor.cpp paletteeditor.cpp
tilesheeteditor-imgui.cpp tilesheeteditor-imgui.cpp
tilesheeteditorview.cpp tilesheeteditorview.cpp
tilesheeteditormodel.cpp tilesheeteditormodel.cpp

View File

@ -2,18 +2,29 @@
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include "paletteeditor.hpp"
#include "tilesheeteditor-imgui.hpp" #include "tilesheeteditor-imgui.hpp"
#include "module.hpp" #include "module.hpp"
namespace nostalgia::core { namespace nostalgia::core {
ox::Vector<studio::EditorMaker> Module::editors(core::Context *ctx) { ox::Vector<studio::EditorMaker> Module::editors(core::Context *ctx) noexcept {
return { return {
{ {
{"ng"}, {"ng"},
[ctx](const ox::String &path) { [ctx](const ox::String &path) -> ox::Result<studio::Editor*> {
try {
return new TileSheetEditorImGui(ctx, path); return new TileSheetEditorImGui(ctx, path);
} catch (const ox::Exception &ex) {
return ex.toError();
}
}
},
{
{"npal"},
[ctx](const ox::String &path) -> ox::Result<studio::Editor*> {
return PaletteEditorImGui::make(ctx, path);
} }
} }
}; };

View File

@ -10,7 +10,7 @@ namespace nostalgia::core {
class Module: public studio::Module { class Module: public studio::Module {
public: public:
ox::Vector<studio::EditorMaker> editors(core::Context *ctx) override; ox::Vector<studio::EditorMaker> editors(core::Context *ctx) noexcept override;
}; };
} }

View File

@ -2,16 +2,14 @@
* Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <QHeaderView> #include <algorithm>
#include <QItemDelegate>
#include <QPainter> #include <imgui.h>
#include <QPushButton>
#include <QTableWidget>
#include <QToolBar>
#include <nostalgia/core/gfx.hpp> #include <nostalgia/core/gfx.hpp>
#include <nostalgia/core/media.hpp>
#include <ox/std/memory.hpp>
#include "util.hpp"
#include "paletteeditor.hpp" #include "paletteeditor.hpp"
namespace nostalgia::core { namespace nostalgia::core {
@ -24,359 +22,294 @@ enum class PaletteEditorCommandId {
}; };
class ColorChannelValidator: public QValidator { class AddColorCommand: public studio::UndoCommand {
public:
explicit ColorChannelValidator(QLineEdit *parent);
QValidator::State validate(QString &input, int&) const override;
private: private:
[[nodiscard]] static QString convert(const QString &input); PaletteEditorImGui *m_editor = nullptr;
};
ColorChannelValidator::ColorChannelValidator(QLineEdit *parent): QValidator(parent) {
connect(parent, &QLineEdit::editingFinished, [parent] {
parent->setText(convert(parent->text()));
});
}
QString ColorChannelValidator::convert(const QString &input) {
int num = 0;
if (input[0] == '_') {
num = input.mid(1).toInt() >> 3;
}
return QString::number(num);
}
QValidator::State ColorChannelValidator::validate(QString &input, int&) const {
if (input.size() == 0) {
return QValidator::State::Intermediate;
}
const auto convert = input[0] == '_';
const auto max = convert ? 255 : 31;
const auto numTxt = convert ? input.mid(1) : input;
bool isNumber = false;
const auto num = numTxt.toInt(&isNumber);
if (isNumber && num >= 0 && num <= max) {
return QValidator::State::Acceptable;
} else if (numTxt == "") {
return QValidator::State::Intermediate;
} else {
return QValidator::State::Invalid;
}
}
class AddColorCommand: public QUndoCommand {
private:
PaletteEditor *m_editor = nullptr;
Color16 m_color = 0; Color16 m_color = 0;
int m_idx = -1; int m_idx = -1;
public: public:
AddColorCommand(PaletteEditor *editor, Color16 color, int idx) { AddColorCommand(PaletteEditorImGui *editor, Color16 color, int idx) noexcept {
m_editor = editor; m_editor = editor;
m_color = color; m_color = color;
m_idx = idx; m_idx = idx;
} }
~AddColorCommand() override = default; ~AddColorCommand() noexcept override = default;
[[nodiscard]] int id() const override { [[nodiscard]]
int commandId() const noexcept override {
return static_cast<int>(PaletteEditorCommandId::AddColor); return static_cast<int>(PaletteEditorCommandId::AddColor);
} }
void redo() override { void redo() noexcept override {
m_editor->addColor(m_idx, m_color); m_editor->addColor(m_idx, m_color);
} }
void undo() override { void undo() noexcept override {
m_editor->rmColor(m_idx); m_editor->rmColor(m_idx);
} }
}; };
class RemoveColorCommand: public QUndoCommand { class RemoveColorCommand: public studio::UndoCommand {
private: private:
PaletteEditor *m_editor = nullptr; PaletteEditorImGui *m_editor = nullptr;
Color16 m_color = 0; Color16 m_color = 0;
int m_idx = -1; int m_idx = -1;
public: public:
RemoveColorCommand(PaletteEditor *editor, Color16 color, int idx) { RemoveColorCommand(PaletteEditorImGui *editor, Color16 color, int idx) noexcept {
m_editor = editor; m_editor = editor;
m_color = color; m_color = color;
m_idx = idx; m_idx = idx;
} }
~RemoveColorCommand() override = default; ~RemoveColorCommand() noexcept override = default;
[[nodiscard]] int id() const override { [[nodiscard]]
int commandId() const noexcept override {
return static_cast<int>(PaletteEditorCommandId::RemoveColor); return static_cast<int>(PaletteEditorCommandId::RemoveColor);
} }
void redo() override { void redo() noexcept override {
m_editor->rmColor(m_idx); m_editor->rmColor(m_idx);
} }
void undo() override { void undo() noexcept override {
m_editor->addColor(m_idx, m_color); m_editor->addColor(m_idx, m_color);
} }
}; };
class UpdateColorCommand: public QUndoCommand { class UpdateColorCommand: public studio::UndoCommand {
private: private:
PaletteEditor *m_editor = nullptr; PaletteEditorImGui *m_editor = nullptr;
Color16 m_oldColor = 0; Color16 m_oldColor = 0;
Color16 m_newColor = 0; Color16 m_newColor = 0;
int m_idx = -1; int m_idx = -1;
public: public:
UpdateColorCommand(PaletteEditor *editor, int idx, Color16 oldColor, Color16 newColor) { UpdateColorCommand(PaletteEditorImGui *editor, int idx, Color16 oldColor, Color16 newColor) noexcept {
m_editor = editor; m_editor = editor;
m_idx = idx; m_idx = idx;
m_oldColor = oldColor; m_oldColor = oldColor;
m_newColor = newColor; m_newColor = newColor;
setObsolete(m_oldColor == m_newColor); //setObsolete(m_oldColor == m_newColor);
} }
~UpdateColorCommand() override = default; ~UpdateColorCommand() noexcept override = default;
[[nodiscard]] int id() const override { bool mergeWith(const UndoCommand *cmd) noexcept final {
if (cmd->commandId() != static_cast<int>(PaletteEditorCommandId::UpdateColor)) {
return false;
}
auto ucCmd = static_cast<const UpdateColorCommand*>(cmd);
if (m_idx != ucCmd->m_idx) {
return false;
}
m_newColor = ucCmd->m_newColor;
return true;
}
int commandId() const noexcept final {
return static_cast<int>(PaletteEditorCommandId::UpdateColor); return static_cast<int>(PaletteEditorCommandId::UpdateColor);
} }
void redo() override { void redo() noexcept final {
m_editor->updateColor(m_idx, m_newColor); m_editor->updateColor(m_idx, m_newColor);
} }
void undo() override { void undo() noexcept final {
m_editor->updateColor(m_idx, m_oldColor); m_editor->updateColor(m_idx, m_oldColor);
} }
}; };
class MoveColorCommand: public QUndoCommand { class MoveColorCommand: public studio::UndoCommand {
private: private:
PaletteEditor *m_editor = nullptr; PaletteEditorImGui *m_editor = nullptr;
int m_idx = -1; std::size_t m_idx = 0;
int m_offset = 0; int m_offset = 0;
public: public:
MoveColorCommand(PaletteEditor *editor, int idx, int offset) { MoveColorCommand(PaletteEditorImGui *editor, std::size_t idx, int offset) noexcept {
m_editor = editor; m_editor = editor;
m_idx = idx; m_idx = idx;
m_offset = offset; m_offset = offset;
} }
~MoveColorCommand() override = default; ~MoveColorCommand() noexcept override = default;
[[nodiscard]] int id() const override { [[nodiscard]]
int commandId() const noexcept override {
return static_cast<int>(PaletteEditorCommandId::MoveColor); return static_cast<int>(PaletteEditorCommandId::MoveColor);
} }
void redo() override { void redo() noexcept override {
m_editor->moveColor(m_idx, m_offset); m_editor->moveColor(m_idx, m_offset);
} }
void undo() override { void undo() noexcept override {
m_editor->moveColor(m_idx + m_offset, -m_offset); m_editor->moveColor(static_cast<int>(m_idx) + m_offset, -m_offset);
} }
}; };
QWidget *PaletteEditorColorTableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex &idx) const { ox::Result<PaletteEditorImGui*> PaletteEditorImGui::make(Context *ctx, const ox::String &path) noexcept {
auto le = new QLineEdit(parent); auto out = ox::UniquePtr<PaletteEditorImGui>(new PaletteEditorImGui);
if (idx.column()) { out->m_ctx = ctx;
auto validator = new ColorChannelValidator(le); out->m_itemPath = path;
le->setValidator(validator); const auto lastSlash = std::find(out->m_itemPath.rbegin(), out->m_itemPath.rend(), '/').offset();
out->m_itemName = out->m_itemPath.substr(lastSlash + 1);
oxRequire(pal, core::readObj<Palette>(out->m_ctx, out->m_itemPath));
out->m_pal = *pal.get();
out->undoStack()->changeTriggered.connect(out.get(), &PaletteEditorImGui::markUnsavedChanges);
return out.release();
}
const ox::String &PaletteEditorImGui::itemName() const {
return m_itemName;
}
studio::UndoStack *PaletteEditorImGui::undoStack() noexcept {
return &m_undoStack;
}
void PaletteEditorImGui::draw(core::Context*) noexcept {
static constexpr auto flags = ImGuiTableFlags_RowBg;
const auto paneSize = ImGui::GetContentRegionAvail();
ImGui::BeginChild("PaletteEditor");
{
ImGui::BeginChild("Colors", ImVec2(paneSize.x - 200, paneSize.y), false);
{
const auto colorsSz = ImGui::GetContentRegionAvail();
static constexpr auto toolbarHeight = 40;
ImGui::BeginChild("Toolbar", ImVec2(colorsSz.x, toolbarHeight), true);
{
const auto sz = ImVec2(70, 24);
if (ImGui::Button("Add", sz)) {
m_undoStack.push(new AddColorCommand(this, 0, m_pal.colors.size()));
} }
return le; ImGui::SameLine();
} ImGui::BeginDisabled(m_selectedRow >= m_pal.colors.size());
{
void PaletteEditorColorTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const { if (ImGui::Button("Remove", sz)) {
if (idx.column() != 3) { m_undoStack.push(new RemoveColorCommand(this, m_pal.colors[static_cast<std::size_t>(m_selectedRow)], m_selectedRow));
QStyledItemDelegate::paint(painter, opt, idx); m_selectedRow = ox::min(m_pal.colors.size() - 1, m_selectedRow);
} else {
auto color = idx.model()->data(idx, Qt::DisplayRole).toString();
painter->fillRect(opt.rect, QColor(color));
} }
} ImGui::SameLine();
ImGui::BeginDisabled(m_selectedRow <= 0);
{
static QTableWidgetItem *mkCell(const QString& v, bool editable = true) { if (ImGui::Button("Move Up", sz)) {
auto c = new QTableWidgetItem; m_undoStack.push(new MoveColorCommand(this, m_selectedRow, -1));
c->setText(v); --m_selectedRow;
c->setFont(QFont("monospace"));
c->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
if (!editable) {
c->setFlags(c->flags() & ~Qt::ItemIsEditable);
} }
return c; }
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(m_selectedRow >= m_pal.colors.size() - 1);
{
if (ImGui::Button("Move Down", sz)) {
m_undoStack.push(new MoveColorCommand(this, m_selectedRow, 1));
++m_selectedRow;
}
}
ImGui::EndDisabled();
}
ImGui::EndDisabled();
}
ImGui::EndChild();
ImGui::BeginTable("Colors", 5, flags, ImVec2(colorsSz.x, colorsSz.y - (toolbarHeight + 5)));
{
ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25);
ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Green", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Color Preview", ImGuiTableColumnFlags_NoHide);
ImGui::TableHeadersRow();
for (auto i = 0u; const auto c : m_pal.colors) {
ImGui::PushID(static_cast<int>(i));
ImGui::TableNextRow();
// Color No.
ImGui::TableNextColumn();
ImGui::Text("%d", i);
// Red
ImGui::TableNextColumn();
ImGui::Text("%d", red16(c));
// Green
ImGui::TableNextColumn();
ImGui::Text("%d", green16(c));
// Blue
ImGui::TableNextColumn();
ImGui::Text("%d", blue16(c));
// ColorPreview
ImGui::TableNextColumn();
const auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1));
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
if (ImGui::Selectable("##ColorRow", i == m_selectedRow, ImGuiSelectableFlags_SpanAllColumns)) {
m_selectedRow = i;
}
ImGui::PopID();
++i;
}
}
ImGui::EndTable();
}
ImGui::EndChild();
if (m_selectedRow < m_pal.colors.size()) {
ImGui::SameLine();
ImGui::BeginChild("ColorEditor", ImVec2(200, paneSize.y), true);
{
const auto c = m_pal.colors[m_selectedRow];
int r = red16(c);
int g = green16(c);
int b = blue16(c);
int a = alpha16(c);
ImGui::InputInt("Red", &r, 1, 5);
ImGui::InputInt("Green", &g, 1, 5);
ImGui::InputInt("Blue", &b, 1, 5);
const auto newColor = color16(r, g, b, a);
if (c != newColor) {
m_undoStack.push(new UpdateColorCommand(this, m_selectedRow, c, newColor));
}
}
ImGui::EndChild();
}
}
ImGui::EndChild();
} }
void PaletteEditorImGui::addColor(int idx, Color16 c) noexcept {
static QTableWidgetItem *mkCell(uint8_t v) { m_pal.colors.insert(static_cast<std::size_t>(idx), c);
return mkCell(QString("%1").arg(v), true);
}
PaletteEditor::PaletteEditor(QString path, const studio::Context *ctx, QWidget *parent): studio::Editor(parent) {
m_ctx = ctx;
m_itemPath = path;
auto canvasLyt = new QVBoxLayout(this);
auto tb = new QToolBar(tr("Tile Sheet Options"));
m_addBtn = new QPushButton(tr("Add"), tb);
m_rmBtn = new QPushButton(tr("Remove"), tb);
m_moveUpBtn = new QPushButton(tr("Move Up"), tb);
m_moveDownBtn = new QPushButton(tr("Move Down"), tb);
m_rmBtn->setEnabled(false);
m_moveUpBtn->setEnabled(false);
m_moveDownBtn->setEnabled(false);
tb->addWidget(m_addBtn);
tb->addWidget(m_rmBtn);
tb->addWidget(m_moveUpBtn);
tb->addWidget(m_moveDownBtn);
canvasLyt->setMenuBar(tb);
m_table = new QTableWidget(this);
m_table->setItemDelegate(&m_colorTableDelegate);
m_table->setColumnCount(4);
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
m_table->setSelectionMode(QAbstractItemView::SingleSelection);
m_table->setHorizontalHeaderLabels(QStringList() << tr("Red") << tr("Green") << tr("Blue") << tr("Color Preview"));
m_table->horizontalHeader()->setStretchLastSection(true);
m_table->verticalHeader()->hide();
canvasLyt->addWidget(m_table);
connect(m_table, &QTableWidget::itemSelectionChanged, this, &PaletteEditor::colorSelected);
connect(m_addBtn, &QPushButton::clicked, this, &PaletteEditor::addColorClicked);
connect(m_rmBtn, &QPushButton::clicked, this, &PaletteEditor::rmColorClicked);
connect(m_moveUpBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorUpClicked);
connect(m_moveDownBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorDownClicked);
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
m_pal = m_ctx->project->loadObj<NostalgiaPalette>(m_itemPath);
load();
}
QString PaletteEditor::itemName() const {
return m_itemPath.mid(m_itemPath.lastIndexOf('/'));
}
void PaletteEditor::addTableRow(int i, Color16 c) {
disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
m_table->insertRow(i);
m_table->setItem(i, 0, mkCell(red16(c)));
m_table->setItem(i, 1, mkCell(green16(c)));
m_table->setItem(i, 2, mkCell(blue16(c)));
m_table->setItem(i, 3, mkCell(toQColor(m_pal->colors[static_cast<std::size_t>(i)]).name(QColor::HexRgb), false));
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
void PaletteEditor::rmTableRow(int idx) {
disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
m_table->removeRow(idx);
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
void PaletteEditor::setTableRow(int idx, Color16 c) {
disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
m_table->item(idx, 0)->setText(QString::number(red16(c)));
m_table->item(idx, 1)->setText(QString::number(green16(c)));
m_table->item(idx, 2)->setText(QString::number(blue16(c)));
m_table->item(idx, 3)->setText(toQColor(m_pal->colors[static_cast<std::size_t>(idx)]).name(QColor::HexRgb));
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
void PaletteEditor::addColor(int idx, Color16 c) {
m_pal->colors.insert(static_cast<std::size_t>(idx), c);
addTableRow(idx, c);
setUnsavedChanges(true); setUnsavedChanges(true);
} }
void PaletteEditor::rmColor(int idx) { void PaletteEditorImGui::rmColor(int idx) noexcept {
rmTableRow(idx); oxIgnoreError(m_pal.colors.erase(static_cast<std::size_t>(idx)));
oxIgnoreError(m_pal->colors.erase(static_cast<std::size_t>(idx)));
setUnsavedChanges(true); setUnsavedChanges(true);
} }
void PaletteEditor::updateColor(int idx, Color16 c) { void PaletteEditorImGui::updateColor(int idx, Color16 c) noexcept {
m_pal->colors[static_cast<std::size_t>(idx)] = c; m_pal.colors[static_cast<std::size_t>(idx)] = c;
setTableRow(idx, c);
setUnsavedChanges(true); setUnsavedChanges(true);
} }
void PaletteEditor::moveColor(int idx, int offset) { void PaletteEditorImGui::moveColor(int idx, int offset) noexcept {
auto c = m_pal->colors[static_cast<std::size_t>(idx)]; const auto c = m_pal.colors[static_cast<std::size_t>(idx)];
oxIgnoreError(m_pal->colors.erase(static_cast<std::size_t>(idx))); oxIgnoreError(m_pal.colors.erase(static_cast<std::size_t>(idx)));
m_pal->colors.insert(static_cast<std::size_t>(idx + offset), c); m_pal.colors.insert(static_cast<std::size_t>(idx + offset), c);
rmTableRow(idx);
addTableRow(idx + offset, c);
setUnsavedChanges(true); setUnsavedChanges(true);
} }
void PaletteEditor::saveItem() { void PaletteEditorImGui::saveItem() {
m_ctx->project->writeObj(m_itemPath, m_pal.get()); //m_ctx->project->writeObj(m_itemPath, &m_pal);
} }
void PaletteEditor::load() { ox::Error PaletteEditorImGui::markUnsavedChanges(int) noexcept {
disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged); setUnsavedChanges(true);
for (std::size_t i = 0; i < m_pal->colors.size(); ++i) { return OxError(0);
auto c = m_pal->colors[i];
addTableRow(i, c);
}
connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}
Color16 PaletteEditor::rowColor(int row) const {
auto r = static_cast<uint8_t>(m_table->item(row, 0)->text().toInt());
auto g = static_cast<uint8_t>(m_table->item(row, 1)->text().toInt());
auto b = static_cast<uint8_t>(m_table->item(row, 2)->text().toInt());
return color16(r, g, b);
}
void PaletteEditor::colorSelected() {
auto selIdxs = m_table->selectionModel()->selectedIndexes();
auto row = !selIdxs.empty() ? selIdxs[0].row() : -1;
if (row > -1) {
m_rmBtn->setEnabled(true);
m_moveUpBtn->setEnabled(row > 0);
m_moveDownBtn->setEnabled(row < m_table->rowCount() - 1);
} else {
m_rmBtn->setEnabled(false);
m_moveUpBtn->setEnabled(false);
m_moveDownBtn->setEnabled(false);
}
}
void PaletteEditor::cellChanged(int row, int) {
auto oldColor = m_pal->colors[static_cast<std::size_t>(row)];
auto newColor = rowColor(row);
undoStack()->push(new UpdateColorCommand(this, row, oldColor, newColor));
}
void PaletteEditor::addColorClicked() {
auto row = m_table->rowCount();
undoStack()->push(new AddColorCommand(this, 0, row));
}
void PaletteEditor::rmColorClicked() {
auto row = m_table->currentRow();
undoStack()->push(new RemoveColorCommand(this, m_pal->colors[static_cast<std::size_t>(row)], row));
}
void PaletteEditor::moveColorUpClicked() {
auto row = m_table->currentRow();
undoStack()->push(new MoveColorCommand(this, row, -1));
}
void PaletteEditor::moveColorDownClicked() {
auto row = m_table->currentRow();
undoStack()->push(new MoveColorCommand(this, row, 1));
} }
} }

View File

@ -4,22 +4,14 @@
#pragma once #pragma once
#include <QStyledItemDelegate> #include "nostalgia/studio/lib/undostack.hpp"
#include <nostalgia/core/gfx.hpp> #include <nostalgia/core/gfx.hpp>
#include <nostalgia/studio/studio.hpp> #include <nostalgia/studio/studio.hpp>
#include <ox/std/memory.hpp>
namespace nostalgia::core { namespace nostalgia::core {
struct PaletteEditorColorTableDelegate: public QStyledItemDelegate { class PaletteEditorImGui: public studio::Editor {
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex &idx) const;
void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const;
};
class PaletteEditor: public studio::Editor {
friend class AddColorCommand; friend class AddColorCommand;
friend class RemoveColorCommand; friend class RemoveColorCommand;
@ -27,58 +19,40 @@ class PaletteEditor: public studio::Editor {
friend class MoveColorCommand; friend class MoveColorCommand;
private: private:
PaletteEditorColorTableDelegate m_colorTableDelegate; Context *m_ctx = nullptr;
const studio::Context *m_ctx = nullptr; ox::String m_itemName;
QString m_itemPath; ox::String m_itemPath;
std::unique_ptr<NostalgiaPalette> m_pal; Palette m_pal;
class QTableWidget *m_table = nullptr; std::size_t m_selectedRow = 0;
class QPushButton *m_addBtn = nullptr; studio::UndoStack m_undoStack;
class QPushButton *m_rmBtn = nullptr;
class QPushButton *m_moveUpBtn = nullptr; PaletteEditorImGui() noexcept = default;
class QPushButton *m_moveDownBtn = nullptr;
public: public:
PaletteEditor(QString path, const studio::Context *ctx, QWidget *parent); static ox::Result<PaletteEditorImGui*> make(Context *ctx, const ox::String &path) noexcept;
/** /**
* Returns the name of item being edited. * Returns the name of item being edited.
*/ */
QString itemName() const override; const ox::String &itemName() const override;
void draw(core::Context*) noexcept override;
studio::UndoStack *undoStack() noexcept final;
protected: protected:
void addColor(int idx, Color16 c); void addColor(int idx, Color16 c) noexcept;
void rmColor(int idx); void rmColor(int idx) noexcept;
void updateColor(int idx, Color16); void updateColor(int idx, Color16) noexcept;
void moveColor(int idx, int offset); void moveColor(int idx, int offset) noexcept;
void saveItem() override; void saveItem() override;
private: private:
void load(); ox::Error markUnsavedChanges(int) noexcept;
[[nodiscard]] Color16 rowColor(int i) const;
void addTableRow(int i, Color16 c);
void rmTableRow(int i);
void setTableRow(int idx, Color16 c);
private slots:
void colorSelected();
void cellChanged(int row, int col);
void addColorClicked();
void rmColorClicked();
void moveColorUpClicked();
void moveColorDownClicked();
}; };

View File

@ -17,7 +17,7 @@ TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, const ox::String &path)
const auto lastSlash = std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset(); const auto lastSlash = std::find(m_itemPath.rbegin(), m_itemPath.rend(), '/').offset();
m_itemName = m_itemPath.substr(lastSlash + 1); m_itemName = m_itemPath.substr(lastSlash + 1);
// init palette idx // init palette idx
const auto palPath = model()->palPath(); const auto &palPath = model()->palPath();
auto sctx = applicationData<studio::StudioContext>(m_ctx); auto sctx = applicationData<studio::StudioContext>(m_ctx);
const auto &palList = sctx->project->fileList(core::FileExt_npal + 1); const auto &palList = sctx->project->fileList(core::FileExt_npal + 1);
for (std::size_t i = 0; const auto &pal : palList) { for (std::size_t i = 0; const auto &pal : palList) {
@ -32,11 +32,11 @@ TileSheetEditorImGui::TileSheetEditorImGui(Context *ctx, const ox::String &path)
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet); m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
} }
ox::String TileSheetEditorImGui::itemName() const noexcept { const ox::String &TileSheetEditorImGui::itemName() const noexcept {
return m_itemPath; return m_itemPath;
} }
ox::String TileSheetEditorImGui::itemDisplayName() const noexcept { const ox::String &TileSheetEditorImGui::itemDisplayName() const noexcept {
return m_itemName; return m_itemName;
} }
@ -303,7 +303,7 @@ ox::Error TileSheetEditorImGui::markUnsavedChanges(int) noexcept {
void TileSheetEditorImGui::SubSheetEditor::draw() noexcept { void TileSheetEditorImGui::SubSheetEditor::draw() noexcept {
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
constexpr auto popupName = "Edit SubSheet"; constexpr auto popupName = "Edit Subsheet";
if (!m_show) { if (!m_show) {
return; return;
} }

View File

@ -58,9 +58,9 @@ class TileSheetEditorImGui: public studio::Editor {
~TileSheetEditorImGui() override = default; ~TileSheetEditorImGui() override = default;
ox::String itemName() const noexcept override; const ox::String &itemName() const noexcept override;
ox::String itemDisplayName() const noexcept override; const ox::String &itemDisplayName() const noexcept override;
void exportFile() override; void exportFile() override;

View File

@ -8,7 +8,7 @@
namespace nostalgia::studio { namespace nostalgia::studio {
ox::String Editor::itemDisplayName() const { const ox::String &Editor::itemDisplayName() const {
return itemName(); return itemName();
} }

View File

@ -30,10 +30,10 @@ class NOSTALGIASTUDIO_EXPORT Editor: public Widget {
* Returns the name of item being edited. * Returns the name of item being edited.
*/ */
[[nodiscard]] [[nodiscard]]
virtual ox::String itemName() const = 0; virtual const ox::String &itemName() const = 0;
[[nodiscard]] [[nodiscard]]
virtual ox::String itemDisplayName() const; virtual const ox::String &itemDisplayName() const;
virtual void cut(); virtual void cut();

View File

@ -14,7 +14,7 @@
namespace nostalgia::studio { namespace nostalgia::studio {
struct EditorMaker { struct EditorMaker {
using Func = std::function<class Editor*(ox::String)>; using Func = std::function<ox::Result<class Editor*>(const ox::String&)>;
ox::Vector<ox::String> fileTypes; ox::Vector<ox::String> fileTypes;
Func make; Func make;
}; };

View File

@ -110,8 +110,8 @@ ox::Error Project::writeObj(const ox::String &path, auto *obj) const noexcept {
// replace garbage last character with new line // replace garbage last character with new line
typeOut.back().value = '\n'; typeOut.back().value = '\n';
// write to FS // write to FS
static constexpr auto descPath = "/.nostalgia/type_descriptors/"; static constexpr auto descPath = "/.nostalgia/type_descriptors";
const auto typePath = ox::sfmt("{}{}", descPath, type->typeName); const auto typePath = ox::sfmt("{}/{}", descPath, type->typeName);
oxReturnError(mkdir(descPath)); oxReturnError(mkdir(descPath));
oxReturnError(writeBuff(typePath, typeOut)); oxReturnError(writeBuff(typePath, typeOut));
fileUpdated.emit(path); fileUpdated.emit(path);
@ -136,7 +136,7 @@ ox::Error Project::subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&s
case ProjectEvent::FileRecognized: case ProjectEvent::FileRecognized:
{ {
oxRequire(files, listFiles()); oxRequire(files, listFiles());
for (auto f : files) { for (const auto &f : files) {
slot(f); slot(f);
} }
connect(this, &Project::fileRecognized, tgt, slot); connect(this, &Project::fileRecognized, tgt, slot);

View File

@ -182,7 +182,7 @@ void StudioUI::drawTabBar() noexcept {
void StudioUI::drawTabs() noexcept { void StudioUI::drawTabs() noexcept {
for (auto it = m_editors.begin(); it != m_editors.end();) { for (auto it = m_editors.begin(); it != m_editors.end();) {
auto const &e = *it; auto const &e = *it;
bool open = true; auto open = true;
if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open)) { if (ImGui::BeginTabItem(e->itemDisplayName().c_str(), &open)) {
m_acitveEditor = e.get(); m_acitveEditor = e.get();
e->draw(m_ctx); e->draw(m_ctx);
@ -289,14 +289,13 @@ ox::Error StudioUI::openFile(const ox::String &path) noexcept {
if (!m_editorMakers.contains(ext)) { if (!m_editorMakers.contains(ext)) {
return OxError(1, "There is no editor for this file extension"); return OxError(1, "There is no editor for this file extension");
} }
try { auto [editor, err] = m_editorMakers[ext](path);
auto editor = m_editorMakers[ext](path); if (err) {
oxErrorf("Could not open Editor: {} ({}:{})", err.msg, err.file, err.line);
return err;
}
editor->closed.connect(this, &StudioUI::closeFile); editor->closed.connect(this, &StudioUI::closeFile);
m_editors.emplace_back(editor); m_editors.emplace_back(editor);
} catch (const ox::Exception &ex) {
oxErrorf("Could not open Editor: {} ({}:{})", ex.msg, ex.file, ex.line);
return ex.toError();
}
m_openFiles.emplace_back(path); m_openFiles.emplace_back(path);
// save to config // save to config
studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) { studio::editConfig<StudioConfig>(m_ctx, [&](StudioConfig *config) {