[nostalgia/core/studio] Add Palette page names to editor
All checks were successful
Build / build (push) Successful in 2m33s

This commit is contained in:
Gary Talent 2024-09-06 21:35:09 -05:00
parent ba4540e43f
commit 52533c8c44
8 changed files with 136 additions and 37 deletions

View File

@ -4,8 +4,6 @@
#include <imgui.h>
#include <ox/std/memory.hpp>
#include <keel/media.hpp>
#include <nostalgia/core/gfx.hpp>
@ -17,6 +15,27 @@ namespace nostalgia::core {
namespace ig = studio::ig;
void PaletteEditorImGui::PageRename::draw(turbine::Context &tctx) noexcept {
if (!m_show) {
return;
}
if (ig::BeginPopup(tctx, "Rename Page", m_show)) {
ig::InputText("Name", m_name);
switch (ig::PopupControlsOkCancel(m_show)) {
case ig::PopupResponse::OK:
inputSubmitted.emit(m_name);
[[fallthrough]];
case ig::PopupResponse::Cancel:
close();
default:
break;
}
ImGui::EndPopup();
}
}
PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
Editor(std::move(path)),
m_sctx(sctx),
@ -26,6 +45,7 @@ PaletteEditorImGui::PaletteEditorImGui(studio::StudioContext &sctx, ox::StringPa
throw OxException(1, "PaletteEditorImGui: invalid Palette object");
}
undoStack()->changeTriggered.connect(this, &PaletteEditorImGui::handleCommand);
m_pageRename.inputSubmitted.connect(this, &PaletteEditorImGui::renamePage);
}
void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) {
@ -49,7 +69,7 @@ void PaletteEditorImGui::keyStateChanged(turbine::Key key, bool down) {
void PaletteEditorImGui::draw(studio::StudioContext&) noexcept {
auto const paneSize = ImGui::GetContentRegionAvail();
{
ImGui::BeginChild("Pages", {250, paneSize.y}, true);
ImGui::BeginChild("Pages", {280, paneSize.y}, true);
drawPagesEditor();
ImGui::EndChild();
}
@ -59,6 +79,7 @@ void PaletteEditorImGui::draw(studio::StudioContext&) noexcept {
drawColorsEditor();
ImGui::EndChild();
}
m_pageRename.draw(m_tctx);
}
ox::Error PaletteEditorImGui::saveItem() noexcept {
@ -166,7 +187,7 @@ 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};
auto const btnSz = ImVec2{paneSz.x / 4 - 5.5f, 24};
if (ImGui::Button("Add", btnSz)) {
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, 0u, m_pal.pages.size());
m_page = m_pal.pages.size() - 1;
@ -177,9 +198,13 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
m_page = std::min(m_page, m_pal.pages.size() - 1);
}
ImGui::SameLine();
if (ImGui::Button("Duplicate", btnSz)) {
if (ImGui::Button("Clone", btnSz)) {
std::ignore = pushCommand<DuplicatePageCommand>(m_pal, m_page, m_pal.pages.size());
}
ImGui::SameLine();
if (ImGui::Button("Rename", btnSz)) {
m_pageRename.show(m_pal.pages[m_page].name);
}
ImGui::BeginTable(
"PageSelect",
2,
@ -187,11 +212,13 @@ void PaletteEditorImGui::drawPagesEditor() noexcept {
{paneSz.x, paneSz.y - (toolbarHeight + 5)});
{
ImGui::TableSetupColumn("Page", ImGuiTableColumnFlags_WidthFixed, 60);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 200);
ImGui::TableHeadersRow();
for (auto i = 0u; i < m_pal.pages.size(); ++i) {
ig::IDStackItem const idStackItem(static_cast<int>(i));
ImGui::TableNextRow();
drawColumn(i + 1);
drawColumnLeftAlign(m_pal.pages[i].name);
ImGui::SameLine();
if (ImGui::Selectable("##PageRow", i == m_page, ImGuiSelectableFlags_SpanAllColumns)) {
m_page = i;
@ -232,6 +259,10 @@ void PaletteEditorImGui::drawColorEditor() noexcept {
}
}
ox::Error PaletteEditorImGui::renamePage(ox::StringView name) noexcept {
return pushCommand<RenamePageCommand>(m_pal, m_page, name);
}
ox::Error PaletteEditorImGui::handleCommand(studio::UndoCommand const*cmd) noexcept {
if (dynamic_cast<RemovePageCommand const*>(cmd)) {
m_page = ox::min(m_page, m_pal.pages.size() - 1);

View File

@ -14,6 +14,23 @@ namespace nostalgia::core {
class PaletteEditorImGui: public studio::Editor {
private:
class PageRename {
private:
ox::IString<50> m_name;
bool m_show = false;
public:
ox::Signal<ox::Error(ox::StringView name)> inputSubmitted;
constexpr void show(ox::StringView const&name) noexcept {
m_show = true;
m_name = name;
}
constexpr void close() noexcept {
m_show = false;
}
[[nodiscard]]
constexpr bool isOpen() const noexcept { return m_show; }
void draw(turbine::Context &tctx) noexcept;
} m_pageRename;
studio::StudioContext &m_sctx;
turbine::Context &m_tctx;
Palette m_pal;
@ -45,6 +62,8 @@ class PaletteEditorImGui: public studio::Editor {
void drawColorEditor() noexcept;
ox::Error renamePage(ox::StringView name) noexcept;
ox::Error handleCommand(studio::UndoCommand const*) noexcept;
};

View File

@ -47,6 +47,26 @@ ox::Error ApplyColorAllPagesCommand::undo() noexcept {
}
RenamePageCommand::RenamePageCommand(Palette &pal, size_t const page, ox::StringParam name) noexcept:
m_pal(pal),
m_page(page),
m_name{std::move(name)} {}
int RenamePageCommand::commandId() const noexcept {
return static_cast<int>(PaletteEditorCommandId::RenamePage);
}
ox::Error RenamePageCommand::redo() noexcept {
std::swap(m_pal.pages[m_page].name, m_name);
return {};
}
ox::Error RenamePageCommand::undo() noexcept {
std::swap(m_pal.pages[m_page].name, m_name);
return {};
}
DuplicatePageCommand::DuplicatePageCommand(Palette &pal, size_t srcIdx, size_t dstIdx) noexcept:
m_pal(pal),
m_dstIdx(dstIdx) {

View File

@ -13,6 +13,7 @@ namespace nostalgia::core {
enum class PaletteEditorCommandId {
ApplyColorAllPages,
RenamePage,
DuplicatePage,
RemovePage,
AddColor,
@ -43,6 +44,26 @@ class ApplyColorAllPagesCommand: public studio::UndoCommand {
ox::Error undo() noexcept final;
};
class RenamePageCommand: public studio::UndoCommand {
private:
Palette &m_pal;
size_t m_page = 0;
ox::String m_name;
public:
RenamePageCommand(Palette &pal, size_t page, ox::StringParam name) noexcept;
~RenamePageCommand() noexcept override = default;
[[nodiscard]]
int commandId() const noexcept final;
ox::Error redo() noexcept final;
ox::Error undo() noexcept final;
};
class DuplicatePageCommand: public studio::UndoCommand {
private:
Palette &m_pal;

View File

@ -29,7 +29,7 @@ bool AboutPopup::isOpen() const noexcept {
return m_stage == Stage::Open;
}
void AboutPopup::draw(studio::StudioContext &sctx) noexcept {
void AboutPopup::draw(StudioContext &sctx) noexcept {
switch (m_stage) {
case Stage::Closed:
break;
@ -40,15 +40,14 @@ void AboutPopup::draw(studio::StudioContext &sctx) noexcept {
case Stage::Open: {
constexpr auto modalFlags =
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
ImGui::SetNextWindowSize(ImVec2(215, 90));
studio::ig::centerNextWindow(sctx.tctx);
ig::centerNextWindow(sctx.tctx);
auto open = true;
if (ImGui::BeginPopupModal("About", &open, modalFlags)) {
ImGui::Text("%s", m_text.c_str());
ImGui::NewLine();
ImGui::Dummy(ImVec2(148.0f, 0.0f));
ImGui::Dummy({148.0f, 0.0f});
ImGui::SameLine();
if (ImGui::Button("Close")) {
if (ig::PushButton("Close")) {
ImGui::CloseCurrentPopup();
open = false;
}

View File

@ -18,7 +18,7 @@ struct StudioContext {
StudioUI &ui;
Project *project = nullptr;
turbine::Context &tctx;
inline StudioContext(StudioUI &pUi, turbine::Context &pTctx) noexcept:
StudioContext(StudioUI &pUi, turbine::Context &pTctx) noexcept:
ui(pUi), tctx(pTctx) {}
};

View File

@ -45,10 +45,10 @@ class DragDropSource {
private:
bool const m_active{};
public:
inline DragDropSource() noexcept:
DragDropSource() noexcept:
m_active(ImGui::BeginDragDropSource()) {
}
inline ~DragDropSource() noexcept {
~DragDropSource() noexcept {
if (m_active) {
ImGui::EndDragDropSource();
}
@ -58,7 +58,7 @@ class DragDropSource {
}
};
inline auto dragDropSource(auto const&cb) noexcept {
auto dragDropSource(auto const&cb) noexcept {
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
if (ig::DragDropSource const tgt; tgt) [[unlikely]] {
return cb();
@ -75,10 +75,10 @@ class DragDropTarget {
private:
bool const m_active{};
public:
inline DragDropTarget() noexcept:
DragDropTarget() noexcept:
m_active(ImGui::BeginDragDropTarget()) {
}
inline ~DragDropTarget() noexcept {
~DragDropTarget() noexcept {
if (m_active) {
ImGui::EndDragDropTarget();
}
@ -128,6 +128,21 @@ void centerNextWindow(turbine::Context &ctx) noexcept;
bool PushButton(ox::CStringView lbl, ImVec2 const&btnSz = BtnSz) noexcept;
template<size_t StrCap>
bool InputText(
ox::CStringView label,
ox::IString<StrCap> &text,
ImGuiInputTextFlags const flags = 0,
ImGuiInputTextCallback const callback = nullptr,
void *user_data = nullptr) noexcept {
auto const out = ImGui::InputText(
label.c_str(), text.data(), StrCap + 1, flags, callback, user_data);
if (out) {
std::ignore = text.unsafeResize(ox::strlen(text.c_str()));
}
return out;
}
enum class PopupResponse {
None,
OK,
@ -136,6 +151,8 @@ enum class PopupResponse {
PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen);
PopupResponse PopupControlsOkCancel(bool &popupOpen);
[[nodiscard]]
bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, ImVec2 const&sz = {285, 0});

View File

@ -73,9 +73,13 @@ PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) {
return out;
}
PopupResponse PopupControlsOkCancel(bool &popupOpen) {
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen);
}
bool BeginPopup(turbine::Context &ctx, ox::CStringView popupName, bool &show, ImVec2 const&sz) {
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
ig::centerNextWindow(ctx);
centerNextWindow(ctx);
ImGui::OpenPopup(popupName.c_str());
ImGui::SetNextWindowSize(sz);
return ImGui::BeginPopupModal(popupName.c_str(), &show, modalFlags);
@ -122,23 +126,11 @@ bool ComboBox(
bool FileComboBox(
ox::CStringView lbl,
studio::StudioContext &sctx,
StudioContext &sctx,
ox::StringView fileExt,
size_t &selectedIdx) noexcept {
auto const&list = sctx.project->fileList(fileExt);
bool out{};
auto const first = selectedIdx < list.size() ? list[selectedIdx].c_str() : "";
if (ImGui::BeginCombo(lbl.c_str(), first, 0)) {
for (auto i = 0u; i < list.size(); ++i) {
const auto selected = (selectedIdx == i);
if (ImGui::Selectable(list[i].c_str(), selected) && selectedIdx != i) {
selectedIdx = i;
out = true;
}
}
ImGui::EndCombo();
}
return out;
return ComboBox(lbl, list, selectedIdx);
}
bool ListBox(
@ -164,14 +156,14 @@ bool ListBox(
}
bool ListBox(ox::CStringView name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept {
return ig::ListBox(name, [list](size_t i) -> ox::CStringView {
return ListBox(name, [list](size_t i) -> ox::CStringView {
return list[i];
}, list.size(), selIdx);
}
FilePicker::FilePicker(
studio::StudioContext &sctx,
StudioContext &sctx,
ox::String title,
ox::String fileExt,
ImVec2 const&size) noexcept:
@ -186,12 +178,12 @@ void FilePicker::draw() noexcept {
return;
}
auto constexpr popupSz = ImVec2{450.f, 0};
ig::IDStackItem const idStackItem(m_title.c_str());
if (ig::BeginPopup(m_sctx.tctx, m_title.c_str(), m_show, popupSz)) {
IDStackItem const idStackItem(m_title);
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
auto const&list = m_sctx.project->fileList(m_fileExt);
size_t selIdx{};
ig::ComboBox(m_title.c_str(), list, selIdx);
if (ig::PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
ComboBox(m_title, list, selIdx);
if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
filePicked.emit(list[selIdx]);
}
ImGui::EndPopup();