Compare commits

...

3 Commits

Author SHA1 Message Date
92e9d9cbfc [keel,studio] Add support for New Item templates
Some checks failed
Build / build (push) Failing after 1m3s
2025-01-18 20:16:29 -06:00
b29b9a9b3a [ox/std] Add UAnyPtr 2025-01-18 20:11:42 -06:00
721f844214 [nostalgia/core/studio/tilesheeteditor] Fix subsheet and palette scrolling 2025-01-18 20:08:09 -06:00
14 changed files with 435 additions and 115 deletions

View File

@ -15,16 +15,20 @@
namespace ox { namespace ox {
class AnyPtr { namespace detail {
template<bool unique>
class AnyPtrT {
private: private:
struct WrapBase { struct WrapBase {
virtual constexpr ~WrapBase() = default; virtual constexpr ~WrapBase() = default;
virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0; virtual constexpr WrapBase *copyTo(ox::Span<char> s) noexcept = 0;
virtual constexpr operator bool() const noexcept = 0; virtual constexpr operator bool() const noexcept = 0;
virtual void free() noexcept = 0;
}; };
template<typename T> template<typename T>
struct Wrap: public WrapBase { struct Wrap final: WrapBase {
T *data{}; T *data{};
constexpr Wrap(T *pData) noexcept: data(pData) { constexpr Wrap(T *pData) noexcept: data(pData) {
} }
@ -39,16 +43,20 @@ class AnyPtr {
constexpr operator bool() const noexcept override { constexpr operator bool() const noexcept override {
return data != nullptr; return data != nullptr;
} }
constexpr void free() noexcept override {
ox::safeDelete(data);
data = {};
}
}; };
WrapBase *m_wrapPtr{}; WrapBase *m_wrapPtr{};
ox::Array<char, sizeof(Wrap<void*>)> m_wrapData; ox::Array<char, sizeof(Wrap<void*>)> m_wrapData;
public: public:
constexpr AnyPtr() noexcept = default; constexpr AnyPtrT() noexcept = default;
template<typename T> template<typename T>
constexpr AnyPtr(T *ptr) noexcept { constexpr AnyPtrT(T *ptr) noexcept {
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
m_wrapPtr = new Wrap(ptr); m_wrapPtr = new Wrap(ptr);
} else { } else {
@ -56,22 +64,39 @@ class AnyPtr {
} }
} }
constexpr AnyPtr(AnyPtr const&other) noexcept { constexpr AnyPtrT(AnyPtrT const&other) noexcept {
if (other) { if (other) {
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData); m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
} }
} }
constexpr ~AnyPtr() noexcept { constexpr AnyPtrT(AnyPtrT &&other) noexcept {
if (other) {
m_wrapPtr = other.m_wrapPtr->copyTo(m_wrapData);
if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
}
other.m_wrapPtr = {};
}
}
constexpr ~AnyPtrT() noexcept {
if constexpr(unique) {
free();
}
if (std::is_constant_evaluated()) { if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr); ox::safeDelete(m_wrapPtr);
} }
} }
template<typename T> template<typename T>
constexpr AnyPtr &operator=(T *ptr) noexcept { constexpr AnyPtrT &operator=(T *ptr) noexcept {
if (std::is_constant_evaluated()) { if constexpr(unique) {
free();
} else if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr); ox::safeDelete(m_wrapPtr);
}
if (std::is_constant_evaluated()) {
m_wrapPtr = new Wrap(ptr); m_wrapPtr = new Wrap(ptr);
} else { } else {
m_wrapPtr = new(m_wrapData.data()) Wrap(ptr); m_wrapPtr = new(m_wrapData.data()) Wrap(ptr);
@ -79,10 +104,14 @@ class AnyPtr {
return *this; return *this;
} }
constexpr AnyPtr &operator=(AnyPtr const&ptr) noexcept { constexpr AnyPtrT &operator=(AnyPtrT const&ptr) noexcept {
if (this != &ptr) { if (this != &ptr) {
if (ptr) { if constexpr(unique) {
free();
} else if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr); ox::safeDelete(m_wrapPtr);
}
if (ptr) {
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData); m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
} else { } else {
m_wrapPtr = nullptr; m_wrapPtr = nullptr;
@ -91,10 +120,40 @@ class AnyPtr {
return *this; return *this;
} }
constexpr AnyPtrT &operator=(AnyPtrT &&ptr) noexcept {
if (this != &ptr) {
if constexpr(unique) {
free();
} else if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
}
if (ptr) {
m_wrapPtr = ptr.m_wrapPtr->copyTo(m_wrapData);
if (std::is_constant_evaluated()) {
ox::safeDelete(ptr.m_wrapPtr);
ptr.m_wrapPtr = nullptr;
}
} else {
m_wrapPtr = nullptr;
}
}
return *this;
}
constexpr operator bool() const noexcept { constexpr operator bool() const noexcept {
return m_wrapPtr && *m_wrapPtr; return m_wrapPtr && *m_wrapPtr;
} }
constexpr void free() noexcept {
if (m_wrapPtr) {
m_wrapPtr->free();
}
if (std::is_constant_evaluated()) {
ox::safeDelete(m_wrapPtr);
}
m_wrapPtr = nullptr;
}
template<typename T> template<typename T>
[[nodiscard]] [[nodiscard]]
constexpr T *get() const noexcept { constexpr T *get() const noexcept {
@ -104,6 +163,12 @@ class AnyPtr {
return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data; return dynamic_cast<Wrap<T>*>(m_wrapPtr)->data;
#endif #endif
} }
}; };
}
using AnyPtr = detail::AnyPtrT<false>;
using UAnyPtr = detail::AnyPtrT<true>;
} }

View File

@ -234,7 +234,7 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true); ImGui::BeginChild("SubSheets", {s_palViewWidth - 24, ySize / 2.f}, true);
{ {
static constexpr auto btnHeight = ig::BtnSz.y; static constexpr auto btnHeight = ig::BtnSz.y;
auto const btnSize = ImVec2{btnHeight, btnHeight}; auto constexpr btnSize = ImVec2{btnHeight, btnHeight};
if (ig::PushButton("+", btnSize)) { if (ig::PushButton("+", btnSize)) {
auto insertOnIdx = m_model.activeSubSheetIdx(); auto insertOnIdx = m_model.activeSubSheetIdx();
auto const&parent = m_model.activeSubSheet(); auto const&parent = m_model.activeSubSheet();
@ -258,16 +258,19 @@ void TileSheetEditorImGui::draw(studio::StudioContext&) noexcept {
m_exportMenu.show(); m_exportMenu.show();
} }
TileSheet::SubSheetIdx path; TileSheet::SubSheetIdx path;
static constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; static constexpr auto flags =
if (ImGui::BeginTable("Subsheets", 4, flags)) { ImGuiTableFlags_RowBg |
ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide); ImGuiTableFlags_NoBordersInBody |
ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25); ImGuiTableFlags_ScrollY;
ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50); if (ImGui::BeginTable("Subsheets", 4, flags)) {
ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50); ImGui::TableSetupColumn("Subsheet", ImGuiTableColumnFlags_NoHide);
ImGui::TableHeadersRow(); ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 25);
drawSubsheetSelector(m_view.img().subsheet, path); ImGui::TableSetupColumn("Columns", ImGuiTableColumnFlags_WidthFixed, 50);
ImGui::EndTable(); ImGui::TableSetupColumn("Rows", ImGuiTableColumnFlags_WidthFixed, 50);
} ImGui::TableHeadersRow();
drawSubsheetSelector(m_view.img().subsheet, path);
ImGui::EndTable();
}
} }
ImGui::EndChild(); ImGui::EndChild();
} }
@ -462,8 +465,12 @@ void TileSheetEditorImGui::drawPaletteMenu() noexcept {
} }
} }
// header // header
auto constexpr palTblFlags =
ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable( if (ImGui::BeginTable(
"PaletteTable", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) { "PaletteTable", 4, palTblFlags)) {
ImGui::TableSetupColumn("Idx", 0, 0.6f); ImGui::TableSetupColumn("Idx", 0, 0.6f);
ImGui::TableSetupColumn("", 0, 0.22f); ImGui::TableSetupColumn("", 0, 0.22f);
ImGui::TableSetupColumn("Name", 0, 3); ImGui::TableSetupColumn("Name", 0, 3);

View File

@ -20,6 +20,8 @@ class Wrap {
virtual ox::CStringView typeName() const noexcept = 0; virtual ox::CStringView typeName() const noexcept = 0;
[[nodiscard]] [[nodiscard]]
virtual int typeVersion() const noexcept = 0; virtual int typeVersion() const noexcept = 0;
[[nodiscard]]
virtual ox::UAnyPtr moveToCopy() noexcept = 0;
}; };
template<typename T> template<typename T>
@ -27,6 +29,11 @@ class WrapT: public Wrap {
public: public:
[[nodiscard]] [[nodiscard]]
virtual constexpr T &obj() noexcept = 0; virtual constexpr T &obj() noexcept = 0;
ox::UAnyPtr moveToCopy() noexcept final {
return new T{std::move(obj())};
}
}; };
template<typename T> template<typename T>
@ -184,7 +191,18 @@ ox::Result<ox::UPtr<Wrap>> convert(
auto &src, auto &src,
ox::StringViewCR dstTypeName, ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept { int const dstTypeVersion) noexcept {
return convert(ctx, WrapRef{src}, dstTypeName, dstTypeVersion); WrapRef ref{src};
return convert(ctx, static_cast<Wrap&>(ref), dstTypeName, dstTypeVersion);
}
ox::Result<ox::UPtr<Wrap>> convert(
keel::Context &ctx,
auto const&src,
ox::StringViewCR dstTypeName,
int const dstTypeVersion) noexcept {
auto srcCpy = src;
WrapRef ref{srcCpy};
return convert(ctx, ref, dstTypeName, dstTypeVersion);
} }
template<typename DstType> template<typename DstType>

View File

@ -14,7 +14,7 @@ namespace studio {
NewMenu::NewMenu() noexcept { NewMenu::NewMenu() noexcept {
setTitle("New Item"); setTitle("New Item");
setSize({230, 140}); setSize({280, 180});
} }
void NewMenu::open() noexcept { void NewMenu::open() noexcept {
@ -33,7 +33,7 @@ bool NewMenu::isOpen() const noexcept {
return m_open; return m_open;
} }
void NewMenu::draw(studio::StudioContext &sctx) noexcept { void NewMenu::draw(StudioContext &sctx) noexcept {
switch (m_stage) { switch (m_stage) {
case Stage::Opening: case Stage::Opening:
ImGui::OpenPopup(title().c_str()); ImGui::OpenPopup(title().c_str());
@ -46,51 +46,73 @@ void NewMenu::draw(studio::StudioContext &sctx) noexcept {
case Stage::NewItemName: case Stage::NewItemName:
drawNewItemName(sctx); drawNewItemName(sctx);
break; break;
case Stage::NewItemTemplate:
drawNewItemTemplate(sctx);
break;
case Stage::Closed: case Stage::Closed:
m_open = false; m_open = false;
break; break;
} }
} }
void NewMenu::addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept { void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept {
m_types.emplace_back(std::move(im)); m_types.emplace_back(std::move(im));
std::sort( std::sort(
m_types.begin(), m_types.end(), m_types.begin(), m_types.end(),
[](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) { [](ox::UPtr<ItemMaker> const&im1, ox::UPtr<ItemMaker> const&im2) {
return im1->typeName < im2->typeName; return im1->typeDisplayName() < im2->typeDisplayName();
}); });
} }
void NewMenu::drawNewItemType(studio::StudioContext &sctx) noexcept { void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept {
drawWindow(sctx.tctx, &m_open, [this] { for (auto const&im : m_types) {
auto const allocSz = m_types.size() * sizeof(char const*); if (im->installTemplate(tmplt)) {
auto mem = ox_malloca(allocSz, char const*, nullptr); break;
auto items = ox::Span{mem.get(), allocSz};
for (auto i = 0u; auto const&im : m_types) {
items[i] = im->typeName.c_str();
++i;
} }
ImGui::ListBox("Item Type", &m_selectedType, items.data(), static_cast<int>(m_types.size())); }
drawFirstPageButtons(); }
void NewMenu::drawNewItemType(StudioContext const&sctx) noexcept {
drawWindow(sctx.tctx, m_open, [this] {
ig::ListBox("Item Type", [&](size_t const i) -> ox::CStringView {
return m_types[i]->typeDisplayName();
}, m_types.size(), m_selectedType, {200, 100});
auto const&im = *m_types[m_selectedType];
drawFirstPageButtons(im.itemTemplates().size() == 1 ?
Stage::NewItemName : Stage::NewItemTemplate);
}); });
} }
void NewMenu::drawNewItemName(studio::StudioContext &sctx) noexcept { void NewMenu::drawNewItemTemplate(StudioContext &sctx) noexcept {
drawWindow(sctx.tctx, &m_open, [this, &sctx] { drawWindow(sctx.tctx, m_open, [this] {
auto const typeIdx = static_cast<std::size_t>(m_selectedType); auto const&templates =
if (typeIdx < m_types.size()) { m_types[m_selectedType]->itemTemplates();
ig::ListBox("Template", [&](size_t const i) -> ox::CStringView {
return templates[i]->name();
}, templates.size(), m_selectedTemplate, {200, 100});
drawButtons(Stage::NewItemType, Stage::NewItemName);
});
}
void NewMenu::drawNewItemName(StudioContext &sctx) noexcept {
drawWindow(sctx.tctx, m_open, [this, &sctx] {
if (m_selectedType < m_types.size()) {
ig::InputText("Name", m_itemName); ig::InputText("Name", m_itemName);
} }
drawLastPageButtons(sctx); drawLastPageButtons(sctx);
}); });
} }
void NewMenu::drawFirstPageButtons() noexcept { void NewMenu::drawButtons(Stage const prev, Stage const next) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
auto const btnSz = ImVec2(60, 20); constexpr ImVec2 btnSz{60, 20};
if (ImGui::Button("Back", btnSz)) {
m_stage = prev;
}
ImGui::SameLine();
if (ImGui::Button("Next", btnSz)) { if (ImGui::Button("Next", btnSz)) {
m_stage = Stage::NewItemName; m_stage = next;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel", btnSz)) { if (ImGui::Button("Cancel", btnSz)) {
@ -99,34 +121,50 @@ void NewMenu::drawFirstPageButtons() noexcept {
} }
} }
void NewMenu::drawLastPageButtons(studio::StudioContext &sctx) noexcept { void NewMenu::drawFirstPageButtons(Stage const next) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 138); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
if (ImGui::Button("Back")) { constexpr ImVec2 btnSz{60, 20};
m_stage = Stage::NewItemType; if (ImGui::Button("Next", btnSz)) {
m_stage = next;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Finish")) { if (ImGui::Button("Cancel", btnSz)) {
finish(sctx);
}
ImGui::SameLine();
if (ImGui::Button("Quit")) {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
m_stage = Stage::Closed; m_stage = Stage::Closed;
} }
} }
void NewMenu::finish(studio::StudioContext &sctx) noexcept { void NewMenu::drawLastPageButtons(StudioContext &sctx) noexcept {
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 198);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
constexpr ImVec2 btnSz{60, 20};
if (ImGui::Button("Back", btnSz)) {
m_stage = Stage::NewItemType;
}
ImGui::SameLine();
if (ImGui::Button("Finish", btnSz)) {
finish(sctx);
}
ImGui::SameLine();
if (ImGui::Button("Quit", btnSz)) {
ImGui::CloseCurrentPopup();
m_stage = Stage::Closed;
}
}
void NewMenu::finish(StudioContext &sctx) noexcept {
if (m_itemName.len() == 0) { if (m_itemName.len() == 0) {
oxLogError(ox::Error(1, "New file error: no file name")); oxLogError(ox::Error{1, "New file error: no file name"});
return; return;
} }
auto const&typeMaker = *m_types[static_cast<std::size_t>(m_selectedType)]; auto const&im = *m_types[static_cast<std::size_t>(m_selectedType)];
if (sctx.project->exists(typeMaker.itemPath(m_itemName))) { if (sctx.project->exists(im.itemPath(m_itemName))) {
oxLogError(ox::Error(1, "New file error: file already exists")); oxLogError(ox::Error{1, "New file error: file already exists"});
return; return;
} }
auto const [path, err] = typeMaker.write(sctx, m_itemName); auto const [path, err] =
im.write(sctx, m_itemName, m_selectedTemplate);
if (err) { if (err) {
oxLogError(err); oxLogError(err);
return; return;

View File

@ -13,13 +13,14 @@
namespace studio { namespace studio {
class NewMenu: public studio::Popup { class NewMenu final: public Popup {
public: public:
enum class Stage { enum class Stage {
Closed, Closed,
Opening, Opening,
NewItemType, NewItemType,
NewItemName, NewItemName,
NewItemTemplate,
}; };
// emits path parameter // emits path parameter
@ -29,8 +30,9 @@ class NewMenu: public studio::Popup {
Stage m_stage = Stage::Closed; Stage m_stage = Stage::Closed;
ox::String m_typeName; ox::String m_typeName;
ox::IString<255> m_itemName; ox::IString<255> m_itemName;
ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types; ox::Vector<ox::UPtr<studio::ItemMaker>> m_types;
int m_selectedType = 0; size_t m_selectedType = 0;
size_t m_selectedTemplate = 0;
bool m_open = false; bool m_open = false;
public: public:
@ -43,37 +45,70 @@ class NewMenu: public studio::Popup {
[[nodiscard]] [[nodiscard]]
bool isOpen() const noexcept override; bool isOpen() const noexcept override;
void draw(studio::StudioContext &sctx) noexcept override; void draw(StudioContext &sctx) noexcept override;
template<typename T> template<typename T>
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept; void addItemType(
ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
T itemTempl,
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
template<typename T> template<typename T>
void addItemType(ox::String name, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept; void addItemType(
ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept;
void addItemMaker(ox::UniquePtr<studio::ItemMaker> &&im) noexcept; void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
private: private:
void drawNewItemType(studio::StudioContext &sctx) noexcept; void drawNewItemType(StudioContext const&sctx) noexcept;
void drawNewItemName(studio::StudioContext &sctx) noexcept; void drawNewItemName(StudioContext &sctx) noexcept;
void drawFirstPageButtons() noexcept; void drawNewItemTemplate(StudioContext &sctx) noexcept;
void drawLastPageButtons(studio::StudioContext &sctx) noexcept; void drawButtons(Stage prev, Stage next) noexcept;
void finish(studio::StudioContext &sctx) noexcept; void drawFirstPageButtons(Stage next) noexcept;
void drawLastPageButtons(StudioContext &sctx) noexcept;
void finish(StudioContext &sctx) noexcept;
}; };
template<typename T> template<typename T>
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, T itemTempl, ox::ClawFormat pFmt) noexcept { void NewMenu::addItemType(
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), std::move(itemTempl), pFmt)); ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
T itemTempl,
ox::ClawFormat const pFmt) noexcept {
m_types.emplace_back(ox::make<ItemMakerT<T>>(
std::move(displayName),
std::move(parentDir),
std::move(fileExt),
std::move(itemTempl),
pFmt));
} }
template<typename T> template<typename T>
void NewMenu::addItemType(ox::String displayName, ox::String parentDir, ox::String fileExt, ox::ClawFormat pFmt) noexcept { void NewMenu::addItemType(
m_types.emplace_back(ox::make<studio::ItemMakerT<T>>(std::move(displayName), std::move(parentDir), std::move(fileExt), pFmt)); ox::StringParam displayName,
ox::StringParam parentDir,
ox::StringParam fileExt,
ox::ClawFormat const pFmt) noexcept {
m_types.emplace_back(ox::make<ItemMakerT<T>>(
std::move(displayName),
std::move(parentDir),
std::move(fileExt),
pFmt));
} }
} }

View File

@ -49,7 +49,7 @@ void NewProject::draw(studio::StudioContext &ctx) noexcept {
} }
void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept { void NewProject::drawNewProjectName(studio::StudioContext &sctx) noexcept {
drawWindow(sctx.tctx, &m_open, [this, &sctx] { drawWindow(sctx.tctx, m_open, [this, &sctx] {
ig::InputText("Name", m_projectName); ig::InputText("Name", m_projectName);
ImGui::Text("Path: %s", m_projectPath.c_str()); ImGui::Text("Path: %s", m_projectPath.c_str());
if (ImGui::Button("Browse")) { if (ImGui::Button("Browse")) {

View File

@ -251,6 +251,10 @@ void StudioUI::loadModule(Module const&mod) noexcept {
for (auto &im : mod.itemMakers(m_sctx)) { for (auto &im : mod.itemMakers(m_sctx)) {
m_newMenu.addItemMaker(std::move(im)); m_newMenu.addItemMaker(std::move(im));
} }
auto tmplts = mod.itemTemplates(m_sctx);
for (auto &t : tmplts) {
m_newMenu.installItemTemplate(t);
}
} }
void StudioUI::loadModules() noexcept { void StudioUI::loadModules() noexcept {

View File

@ -214,7 +214,8 @@ bool ListBox(
ox::CStringViewCR name, ox::CStringViewCR name,
std::function<ox::CStringView(size_t)> const&f, std::function<ox::CStringView(size_t)> const&f,
size_t strCnt, size_t strCnt,
size_t &selIdx) noexcept; size_t &selIdx,
ImVec2 const&sz = {0, 0}) noexcept;
/** /**
* *

View File

@ -4,7 +4,9 @@
#pragma once #pragma once
#include <ox/claw/claw.hpp> #include <algorithm>
#include <ox/model/typenamecatcher.hpp>
#include <keel/media.hpp> #include <keel/media.hpp>
#include <turbine/context.hpp> #include <turbine/context.hpp>
@ -13,75 +15,212 @@
namespace studio { namespace studio {
class ItemMaker { class ItemTemplate {
private:
ox::String const m_name{"Default"};
public: public:
ox::String const typeName; explicit ItemTemplate() noexcept = default;
ox::String const parentDir;
ox::String const fileExt; explicit ItemTemplate(ox::StringParam name) noexcept: m_name{std::move(name)} {}
constexpr explicit ItemMaker(
virtual ~ItemTemplate() = default;
[[nodiscard]]
constexpr ox::String const&name() const noexcept {
return m_name;
}
constexpr bool operator<=>(ItemTemplate const&other) const noexcept {
return m_name != other.name() ? (m_name < other.name() ? -1 : 1) : 0;
}
[[nodiscard]]
ox::CStringView displayName() const noexcept {
return m_name;
}
[[nodiscard]]
virtual ox::CStringView typeName() const noexcept = 0;
[[nodiscard]]
virtual int typeVersion() const noexcept = 0;
virtual ox::Result<ox::UAnyPtr> getItem(
keel::Context &ctx, ox::StringViewCR name, int version) noexcept = 0;
};
template<typename T>
class ItemTemplateT: public ItemTemplate {
private:
T const m_item;
public:
constexpr ItemTemplateT() noexcept = default;
explicit ItemTemplateT(ox::StringParam name, T item) noexcept:
ItemTemplate{std::move(name)}, m_item{std::move(item)} {}
[[nodiscard]]
ox::CStringView typeName() const noexcept final {
return ox::ModelTypeName_v<T>;
}
[[nodiscard]]
int typeVersion() const noexcept final {
return ox::ModelTypeVersion_v<T>;
}
ox::Result<ox::UAnyPtr> getItem(
keel::Context &kctx, ox::StringViewCR name, int const version) noexcept final {
if (ox::ModelTypeVersion_v<T> != version || ox::ModelTypeName_v<T> != name) {
OX_REQUIRE_M(item, keel::convert(kctx, m_item, name, version));
auto out = ox::Result<ox::UAnyPtr>{item->moveToCopy()};
ox::safeDelete(item.release());
return out;
}
return ox::UAnyPtr{new T{m_item}};
}
};
class ItemMaker {
private:
ox::Vector<ox::UPtr<ItemTemplate>> m_templates;
ox::String const m_parentDir;
ox::String const m_fileExt;
ox::String const m_typeDisplayName;
public:
constexpr ItemMaker(
ox::StringParam pName, ox::StringParam pName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam pFileExt) noexcept: ox::StringParam pFileExt) noexcept:
typeName{std::move(pName)}, m_parentDir{std::move(pParentDir)},
parentDir{std::move(pParentDir)}, m_fileExt{std::move(pFileExt)},
fileExt{std::move(pFileExt)} { m_typeDisplayName{std::move(pName)} {
} }
virtual ~ItemMaker() noexcept = default; virtual ~ItemMaker() noexcept = default;
[[nodiscard]] [[nodiscard]]
virtual ox::String itemPath(ox::StringView pName) const noexcept { ox::String const&typeDisplayName() const noexcept {
return ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt); return m_typeDisplayName;
} }
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
if (typeName() == tmpl->typeName() &&
typeVersion() <= tmpl->typeVersion()) {
m_templates.emplace_back(std::move(tmpl));
// begin() + 1 because 'Default' should always be first
std::sort(m_templates.begin() + 1, m_templates.end());
return true;
}
return false;
}
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
return installTemplate(tmpl);
}
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
return m_templates;
}
[[nodiscard]]
ox::String itemPath(ox::StringViewCR pName) const noexcept {
return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt);
}
[[nodiscard]]
virtual ox::StringView typeName() const noexcept = 0;
[[nodiscard]]
virtual int typeVersion() const noexcept = 0;
/** /**
* Returns path of the file created. * Returns path of the file created.
* @param ctx * @param ctx
* @param pName * @param pName
* @param pTemplateIdx
* @return path of file or error in Result * @return path of file or error in Result
*/ */
virtual ox::Result<ox::String> write(StudioContext &ctx, ox::StringView pName) const noexcept = 0; virtual ox::Result<ox::String> write(
StudioContext &ctx, ox::StringViewCR pName, size_t pTemplateIdx) const noexcept = 0;
}; };
template<typename T> template<typename T>
class ItemMakerT: public ItemMaker { class ItemMakerT final: public ItemMaker {
private: private:
T const m_item;
ox::ClawFormat const m_fmt; ox::ClawFormat const m_fmt;
public: public:
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
ox::ClawFormat pFmt = ox::ClawFormat::Metal) noexcept: ox::ClawFormat const pFmt = ox::ClawFormat::Metal) noexcept:
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)), ItemMaker(
m_fmt(pFmt) { std::move(pDisplayName),
std::move(pParentDir),
std::move(fileExt)),
m_fmt{pFmt} {
installTemplate(ox::make_unique<ItemTemplateT<T>>());
} }
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
T pItem, T const&pItem,
ox::ClawFormat pFmt) noexcept: ox::ClawFormat const pFmt) noexcept:
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)), ItemMaker(
m_item(std::move(pItem)), std::move(pDisplayName),
m_fmt(pFmt) { std::move(pParentDir),
std::move(fileExt)),
m_fmt{pFmt} {
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
} }
constexpr ItemMakerT( constexpr ItemMakerT(
ox::StringParam pDisplayName, ox::StringParam pDisplayName,
ox::StringParam pParentDir, ox::StringParam pParentDir,
ox::StringParam fileExt, ox::StringParam fileExt,
T &&pItem, T &&pItem,
ox::ClawFormat pFmt) noexcept: ox::ClawFormat const pFmt) noexcept:
ItemMaker(std::move(pDisplayName), std::move(pParentDir), std::move(fileExt)), ItemMaker(
m_item(std::move(pItem)), std::move(pDisplayName),
m_fmt(pFmt) { std::move(pParentDir),
std::move(fileExt)),
m_fmt{pFmt} {
installTemplate(ox::make_unique<ItemTemplateT<T>>(std::move(pItem)));
} }
ox::Result<ox::String> write(studio::StudioContext &sctx, ox::StringView const pName) const noexcept override {
ox::StringView typeName() const noexcept override {
return ox::ModelTypeName_v<T>;
}
int typeVersion() const noexcept override {
return ox::ModelTypeVersion_v<T>;
}
ox::Result<ox::String> write(
StudioContext &sctx,
ox::StringViewCR pName,
size_t const pTemplateIdx) const noexcept override {
auto const path = itemPath(pName); auto const path = itemPath(pName);
createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap()); createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap());
OX_RETURN_ERROR(sctx.project->writeObj(path, m_item, m_fmt)); auto const&templates = itemTemplates();
auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0;
OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem(
keelCtx(sctx), typeName(), typeVersion()));
auto item = tmpl.template get<T>();
OX_RETURN_ERROR(sctx.project->writeObj(path, *item, m_fmt));
tmpl.free();
return path; return path;
} }
}; };
} }

View File

@ -31,6 +31,8 @@ class Module {
virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const; virtual ox::Vector<ox::UPtr<ItemMaker>> itemMakers(studio::StudioContext&) const;
virtual ox::Vector<ox::UPtr<ItemTemplate>> itemTemplates(studio::StudioContext&) const;
}; };
template<typename Editor> template<typename Editor>

View File

@ -47,7 +47,7 @@ class Popup {
return m_title; return m_title;
} }
void drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents); void drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents);
}; };

View File

@ -136,11 +136,12 @@ bool FileComboBox(
bool ListBox( bool ListBox(
ox::CStringViewCR name, ox::CStringViewCR name,
std::function<ox::CStringView(size_t)> const&f, std::function<ox::CStringView(size_t)> const&f,
size_t strCnt, size_t const strCnt,
size_t &selIdx) noexcept { size_t &selIdx,
ImVec2 const&sz) noexcept {
auto out = false; auto out = false;
if (ImGui::BeginListBox(name.c_str())) { if (ImGui::BeginListBox(name.c_str(), sz)) {
for (auto i = 0u; i < strCnt; ++i) { for (size_t i = 0; i < strCnt; ++i) {
auto str = f(i); auto str = f(i);
ig::IDStackItem const idStackItem2(static_cast<int>(i)); ig::IDStackItem const idStackItem2(static_cast<int>(i));
if (ImGui::Selectable(str.c_str(), selIdx == i)) { if (ImGui::Selectable(str.c_str(), selIdx == i)) {
@ -161,6 +162,12 @@ bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t
}, list.size(), selIdx); }, list.size(), selIdx);
} }
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const&list, size_t &selIdx) noexcept {
return ListBox(name, [list](size_t i) -> ox::CStringView {
return list[i];
}, list.size(), selIdx);
}
FilePicker::FilePicker( FilePicker::FilePicker(
StudioContext &sctx, StudioContext &sctx,

View File

@ -6,11 +6,15 @@
namespace studio { namespace studio {
ox::Vector<EditorMaker> Module::editors(studio::StudioContext&) const { ox::Vector<EditorMaker> Module::editors(StudioContext&) const {
return {}; return {};
} }
ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(studio::StudioContext&) const { ox::Vector<ox::UPtr<ItemMaker>> Module::itemMakers(StudioContext&) const {
return {};
}
ox::Vector<ox::UPtr<ItemTemplate>> Module::itemTemplates(StudioContext&) const {
return {}; return {};
} }

View File

@ -7,11 +7,11 @@
namespace studio { namespace studio {
void Popup::drawWindow(turbine::Context &ctx, bool *open, std::function<void()> const&drawContents) { void Popup::drawWindow(turbine::Context &ctx, bool &open, std::function<void()> const&drawContents) {
studio::ig::centerNextWindow(ctx); studio::ig::centerNextWindow(ctx);
ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size)); ImGui::SetNextWindowSize(static_cast<ImVec2>(m_size));
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
if (ImGui::BeginPopupModal(m_title.c_str(), open, modalFlags)) { if (ImGui::BeginPopupModal(m_title.c_str(), &open, modalFlags)) {
drawContents(); drawContents();
ImGui::EndPopup(); ImGui::EndPopup();
} }