Squashed 'deps/nostalgia/' changes from b46cb65b..5433fd9b
5433fd9b [nostalgia/core] Add new version of Palette with pages git-subtree-dir: deps/nostalgia git-subtree-split: 5433fd9b1dd9238260be0a37317610c467fd0206
This commit is contained in:
		@@ -25,5 +25,9 @@ using ContextUPtr = ox::UPtr<Context, ContextDeleter>;
 | 
			
		||||
 | 
			
		||||
ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms = {}) noexcept;
 | 
			
		||||
 | 
			
		||||
keel::Context &keelCtx(Context &ctx) noexcept;
 | 
			
		||||
 | 
			
		||||
turbine::Context &turbineCtx(Context &ctx) noexcept;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include <ox/model/def.hpp>
 | 
			
		||||
 | 
			
		||||
#include "context.hpp"
 | 
			
		||||
#include "palette.hpp"
 | 
			
		||||
 | 
			
		||||
namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
@@ -100,6 +101,17 @@ oxModelBegin(TileSheetSet)
 | 
			
		||||
	oxModelField(entries)
 | 
			
		||||
oxModelEnd()
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page = 0) noexcept;
 | 
			
		||||
 | 
			
		||||
ox::Error loadSpritePalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page = 0) noexcept;
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
#include <ox/std/vector.hpp>
 | 
			
		||||
#include <ox/model/def.hpp>
 | 
			
		||||
 | 
			
		||||
#include <turbine/turbine.hpp>
 | 
			
		||||
 | 
			
		||||
#include "color.hpp"
 | 
			
		||||
 | 
			
		||||
@@ -22,25 +23,53 @@ struct NostalgiaPalette {
 | 
			
		||||
	ox::Vector<Color16> colors = {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Palette {
 | 
			
		||||
struct PaletteV1 {
 | 
			
		||||
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Palette";
 | 
			
		||||
	static constexpr auto TypeVersion = 1;
 | 
			
		||||
	ox::Vector<Color16> colors = {};
 | 
			
		||||
	[[nodiscard]]
 | 
			
		||||
	constexpr Color16 color(auto idx) const noexcept {
 | 
			
		||||
		if (idx < colors.size()) [[likely]] {
 | 
			
		||||
			return colors[idx];
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	ox::Vector<Color16> colors;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct PaletteV2 {
 | 
			
		||||
	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 Palette = PaletteV2;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]]
 | 
			
		||||
constexpr Color16 color(Palette const&pal, size_t page, size_t idx) noexcept {
 | 
			
		||||
	if (page < pal.pages.size() && idx < pal.pages[page].size()) [[likely]] {
 | 
			
		||||
		return pal.pages[page][idx];
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]]
 | 
			
		||||
constexpr Color16 color(Palette const&pal, size_t idx) noexcept {
 | 
			
		||||
	auto constexpr page = 0;
 | 
			
		||||
	return color(pal, page, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]]
 | 
			
		||||
constexpr size_t colors(Palette const&pal, size_t page = 0) noexcept {
 | 
			
		||||
	if (page < pal.pages.size()) [[likely]] {
 | 
			
		||||
		return pal.pages[page].size();
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
oxModelBegin(NostalgiaPalette)
 | 
			
		||||
	oxModelField(colors)
 | 
			
		||||
oxModelEnd()
 | 
			
		||||
 | 
			
		||||
oxModelBegin(Palette)
 | 
			
		||||
oxModelBegin(PaletteV1)
 | 
			
		||||
	oxModelField(colors)
 | 
			
		||||
oxModelEnd()
 | 
			
		||||
 | 
			
		||||
oxModelBegin(PaletteV2)
 | 
			
		||||
	oxModelField(pages)
 | 
			
		||||
oxModelEnd()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
 * Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <turbine/turbine.hpp>
 | 
			
		||||
 | 
			
		||||
#include <nostalgia/core/gfx.hpp>
 | 
			
		||||
 | 
			
		||||
#include "context.hpp"
 | 
			
		||||
@@ -23,4 +25,12 @@ ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms) no
 | 
			
		||||
	return ContextUPtr(std::move(ctx));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
keel::Context &keelCtx(Context &ctx) noexcept {
 | 
			
		||||
	return turbine::keelCtx(ctx.turbineCtx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
turbine::Context &turbineCtx(Context &ctx) noexcept {
 | 
			
		||||
	return ctx.turbineCtx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include <teagba/gfx.hpp>
 | 
			
		||||
#include <teagba/registers.hpp>
 | 
			
		||||
 | 
			
		||||
#include <keel/keel.hpp>
 | 
			
		||||
#include <turbine/turbine.hpp>
 | 
			
		||||
 | 
			
		||||
#include <nostalgia/core/color.hpp>
 | 
			
		||||
@@ -25,12 +26,6 @@ constexpr auto GbaTileColumns = 32;
 | 
			
		||||
constexpr auto GbaTileRows = 32;
 | 
			
		||||
constexpr auto SpriteCount = 128;
 | 
			
		||||
 | 
			
		||||
struct GbaPaletteTarget {
 | 
			
		||||
	static constexpr auto TypeName = Palette::TypeName;
 | 
			
		||||
	static constexpr auto TypeVersion = Palette::TypeVersion;
 | 
			
		||||
	volatile uint16_t *palette = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GbaTileMapTarget {
 | 
			
		||||
	static constexpr auto TypeName = CompactTileSheet::TypeName;
 | 
			
		||||
	static constexpr auto TypeVersion = CompactTileSheet::TypeVersion;
 | 
			
		||||
@@ -44,22 +39,6 @@ struct GbaTileMapTarget {
 | 
			
		||||
	TileSheetSetEntry const*setEntry = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) noexcept {
 | 
			
		||||
	oxReturnError(io->template setTypeInfo<Palette>());
 | 
			
		||||
	if (t->palette) {
 | 
			
		||||
		const auto colorHandler = [t](std::size_t i, const Color16 *c) {
 | 
			
		||||
			t->palette[i] = *c;
 | 
			
		||||
			return ox::Error{};
 | 
			
		||||
		};
 | 
			
		||||
		return io->template field<Color16, decltype(colorHandler)>("colors", colorHandler);
 | 
			
		||||
	} else {
 | 
			
		||||
		constexpr auto colorHandler = [](std::size_t, const Color16*) {
 | 
			
		||||
			return ox::Error{};
 | 
			
		||||
		};
 | 
			
		||||
		return io->template field<Color16, decltype(colorHandler)>("colors", colorHandler);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]]
 | 
			
		||||
static bool loadPixel(TileSheetSetEntry const&setEntry, size_t §ionIdx, int tileIdx) noexcept {
 | 
			
		||||
	if (setEntry.sections.size() <= sectionIdx) {
 | 
			
		||||
@@ -156,27 +135,48 @@ ox::Error initGfx(Context&, InitParams const&) noexcept {
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context&,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page) noexcept {
 | 
			
		||||
	if (palette.pages.empty()) {
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
	auto const paletteMem = MEM_BG_PALETTE + palBank * 16;
 | 
			
		||||
	for (auto i = 0u; i < colors(palette, page); ++i) {
 | 
			
		||||
		paletteMem[i] = color(palette, page, i);
 | 
			
		||||
	}
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadSpritePalette(
 | 
			
		||||
		Context&,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page) noexcept {
 | 
			
		||||
	if (palette.pages.empty()) {
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
	auto const paletteMem = MEM_SPRITE_PALETTE;
 | 
			
		||||
	for (auto i = 0u; i < colors(palette, page); ++i) {
 | 
			
		||||
		paletteMem[i] = color(palette, page, i);
 | 
			
		||||
	}
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
		ox::FileAddress const&paletteAddr) noexcept {
 | 
			
		||||
	auto &rom = ctx.rom();
 | 
			
		||||
	GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE + palBank * 16};
 | 
			
		||||
	oxRequire(palStat, rom.stat(paletteAddr));
 | 
			
		||||
	oxRequire(pal, rom.directAccess(paletteAddr));
 | 
			
		||||
	oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget));
 | 
			
		||||
	return {};
 | 
			
		||||
	oxRequire(pal, keel::readObj<Palette>(keelCtx(ctx), paletteAddr));
 | 
			
		||||
	return loadBgPalette(ctx, palBank, *pal, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadSpritePalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		ox::FileAddress const&paletteAddr) noexcept {
 | 
			
		||||
	auto &rom = ctx.rom();
 | 
			
		||||
	GbaPaletteTarget const palTarget{.palette = MEM_SPRITE_PALETTE};
 | 
			
		||||
	oxRequire(palStat, rom.stat(paletteAddr));
 | 
			
		||||
	oxRequire(pal, rom.directAccess(paletteAddr));
 | 
			
		||||
	oxReturnError(ox::readMC(pal, static_cast<std::size_t>(palStat.size), &palTarget));
 | 
			
		||||
	return {};
 | 
			
		||||
	oxRequire(pal, keel::readObj<Palette>(keelCtx(ctx), paletteAddr));
 | 
			
		||||
	return loadSpritePalette(ctx, *pal, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ox::Error loadTileSheetSet(
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,8 @@ namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
class KeelModule: public keel::Module {
 | 
			
		||||
	private:
 | 
			
		||||
		NostalgiaPaletteToPaletteConverter m_nostalgiaPaletteToPaletteConverter;
 | 
			
		||||
		NostalgiaPaletteToPaletteV1Converter m_nostalgiaPaletteToPaletteV1Converter;
 | 
			
		||||
		PaletteV1ToPaletteV2Converter m_paletteV1ToPaletteV2Converter;
 | 
			
		||||
		TileSheetV1ToTileSheetV2Converter m_tileSheetV1ToTileSheetV2Converter;
 | 
			
		||||
		TileSheetV2ToTileSheetV3Converter m_tileSheetV2ToTileSheetV3Converter;
 | 
			
		||||
		TileSheetV3ToTileSheetV4Converter m_tileSheetV3ToTileSheetV4Converter;
 | 
			
		||||
@@ -36,14 +37,16 @@ class KeelModule: public keel::Module {
 | 
			
		||||
				keel::generateTypeDesc<TileSheetV3>,
 | 
			
		||||
				keel::generateTypeDesc<TileSheetV4>,
 | 
			
		||||
				keel::generateTypeDesc<CompactTileSheet>,
 | 
			
		||||
				keel::generateTypeDesc<Palette>,
 | 
			
		||||
				keel::generateTypeDesc<PaletteV1>,
 | 
			
		||||
				keel::generateTypeDesc<PaletteV2>,
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		ox::Vector<keel::BaseConverter const*> converters() const noexcept final {
 | 
			
		||||
			return {
 | 
			
		||||
				&m_nostalgiaPaletteToPaletteConverter,
 | 
			
		||||
				&m_nostalgiaPaletteToPaletteV1Converter,
 | 
			
		||||
				&m_paletteV1ToPaletteV2Converter,
 | 
			
		||||
				&m_tileSheetV1ToTileSheetV2Converter,
 | 
			
		||||
				&m_tileSheetV2ToTileSheetV3Converter,
 | 
			
		||||
				&m_tileSheetV3ToTileSheetV4Converter,
 | 
			
		||||
@@ -68,6 +71,17 @@ class KeelModule: public keel::Module {
 | 
			
		||||
					}
 | 
			
		||||
					return {};
 | 
			
		||||
				},
 | 
			
		||||
				[](keel::Context &ctx, ox::Buffer &buff) -> ox::Error {
 | 
			
		||||
					oxRequire(hdr, keel::readAssetHeader(buff));
 | 
			
		||||
					auto const typeId = ox::buildTypeId(
 | 
			
		||||
							hdr.clawHdr.typeName, hdr.clawHdr.typeVersion, hdr.clawHdr.typeParams);
 | 
			
		||||
					if (typeId == ox::buildTypeId<NostalgiaPalette>() ||
 | 
			
		||||
					    typeId == ox::buildTypeId<PaletteV1>()) {
 | 
			
		||||
						oxReturnError(keel::convertBuffToBuff<core::Palette>(
 | 
			
		||||
								ctx, buff, ox::ClawFormat::Metal).moveTo(buff));
 | 
			
		||||
					}
 | 
			
		||||
					return {};
 | 
			
		||||
				},
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,22 @@
 | 
			
		||||
 | 
			
		||||
namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
ox::Error NostalgiaPaletteToPaletteConverter::convert(
 | 
			
		||||
ox::Error NostalgiaPaletteToPaletteV1Converter::convert(
 | 
			
		||||
		keel::Context&,
 | 
			
		||||
		NostalgiaPalette &src,
 | 
			
		||||
		Palette &dst) const noexcept {
 | 
			
		||||
		PaletteV1 &dst) const noexcept {
 | 
			
		||||
	dst.colors = std::move(src.colors);
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error PaletteV1ToPaletteV2Converter::convert(
 | 
			
		||||
		keel::Context&,
 | 
			
		||||
		PaletteV1 &src,
 | 
			
		||||
		PaletteV2 &dst) const noexcept {
 | 
			
		||||
	dst.pages.emplace_back(std::move(src.colors));
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error TileSheetV1ToTileSheetV2Converter::convert(
 | 
			
		||||
		keel::Context&,
 | 
			
		||||
		TileSheetV1 &src,
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,12 @@ namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
// Type converters
 | 
			
		||||
 | 
			
		||||
class NostalgiaPaletteToPaletteConverter: public keel::Converter<NostalgiaPalette, Palette> {
 | 
			
		||||
	ox::Error convert(keel::Context&, NostalgiaPalette &src, Palette &dst) const noexcept final;
 | 
			
		||||
class NostalgiaPaletteToPaletteV1Converter: public keel::Converter<NostalgiaPalette, PaletteV1> {
 | 
			
		||||
	ox::Error convert(keel::Context&, NostalgiaPalette &src, PaletteV1 &dst) const noexcept final;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PaletteV1ToPaletteV2Converter: public keel::Converter<PaletteV1, PaletteV2> {
 | 
			
		||||
	ox::Error convert(keel::Context&, PaletteV1 &src, PaletteV2 &dst) const noexcept final;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class TileSheetV1ToTileSheetV2Converter: public keel::Converter<TileSheetV1, TileSheetV2> {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,4 +29,12 @@ ox::Result<ContextUPtr> init(turbine::Context &tctx, InitParams const¶ms) no
 | 
			
		||||
	return ContextUPtr(ctx.release());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
keel::Context &keelCtx(Context &ctx) noexcept {
 | 
			
		||||
	return turbine::keelCtx(ctx.turbineCtx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
turbine::Context &turbineCtx(Context &ctx) noexcept {
 | 
			
		||||
	return ctx.turbineCtx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
 | 
			
		||||
#include <nostalgia/core/context.hpp>
 | 
			
		||||
#include <nostalgia/core/gfx.hpp>
 | 
			
		||||
#include <nostalgia/core/palette.hpp>
 | 
			
		||||
#include <nostalgia/core/tilesheet.hpp>
 | 
			
		||||
 | 
			
		||||
#include "context.hpp"
 | 
			
		||||
@@ -359,9 +360,10 @@ static void loadPalette(
 | 
			
		||||
		ox::Array<GLfloat, 1024> &palette,
 | 
			
		||||
		size_t palOffset,
 | 
			
		||||
		GLuint shaderPgrm,
 | 
			
		||||
		Palette const&pal) noexcept {
 | 
			
		||||
		Palette const&pal,
 | 
			
		||||
		size_t page = 0) noexcept {
 | 
			
		||||
	static constexpr std::size_t ColorCnt = 256;
 | 
			
		||||
	for (auto i = palOffset; auto const c : pal.colors) {
 | 
			
		||||
	for (auto i = palOffset; auto const c : pal.pages[page]) {
 | 
			
		||||
		palette[i++] = redf(c);
 | 
			
		||||
		palette[i++] = greenf(c);
 | 
			
		||||
		palette[i++] = bluef(c);
 | 
			
		||||
@@ -506,6 +508,24 @@ static ox::Result<TileSheetData> normalizeTileSheet(
 | 
			
		||||
	return TileSheetData{std::move(pixels), width, height};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page) noexcept {
 | 
			
		||||
	renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, palette, page);
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadSpritePalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		Palette const&palette,
 | 
			
		||||
		size_t page) noexcept {
 | 
			
		||||
	ox::Array<GLfloat, 1024> pal;
 | 
			
		||||
	renderer::loadPalette(pal, 0, ctx.spriteShader, palette, page);
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error loadBgPalette(
 | 
			
		||||
		Context &ctx,
 | 
			
		||||
		size_t palBank,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,112 +19,20 @@ PaletteEditorImGui::PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView p
 | 
			
		||||
	Editor(path),
 | 
			
		||||
	m_ctx(ctx),
 | 
			
		||||
	m_pal(*keel::readObj<Palette>(keelCtx(m_ctx), ox::FileAddress(itemPath())).unwrapThrow()) {
 | 
			
		||||
	undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::draw(turbine::Context&) noexcept {
 | 
			
		||||
	static constexpr auto flags = ImGuiTableFlags_RowBg;
 | 
			
		||||
	const auto paneSize = ImGui::GetContentRegionAvail();
 | 
			
		||||
	ImGui::BeginChild("Colors", ImVec2(paneSize.x - 208, paneSize.y), true);
 | 
			
		||||
	auto const paneSize = ImGui::GetContentRegionAvail();
 | 
			
		||||
	{
 | 
			
		||||
		const auto colorsSz = ImGui::GetContentRegionAvail();
 | 
			
		||||
		static constexpr auto toolbarHeight = 40;
 | 
			
		||||
		{
 | 
			
		||||
			const auto sz = ImVec2(70, 24);
 | 
			
		||||
			if (ImGui::Button("Add", sz)) {
 | 
			
		||||
				const auto colorSz = static_cast<int>(m_pal.colors.size());
 | 
			
		||||
				constexpr Color16 c = 0;
 | 
			
		||||
				undoStack()->push(ox::make_unique<AddColorCommand>(&m_pal, c, colorSz));
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			ImGui::BeginDisabled(m_selectedRow >= m_pal.colors.size());
 | 
			
		||||
			{
 | 
			
		||||
				if (ImGui::Button("Remove", sz)) {
 | 
			
		||||
					undoStack()->push(
 | 
			
		||||
							ox::make_unique<RemoveColorCommand>(
 | 
			
		||||
								&m_pal,
 | 
			
		||||
								m_pal.colors[static_cast<std::size_t>(m_selectedRow)],
 | 
			
		||||
								static_cast<int>(m_selectedRow)));
 | 
			
		||||
					m_selectedRow = ox::min(m_pal.colors.size() - 1, m_selectedRow);
 | 
			
		||||
				}
 | 
			
		||||
				ImGui::SameLine();
 | 
			
		||||
				ImGui::BeginDisabled(m_selectedRow <= 0);
 | 
			
		||||
				{
 | 
			
		||||
					if (ImGui::Button("Move Up", sz)) {
 | 
			
		||||
						undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_selectedRow, -1));
 | 
			
		||||
						--m_selectedRow;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				ImGui::EndDisabled();
 | 
			
		||||
				ImGui::SameLine();
 | 
			
		||||
				ImGui::BeginDisabled(m_selectedRow >= m_pal.colors.size() - 1);
 | 
			
		||||
				{
 | 
			
		||||
					if (ImGui::Button("Move Down", sz)) {
 | 
			
		||||
						undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_selectedRow, 1));
 | 
			
		||||
						++m_selectedRow;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				ImGui::EndDisabled();
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::EndDisabled();
 | 
			
		||||
		}
 | 
			
		||||
		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();
 | 
			
		||||
			constexpr auto colVal = [] (unsigned num) {
 | 
			
		||||
				ox::Array<char, 4> numStr;
 | 
			
		||||
				ImGui::TableNextColumn();
 | 
			
		||||
				ox_itoa(num, numStr.data());
 | 
			
		||||
				ImGui::SetCursorPosX(
 | 
			
		||||
						ImGui::GetCursorPosX() + ImGui::GetColumnWidth() - ImGui::CalcTextSize(numStr.data()).x);
 | 
			
		||||
				ImGui::Text("%s", numStr.data());
 | 
			
		||||
			};
 | 
			
		||||
			for (auto i = 0u; const auto c : m_pal.colors) {
 | 
			
		||||
				ImGui::PushID(static_cast<int>(i));
 | 
			
		||||
				ImGui::TableNextRow();
 | 
			
		||||
				// Color No.
 | 
			
		||||
				colVal(i);
 | 
			
		||||
				// Red
 | 
			
		||||
				colVal(red16(c));
 | 
			
		||||
				// Green
 | 
			
		||||
				colVal(green16(c));
 | 
			
		||||
				// Blue
 | 
			
		||||
				colVal(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::BeginChild("Pages", ImVec2(250, paneSize.y), true);
 | 
			
		||||
		drawPagesEditor();
 | 
			
		||||
		ImGui::EndChild();
 | 
			
		||||
	}
 | 
			
		||||
	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 const 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) {
 | 
			
		||||
				undoStack()->push(ox::make_unique<UpdateColorCommand>(&m_pal, static_cast<int>(m_selectedRow), c, newColor));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	ImGui::SameLine();
 | 
			
		||||
	{
 | 
			
		||||
		ImGui::BeginChild("Colors", ImVec2(-1, paneSize.y), true);
 | 
			
		||||
		drawColorsEditor();
 | 
			
		||||
		ImGui::EndChild();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -134,4 +42,163 @@ ox::Error PaletteEditorImGui::saveItem() noexcept {
 | 
			
		||||
	return sctx->project->writeObj(itemPath(), m_pal);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::drawColumn(ox::CStringView txt) noexcept {
 | 
			
		||||
	ImGui::TableNextColumn();
 | 
			
		||||
	ImGui::SetCursorPosX(
 | 
			
		||||
			ImGui::GetCursorPosX() + ImGui::GetColumnWidth() - ImGui::CalcTextSize(txt.data()).x);
 | 
			
		||||
	ImGui::Text("%s", txt.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::drawColumn(uint64_t i) noexcept {
 | 
			
		||||
	ox::Array<char, 10> numStr;
 | 
			
		||||
	ox_itoa(i, numStr.data());
 | 
			
		||||
	drawColumn(numStr.data());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::drawColorsEditor() noexcept {
 | 
			
		||||
	constexpr auto tableFlags = ImGuiTableFlags_RowBg;
 | 
			
		||||
	auto const colorsSz = ImGui::GetContentRegionAvail();
 | 
			
		||||
	auto const colorEditor = m_selectedColorRow < colors(m_pal, m_page);
 | 
			
		||||
	auto const colorEditorWidth = 220;
 | 
			
		||||
	static constexpr auto toolbarHeight = 40;
 | 
			
		||||
	{
 | 
			
		||||
		auto const sz = ImVec2(70, 24);
 | 
			
		||||
		if (ImGui::Button("Add", sz)) {
 | 
			
		||||
			auto const colorSz = static_cast<int>(colors(m_pal, m_page));
 | 
			
		||||
			constexpr Color16 c = 0;
 | 
			
		||||
			undoStack()->push(ox::make_unique<AddColorCommand>(&m_pal, c, m_page, colorSz));
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::SameLine();
 | 
			
		||||
		ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page));
 | 
			
		||||
		{
 | 
			
		||||
			if (ImGui::Button("Remove", sz)) {
 | 
			
		||||
				undoStack()->push(
 | 
			
		||||
						ox::make_unique<RemoveColorCommand>(
 | 
			
		||||
								&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::BeginDisabled(m_selectedColorRow <= 0);
 | 
			
		||||
			{
 | 
			
		||||
				if (ImGui::Button("Move Up", sz)) {
 | 
			
		||||
					undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_page, m_selectedColorRow, -1));
 | 
			
		||||
					--m_selectedColorRow;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::EndDisabled();
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			ImGui::BeginDisabled(m_selectedColorRow >= colors(m_pal, m_page) - 1);
 | 
			
		||||
			{
 | 
			
		||||
				if (ImGui::Button("Move Down", sz)) {
 | 
			
		||||
					undoStack()->push(ox::make_unique<MoveColorCommand>(&m_pal, m_page, m_selectedColorRow, 1));
 | 
			
		||||
					++m_selectedColorRow;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::EndDisabled();
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::EndDisabled();
 | 
			
		||||
	}
 | 
			
		||||
	auto const tblWidth = (colorsSz.x - colorEditorWidth - 8) * colorEditor;
 | 
			
		||||
	ImGui::BeginTable("Colors", 5, tableFlags, ImVec2(tblWidth, 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; auto const c : m_pal.pages[m_page]) {
 | 
			
		||||
			ImGui::PushID(static_cast<int>(i));
 | 
			
		||||
			ImGui::TableNextRow();
 | 
			
		||||
			drawColumn(i);
 | 
			
		||||
			drawColumn(red16(c));
 | 
			
		||||
			drawColumn(green16(c));
 | 
			
		||||
			drawColumn(blue16(c));
 | 
			
		||||
			ImGui::TableNextColumn();
 | 
			
		||||
			auto const ic = ImGui::GetColorU32(ImVec4(redf(c), greenf(c), bluef(c), 1));
 | 
			
		||||
			ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ic);
 | 
			
		||||
			if (ImGui::Selectable("##ColorRow", i == m_selectedColorRow, ImGuiSelectableFlags_SpanAllColumns)) {
 | 
			
		||||
				m_selectedColorRow = i;
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::PopID();
 | 
			
		||||
			++i;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::EndTable();
 | 
			
		||||
	if (colorEditor) {
 | 
			
		||||
		ImGui::SameLine();
 | 
			
		||||
		ImGui::BeginChild("ColorEditor", ImVec2(colorEditorWidth, -1), true);
 | 
			
		||||
		drawColorEditor();
 | 
			
		||||
		ImGui::EndChild();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::drawPagesEditor() noexcept {
 | 
			
		||||
	constexpr auto tableFlags = ImGuiTableFlags_RowBg;
 | 
			
		||||
	auto const paneSz = ImGui::GetContentRegionAvail();
 | 
			
		||||
	constexpr auto toolbarHeight = 40;
 | 
			
		||||
	auto const btnSz = ImVec2(paneSz.x / 3 - 5.5f, 24);
 | 
			
		||||
	if (ImGui::Button("Add", btnSz)) {
 | 
			
		||||
		undoStack()->push(ox::make_unique<AddPageCommand>(m_pal));
 | 
			
		||||
		m_page = m_pal.pages.size() - 1;
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::SameLine();
 | 
			
		||||
	if (ImGui::Button("Remove", btnSz)) {
 | 
			
		||||
		undoStack()->push(ox::make_unique<RemovePageCommand>(m_pal, m_page));
 | 
			
		||||
		m_page = std::min(m_page, m_pal.pages.size() - 1);
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::SameLine();
 | 
			
		||||
	if (ImGui::Button("Duplicate", btnSz)) {
 | 
			
		||||
		undoStack()->push(ox::make_unique<DuplicatePageCommand>(m_pal, m_page, m_pal.pages.size()));
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::BeginTable("PageSelect", 2, tableFlags, ImVec2(paneSz.x, paneSz.y - (toolbarHeight + 5)));
 | 
			
		||||
	{
 | 
			
		||||
		ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60);
 | 
			
		||||
		ImGui::TableSetupColumn("Colors", ImGuiTableColumnFlags_NoHide);
 | 
			
		||||
		ImGui::TableHeadersRow();
 | 
			
		||||
		for (auto i = 0u; i < m_pal.pages.size(); ++i) {
 | 
			
		||||
			ImGui::PushID(static_cast<int>(i));
 | 
			
		||||
			ImGui::TableNextRow();
 | 
			
		||||
			drawColumn(i + 1);
 | 
			
		||||
			drawColumn(colors(m_pal, i));
 | 
			
		||||
			ImGui::SameLine();
 | 
			
		||||
			if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) {
 | 
			
		||||
				m_page = i;
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::PopID();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ImGui::EndTable();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PaletteEditorImGui::drawColorEditor() noexcept {
 | 
			
		||||
	auto const c = color(m_pal, m_page, m_selectedColorRow);
 | 
			
		||||
	int r = red16(c);
 | 
			
		||||
	int g = green16(c);
 | 
			
		||||
	int b = blue16(c);
 | 
			
		||||
	int const a = alpha16(c);
 | 
			
		||||
	ImGui::InputInt("Red", &r, 1, 5);
 | 
			
		||||
	ImGui::InputInt("Green", &g, 1, 5);
 | 
			
		||||
	ImGui::InputInt("Blue", &b, 1, 5);
 | 
			
		||||
	auto const newColor = color16(r, g, b, a);
 | 
			
		||||
	if (c != newColor) {
 | 
			
		||||
		undoStack()->push(ox::make_unique<UpdateColorCommand>(
 | 
			
		||||
				&m_pal, m_page, static_cast<int>(m_selectedColorRow), c, newColor));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept {
 | 
			
		||||
	if (dynamic_cast<AddPageCommand 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);
 | 
			
		||||
	} else if (auto const dupPageCmd = dynamic_cast<DuplicatePageCommand const*>(cmd)) {
 | 
			
		||||
		m_page = ox::clamp<size_t>(dupPageCmd->insertIdx(), 0, m_pal.pages.size() - 1);
 | 
			
		||||
	}
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,8 @@ class PaletteEditorImGui: public studio::Editor {
 | 
			
		||||
	private:
 | 
			
		||||
		turbine::Context &m_ctx;
 | 
			
		||||
		Palette m_pal;
 | 
			
		||||
		std::size_t m_selectedRow = 0;
 | 
			
		||||
		size_t m_selectedColorRow = 0;
 | 
			
		||||
		size_t m_page = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path);
 | 
			
		||||
@@ -26,6 +27,18 @@ class PaletteEditorImGui: public studio::Editor {
 | 
			
		||||
	protected:
 | 
			
		||||
		ox::Error saveItem() noexcept final;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		static void drawColumn(ox::CStringView txt) noexcept;
 | 
			
		||||
 | 
			
		||||
		static void drawColumn(uint64_t i) noexcept;
 | 
			
		||||
 | 
			
		||||
		void drawColorsEditor() noexcept;
 | 
			
		||||
 | 
			
		||||
		void drawPagesEditor() noexcept;
 | 
			
		||||
 | 
			
		||||
		void drawColorEditor() noexcept;
 | 
			
		||||
 | 
			
		||||
		ox::Error handleCommand(studio::UndoCommand const*) noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,74 @@
 | 
			
		||||
 | 
			
		||||
namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
AddColorCommand::AddColorCommand(Palette *pal, Color16 color, int idx) noexcept {
 | 
			
		||||
AddPageCommand::AddPageCommand(Palette &pal) noexcept:
 | 
			
		||||
	m_pal(pal) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AddPageCommand::commandId() const noexcept {
 | 
			
		||||
	return static_cast<int>(PaletteEditorCommandId::AddPage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AddPageCommand::redo() noexcept {
 | 
			
		||||
	m_pal.pages.emplace_back();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AddPageCommand::undo() noexcept {
 | 
			
		||||
	oxIgnoreError(m_pal.pages.erase(static_cast<std::size_t>(m_pal.pages.size() - 1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DuplicatePageCommand::DuplicatePageCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept:
 | 
			
		||||
		m_pal(pal),
 | 
			
		||||
		m_dstIdx(dstIdx) {
 | 
			
		||||
	auto const&src = m_pal.pages[srcIdx];
 | 
			
		||||
	m_page.reserve(src.size());
 | 
			
		||||
	for (auto const&s : src) {
 | 
			
		||||
		m_page.emplace_back(s);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DuplicatePageCommand::commandId() const noexcept {
 | 
			
		||||
	return static_cast<int>(PaletteEditorCommandId::DuplicatePage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DuplicatePageCommand::redo() noexcept {
 | 
			
		||||
	m_pal.pages.emplace(m_dstIdx, std::move(m_page));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DuplicatePageCommand::undo() noexcept {
 | 
			
		||||
	m_page = std::move(m_pal.pages[m_dstIdx]);
 | 
			
		||||
	oxIgnoreError(m_pal.pages.erase(static_cast<std::size_t>(m_dstIdx)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t DuplicatePageCommand::insertIdx() const noexcept {
 | 
			
		||||
	return m_dstIdx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RemovePageCommand::RemovePageCommand(Palette &pal, size_t idx) noexcept:
 | 
			
		||||
	m_pal(pal),
 | 
			
		||||
	m_idx(idx) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RemovePageCommand::commandId() const noexcept {
 | 
			
		||||
	return static_cast<int>(PaletteEditorCommandId::RemovePage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemovePageCommand::redo() noexcept {
 | 
			
		||||
	m_page = std::move(m_pal.pages[m_idx]);
 | 
			
		||||
	oxIgnoreError(m_pal.pages.erase(static_cast<std::size_t>(m_idx)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemovePageCommand::undo() noexcept {
 | 
			
		||||
	m_pal.pages.insert(m_idx, std::move(m_page));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
AddColorCommand::AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept {
 | 
			
		||||
	m_pal = pal;
 | 
			
		||||
	m_color = color;
 | 
			
		||||
	m_page = page;
 | 
			
		||||
	m_idx = idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -17,17 +82,18 @@ int AddColorCommand::commandId() const noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AddColorCommand::redo() noexcept {
 | 
			
		||||
	m_pal->colors.insert(static_cast<std::size_t>(m_idx), m_color);
 | 
			
		||||
	m_pal->pages[m_page].insert(static_cast<std::size_t>(m_idx), m_color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AddColorCommand::undo() noexcept {
 | 
			
		||||
	oxIgnoreError(m_pal->colors.erase(static_cast<std::size_t>(m_idx)));
 | 
			
		||||
	oxIgnoreError(m_pal->pages[m_page].erase(static_cast<std::size_t>(m_idx)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RemoveColorCommand::RemoveColorCommand(Palette *pal, Color16 color, int idx) noexcept {
 | 
			
		||||
RemoveColorCommand::RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept {
 | 
			
		||||
	m_pal = pal;
 | 
			
		||||
	m_color = color;
 | 
			
		||||
	m_page = page;
 | 
			
		||||
	m_idx = idx;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -36,16 +102,22 @@ int RemoveColorCommand::commandId() const noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoveColorCommand::redo() noexcept {
 | 
			
		||||
	oxIgnoreError(m_pal->colors.erase(static_cast<std::size_t>(m_idx)));
 | 
			
		||||
	oxIgnoreError(m_pal->pages[m_page].erase(static_cast<std::size_t>(m_idx)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemoveColorCommand::undo() noexcept {
 | 
			
		||||
m_pal->colors.insert(static_cast<std::size_t>(m_idx), m_color);
 | 
			
		||||
m_pal->pages[m_page].insert(static_cast<std::size_t>(m_idx), m_color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
UpdateColorCommand::UpdateColorCommand(Palette *pal, int idx, Color16 oldColor, Color16 newColor) noexcept {
 | 
			
		||||
UpdateColorCommand::UpdateColorCommand(
 | 
			
		||||
		Palette *pal,
 | 
			
		||||
		size_t page,
 | 
			
		||||
		int idx,
 | 
			
		||||
		Color16 oldColor,
 | 
			
		||||
		Color16 newColor) noexcept {
 | 
			
		||||
	m_pal = pal;
 | 
			
		||||
	m_page = page;
 | 
			
		||||
	m_idx = idx;
 | 
			
		||||
	m_oldColor = oldColor;
 | 
			
		||||
	m_newColor = newColor;
 | 
			
		||||
@@ -70,16 +142,17 @@ int UpdateColorCommand::commandId() const noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UpdateColorCommand::redo() noexcept {
 | 
			
		||||
	m_pal->colors[static_cast<std::size_t>(m_idx)] = m_newColor;
 | 
			
		||||
	m_pal->pages[m_page][static_cast<std::size_t>(m_idx)] = m_newColor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UpdateColorCommand::undo() noexcept {
 | 
			
		||||
	m_pal->colors[static_cast<std::size_t>(m_idx)] = m_oldColor;
 | 
			
		||||
	m_pal->pages[m_page][static_cast<std::size_t>(m_idx)] = m_oldColor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MoveColorCommand::MoveColorCommand(Palette *pal, std::size_t idx, int offset) noexcept {
 | 
			
		||||
MoveColorCommand::MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept {
 | 
			
		||||
	m_pal = pal;
 | 
			
		||||
	m_page = page;
 | 
			
		||||
	m_idx = idx;
 | 
			
		||||
	m_offset = offset;
 | 
			
		||||
}
 | 
			
		||||
@@ -97,9 +170,9 @@ void MoveColorCommand::undo() noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MoveColorCommand::moveColor(int idx, int offset) noexcept {
 | 
			
		||||
	const auto c = m_pal->colors[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);
 | 
			
		||||
	const auto c = m_pal->pages[m_page][static_cast<std::size_t>(idx)];
 | 
			
		||||
	oxIgnoreError(m_pal->pages[m_page].erase(static_cast<std::size_t>(idx)));
 | 
			
		||||
	m_pal->pages[m_page].insert(static_cast<std::size_t>(idx + offset), c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,9 @@
 | 
			
		||||
namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
enum class PaletteEditorCommandId {
 | 
			
		||||
	AddPage,
 | 
			
		||||
	DuplicatePage,
 | 
			
		||||
	RemovePage,
 | 
			
		||||
	AddColor,
 | 
			
		||||
	RemoveColor,
 | 
			
		||||
	UpdateColor,
 | 
			
		||||
@@ -20,14 +23,76 @@ enum class PaletteEditorCommandId {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddPageCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette &m_pal;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		AddPageCommand(Palette &pal) noexcept;
 | 
			
		||||
 | 
			
		||||
		~AddPageCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		int commandId() const noexcept final;
 | 
			
		||||
 | 
			
		||||
		void redo() noexcept final;
 | 
			
		||||
 | 
			
		||||
		void undo() noexcept final;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class DuplicatePageCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette &m_pal;
 | 
			
		||||
		size_t m_dstIdx = 0;
 | 
			
		||||
		ox::Vector<Color16> m_page;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		DuplicatePageCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept;
 | 
			
		||||
 | 
			
		||||
		~DuplicatePageCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		int commandId() const noexcept final;
 | 
			
		||||
 | 
			
		||||
		void redo() noexcept final;
 | 
			
		||||
 | 
			
		||||
		void undo() noexcept final;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		size_t insertIdx() const noexcept;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RemovePageCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette &m_pal;
 | 
			
		||||
		size_t m_idx = 0;
 | 
			
		||||
		ox::Vector<Color16> m_page;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		RemovePageCommand(Palette &pal, size_t idx) noexcept;
 | 
			
		||||
 | 
			
		||||
		~RemovePageCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		int commandId() const noexcept final;
 | 
			
		||||
 | 
			
		||||
		void redo() noexcept final;
 | 
			
		||||
 | 
			
		||||
		void undo() noexcept final;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AddColorCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette *m_pal = nullptr;
 | 
			
		||||
		Color16 m_color = 0;
 | 
			
		||||
		int m_idx = -1;
 | 
			
		||||
		size_t m_page = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		AddColorCommand(Palette *pal, Color16 color, int idx) noexcept;
 | 
			
		||||
		AddColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept;
 | 
			
		||||
 | 
			
		||||
		~AddColorCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
@@ -44,10 +109,11 @@ class RemoveColorCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette *m_pal = nullptr;
 | 
			
		||||
		Color16 m_color = 0;
 | 
			
		||||
		size_t m_page = 0;
 | 
			
		||||
		int m_idx = -1;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		RemoveColorCommand(Palette *pal, Color16 color, int idx) noexcept;
 | 
			
		||||
		RemoveColorCommand(Palette *pal, Color16 color, size_t page, int idx) noexcept;
 | 
			
		||||
 | 
			
		||||
		~RemoveColorCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
@@ -65,10 +131,11 @@ class UpdateColorCommand: public studio::UndoCommand {
 | 
			
		||||
		Palette *m_pal = nullptr;
 | 
			
		||||
		Color16 m_oldColor = 0;
 | 
			
		||||
		Color16 m_newColor = 0;
 | 
			
		||||
		size_t m_page = 0;
 | 
			
		||||
		int m_idx = -1;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		UpdateColorCommand(Palette *pal, int idx, Color16 oldColor, Color16 newColor) noexcept;
 | 
			
		||||
		UpdateColorCommand(Palette *pal, size_t page, int idx, Color16 oldColor, Color16 newColor) noexcept;
 | 
			
		||||
 | 
			
		||||
		~UpdateColorCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
@@ -87,11 +154,12 @@ class UpdateColorCommand: public studio::UndoCommand {
 | 
			
		||||
class MoveColorCommand: public studio::UndoCommand {
 | 
			
		||||
	private:
 | 
			
		||||
		Palette *m_pal = nullptr;
 | 
			
		||||
		size_t m_page = 0;
 | 
			
		||||
		std::size_t m_idx = 0;
 | 
			
		||||
		int m_offset = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		MoveColorCommand(Palette *pal, std::size_t idx, int offset) noexcept;
 | 
			
		||||
		MoveColorCommand(Palette *pal, size_t page, std::size_t idx, int offset) noexcept;
 | 
			
		||||
 | 
			
		||||
		~MoveColorCommand() noexcept override = default;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,10 +56,11 @@ static ox::Error toPngFile(
 | 
			
		||||
		ox::CStringView const&path,
 | 
			
		||||
		ox::Vector<uint32_t> &&pixels,
 | 
			
		||||
		Palette const&pal,
 | 
			
		||||
		size_t page,
 | 
			
		||||
		unsigned width,
 | 
			
		||||
		unsigned height) noexcept {
 | 
			
		||||
	for (auto &c : pixels) {
 | 
			
		||||
		c = color32(pal.color(c)) | static_cast<Color32>(0XFF << 24);
 | 
			
		||||
		c = color32(color(pal, page, c)) | static_cast<Color32>(0XFF << 24);
 | 
			
		||||
	}
 | 
			
		||||
	constexpr auto fmt = LCT_RGBA;
 | 
			
		||||
	return OxError(static_cast<ox::ErrorCode>(
 | 
			
		||||
@@ -112,7 +113,7 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
 | 
			
		||||
	auto const popupOpen = m_subsheetEditor.isOpen() && m_exportMenu.isOpen();
 | 
			
		||||
	auto const pal = m_model.pal();
 | 
			
		||||
	if (pal && !popupOpen) {
 | 
			
		||||
		const auto colorCnt = pal->colors.size();
 | 
			
		||||
		const auto colorCnt = pal->pages[m_palPage].size();
 | 
			
		||||
		if (key == turbine::Key::Alpha_D) {
 | 
			
		||||
			m_tool = Tool::Draw;
 | 
			
		||||
			setCopyEnabled(false);
 | 
			
		||||
@@ -306,6 +307,7 @@ ox::Error TileSheetEditorImGui::exportSubhseetToPng(int scale) noexcept {
 | 
			
		||||
			path,
 | 
			
		||||
			std::move(pixels),
 | 
			
		||||
			*pal,
 | 
			
		||||
			m_palPage,
 | 
			
		||||
			static_cast<unsigned>(width * scale),
 | 
			
		||||
			static_cast<unsigned>(height * scale));
 | 
			
		||||
	if (err) {
 | 
			
		||||
@@ -315,8 +317,8 @@ ox::Error TileSheetEditorImGui::exportSubhseetToPng(int scale) noexcept {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
	const auto winPos = ImGui::GetWindowPos();
 | 
			
		||||
	const auto fbSizei = ox::Size(static_cast<int>(fbSize.x), static_cast<int>(fbSize.y));
 | 
			
		||||
	auto const winPos = ImGui::GetWindowPos();
 | 
			
		||||
	auto const fbSizei = ox::Size(static_cast<int>(fbSize.x), static_cast<int>(fbSize.y));
 | 
			
		||||
	if (m_framebuffer.width != fbSizei.width || m_framebuffer.height != fbSizei.height) {
 | 
			
		||||
		glutils::resizeInitFrameBuffer(m_framebuffer, fbSizei.width, fbSizei.height);
 | 
			
		||||
		m_view.resizeView(fbSize);
 | 
			
		||||
@@ -334,11 +336,11 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
		ImVec2(0, 1),
 | 
			
		||||
		ImVec2(1, 0));
 | 
			
		||||
	// handle input, this must come after drawing
 | 
			
		||||
	const auto &io = ImGui::GetIO();
 | 
			
		||||
	const auto mousePos = ox::Vec2(io.MousePos);
 | 
			
		||||
	auto const&io = ImGui::GetIO();
 | 
			
		||||
	auto const mousePos = ox::Vec2(io.MousePos);
 | 
			
		||||
	if (ImGui::IsItemHovered()) {
 | 
			
		||||
		const auto wheel = io.MouseWheel;
 | 
			
		||||
		const auto wheelh = io.MouseWheelH;
 | 
			
		||||
		auto const wheel = io.MouseWheel;
 | 
			
		||||
		auto const wheelh = io.MouseWheelH;
 | 
			
		||||
		if (wheel != 0) {
 | 
			
		||||
			const auto zoomMod = ox::defines::OS == ox::OS::Darwin ?
 | 
			
		||||
			                     io.KeySuper : turbine::buttonDown(m_ctx, turbine::Key::Mod_Ctrl);
 | 
			
		||||
@@ -365,7 +367,7 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (ImGui::BeginPopupContextItem("TileMenu", ImGuiPopupFlags_MouseButtonRight)) {
 | 
			
		||||
		const auto popupPos = ox::Vec2(ImGui::GetWindowPos());
 | 
			
		||||
		auto const popupPos = ox::Vec2(ImGui::GetWindowPos());
 | 
			
		||||
		if (ImGui::MenuItem("Insert Tile")) {
 | 
			
		||||
			m_view.insertTile(fbSize, clickPos(winPos, popupPos));
 | 
			
		||||
		}
 | 
			
		||||
@@ -382,11 +384,13 @@ void TileSheetEditorImGui::drawTileSheet(ox::Vec2 const&fbSize) noexcept {
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
	auto &sctx = *applicationData<studio::StudioContext>(m_ctx);
 | 
			
		||||
	const auto &files = sctx.project->fileList(core::FileExt_npal);
 | 
			
		||||
	const auto first = m_selectedPaletteIdx < files.size() ?
 | 
			
		||||
	auto const&files = sctx.project->fileList(core::FileExt_npal);
 | 
			
		||||
	auto const first = m_selectedPaletteIdx < files.size() ?
 | 
			
		||||
		files[m_selectedPaletteIdx].c_str() : "";
 | 
			
		||||
	auto const comboWidthSub = 62;
 | 
			
		||||
	ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub);
 | 
			
		||||
	if (ImGui::BeginCombo("Palette", first, 0)) {
 | 
			
		||||
		for (auto n = 0u; n < files.size(); n++) {
 | 
			
		||||
		for (auto n = 0u; n < files.size(); ++n) {
 | 
			
		||||
			const auto selected = (m_selectedPaletteIdx == n);
 | 
			
		||||
			if (ImGui::Selectable(files[n].c_str(), selected) && m_selectedPaletteIdx != n) {
 | 
			
		||||
				m_selectedPaletteIdx = n;
 | 
			
		||||
@@ -398,6 +402,28 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::EndCombo();
 | 
			
		||||
	}
 | 
			
		||||
	auto const pages = m_model.pal()->pages.size();
 | 
			
		||||
	if (pages > 1) {
 | 
			
		||||
		ImGui::Indent(20);
 | 
			
		||||
		ox::Array<char, 10> numStr;
 | 
			
		||||
		ox_itoa(m_palPage + 1, numStr.data());
 | 
			
		||||
		ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub);
 | 
			
		||||
		if (ImGui::BeginCombo("Page", numStr.data(), 0)) {
 | 
			
		||||
			for (auto n = 0u; n < pages; ++n) {
 | 
			
		||||
				const auto selected = (m_palPage == n);
 | 
			
		||||
				ox_itoa(n + 1, numStr.data());
 | 
			
		||||
				if (ImGui::Selectable(numStr.data(), selected) && m_palPage != n) {
 | 
			
		||||
					m_palPage = n;
 | 
			
		||||
					m_model.setPalettePage(m_palPage);
 | 
			
		||||
				}
 | 
			
		||||
				if (selected) {
 | 
			
		||||
					ImGui::SetItemDefaultFocus();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			ImGui::EndCombo();
 | 
			
		||||
		}
 | 
			
		||||
		ImGui::Indent(-20);
 | 
			
		||||
	}
 | 
			
		||||
	// header
 | 
			
		||||
	if (ImGui::BeginTable("PaletteTable", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
 | 
			
		||||
		ImGui::TableSetupColumn("No.", 0, 0.45f);
 | 
			
		||||
@@ -405,7 +431,7 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
 | 
			
		||||
		ImGui::TableSetupColumn("Color16", 0, 3);
 | 
			
		||||
		ImGui::TableHeadersRow();
 | 
			
		||||
		if (auto pal = m_view.pal()) {
 | 
			
		||||
			for (auto i = 0u; auto c: pal->colors) {
 | 
			
		||||
			for (auto i = 0u; auto c: pal->pages[m_palPage]) {
 | 
			
		||||
				ImGui::PushID(static_cast<int>(i));
 | 
			
		||||
				// Column: color idx
 | 
			
		||||
				ImGui::TableNextColumn();
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +69,7 @@ class TileSheetEditorImGui: public studio::Editor {
 | 
			
		||||
		float m_palViewWidth = 300;
 | 
			
		||||
		ox::Vec2 m_prevMouseDownPos;
 | 
			
		||||
		Tool m_tool = Tool::Draw;
 | 
			
		||||
		size_t m_palPage = 0;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringView path);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <ox/claw/read.hpp>
 | 
			
		||||
#include <ox/std/algorithm.hpp>
 | 
			
		||||
#include <ox/std/buffer.hpp>
 | 
			
		||||
#include <ox/std/memory.hpp>
 | 
			
		||||
 | 
			
		||||
@@ -25,8 +26,8 @@
 | 
			
		||||
 | 
			
		||||
namespace nostalgia::core {
 | 
			
		||||
 | 
			
		||||
const Palette TileSheetEditorModel::s_defaultPalette = {
 | 
			
		||||
	.colors = ox::Vector<Color16>(128),
 | 
			
		||||
Palette const TileSheetEditorModel::s_defaultPalette = {
 | 
			
		||||
	.pages = {ox::Vector<Color16>(128)},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TileSheetEditorModel::TileSheetEditorModel(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack):
 | 
			
		||||
@@ -114,6 +115,15 @@ ox::Error TileSheetEditorModel::setPalette(ox::StringView path) noexcept {
 | 
			
		||||
	return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorModel::setPalettePage(size_t pg) noexcept {
 | 
			
		||||
	m_palettePage = ox::clamp<size_t>(pg, 0, m_pal->pages.size() - 1);
 | 
			
		||||
	m_updated = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t TileSheetEditorModel::palettePage() const noexcept {
 | 
			
		||||
	return m_palettePage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TileSheetEditorModel::drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept {
 | 
			
		||||
	const auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
 | 
			
		||||
	if (pt.x >= activeSubSheet.columns * TileWidth || pt.y >= activeSubSheet.rows * TileHeight) {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,13 @@ class TileSheetEditorModel: public ox::SignalHandler {
 | 
			
		||||
		ox::Signal<ox::Error()> paletteChanged;
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		static const Palette s_defaultPalette;
 | 
			
		||||
		static Palette const s_defaultPalette;
 | 
			
		||||
		turbine::Context &m_ctx;
 | 
			
		||||
		ox::String m_path;
 | 
			
		||||
		TileSheet m_img;
 | 
			
		||||
		TileSheet::SubSheetIdx m_activeSubsSheetIdx;
 | 
			
		||||
		keel::AssetRef<Palette> m_pal;
 | 
			
		||||
		size_t m_palettePage{};
 | 
			
		||||
		studio::UndoStack &m_undoStack;
 | 
			
		||||
		class DrawCommand *m_ongoingDrawCommand = nullptr;
 | 
			
		||||
		bool m_updated = false;
 | 
			
		||||
@@ -48,19 +49,24 @@ class TileSheetEditorModel: public ox::SignalHandler {
 | 
			
		||||
		void paste();
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr const TileSheet &img() const noexcept;
 | 
			
		||||
		constexpr TileSheet const&img() const noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr TileSheet &img() noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		constexpr const Palette *pal() const noexcept;
 | 
			
		||||
		constexpr Palette const*pal() const noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		ox::StringView palPath() const noexcept;
 | 
			
		||||
 | 
			
		||||
		ox::Error setPalette(ox::StringView path) noexcept;
 | 
			
		||||
 | 
			
		||||
		void setPalettePage(size_t pg) noexcept;
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]]
 | 
			
		||||
		size_t palettePage() const noexcept;
 | 
			
		||||
 | 
			
		||||
		void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
 | 
			
		||||
 | 
			
		||||
		void endDrawCommand() noexcept;
 | 
			
		||||
@@ -125,7 +131,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr const TileSheet &TileSheetEditorModel::img() const noexcept {
 | 
			
		||||
constexpr TileSheet const&TileSheetEditorModel::img() const noexcept {
 | 
			
		||||
	return m_img;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -133,7 +139,7 @@ constexpr TileSheet &TileSheetEditorModel::img() noexcept {
 | 
			
		||||
	return m_img;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr const Palette *TileSheetEditorModel::pal() const noexcept {
 | 
			
		||||
constexpr Palette const*TileSheetEditorModel::pal() const noexcept {
 | 
			
		||||
	if (m_pal) {
 | 
			
		||||
		return m_pal.get();
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
 | 
			
		||||
	m_bufferSet.elements.resize(pixels * VertexEboLength);
 | 
			
		||||
	// set pixels
 | 
			
		||||
	walkPixels(*subSheet, m_model.img().bpp, [&](std::size_t i, uint8_t p) {
 | 
			
		||||
		auto color = pal->color(p);
 | 
			
		||||
		auto color = core::color(*pal, m_model.palettePage(), p);
 | 
			
		||||
		const auto pt = idxToPt(static_cast<int>(i), subSheet->columns);
 | 
			
		||||
		const auto fx = static_cast<float>(pt.x);
 | 
			
		||||
		const auto fy = static_cast<float>(pt.y);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user