From 69241476868bc4cfd69f3920bfd1b262e95025e9 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Sat, 18 Jan 2025 23:45:04 -0600 Subject: [PATCH] [studio] Add ability to add file through dir context menu Also, fix dir context menu to work when dir is closed, and fix it not to override last file in the directory. --- src/olympic/studio/applib/src/newmenu.cpp | 16 ++++-- src/olympic/studio/applib/src/newmenu.hpp | 3 + .../studio/applib/src/projectexplorer.hpp | 3 +- .../studio/applib/src/projecttreemodel.cpp | 15 +++++ .../studio/applib/src/projecttreemodel.hpp | 2 + src/olympic/studio/applib/src/studioapp.cpp | 6 ++ src/olympic/studio/applib/src/studioapp.hpp | 2 + .../modlib/include/studio/itemmaker.hpp | 57 +++++++++++++++---- 8 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/olympic/studio/applib/src/newmenu.cpp b/src/olympic/studio/applib/src/newmenu.cpp index 1fbe1b641..a0b5be552 100644 --- a/src/olympic/studio/applib/src/newmenu.cpp +++ b/src/olympic/studio/applib/src/newmenu.cpp @@ -22,6 +22,12 @@ void NewMenu::open() noexcept { m_selectedType = 0; m_itemName = ""; m_typeName = ""; + m_path = ""; +} + +void NewMenu::openPath(ox::StringParam path) noexcept { + open(); + m_path = std::move(path); } void NewMenu::close() noexcept { @@ -158,14 +164,14 @@ void NewMenu::finish(StudioContext &sctx) noexcept { oxLogError(ox::Error{1, "New file error: no file name"}); return; } - auto const&im = *m_types[static_cast(m_selectedType)]; - if (sctx.project->exists(im.itemPath(m_itemName))) { + auto const&im = *m_types[m_selectedType]; + auto const path = m_path.len() ? + im.itemPath(m_itemName, m_path) : im.itemPath(m_itemName); + if (sctx.project->exists(path)) { oxLogError(ox::Error{1, "New file error: file already exists"}); return; } - auto const [path, err] = - im.write(sctx, m_itemName, m_selectedTemplate); - if (err) { + if (auto const err = im.write(sctx, path, m_selectedTemplate)) { oxLogError(err); return; } diff --git a/src/olympic/studio/applib/src/newmenu.hpp b/src/olympic/studio/applib/src/newmenu.hpp index c535764ae..30ebb685b 100644 --- a/src/olympic/studio/applib/src/newmenu.hpp +++ b/src/olympic/studio/applib/src/newmenu.hpp @@ -30,6 +30,7 @@ class NewMenu final: public Popup { Stage m_stage = Stage::Closed; ox::String m_typeName; ox::IString<255> m_itemName; + ox::String m_path; ox::Vector> m_types; size_t m_selectedType = 0; size_t m_selectedTemplate = 0; @@ -38,6 +39,8 @@ class NewMenu final: public Popup { public: NewMenu() noexcept; + void openPath(ox::StringParam path) noexcept; + void open() noexcept override; void close() noexcept override; diff --git a/src/olympic/studio/applib/src/projectexplorer.hpp b/src/olympic/studio/applib/src/projectexplorer.hpp index 87680e363..9e1981b6b 100644 --- a/src/olympic/studio/applib/src/projectexplorer.hpp +++ b/src/olympic/studio/applib/src/projectexplorer.hpp @@ -19,7 +19,8 @@ class ProjectExplorer: public Widget { public: // slots - ox::Signal fileChosen; + ox::Signal fileChosen; + ox::Signal addItem; explicit ProjectExplorer(turbine::Context &ctx) noexcept; diff --git a/src/olympic/studio/applib/src/projecttreemodel.cpp b/src/olympic/studio/applib/src/projecttreemodel.cpp index 48ced550d..73b60ac8d 100644 --- a/src/olympic/studio/applib/src/projecttreemodel.cpp +++ b/src/olympic/studio/applib/src/projecttreemodel.cpp @@ -4,6 +4,8 @@ #include +#include + #include "projectexplorer.hpp" #include "projecttreemodel.hpp" @@ -30,10 +32,14 @@ bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept { constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; if (!m_children.empty()) { if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) { + drawDirContextMenu(); for (auto const&child : m_children) { updated = child->draw(ctx) || updated; } ImGui::TreePop(); + } else { + ig::IDStackItem const idStackItem{m_name}; + drawDirContextMenu(); } } else { auto const path = fullPath(); @@ -58,6 +64,15 @@ void ProjectTreeModel::setChildren(ox::Vector> childr m_children = std::move(children); } +void ProjectTreeModel::drawDirContextMenu() const noexcept { + if (ImGui::BeginPopupContextItem("DirMenu", ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::MenuItem("Add Item")) { + m_explorer.addItem.emit(fullPath()); + } + ImGui::EndPopup(); + } +} + ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept { if (m_parent) { return m_parent->fullPath() + "/" + ox::StringView(m_name); diff --git a/src/olympic/studio/applib/src/projecttreemodel.hpp b/src/olympic/studio/applib/src/projecttreemodel.hpp index 2d9313d54..f3ae3743a 100644 --- a/src/olympic/studio/applib/src/projecttreemodel.hpp +++ b/src/olympic/studio/applib/src/projecttreemodel.hpp @@ -31,6 +31,8 @@ class ProjectTreeModel { void setChildren(ox::Vector> children) noexcept; private: + void drawDirContextMenu() const noexcept; + [[nodiscard]] ox::BasicString<255> fullPath() const noexcept; diff --git a/src/olympic/studio/applib/src/studioapp.cpp b/src/olympic/studio/applib/src/studioapp.cpp index c4193a8dc..40111f8ac 100644 --- a/src/olympic/studio/applib/src/studioapp.cpp +++ b/src/olympic/studio/applib/src/studioapp.cpp @@ -52,6 +52,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce m_aboutPopup(m_tctx) { turbine::setApplicationData(m_tctx, &m_sctx); m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); + m_projectExplorer.addItem.connect(this, &StudioUI::addFile); m_newProject.finished.connect(this, &StudioUI::createOpenProject); m_newMenu.finished.connect(this, &StudioUI::openFile); ImGui::GetIO().IniFilename = nullptr; @@ -342,6 +343,11 @@ void StudioUI::handleKeyInput() noexcept { } } +ox::Error StudioUI::addFile(ox::StringViewCR path) noexcept { + m_newMenu.openPath(path); + return {}; +} + ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept { std::error_code ec; std::filesystem::create_directories(toStdStringView(path), ec); diff --git a/src/olympic/studio/applib/src/studioapp.hpp b/src/olympic/studio/applib/src/studioapp.hpp index 649078600..32c4e44a8 100644 --- a/src/olympic/studio/applib/src/studioapp.hpp +++ b/src/olympic/studio/applib/src/studioapp.hpp @@ -83,6 +83,8 @@ class StudioUI: public ox::SignalHandler { void handleKeyInput() noexcept; + ox::Error addFile(ox::StringViewCR path) noexcept; + ox::Error createOpenProject(ox::StringViewCR path) noexcept; ox::Error openProjectPath(ox::StringParam path) noexcept; diff --git a/src/olympic/studio/modlib/include/studio/itemmaker.hpp b/src/olympic/studio/modlib/include/studio/itemmaker.hpp index edc536da5..e0b70e617 100644 --- a/src/olympic/studio/modlib/include/studio/itemmaker.hpp +++ b/src/olympic/studio/modlib/include/studio/itemmaker.hpp @@ -128,6 +128,16 @@ class ItemMaker { return m_templates; } + [[nodiscard]] + ox::String const&defaultPath() const noexcept { + return m_parentDir; + } + + [[nodiscard]] + ox::String itemPath(ox::StringViewCR pName, ox::StringViewCR pPath) const noexcept { + return ox::sfmt("/{}/{}.{}", pPath, pName, m_fileExt); + } + [[nodiscard]] ox::String itemPath(ox::StringViewCR pName) const noexcept { return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt); @@ -142,12 +152,40 @@ class ItemMaker { /** * Returns path of the file created. * @param ctx + * @param pPath + * @param pTemplateIdx + * @return path of file or error in Result + */ + ox::Error write( + StudioContext &ctx, + ox::StringViewCR pPath, + size_t pTemplateIdx) const noexcept { + return writeItem(ctx, pPath, pTemplateIdx); + } + + /** + * Returns path of the file created. + * @param ctx + * @param pPath * @param pName * @param pTemplateIdx * @return path of file or error in Result */ - virtual ox::Result write( - StudioContext &ctx, ox::StringViewCR pName, size_t pTemplateIdx) const noexcept = 0; + ox::Error write( + StudioContext &ctx, + ox::StringViewCR pPath, + ox::StringViewCR pName, + size_t pTemplateIdx) const noexcept { + auto const path = itemPath(pName, pPath); + return writeItem(ctx, path, pTemplateIdx); + } + + protected: + virtual ox::Error writeItem( + StudioContext &ctx, + ox::StringViewCR pPath, + size_t pTemplateIdx) const noexcept = 0; + }; template @@ -205,20 +243,19 @@ class ItemMakerT final: public ItemMaker { return ox::ModelTypeVersion_v; } - ox::Result write( - StudioContext &sctx, - ox::StringViewCR pName, + ox::Error writeItem( + StudioContext &ctx, + ox::StringViewCR pPath, size_t const pTemplateIdx) const noexcept override { - auto const path = itemPath(pName); - createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap()); + createUuidMapping(keelCtx(ctx.tctx), pPath, ox::UUID::generate().unwrap()); auto const&templates = itemTemplates(); auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0; OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem( - keelCtx(sctx), typeName(), typeVersion())); + keelCtx(ctx), typeName(), typeVersion())); auto item = tmpl.template get(); - OX_RETURN_ERROR(sctx.project->writeObj(path, *item, m_fmt)); + OX_RETURN_ERROR(ctx.project->writeObj(pPath, *item, m_fmt)); tmpl.free(); - return path; + return {}; } };