[nostalgia/core] Revise Palette system, give TileSheetEditor a config file
All checks were successful
Build / build (push) Successful in 2m36s

* Palettes now have names for colors
* Pages are always the same length
* Added 'Apply to all pages' button for color editor
This commit is contained in:
Gary Talent 2024-05-30 22:54:36 -05:00
parent 8826d51edd
commit bfd4bc3c41
14 changed files with 501 additions and 173 deletions

View File

@ -112,12 +112,12 @@ int tileRows(Context&) noexcept;
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank, size_t palBank,
Palette const&palette, CompactPalette const&palette,
size_t page = 0) noexcept; size_t page = 0) noexcept;
ox::Error loadSpritePalette( ox::Error loadSpritePalette(
Context &ctx, Context &ctx,
Palette const&palette, CompactPalette const&palette,
size_t page = 0) noexcept; size_t page = 0) noexcept;
ox::Error loadBgPalette( ox::Error loadBgPalette(

View File

@ -36,7 +36,43 @@ struct PaletteV2 {
ox::Vector<ox::Vector<Color16>> pages; ox::Vector<ox::Vector<Color16>> pages;
}; };
using Palette = PaletteV2; struct PaletteV3 {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
static constexpr auto TypeVersion = 3;
static constexpr auto Preloadable = true;
struct ColorInfo {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette.ColorInfo";
static constexpr auto TypeVersion = 3;
ox::String name;
constexpr ColorInfo() noexcept = default;
constexpr explicit ColorInfo(ox::StringView pName) noexcept:
name(pName) {}
constexpr explicit ColorInfo(ox::String &&pName) noexcept:
name(std::move(pName)) {}
};
ox::Vector<ColorInfo> colorInfo;
ox::Vector<ox::Vector<Color16>> pages;
};
using Palette = PaletteV3;
struct CompactPaletteV1 {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
static constexpr auto TypeVersion = 2;
static constexpr auto Preloadable = true;
ox::Vector<ox::Vector<Color16>> pages{};
};
using CompactPalette = CompactPaletteV1;
[[nodiscard]]
constexpr bool valid(Palette const&p) noexcept {
auto const colors = p.colorInfo.size();
return ox::all_of(p.pages.begin(), p.pages.end(), [colors](auto const&page) {
return page.size() == colors;
});
}
[[nodiscard]] [[nodiscard]]
constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept { constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept {
@ -47,13 +83,53 @@ constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept {
} }
[[nodiscard]] [[nodiscard]]
constexpr Color16 color(Palette const&pal, size_t idx) noexcept { constexpr Color16 color(CompactPalette const&pal, size_t page, size_t idx) noexcept {
auto constexpr page = 0; if (page < pal.pages.size() && idx < pal.pages[page].size()) [[likely]] {
return color(pal, page, idx); return pal.pages[page][idx];
}
return 0;
} }
[[nodiscard]] [[nodiscard]]
constexpr size_t colors(Palette const&pal, size_t page = 0) noexcept { constexpr Color16 color(Palette const&pal, size_t idx) noexcept {
return color(pal, 0, idx);
}
[[nodiscard]]
constexpr Color16 color(CompactPalette const&pal, size_t idx) noexcept {
return color(pal, 0, idx);
}
[[nodiscard]]
constexpr auto &colors(Palette &pal, size_t page = 0) noexcept {
return pal.pages[page];
}
[[nodiscard]]
constexpr auto &colors(CompactPalette &pal, size_t page = 0) noexcept {
return pal.pages[page];
}
[[nodiscard]]
constexpr auto &colors(Palette const&pal, size_t page = 0) noexcept {
return pal.pages[page];
}
[[nodiscard]]
constexpr auto &colors(CompactPalette const&pal, size_t page = 0) noexcept {
return pal.pages[page];
}
[[nodiscard]]
constexpr size_t colorCnt(Palette const&pal, size_t page = 0) noexcept {
if (page < pal.pages.size()) [[likely]] {
return pal.pages[page].size();
}
return 0;
}
[[nodiscard]]
constexpr size_t colorCnt(CompactPalette const&pal, size_t page = 0) noexcept {
if (page < pal.pages.size()) [[likely]] { if (page < pal.pages.size()) [[likely]] {
return pal.pages[page].size(); return pal.pages[page].size();
} }
@ -69,6 +145,15 @@ constexpr size_t largestPage(Palette const&pal) noexcept {
return out; return out;
} }
[[nodiscard]]
constexpr size_t largestPage(CompactPalette const&pal) noexcept {
size_t out{};
for (auto const&page : pal.pages) {
out = ox::max(out, page.size());
}
return out;
}
oxModelBegin(NostalgiaPalette) oxModelBegin(NostalgiaPalette)
oxModelField(colors) oxModelField(colors)
oxModelEnd() oxModelEnd()
@ -81,4 +166,17 @@ oxModelBegin(PaletteV2)
oxModelField(pages) oxModelField(pages)
oxModelEnd() oxModelEnd()
oxModelBegin(PaletteV3::ColorInfo)
oxModelField(name)
oxModelEnd()
oxModelBegin(PaletteV3)
oxModelField(colorInfo)
oxModelField(pages)
oxModelEnd()
oxModelBegin(CompactPaletteV1)
oxModelField(pages)
oxModelEnd()
} }

View File

@ -136,13 +136,13 @@ ox::Error initGfx(Context&, InitParams const&) noexcept {
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context&, Context&,
size_t palBank, size_t palBank,
Palette const&palette, CompactPalette const&palette,
size_t page) noexcept { size_t page) noexcept {
if (palette.pages.empty()) { if (palette.pages.empty()) {
return {}; return {};
} }
auto const paletteMem = MEM_BG_PALETTE + palBank * 16; auto const paletteMem = MEM_BG_PALETTE + palBank * 16;
for (auto i = 0u; i < colors(palette, page); ++i) { for (auto i = 0u; i < colorCnt(palette, page); ++i) {
paletteMem[i] = color(palette, page, i); paletteMem[i] = color(palette, page, i);
} }
return {}; return {};
@ -150,13 +150,13 @@ ox::Error loadBgPalette(
ox::Error loadSpritePalette( ox::Error loadSpritePalette(
Context&, Context&,
Palette const&palette, CompactPalette const&palette,
size_t page) noexcept { size_t page) noexcept {
if (palette.pages.empty()) { if (palette.pages.empty()) {
return {}; return {};
} }
auto const paletteMem = MEM_SPRITE_PALETTE; auto const paletteMem = MEM_SPRITE_PALETTE;
for (auto i = 0u; i < colors(palette, page); ++i) { for (auto i = 0u; i < colorCnt(palette, page); ++i) {
paletteMem[i] = color(palette, page, i); paletteMem[i] = color(palette, page, i);
} }
return {}; return {};
@ -166,14 +166,14 @@ ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank, size_t palBank,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
oxRequire(pal, keel::readObj<Palette>(keelCtx(ctx), paletteAddr)); oxRequire(pal, keel::readObj<CompactPalette>(keelCtx(ctx), paletteAddr));
return loadBgPalette(ctx, palBank, *pal, 0); return loadBgPalette(ctx, palBank, *pal, 0);
} }
ox::Error loadSpritePalette( ox::Error loadSpritePalette(
Context &ctx, Context &ctx,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
oxRequire(pal, keel::readObj<Palette>(keelCtx(ctx), paletteAddr)); oxRequire(pal, keel::readObj<CompactPalette>(keelCtx(ctx), paletteAddr));
return loadSpritePalette(ctx, *pal, 0); return loadSpritePalette(ctx, *pal, 0);
} }

View File

@ -18,6 +18,8 @@ static class: public keel::Module {
private: private:
NostalgiaPaletteToPaletteV1Converter m_nostalgiaPaletteToPaletteV1Converter; NostalgiaPaletteToPaletteV1Converter m_nostalgiaPaletteToPaletteV1Converter;
PaletteV1ToPaletteV2Converter m_paletteV1ToPaletteV2Converter; PaletteV1ToPaletteV2Converter m_paletteV1ToPaletteV2Converter;
PaletteV2ToPaletteV3Converter m_paletteV2ToPaletteV3Converter;
PaletteToCompactPaletteConverter m_paletteToCompactPaletteConverter;
TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter; TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter;
TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter; TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter;
TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter; TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter;
@ -47,6 +49,8 @@ static class: public keel::Module {
return { return {
&m_nostalgiaPaletteToPaletteV1Converter, &m_nostalgiaPaletteToPaletteV1Converter,
&m_paletteV1ToPaletteV2Converter, &m_paletteV1ToPaletteV2Converter,
&m_paletteV2ToPaletteV3Converter,
&m_paletteToCompactPaletteConverter,
&m_tileSheetV1ToTileSheetV2Converter, &m_tileSheetV1ToTileSheetV2Converter,
&m_tileSheetV2ToTileSheetV3Converter, &m_tileSheetV2ToTileSheetV3Converter,
&m_tileSheetV3ToTileSheetV4Converter, &m_tileSheetV3ToTileSheetV4Converter,
@ -71,8 +75,10 @@ static class: public keel::Module {
}, },
[](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> { [](keel::Context &ctx, ox::Buffer &buff, ox::StringView typeId) -> ox::Result<bool> {
if (typeId == ox::ModelTypeId_v<NostalgiaPalette> || if (typeId == ox::ModelTypeId_v<NostalgiaPalette> ||
typeId == ox::ModelTypeId_v<PaletteV1>) { typeId == ox::ModelTypeId_v<PaletteV1> ||
oxReturnError(keel::convertBuffToBuff<Palette>( typeId == ox::ModelTypeId_v<PaletteV2> ||
typeId == ox::ModelTypeId_v<PaletteV3>) {
oxReturnError(keel::convertBuffToBuff<CompactPalette>(
ctx, buff, ox::ClawFormat::Metal).moveTo(buff)); ctx, buff, ox::ClawFormat::Metal).moveTo(buff));
return true; return true;
} }

View File

@ -22,6 +22,27 @@ ox::Error PaletteV1ToPaletteV2Converter::convert(
return {}; return {};
} }
ox::Error PaletteV2ToPaletteV3Converter::convert(
keel::Context&,
PaletteV2 &src,
PaletteV3 &dst) const noexcept {
dst.pages = std::move(src.pages);
if (!dst.pages.empty()) {
for (size_t i = 0; i < dst.pages[0].size(); ++i) {
dst.colorInfo.emplace_back(ox::sfmt("Color {}", i));
}
}
return {};
}
ox::Error PaletteToCompactPaletteConverter::convert(
keel::Context&,
Palette &src,
CompactPalette &dst) const noexcept {
dst.pages = std::move(src.pages);
return {};
}
ox::Error TileSheetV1ToTileSheetV2Converter::convert( ox::Error TileSheetV1ToTileSheetV2Converter::convert(
keel::Context&, keel::Context&,
TileSheetV1 &src, TileSheetV1 &src,

View File

@ -21,7 +21,15 @@ class NostalgiaPaletteToPaletteV1Converter: public keel::Converter<NostalgiaPale
}; };
class PaletteV1ToPaletteV2Converter: public keel::Converter<PaletteV1, PaletteV2> { class PaletteV1ToPaletteV2Converter: public keel::Converter<PaletteV1, PaletteV2> {
ox::Error convert(keel::Context&, PaletteV1 &src, PaletteV2 &dst) const noexcept final; ox::Error convert(keel::Context&, PaletteV1 &src, PaletteV2 &dst) const noexcept final;
};
class PaletteV2ToPaletteV3Converter: public keel::Converter<PaletteV2, PaletteV3> {
ox::Error convert(keel::Context&, PaletteV2 &src, PaletteV3 &dst) const noexcept final;
};
class PaletteToCompactPaletteConverter: public keel::Converter<Palette, CompactPalette> {
ox::Error convert(keel::Context&, Palette &src, CompactPalette &dst) const noexcept final;
}; };
class TileSheetV1ToTileSheetV2Converter: public keel::Converter<TileSheetV1, TileSheetV2> { class TileSheetV1ToTileSheetV2Converter: public keel::Converter<TileSheetV1, TileSheetV2> {

View File

@ -364,7 +364,7 @@ static void loadPalette(
ox::Array<GLfloat, 1024> &palette, ox::Array<GLfloat, 1024> &palette,
size_t palOffset, size_t palOffset,
GLuint shaderPgrm, GLuint shaderPgrm,
Palette const&pal, CompactPalette const&pal,
size_t page = 0) noexcept { size_t page = 0) noexcept {
static constexpr std::size_t ColorCnt = 256; static constexpr std::size_t ColorCnt = 256;
for (auto i = palOffset; auto const c : pal.pages[page]) { for (auto i = palOffset; auto const c : pal.pages[page]) {
@ -516,7 +516,7 @@ static ox::Result<TileSheetData> normalizeTileSheet(
ox::Error loadBgPalette( ox::Error loadBgPalette(
Context &ctx, Context &ctx,
size_t palBank, size_t palBank,
Palette const&palette, CompactPalette const&palette,
size_t page) noexcept { size_t page) noexcept {
renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page); renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page);
return {}; return {};
@ -524,7 +524,7 @@ ox::Error loadBgPalette(
ox::Error loadSpritePalette( ox::Error loadSpritePalette(
Context &ctx, Context &ctx,
Palette const&palette, CompactPalette const&palette,
size_t page) noexcept { size_t page) noexcept {
ox::Array<GLfloat, 1024> pal; ox::Array<GLfloat, 1024> pal;
renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page); renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page);
@ -536,7 +536,7 @@ ox::Error loadBgPalette(
size_t palBank, size_t palBank,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx); auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr)); oxRequire(palette, readObj<CompactPalette>(kctx, paletteAddr));
renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, *palette); renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, *palette);
return {}; return {};
} }
@ -545,7 +545,7 @@ ox::Error loadSpritePalette(
Context &ctx, Context &ctx,
ox::FileAddress const&paletteAddr) noexcept { ox::FileAddress const&paletteAddr) noexcept {
auto &kctx = keelCtx(ctx.turbineCtx); auto &kctx = keelCtx(ctx.turbineCtx);
oxRequire(palette, readObj<Palette>(kctx, paletteAddr)); oxRequire(palette, readObj<CompactPalette>(kctx, paletteAddr));
ox::Array<GLfloat, 1024> pal; ox::Array<GLfloat, 1024> pal;
renderer::loadPalette(pal, 0, ctx.spriteShader, *palette); renderer::loadPalette(pal, 0, ctx.spriteShader, *palette);
return {}; return {};

View File

@ -15,11 +15,16 @@
namespace nostalgia::core { namespace nostalgia::core {
PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::CRStringView path): namespace ig = studio::ig;
Editor(path),
m_sctx(sctx), PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringView const path):
m_tctx(sctx.tctx), Editor(path),
m_pal(*keel::readObj<Palette>(keelCtx(m_tctx), ox::FileAddress(itemPath())).unwrapThrow()) { m_sctx(sctx),
m_tctx(sctx.tctx),
m_pal(*keel::readObj<Palette>(keelCtx(m_tctx), itemPath()).unwrapThrow()) {
if (!valid(m_pal)) {
throw OxException(1, "PaletteEditorImGui: invalid Palette object");
}
undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand); undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
} }
@ -36,10 +41,9 @@ void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) {
if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Alt)) { if (turbine::buttonDown(m_tctx, turbine::Key::Mod_Alt)) {
m_selectedColorRow = m_selectedColorRow =
ox::min<std::size_t>( ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), m_pal.pages.size() - 1); static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), m_pal.pages.size() - 1);
} }
} }
} }
void PaletteEditorImGui::draw(studio::StudioContext&) noexcept { void PaletteEditorImGui::draw(studio::StudioContext&) noexcept {
@ -58,7 +62,7 @@ void PaletteEditorImGui::draw(studio::StudioContext&) noexcept {
} }
ox::Error PaletteEditorImGui::saveItem() noexcept { ox::Error PaletteEditorImGui::saveItem() noexcept {
return m_sctx.project->writeObj(itemPath(), m_pal); return m_sctx.project->writeObj(itemPath(), m_pal, ox::ClawFormat::Organic);
} }
void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept { void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept {
@ -71,42 +75,39 @@ void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept {
void PaletteEditorImGui::drawColorsEditor() noexcept { void PaletteEditorImGui::drawColorsEditor() noexcept {
constexpr auto tableFlags = ImGuiTableFlags_RowBg; constexpr auto tableFlags = ImGuiTableFlags_RowBg;
auto const colorsSz = ImGui::GetContentRegionAvail(); auto const colorsSz = ImGui::GetContentRegionAvail();
auto const colorEditor = m_selectedColorRow < colors(m_pal, m_page); auto const colorEditor = m_selectedColorRow < colorCnt(m_pal, m_page);
auto const colorEditorWidth = 220; auto const colorEditorWidth = 220;
static constexpr auto toolbarHeight = 40; static constexpr auto toolbarHeight = 40;
{ {
auto const sz = ImVec2(70, 24); auto const sz = ImVec2(70, 24);
if (ImGui::Button("Add", sz)) { if (ImGui::Button("Add", sz)) {
auto const colorSz = static_cast<int>(colors(m_pal, m_page)); auto const colorSz = colorCnt(m_pal, m_page);
constexpr Color16 c = 0; constexpr Color16 c = 0;
std::ignore = undoStack()->push(ox::make_unique<AddColorCommand>(&m_pal, c, m_page, colorSz)); std::ignore = pushCommand<AddColorCommand>(m_pal, c, colorSz);
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page)); ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page));
{ {
if (ImGui::Button("Remove", sz)) { if (ImGui::Button("Remove", sz)) {
std::ignore = undoStack()->push( std::ignore = pushCommand<RemoveColorCommand>(m_pal, m_selectedColorRow);
ox::make_unique<RemoveColorCommand>( m_selectedColorRow = ox::min(colorCnt(m_pal, m_page) - 1, m_selectedColorRow);
&m_pal,
color(m_pal, m_page, static_cast<std::size_t>(m_selectedColorRow)),
m_page,
static_cast<int>(m_selectedColorRow)));
m_selectedColorRow = ox::min(colors(m_pal, m_page) - 1, m_selectedColorRow);
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(m_selectedColorRow <= 0); ImGui::BeginDisabled(m_selectedColorRow <= 0);
{ {
if (ImGui::Button("Move Up", sz)) { if (ImGui::Button("Move Up", sz)) {
std::ignore = undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_page, m_selectedColorRow, -1)); std::ignore = pushCommand<MoveColorCommand>(
m_pal, m_page, m_selectedColorRow, m_selectedColorRow - 1);
--m_selectedColorRow; --m_selectedColorRow;
} }
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page) - 1); ImGui::BeginDisabled(m_selectedColorRow >= colorCnt(m_pal, m_page) - 1);
{ {
if (ImGui::Button("Move Down", sz)) { if (ImGui::Button("Move Down", sz)) {
std::ignore = undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_page, m_selectedColorRow, 1)); std::ignore = pushCommand<MoveColorCommand>(
m_pal, m_page, m_selectedColorRow, m_selectedColorRow + 1);
++m_selectedColorRow; ++m_selectedColorRow;
} }
} }
@ -114,8 +115,13 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
} }
auto const tblWidth = (colorsSz.x - colorEditorWidth - 8) * colorEditor; auto const tblWidth = (colorsSz.x - static_cast<float>(colorEditorWidth) - 8.f)
ImGui::BeginTable("Colors", 5, tableFlags, ImVec2(tblWidth, colorsSz.y - (toolbarHeight + 5))); * static_cast<float>(colorEditor);
ImGui::BeginTable(
"Colors",
5,
tableFlags,
ImVec2(tblWidth, colorsSz.y - (toolbarHeight + 5)));
{ {
ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25); ImGui::TableSetupColumn("Idx", ImGuiTableColumnFlags_WidthFixed, 25);
ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Red", ImGuiTableColumnFlags_WidthFixed, 50);
@ -123,17 +129,19 @@ void PaletteEditorImGui::drawColorsEditor() noexcept {
ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Blue", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::TableSetupColumn("Color Preview", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Color Preview", ImGuiTableColumnFlags_NoHide);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
for (auto i = 0u; auto const c : m_pal.pages[m_page]) { for (auto i = 0u; auto const&c : m_pal.pages[m_page]) {
ImGui::PushID(static_cast<int>(i)); ImGui::PushID(static_cast<int>(i));
ImGui::TableNextRow(); ImGui::TableNextRow();
drawColumn(i); drawColumn(i + 1);
drawColumn(red16(c)); drawColumn(red16(c));
drawColumn(green16(c)); drawColumn(green16(c));
drawColumn(blue16(c)); drawColumn(blue16(c));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
auto const ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1)); auto const ic = ImGui::GetColorU32(
ImVec4(redf(c), greenf(c), bluef(c), 1));
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic); ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
if (ImGui::Selectable("##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) { if (ImGui::Selectable(
"##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) {
m_selectedColorRow = i; m_selectedColorRow = i;
} }
ImGui::PopID(); ImGui::PopID();
@ -155,28 +163,30 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
constexpr auto toolbarHeight = 40; constexpr auto toolbarHeight = 40;
auto const btnSz = ImVec2(paneSz.x / 3 - 5.5f, 24); auto const btnSz = ImVec2(paneSz.x / 3 - 5.5f, 24);
if (ImGui::Button("Add", btnSz)) { if (ImGui::Button("Add", btnSz)) {
std::ignore = undoStack()->push(ox::make_unique<AddPageCommand>(m_pal)); std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
m_page = m_pal.pages.size() - 1; m_page = m_pal.pages.size() - 1;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Remove", btnSz)) { if (ImGui::Button("Remove", btnSz)) {
std::ignore = undoStack()->push(ox::make_unique<RemovePageCommand>(m_pal, m_page)); std::ignore = pushCommand<RemovePageCommand>(m_pal, m_page);
m_page = std::min(m_page, m_pal.pages.size() - 1); m_page = std::min(m_page, m_pal.pages.size() - 1);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Duplicate", btnSz)) { if (ImGui::Button("Duplicate", btnSz)) {
std::ignore = undoStack()->push(ox::make_unique<DuplicatePageCommand>(m_pal, m_page, m_pal.pages.size())); std::ignore = pushCommand<DuplicatePageCommand>(m_pal, m_page, m_pal.pages.size());
} }
ImGui::BeginTable("PageSelect", 2, tableFlags, ImVec2(paneSz.x, paneSz.y - (toolbarHeight + 5))); ImGui::BeginTable(
"PageSelect",
2,
tableFlags,
ImVec2(paneSz.x, paneSz.y - (toolbarHeight + 5)));
{ {
ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60); ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60);
ImGui::TableSetupColumn("Colors", ImGuiTableColumnFlags_NoHide);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
for (auto i = 0u; i < m_pal.pages.size(); ++i) { for (auto i = 0u; i < m_pal.pages.size(); ++i) {
ImGui::PushID(static_cast<int>(i)); ImGui::PushID(static_cast<int>(i));
ImGui::TableNextRow(); ImGui::TableNextRow();
drawColumn(i + 1); drawColumn(i + 1);
drawColumn(colors(m_pal, i));
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) { if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) {
m_page = i; m_page = i;
@ -193,20 +203,33 @@ void PaletteEditorImGui::drawColorEditor() noexcept {
int g = green16(c); int g = green16(c);
int b = blue16(c); int b = blue16(c);
int const a = alpha16(c); int const a = alpha16(c);
auto const&currentName = m_pal.colorInfo[m_selectedColorRow].name;
ox::IString<50> name;
name = currentName;
ImGui::InputText("Name", name.data(), name.cap() + 1);
ImGui::Separator();
ImGui::InputInt("Red", &r, 1, 5); ImGui::InputInt("Red", &r, 1, 5);
ImGui::InputInt("Green", &g, 1, 5); ImGui::InputInt("Green", &g, 1, 5);
ImGui::InputInt("Blue", &b, 1, 5); ImGui::InputInt("Blue", &b, 1, 5);
if (ig::PushButton("Apply to all pages", {-1, ig::BtnSz.y})) {
std::ignore = pushCommand<ApplyColorAllPagesCommand>(
m_pal, m_page, m_selectedColorRow);
}
r = ox::max(r, 0);
g = ox::max(g, 0);
b = ox::max(b, 0);
auto const newColor = color16(r, g, b, a); auto const newColor = color16(r, g, b, a);
if (c != newColor) { if (c != newColor) {
std::ignore = undoStack()->push(ox::make_unique<UpdateColorCommand>( std::ignore = pushCommand<UpdateColorCommand>(m_pal, m_page, m_selectedColorRow, newColor);
&m_pal, m_page, static_cast<int>(m_selectedColorRow), c, newColor)); }
if (currentName != name.data()) {
std::ignore = pushCommand<UpdateColorInfoCommand>(
m_pal, m_selectedColorRow, Palette::ColorInfo{name.data()});
} }
} }
ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept { ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept {
if (dynamic_cast<AddPageCommand const*>(cmd)) { if (dynamic_cast<RemovePageCommand const*>(cmd)) {
m_page = m_pal.pages.size() - 1;
} else if (dynamic_cast<RemovePageCommand const*>(cmd)) {
m_page = ox::min(m_page, m_pal.pages.size() - 1); m_page = ox::min(m_page, m_pal.pages.size() - 1);
} else if (auto const dupPageCmd = dynamic_cast<DuplicatePageCommand const*>(cmd)) { } else if (auto const dupPageCmd = dynamic_cast<DuplicatePageCommand const*>(cmd)) {
m_page = ox::clamp<size_t>(dupPageCmd->insertIdx(), 0, m_pal.pages.size() - 1); m_page = ox::clamp<size_t>(dupPageCmd->insertIdx(), 0, m_pal.pages.size() - 1);

View File

@ -21,7 +21,7 @@ class PaletteEditorImGui: public studio::Editor {
size_t m_page = 0; size_t m_page = 0;
public: public:
PaletteEditorImGui(studio::StudioContext &sctx, ox::CRStringView path); PaletteEditorImGui(studio::StudioContext &sctx, ox::StringView path);
void keyStateChanged(turbine::Key key, bool down) override; void keyStateChanged(turbine::Key key, bool down) override;

View File

@ -6,21 +6,44 @@
namespace nostalgia::core { namespace nostalgia::core {
AddPageCommand::AddPageCommand(Palette &pal) noexcept: ApplyColorAllPagesCommand::ApplyColorAllPagesCommand(Palette &pal, size_t const page, size_t const idx):
m_pal(pal) { m_pal(pal),
m_page(page),
m_idx(idx),
m_origColors([this] {
ox::Vector<Color16> colors;
colors.reserve(m_pal.pages.size());
for (auto const&p : m_pal.pages) {
colors.emplace_back(p[m_idx]);
}
return colors;
}()) {
auto const c = color(m_pal, m_page, m_idx);
if (ox::all_of(m_pal.pages.begin(), m_pal.pages.end(), [this, c](ox::SpanView<Color16> const page) {
return page[m_idx] == c;
})) {
throw studio::NoChangesException();
}
} }
int AddPageCommand::commandId() const noexcept { int ApplyColorAllPagesCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::AddPage); return static_cast<int>(PaletteEditorCommandId::ApplyColorAllPages);
} }
ox::Error AddPageCommand::redo() noexcept { ox::Error ApplyColorAllPagesCommand::redo() noexcept {
m_pal.pages.emplace_back(); auto const c = color(m_pal, m_page, m_idx);
for (auto &page : m_pal.pages) {
page[m_idx] = c;
}
return {}; return {};
} }
ox::Error AddPageCommand::undo() noexcept { ox::Error ApplyColorAllPagesCommand::undo() noexcept {
return m_pal.pages.erase(static_cast<std::size_t>(m_pal.pages.size() - 1)).error; for (size_t p = 0u; auto &page : m_pal.pages) {
page[m_idx] = m_origColors[p];
++p;
}
return {};
} }
@ -55,78 +78,134 @@ size_t DuplicatePageCommand::insertIdx() const noexcept {
RemovePageCommand::RemovePageCommand(Palette &pal, size_t idx) noexcept: RemovePageCommand::RemovePageCommand(Palette &pal, size_t idx) noexcept:
m_pal(pal), m_pal(pal),
m_idx(idx) { m_idx(idx) {}
}
int RemovePageCommand::commandId() const noexcept { int RemovePageCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::RemovePage); return static_cast<int>(PaletteEditorCommandId::RemovePage);
} }
ox::Error RemovePageCommand::redo() noexcept { ox::Error RemovePageCommand::redo() noexcept {
m_page = std::move(m_pal.pages[m_idx]); m_page = std::move(colors(m_pal, m_idx));
return m_pal.pages.erase(static_cast<std::size_t>(m_idx)).error; return m_pal.pages.erase(static_cast<std::size_t>(m_idx)).error;
} }
ox::Error RemovePageCommand::undo() noexcept { ox::Error RemovePageCommand::undo() noexcept {
m_pal.pages.insert(m_idx, std::move(m_page)); m_pal.pages.emplace(m_idx, std::move(m_page));
return {}; return {};
} }
AddColorCommand::AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept { AddColorCommand::AddColorCommand(Palette &pal, Color16 const color, size_t const idx) noexcept:
m_pal = pal; m_pal(pal),
m_color = color; m_color(color),
m_page = page; m_idx(idx) {}
m_idx = idx;
}
int AddColorCommand::commandId() const noexcept { int AddColorCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::AddColor); return static_cast<int>(PaletteEditorCommandId::AddColor);
} }
ox::Error AddColorCommand::redo() noexcept { ox::Error AddColorCommand::redo() noexcept {
m_pal->pages[m_page].insert(static_cast<std::size_t>(m_idx), m_color); for (auto &page : m_pal.pages) {
page.emplace(static_cast<size_t>(m_idx), m_color);
}
return {}; return {};
} }
ox::Error AddColorCommand::undo() noexcept { ox::Error AddColorCommand::undo() noexcept {
return m_pal->pages[m_page].erase(static_cast<std::size_t>(m_idx)).error; for (auto &page : m_pal.pages) {
oxReturnError(page.erase(static_cast<std::size_t>(m_idx)));
}
return {};
} }
RemoveColorCommand::RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept { RemoveColorCommand::RemoveColorCommand(Palette &pal, size_t const idx) noexcept:
m_pal = pal; m_pal(pal),
m_color = color; m_idx(idx),
m_page = page; m_colors([this] {
m_idx = idx; ox::Vector<Color16> colors;
} colors.reserve(m_pal.pages.size());
for (auto const&p : m_pal.pages) {
colors.emplace_back(p[m_idx]);
}
return colors;
}()) {}
int RemoveColorCommand::commandId() const noexcept { int RemoveColorCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::RemoveColor); return static_cast<int>(PaletteEditorCommandId::RemoveColor);
} }
ox::Error RemoveColorCommand::redo() noexcept { ox::Error RemoveColorCommand::redo() noexcept {
return m_pal->pages[m_page].erase(static_cast<std::size_t>(m_idx)).error; for (auto &page : m_pal.pages) {
oxReturnError(page.erase(m_idx));
}
return {};
} }
ox::Error RemoveColorCommand::undo() noexcept { ox::Error RemoveColorCommand::undo() noexcept {
m_pal->pages[m_page].insert(static_cast<std::size_t>(m_idx), m_color); for (size_t p = 0; auto &page : m_pal.pages) {
page.emplace(m_idx, m_colors[p]);
++p;
}
return {}; return {};
} }
UpdateColorInfoCommand::UpdateColorInfoCommand(
Palette &pal,
size_t idx,
Palette::ColorInfo newColorInfo):
m_pal(pal),
m_idx(idx),
m_altColorInfo(std::move(newColorInfo)) {
if (m_pal.colorInfo[m_idx].name == m_altColorInfo.name) {
throw studio::NoChangesException();
}
}
bool UpdateColorInfoCommand::mergeWith(UndoCommand const&cmd) noexcept {
if (cmd.commandId() != static_cast<int>(PaletteEditorCommandId::UpdateColor)) {
return false;
}
auto ucCmd = static_cast<UpdateColorInfoCommand const*>(&cmd);
if (m_idx != ucCmd->m_idx) {
return false;
}
return true;
}
[[nodiscard]]
int UpdateColorInfoCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::UpdateColor);
}
ox::Error UpdateColorInfoCommand::redo() noexcept {
swap();
return {};
}
ox::Error UpdateColorInfoCommand::undo() noexcept {
swap();
return {};
}
void UpdateColorInfoCommand::swap() noexcept {
std::swap(m_pal.colorInfo[m_idx], m_altColorInfo);
}
UpdateColorCommand::UpdateColorCommand( UpdateColorCommand::UpdateColorCommand(
Palette *pal, Palette &pal,
size_t page, size_t page,
int idx, size_t idx,
Color16 oldColor, Color16 newColor):
Color16 newColor) noexcept { m_pal(pal),
m_pal = pal; m_page(page),
m_page = page; m_idx(idx),
m_idx = idx; m_altColor(newColor) {
m_oldColor = oldColor; if (color(m_pal, m_page, m_idx) == newColor) {
m_newColor = newColor; throw studio::NoChangesException();
//setObsolete(m_oldColor == m_newColor); }
} }
bool UpdateColorCommand::mergeWith(UndoCommand const&cmd) noexcept { bool UpdateColorCommand::mergeWith(UndoCommand const&cmd) noexcept {
@ -137,7 +216,6 @@ bool UpdateColorCommand::mergeWith(UndoCommand const&cmd) noexcept {
if (m_idx != ucCmd->m_idx) { if (m_idx != ucCmd->m_idx) {
return false; return false;
} }
m_newColor = ucCmd->m_newColor;
return true; return true;
} }
@ -147,41 +225,46 @@ int UpdateColorCommand::commandId() const noexcept {
} }
ox::Error UpdateColorCommand::redo() noexcept { ox::Error UpdateColorCommand::redo() noexcept {
m_pal->pages[m_page][static_cast<std::size_t>(m_idx)] = m_newColor; swap();
return {}; return {};
} }
ox::Error UpdateColorCommand::undo() noexcept { ox::Error UpdateColorCommand::undo() noexcept {
m_pal->pages[m_page][static_cast<std::size_t>(m_idx)] = m_oldColor; swap();
return {}; return {};
} }
void UpdateColorCommand::swap() noexcept {
MoveColorCommand::MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept { auto &dst = colors(m_pal, m_page)[m_idx];
m_pal = pal; std::swap(dst, m_altColor);
m_page = page;
m_idx = idx;
m_offset = offset;
} }
MoveColorCommand::MoveColorCommand(
Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept:
m_pal(pal),
m_page(page),
m_srcIdx(srcIdx),
m_dstIdx(dstIdx) {}
int MoveColorCommand::commandId() const noexcept { int MoveColorCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::MoveColor); return static_cast<int>(PaletteEditorCommandId::MoveColor);
} }
ox::Error MoveColorCommand::redo() noexcept { ox::Error MoveColorCommand::redo() noexcept {
moveColor(static_cast<int>(m_idx), m_offset); moveColor(m_srcIdx, m_dstIdx);
return {}; return {};
} }
ox::Error MoveColorCommand::undo() noexcept { ox::Error MoveColorCommand::undo() noexcept {
moveColor(static_cast<int>(m_idx) + m_offset, -m_offset); moveColor(m_dstIdx, m_srcIdx);
return {}; return {};
} }
void MoveColorCommand::moveColor(int idx, int offset) noexcept { void MoveColorCommand::moveColor(size_t srcIdx, size_t dstIdx) noexcept {
const auto c = m_pal->pages[m_page][static_cast<std::size_t>(idx)]; auto const c = color(m_pal, m_page, srcIdx);
std::ignore = m_pal->pages[m_page].erase(static_cast<std::size_t>(idx)); std::ignore = colors(m_pal, m_page).erase(srcIdx);
m_pal->pages[m_page].insert(static_cast<std::size_t>(idx + offset), c); colors(m_pal, m_page).emplace(dstIdx, c);
} }
} }

View File

@ -13,24 +13,28 @@
namespace nostalgia::core { namespace nostalgia::core {
enum class PaletteEditorCommandId { enum class PaletteEditorCommandId {
AddPage, ApplyColorAllPages,
DuplicatePage, DuplicatePage,
RemovePage, RemovePage,
AddColor, AddColor,
RemoveColor, RemoveColor,
UpdateColorInfo,
UpdateColor, UpdateColor,
MoveColor, MoveColor,
}; };
class AddPageCommand: public studio::UndoCommand { class ApplyColorAllPagesCommand: public studio::UndoCommand {
private: private:
Palette &m_pal; Palette &m_pal;
size_t const m_page{};
size_t const m_idx{};
ox::Vector<Color16> const m_origColors;
public: public:
AddPageCommand(Palette &pal) noexcept; ApplyColorAllPagesCommand(Palette &pal, size_t page, size_t idx);
~AddPageCommand() noexcept override = default; ~ApplyColorAllPagesCommand() noexcept override = default;
[[nodiscard]] [[nodiscard]]
int commandId() const noexcept final; int commandId() const noexcept final;
@ -38,7 +42,6 @@ class AddPageCommand: public studio::UndoCommand {
ox::Error redo() noexcept final; ox::Error redo() noexcept final;
ox::Error undo() noexcept final; ox::Error undo() noexcept final;
}; };
class DuplicatePageCommand: public studio::UndoCommand { class DuplicatePageCommand: public studio::UndoCommand {
@ -86,13 +89,12 @@ class RemovePageCommand: public studio::UndoCommand {
class AddColorCommand: public studio::UndoCommand { class AddColorCommand: public studio::UndoCommand {
private: private:
Palette *m_pal = nullptr; Palette &m_pal;
Color16 m_color = 0; Color16 m_color = 0;
int m_idx = -1; size_t const m_idx = 0;
size_t m_page = 0;
public: public:
AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept; AddColorCommand(Palette &pal, Color16 color, size_t idx) noexcept;
~AddColorCommand() noexcept override = default; ~AddColorCommand() noexcept override = default;
@ -107,13 +109,12 @@ class AddColorCommand: public studio::UndoCommand {
class RemoveColorCommand: public studio::UndoCommand { class RemoveColorCommand: public studio::UndoCommand {
private: private:
Palette *m_pal = nullptr; Palette &m_pal;
Color16 m_color = 0; size_t const m_idx = 0;
size_t m_page = 0; ox::Vector<Color16> const m_colors;
int m_idx = -1;
public: public:
RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept; RemoveColorCommand(Palette &pal, size_t idx) noexcept;
~RemoveColorCommand() noexcept override = default; ~RemoveColorCommand() noexcept override = default;
@ -126,16 +127,48 @@ class RemoveColorCommand: public studio::UndoCommand {
}; };
class UpdateColorCommand: public studio::UndoCommand { class UpdateColorInfoCommand: public studio::UndoCommand {
private: private:
Palette *m_pal = nullptr; Palette &m_pal;
Color16 m_oldColor = 0; size_t const m_idx{};
Color16 m_newColor = 0; Palette::ColorInfo m_altColorInfo;
size_t m_page = 0;
int m_idx = -1;
public: public:
UpdateColorCommand(Palette *pal, size_t page, int idx, Color16 oldColor, Color16 newColor) noexcept; UpdateColorInfoCommand(
Palette &pal,
size_t idx,
Palette::ColorInfo newColorInfo);
~UpdateColorInfoCommand() noexcept override = default;
[[nodiscard]]
bool mergeWith(const UndoCommand &cmd) noexcept final;
[[nodiscard]]
int commandId() const noexcept final;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
private:
void swap() noexcept;
};
class UpdateColorCommand: public studio::UndoCommand {
private:
Palette &m_pal;
size_t const m_page = 0;
size_t const m_idx{};
Color16 m_altColor{};
public:
UpdateColorCommand(
Palette &pal,
size_t page,
size_t idx,
Color16 newColor);
~UpdateColorCommand() noexcept override = default; ~UpdateColorCommand() noexcept override = default;
@ -149,17 +182,20 @@ class UpdateColorCommand: public studio::UndoCommand {
ox::Error undo() noexcept final; ox::Error undo() noexcept final;
private:
void swap() noexcept;
}; };
class MoveColorCommand: public studio::UndoCommand { class MoveColorCommand: public studio::UndoCommand {
private: private:
Palette *m_pal = nullptr; Palette &m_pal;
size_t m_page = 0; size_t const m_page = 0;
std::size_t m_idx = 0; std::size_t const m_srcIdx = 0;
int m_offset = 0; std::size_t const m_dstIdx = 0;
public: public:
MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept; MoveColorCommand(Palette &pal, size_t page, size_t srcIdx, size_t dstIdx) noexcept;
~MoveColorCommand() noexcept override = default; ~MoveColorCommand() noexcept override = default;
@ -172,7 +208,7 @@ class MoveColorCommand: public studio::UndoCommand {
ox::Error undo() noexcept override; ox::Error undo() noexcept override;
private: private:
void moveColor(int idx, int offset) noexcept; void moveColor(size_t srcIdx, size_t dstIdx) noexcept;
}; };
} }

View File

@ -7,6 +7,7 @@
#include <ox/std/point.hpp> #include <ox/std/point.hpp>
#include <keel/media.hpp> #include <keel/media.hpp>
#include <studio/studio.hpp>
#include "tilesheeteditor-imgui.hpp" #include "tilesheeteditor-imgui.hpp"
@ -14,6 +15,36 @@ namespace nostalgia::core {
namespace ig = studio::ig; namespace ig = studio::ig;
static ox::String configName(ox::StringView str) noexcept {
auto out = ox::String{str};
for (auto &c : out) {
if (c == '/' || c == '\\') {
c = '%';
}
}
return out;
}
struct TileSheetEditorConfig {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetEditorConfig";
static constexpr auto TypeVersion = 1;
TileSheet::SubSheetIdx activeSubsheet{};
};
oxModelBegin(TileSheetEditorConfig)
oxModelFieldRename(activeSubsheet, active_subsheet)
oxModelEnd()
struct TileSheetEditorConfigs {
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetEditorConfigs";
static constexpr auto TypeVersion = 1;
ox::HashMap<ox::String, TileSheetEditorConfig> configs;
};
oxModelBegin(TileSheetEditorConfigs)
oxModelField(configs)
oxModelEnd()
static ox::Vector<uint32_t> normalizePixelSizes( static ox::Vector<uint32_t> normalizePixelSizes(
ox::Vector<uint8_t> const&inPixels, ox::Vector<uint8_t> const&inPixels,
int const bpp) noexcept { int const bpp) noexcept {
@ -75,7 +106,7 @@ static ox::Error toPngFile(
8))); 8)));
} }
TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRStringView path): TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringView const path):
Editor(path), Editor(path),
m_sctx(sctx), m_sctx(sctx),
m_tctx(m_sctx.tctx), m_tctx(m_sctx.tctx),
@ -87,6 +118,12 @@ TileSheetEditorImGui::TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRSt
m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet); m_subsheetEditor.inputSubmitted.connect(this, &TileSheetEditorImGui::updateActiveSubsheet);
m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng); m_exportMenu.inputSubmitted.connect(this, &TileSheetEditorImGui::exportSubhseetToPng);
m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection); m_model.paletteChanged.connect(this, &TileSheetEditorImGui::setPaletteSelection);
// load config
auto const&config = studio::readConfig<TileSheetEditorConfig>(
keelCtx(m_sctx), configName(itemPath()));
if (config.ok()) {
m_model.setActiveSubsheet(validateSubSheetIdx(m_model.img(), config.value.activeSubsheet));
}
} }
void TileSheetEditorImGui::exportFile() { void TileSheetEditorImGui::exportFile() {
@ -120,7 +157,7 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
auto const popupOpen = m_subsheetEditor.isOpen() && m_exportMenu.isOpen(); auto const popupOpen = m_subsheetEditor.isOpen() && m_exportMenu.isOpen();
auto const pal = m_model.pal(); auto const pal = m_model.pal();
if (!popupOpen) { if (!popupOpen) {
auto const colorCnt = pal.pages[m_model.palettePage()].size(); auto const colorCnt = core::colorCnt(pal, m_model.palettePage());
if (key == turbine::Key::Alpha_D) { if (key == turbine::Key::Alpha_D) {
m_tool = TileSheetTool::Draw; m_tool = TileSheetTool::Draw;
setCopyEnabled(false); setCopyEnabled(false);
@ -144,7 +181,8 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
static_cast<uint32_t>(key - turbine::Key::Num_1), m_model.pal().pages.size() - 1); static_cast<uint32_t>(key - turbine::Key::Num_1), m_model.pal().pages.size() - 1);
m_model.setPalettePage(idx); m_model.setPalettePage(idx);
} else if (key <= turbine::Key::Num_0 + colorCnt) { } else if (key <= turbine::Key::Num_0 + colorCnt) {
auto const idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1); auto const idx = ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1);
m_view.setPalIdx(idx); m_view.setPalIdx(idx);
} }
} else if (key == turbine::Key::Num_0) { } else if (key == turbine::Key::Num_0) {
@ -208,7 +246,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
auto const&parent = m_model.activeSubSheet(); auto const&parent = m_model.activeSubSheet();
m_model.addSubsheet(insertOnIdx); m_model.addSubsheet(insertOnIdx);
insertOnIdx.emplace_back(parent.subsheets.size() - 1); insertOnIdx.emplace_back(parent.subsheets.size() - 1);
m_model.setActiveSubsheet(insertOnIdx); setActiveSubsheet(insertOnIdx);
} }
ImGui::SameLine(); ImGui::SameLine();
if (ig::PushButton("-", btnSize)) { if (ig::PushButton("-", btnSize)) {
@ -241,7 +279,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
} }
ImGui::EndChild(); ImGui::EndChild();
m_subsheetEditor.draw(m_tctx); m_subsheetEditor.draw(m_tctx);
m_exportMenu.draw(m_sctx); m_exportMenu.draw(m_tctx);
} }
void TileSheetEditorImGui::drawSubsheetSelector( void TileSheetEditorImGui::drawSubsheetSelector(
@ -261,7 +299,7 @@ void TileSheetEditorImGui::drawSubsheetSelector(
auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags); auto const open = ImGui::TreeNodeEx(lbl.c_str(), flags);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked()) {
m_model.setActiveSubsheet(path); setActiveSubsheet(path);
} }
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) { if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
showSubsheetEditor(); showSubsheetEditor();
@ -432,31 +470,35 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
ImGui::Indent(-20); ImGui::Indent(-20);
} }
// header // header
if (ImGui::BeginTable("PaletteTable", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { if (ImGui::BeginTable(
"PaletteTable", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("No.", 0, 0.45f); ImGui::TableSetupColumn("No.", 0, 0.45f);
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Color16", 0, 3); ImGui::TableSetupColumn("Color16", 0, 3);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
{ {
auto const&pal = m_model.pal(); auto const&pal = m_model.pal();
for (auto i = 0u; auto c: pal.pages[m_model.palettePage()]) { if (pal.pages.size() > m_model.palettePage()) {
ImGui::PushID(static_cast<int>(i)); for (auto i = 0u; auto const&c: pal.pages[m_model.palettePage()]) {
// Column: color idx ImGui::PushID(static_cast<int>(i));
ImGui::TableNextColumn(); // Column: color idx
auto const label = ox::itoa(i + 1); ImGui::TableNextColumn();
auto const rowSelected = i == m_view.palIdx(); auto const label = ox::itoa(i + 1);
if (ImGui::Selectable(label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) { auto const rowSelected = i == m_view.palIdx();
m_view.setPalIdx(i); if (ImGui::Selectable(
label.c_str(), rowSelected, ImGuiSelectableFlags_SpanAllColumns)) {
m_view.setPalIdx(i);
}
// Column: color RGB
ImGui::TableNextColumn();
auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1));
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
ImGui::TableNextColumn();
ImGui::Text("(%02d, %02d, %02d)", red16(c), green16(c), blue16(c));
ImGui::TableNextRow();
ImGui::PopID();
++i;
} }
// Column: color RGB
ImGui::TableNextColumn();
auto ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1));
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
ImGui::TableNextColumn();
ImGui::Text("(%02d, %02d, %02d)", red16(c), green16(c), blue16(c));
ImGui::TableNextRow();
ImGui::PopID();
++i;
} }
} }
ImGui::EndTable(); ImGui::EndTable();
@ -480,12 +522,20 @@ ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept {
return {}; return {};
} }
void TileSheetEditorImGui::setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept {
m_model.setActiveSubsheet(path);
studio::editConfig<TileSheetEditorConfig>(keelCtx(m_sctx), configName(itemPath()),
[&path](TileSheetEditorConfig *config) {
config->activeSubsheet = std::move(path);
});
}
ox::Error TileSheetEditorImGui::markUnsavedChanges(studio::UndoCommand const*) noexcept { ox::Error TileSheetEditorImGui::markUnsavedChanges(studio::UndoCommand const*) noexcept {
setUnsavedChanges(true); setUnsavedChanges(true);
return {}; return {};
} }
void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &sctx) noexcept { void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &tctx) noexcept {
constexpr auto popupName = "Edit Subsheet"; constexpr auto popupName = "Edit Subsheet";
if (!m_show) { if (!m_show) {
return; return;
@ -494,7 +544,7 @@ void TileSheetEditorImGui::SubSheetEditor::draw(turbine::Context &sctx) noexcept
auto constexpr popupWidth = 235.f; auto constexpr popupWidth = 235.f;
auto const popupHeight = modSize ? 130.f : 85.f; auto const popupHeight = modSize ? 130.f : 85.f;
auto const popupSz = ImVec2(popupWidth, popupHeight); auto const popupSz = ImVec2(popupWidth, popupHeight);
if (ig::BeginPopup(sctx, popupName, m_show, popupSz)) { if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
ImGui::InputText("Name", m_name.data(), m_name.cap()); ImGui::InputText("Name", m_name.data(), m_name.cap());
if (modSize) { if (modSize) {
ImGui::InputInt("Columns", &m_cols); ImGui::InputInt("Columns", &m_cols);
@ -511,7 +561,7 @@ void TileSheetEditorImGui::SubSheetEditor::close() noexcept {
m_show = false; m_show = false;
} }
void TileSheetEditorImGui::ExportMenu::draw(studio::StudioContext &sctx) noexcept { void TileSheetEditorImGui::ExportMenu::draw(turbine::Context &tctx) noexcept {
constexpr auto popupName = "Export Tile Sheet"; constexpr auto popupName = "Export Tile Sheet";
if (!m_show) { if (!m_show) {
return; return;
@ -519,7 +569,7 @@ void TileSheetEditorImGui::ExportMenu::draw(studio::StudioContext &sctx) noexcep
constexpr auto popupWidth = 235.f; constexpr auto popupWidth = 235.f;
constexpr auto popupHeight = 85.f; constexpr auto popupHeight = 85.f;
constexpr auto popupSz = ImVec2(popupWidth, popupHeight); constexpr auto popupSz = ImVec2(popupWidth, popupHeight);
if (ig::BeginPopup(sctx.tctx, popupName, m_show, popupSz)) { if (ig::BeginPopup(tctx, popupName, m_show, popupSz)) {
ImGui::InputInt("Scale", &m_scale); ImGui::InputInt("Scale", &m_scale);
m_scale = ox::clamp(m_scale, 1, 50); m_scale = ox::clamp(m_scale, 1, 50);
if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) { if (ig::PopupControlsOkCancel(popupWidth, m_show) == ig::PopupResponse::OK) {

View File

@ -48,7 +48,7 @@ class TileSheetEditorImGui: public studio::Editor {
m_show = true; m_show = true;
m_scale = 5; m_scale = 5;
} }
void draw(studio::StudioContext &sctx) noexcept; void draw(turbine::Context &sctx) noexcept;
void close() noexcept; void close() noexcept;
[[nodiscard]] [[nodiscard]]
inline bool isOpen() const noexcept { return m_show; } inline bool isOpen() const noexcept { return m_show; }
@ -67,7 +67,7 @@ class TileSheetEditorImGui: public studio::Editor {
TileSheetTool m_tool = TileSheetTool::Draw; TileSheetTool m_tool = TileSheetTool::Draw;
public: public:
TileSheetEditorImGui(studio::StudioContext &sctx, ox::CRStringView path); TileSheetEditorImGui(studio::StudioContext &sctx, ox::StringView path);
~TileSheetEditorImGui() override = default; ~TileSheetEditorImGui() override = default;
@ -111,6 +111,8 @@ class TileSheetEditorImGui: public studio::Editor {
private: private:
ox::Error markUnsavedChanges(studio::UndoCommand const*) noexcept; ox::Error markUnsavedChanges(studio::UndoCommand const*) noexcept;
void setActiveSubsheet(TileSheet::SubSheetIdx path) noexcept;
}; };
} }

View File

@ -27,7 +27,8 @@
namespace nostalgia::core { namespace nostalgia::core {
Palette const TileSheetEditorModel::s_defaultPalette = { Palette const TileSheetEditorModel::s_defaultPalette = {
.pages = {ox::Vector<Color16>(128)}, .colorInfo = {ox::Vector<Palette::ColorInfo>{{}}},
.pages = {{ox::Vector<Color16>(128)}},
}; };
// delete pixels of all non-leaf nodes // delete pixels of all non-leaf nodes