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 1fbe1b64..a0b5be55 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 c535764a..30ebb685 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 87680e36..9e1981b6 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 48ced550..73b60ac8 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 2d9313d5..f3ae3743 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 c4193a8d..40111f8a 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 64907860..32c4e44a 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 edc536da..e0b70e61 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 {}; } };