[nostalgia/core/studio] Add key shortcuts for switching Palette pages
All checks were successful
Build / build (push) Successful in 2m22s

This commit is contained in:
Gary Talent 2024-01-17 22:48:53 -06:00
parent b31062e609
commit 2bc2003caa
10 changed files with 94 additions and 60 deletions

View File

@ -22,6 +22,25 @@ PaletteEditorImGui::PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView p
undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand); undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
} }
void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) {
if (!down) {
return;
}
if (key >= turbine::Key::Num_1 && key <= turbine::Key::Num_9) {
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Alt)) {
m_page = ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1), m_pal.pages.size() - 1);
}
} else if (key == turbine::Key::Num_0) {
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Alt)) {
m_selectedColorRow =
ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), m_pal.pages.size() - 1);
}
}
}
void PaletteEditorImGui::draw(turbine::Context&) noexcept { void PaletteEditorImGui::draw(turbine::Context&) noexcept {
auto const paneSize = ImGui::GetContentRegionAvail(); auto const paneSize = ImGui::GetContentRegionAvail();
{ {

View File

@ -22,6 +22,8 @@ class PaletteEditorImGui: public studio::Editor {
public: public:
PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path); PaletteEditorImGui(turbine::Context &ctx, ox::CRStringView path);
void keyStateChanged(turbine::Key key, bool down) override;
void draw(turbine::Context&) noexcept final; void draw(turbine::Context&) noexcept final;
protected: protected:

View File

@ -112,8 +112,8 @@ 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 (pal && !popupOpen) { if (!popupOpen) {
const auto colorCnt = pal->pages[m_palPage].size(); const auto colorCnt = pal.pages[m_model.palettePage()].size();
if (key == turbine::Key::Alpha_D) { if (key == turbine::Key::Alpha_D) {
m_tool = Tool::Draw; m_tool = Tool::Draw;
setCopyEnabled(false); setCopyEnabled(false);
@ -131,12 +131,25 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key key, bool down) {
setCutEnabled(false); setCutEnabled(false);
setPasteEnabled(false); setPasteEnabled(false);
m_model.clearSelection(); m_model.clearSelection();
} else if (key >= turbine::Key::Num_1 && key <= turbine::Key::Num_9 && key <= turbine::Key::Num_0 + colorCnt) { } else if (key >= turbine::Key::Num_1 && key <= turbine::Key::Num_9) {
auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1); if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Alt)) {
m_view.setPalIdx(idx); auto const idx = ox::min<std::size_t>(
} else if (key == turbine::Key::Num_0 && colorCnt >= 10) { static_cast<uint32_t>(key - turbine::Key::Num_1), m_model.pal().pages.size() - 1);
auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), colorCnt - 1); m_model.setPalettePage(idx);
m_view.setPalIdx(idx); } else if (key <= turbine::Key::Num_0 + colorCnt) {
auto idx = ox::min<std::size_t>(static_cast<uint32_t>(key - turbine::Key::Num_1), colorCnt - 1);
m_view.setPalIdx(idx);
}
} else if (key == turbine::Key::Num_0) {
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Alt)) {
auto const idx = ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), m_model.pal().pages.size() - 1);
m_model.setPalettePage(idx);
} else if (colorCnt >= 10) {
auto idx = ox::min<std::size_t>(
static_cast<uint32_t>(key - turbine::Key::Num_1 + 9), colorCnt - 1);
m_view.setPalIdx(idx);
}
} }
} }
} }
@ -306,8 +319,8 @@ ox::Error TileSheetEditorImGui::exportSubhseetToPng(int scale) noexcept {
auto const err = toPngFile( auto const err = toPngFile(
path, path,
std::move(pixels), std::move(pixels),
*pal, pal,
m_palPage, m_model.palettePage(),
static_cast<unsigned>(width * scale), static_cast<unsigned>(width * scale),
static_cast<unsigned>(height * scale)); static_cast<unsigned>(height * scale));
if (err) { if (err) {
@ -402,19 +415,18 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
} }
ImGui::EndCombo(); ImGui::EndCombo();
} }
auto const pages = m_model.pal()->pages.size(); auto const pages = m_model.pal().pages.size();
if (pages > 1) { if (pages > 1) {
ImGui::Indent(20); ImGui::Indent(20);
ox::Array<char, 10> numStr; ox::Array<char, 10> numStr;
ox_itoa(m_palPage + 1, numStr.data()); ox_itoa(m_model.palettePage() + 1, numStr.data());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - comboWidthSub);
if (ImGui::BeginCombo("Page", numStr.data(), 0)) { if (ImGui::BeginCombo("Page", numStr.data(), 0)) {
for (auto n = 0u; n < pages; ++n) { for (auto n = 0u; n < pages; ++n) {
const auto selected = (m_palPage == n); const auto selected = (m_model.palettePage() == n);
ox_itoa(n + 1, numStr.data()); ox_itoa(n + 1, numStr.data());
if (ImGui::Selectable(numStr.data(), selected) && m_palPage != n) { if (ImGui::Selectable(numStr.data(), selected) && m_model.palettePage() != n) {
m_palPage = n; m_model.setPalettePage(n);
m_model.setPalettePage(m_palPage);
} }
if (selected) { if (selected) {
ImGui::SetItemDefaultFocus(); ImGui::SetItemDefaultFocus();
@ -430,8 +442,9 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept {
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Color16", 0, 3); ImGui::TableSetupColumn("Color16", 0, 3);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
if (auto pal = m_view.pal()) { {
for (auto i = 0u; auto c: pal->pages[m_palPage]) { auto const&pal = m_model.pal();
for (auto i = 0u; auto c: pal.pages[m_model.palettePage()]) {
ImGui::PushID(static_cast<int>(i)); ImGui::PushID(static_cast<int>(i));
// Column: color idx // Column: color idx
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -473,7 +486,7 @@ ox::Error TileSheetEditorImGui::setPaletteSelection() noexcept {
return {}; return {};
} }
ox::Error TileSheetEditorImGui::markUnsavedChanges(const studio::UndoCommand*) noexcept { ox::Error TileSheetEditorImGui::markUnsavedChanges(studio::UndoCommand const*) noexcept {
setUnsavedChanges(true); setUnsavedChanges(true);
return {}; return {};
} }

View File

@ -69,7 +69,6 @@ class TileSheetEditorImGui: public studio::Editor {
float m_palViewWidth = 300; float m_palViewWidth = 300;
ox::Vec2 m_prevMouseDownPos; ox::Vec2 m_prevMouseDownPos;
Tool m_tool = Tool::Draw; Tool m_tool = Tool::Draw;
size_t m_palPage = 0;
public: public:
TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringView path); TileSheetEditorImGui(turbine::Context &ctx, ox::CRStringView path);
@ -111,7 +110,7 @@ class TileSheetEditorImGui: public studio::Editor {
// slots // slots
private: private:
ox::Error markUnsavedChanges(const studio::UndoCommand*) noexcept; ox::Error markUnsavedChanges(studio::UndoCommand const*) noexcept;
}; };

View File

@ -228,6 +228,7 @@ ox::Error TileSheetEditorModel::markUpdatedCmdId(studio::UndoCommand const*cmd)
const auto cmdId = cmd->commandId(); const auto cmdId = cmd->commandId();
if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) { if (static_cast<CommandId>(cmdId) == CommandId::PaletteChange) {
oxReturnError(readObj<Palette>(keelCtx(m_ctx), m_img.defaultPalette).moveTo(m_pal)); oxReturnError(readObj<Palette>(keelCtx(m_ctx), m_img.defaultPalette).moveTo(m_pal));
m_palettePage = ox::min<size_t>(m_pal->pages.size(), 0);
paletteChanged.emit(); paletteChanged.emit();
} }
auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd); auto tsCmd = dynamic_cast<const TileSheetCommand*>(cmd);

View File

@ -55,7 +55,7 @@ class TileSheetEditorModel: public ox::SignalHandler {
constexpr TileSheet &img() noexcept; constexpr TileSheet &img() noexcept;
[[nodiscard]] [[nodiscard]]
constexpr Palette const*pal() const noexcept; constexpr Palette const&pal() const noexcept;
[[nodiscard]] [[nodiscard]]
ox::StringView palPath() const noexcept; ox::StringView palPath() const noexcept;
@ -139,11 +139,11 @@ constexpr TileSheet &TileSheetEditorModel::img() noexcept {
return m_img; return m_img;
} }
constexpr Palette const*TileSheetEditorModel::pal() const noexcept { constexpr Palette const&TileSheetEditorModel::pal() const noexcept {
if (m_pal) { if (m_pal) {
return m_pal.get(); return *m_pal;
} }
return &s_defaultPalette; return s_defaultPalette;
} }
constexpr studio::UndoStack *TileSheetEditorModel::undoStack() noexcept { constexpr studio::UndoStack *TileSheetEditorModel::undoStack() noexcept {

View File

@ -66,7 +66,7 @@ void TileSheetEditorView::deleteTile(ox::Vec2 const&paneSize, ox::Vec2 const&cli
m_model.deleteTiles(m_model.activeSubSheetIdx(), tileIdx, 1); m_model.deleteTiles(m_model.activeSubSheetIdx(), tileIdx, 1);
} }
void TileSheetEditorView::clickDraw(ox::Vec2 const &paneSize, ox::Vec2 const&clickPos) noexcept { void TileSheetEditorView::clickDraw(ox::Vec2 const&paneSize, ox::Vec2 const&clickPos) noexcept {
const auto pt = clickPoint(paneSize, clickPos); const auto pt = clickPoint(paneSize, clickPos);
m_model.drawCommand(pt, m_palIdx); m_model.drawCommand(pt, m_palIdx);
} }

View File

@ -80,9 +80,6 @@ class TileSheetEditorView: public ox::SignalHandler {
[[nodiscard]] [[nodiscard]]
constexpr TileSheet &img() noexcept; constexpr TileSheet &img() noexcept;
[[nodiscard]]
constexpr const Palette *pal() const noexcept;
[[nodiscard]] [[nodiscard]]
constexpr auto &model() noexcept { constexpr auto &model() noexcept {
return m_model; return m_model;
@ -126,8 +123,4 @@ constexpr TileSheet &TileSheetEditorView::img() noexcept {
return m_model.img(); return m_model.img();
} }
constexpr const Palette *TileSheetEditorView::pal() const noexcept {
return m_model.pal();
}
} }

View File

@ -17,8 +17,8 @@ void TileSheetPixels::setPixelSizeMod(float sm) noexcept {
} }
ox::Error TileSheetPixels::buildShader() noexcept { ox::Error TileSheetPixels::buildShader() noexcept {
const auto Vshad = ox::sfmt(VShad, gl::GlslVersion); auto const Vshad = ox::sfmt(VShad, gl::GlslVersion);
const auto Fshad = ox::sfmt(FShad, gl::GlslVersion); auto const Fshad = ox::sfmt(FShad, gl::GlslVersion);
return glutils::buildShaderProgram(Vshad, Fshad).moveTo(m_shader); return glutils::buildShaderProgram(Vshad, Fshad).moveTo(m_shader);
} }
@ -28,7 +28,7 @@ void TileSheetPixels::draw(bool update, ox::Vec2 const&scroll) noexcept {
if (update) { if (update) {
glutils::sendVbo(m_bufferSet); glutils::sendVbo(m_bufferSet);
} }
const auto uniformScroll = glGetUniformLocation(m_shader, "vScroll"); auto const uniformScroll = glGetUniformLocation(m_shader, "vScroll");
glUniform2f(uniformScroll, scroll.x, scroll.y); glUniform2f(uniformScroll, scroll.x, scroll.y);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_bufferSet.elements.size()), GL_UNSIGNED_INT, nullptr); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_bufferSet.elements.size()), GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0); glBindVertexArray(0);
@ -41,10 +41,10 @@ void TileSheetPixels::initBufferSet(ox::Vec2 const&paneSize) noexcept {
m_bufferSet.ebo = glutils::generateBuffer(); m_bufferSet.ebo = glutils::generateBuffer();
update(paneSize); update(paneSize);
// vbo layout // vbo layout
const auto posAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vPosition")); auto const posAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vPosition"));
glEnableVertexAttribArray(posAttr); glEnableVertexAttribArray(posAttr);
glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), nullptr); glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), nullptr);
const auto colorAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vColor")); auto const colorAttr = static_cast<GLuint>(glGetAttribLocation(m_shader, "vColor"));
glEnableVertexAttribArray(colorAttr); glEnableVertexAttribArray(colorAttr);
glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float), glVertexAttribPointer(colorAttr, 3, GL_FLOAT, GL_FALSE, VertexVboRowLength * sizeof(float),
std::bit_cast<void*>(uintptr_t{2 * sizeof(float)})); std::bit_cast<void*>(uintptr_t{2 * sizeof(float)}));
@ -58,9 +58,9 @@ void TileSheetPixels::update(ox::Vec2 const&paneSize) noexcept {
} }
ox::Vec2 TileSheetPixels::pixelSize(ox::Vec2 const&paneSize) const noexcept { ox::Vec2 TileSheetPixels::pixelSize(ox::Vec2 const&paneSize) const noexcept {
const auto [sw, sh] = paneSize; auto const [sw, sh] = paneSize;
constexpr float ymod = 0.35f / 10.0f; constexpr float ymod = 0.35f / 10.0f;
const auto xmod = ymod * sh / sw; auto const xmod = ymod * sh / sw;
return {xmod * m_pixelSizeMod, ymod * m_pixelSizeMod}; return {xmod * m_pixelSizeMod, ymod * m_pixelSizeMod};
} }
@ -71,21 +71,21 @@ void TileSheetPixels::setPixelBufferObject(
Color16 color, Color16 color,
float *vbo, float *vbo,
GLuint *ebo) const noexcept { GLuint *ebo) const noexcept {
const auto [xmod, ymod] = pixelSize(paneSize); auto const [xmod, ymod] = pixelSize(paneSize);
x *= xmod; x *= xmod;
y *= -ymod; y *= -ymod;
x -= 1.0f; x -= 1.0f;
y += 1.0f - ymod; y += 1.0f - ymod;
const auto r = redf(color), g = greenf(color), b = bluef(color); auto const r = redf(color), g = greenf(color), b = bluef(color);
// don't worry, these memcpys gets optimized to something much more ideal // don't worry, these memcpys gets optimized to something much more ideal
const ox::Array<float, VertexVboLength> vertices = { ox::Array<float, VertexVboLength> const vertices = {
x, y, r, g, b, // bottom left x, y, r, g, b, // bottom left
x + xmod, y, r, g, b, // bottom right x + xmod, y, r, g, b, // bottom right
x + xmod, y + ymod, r, g, b, // top right x + xmod, y + ymod, r, g, b, // top right
x, y + ymod, r, g, b, // top left x, y + ymod, r, g, b, // top left
}; };
memcpy(vbo, vertices.data(), sizeof(vertices)); memcpy(vbo, vertices.data(), sizeof(vertices));
const ox::Array<GLuint, VertexEboLength> elms = { ox::Array<GLuint, VertexEboLength> const elms = {
vertexRow + 0, vertexRow + 1, vertexRow + 2, vertexRow + 0, vertexRow + 1, vertexRow + 2,
vertexRow + 2, vertexRow + 3, vertexRow + 0, vertexRow + 2, vertexRow + 3, vertexRow + 0,
}; };
@ -94,21 +94,21 @@ void TileSheetPixels::setPixelBufferObject(
void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept { void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
// set buffer lengths // set buffer lengths
const auto subSheet = m_model.activeSubSheet(); auto const subSheet = m_model.activeSubSheet();
const auto pal = m_model.pal(); auto const&pal = m_model.pal();
const auto width = subSheet->columns * TileWidth; auto const width = subSheet->columns * TileWidth;
const auto height = subSheet->rows * TileHeight; auto const height = subSheet->rows * TileHeight;
const auto pixels = static_cast<unsigned>(width * height); auto const pixels = static_cast<size_t>(width) * static_cast<size_t>(height);
m_bufferSet.vertices.resize(pixels * VertexVboLength); m_bufferSet.vertices.resize(pixels * VertexVboLength);
m_bufferSet.elements.resize(pixels * VertexEboLength); m_bufferSet.elements.resize(pixels * VertexEboLength);
// set pixels // set pixels
walkPixels(*subSheet, m_model.img().bpp, [&](std::size_t i, uint8_t p) { walkPixels(*subSheet, m_model.img().bpp, [&](std::size_t i, uint8_t p) {
auto color = core::color(*pal, m_model.palettePage(), p); auto color = core::color(pal, m_model.palettePage(), p);
const auto pt = idxToPt(static_cast<int>(i), subSheet->columns); auto const pt = idxToPt(static_cast<int>(i), subSheet->columns);
const auto fx = static_cast<float>(pt.x); auto const fx = static_cast<float>(pt.x);
const auto fy = static_cast<float>(pt.y); auto const fy = static_cast<float>(pt.y);
const auto vbo = &m_bufferSet.vertices[i * VertexVboLength]; auto const vbo = &m_bufferSet.vertices[i * VertexVboLength];
const auto ebo = &m_bufferSet.elements[i * VertexEboLength]; auto const ebo = &m_bufferSet.elements[i * VertexEboLength];
if (i * VertexVboLength + VertexVboLength > m_bufferSet.vertices.size()) { if (i * VertexVboLength + VertexVboLength > m_bufferSet.vertices.size()) {
return; return;
} }
@ -116,9 +116,9 @@ void TileSheetPixels::setBufferObjects(ox::Vec2 const&paneSize) noexcept {
return; return;
} }
if (m_model.pixelSelected(i)) { if (m_model.pixelSelected(i)) {
const auto r = red16(color) / 2; auto const r = red16(color) / 2;
const auto g = (green16(color) + 20) / 2; auto const g = (green16(color) + 20) / 2;
const auto b = (blue16(color) + 31) / 2; auto const b = (blue16(color) + 31) / 2;
color = color16(r, g, b); color = color16(r, g, b);
} }
setPixelBufferObject(paneSize, static_cast<unsigned>(i * VertexVboRows), fx, fy, color, vbo, ebo); setPixelBufferObject(paneSize, static_cast<unsigned>(i * VertexVboRows), fx, fy, color, vbo, ebo);

View File

@ -43,7 +43,7 @@ class TileSheetPixels {
float m_pixelSizeMod = 1; float m_pixelSizeMod = 1;
glutils::GLProgram m_shader; glutils::GLProgram m_shader;
glutils::BufferSet m_bufferSet; glutils::BufferSet m_bufferSet;
const class TileSheetEditorModel &m_model; class TileSheetEditorModel const&m_model;
public: public:
explicit TileSheetPixels(class TileSheetEditorModel &model) noexcept; explicit TileSheetPixels(class TileSheetEditorModel &model) noexcept;
@ -62,7 +62,14 @@ class TileSheetPixels {
ox::Vec2 pixelSize(ox::Vec2 const&paneSize) const noexcept; ox::Vec2 pixelSize(ox::Vec2 const&paneSize) const noexcept;
private: private:
void setPixelBufferObject(ox::Vec2 const&paneS, unsigned vertexRow, float x, float y, Color16 color, float *vbo, GLuint *ebo) const noexcept; void setPixelBufferObject(
ox::Vec2 const&paneS,
unsigned vertexRow,
float x,
float y,
Color16 color,
float *vbo,
GLuint *ebo) const noexcept;
void setBufferObjects(ox::Vec2 const&paneS) noexcept; void setBufferObjects(ox::Vec2 const&paneS) noexcept;