[studio] Add ability to add file through dir context menu
All checks were successful
Build / build (push) Successful in 3m15s

Also, fix dir context menu to work when dir is closed, and fix it not to
override last file in the directory.
This commit is contained in:
Gary Talent 2025-01-18 23:45:04 -06:00
parent 6e2b4fa7b4
commit 6924147686
8 changed files with 88 additions and 16 deletions

View File

@ -22,6 +22,12 @@ void NewMenu::open() noexcept {
m_selectedType = 0; m_selectedType = 0;
m_itemName = ""; m_itemName = "";
m_typeName = ""; m_typeName = "";
m_path = "";
}
void NewMenu::openPath(ox::StringParam path) noexcept {
open();
m_path = std::move(path);
} }
void NewMenu::close() noexcept { void NewMenu::close() noexcept {
@ -158,14 +164,14 @@ void NewMenu::finish(StudioContext &sctx) noexcept {
oxLogError(ox::Error{1, "New file error: no file name"}); oxLogError(ox::Error{1, "New file error: no file name"});
return; return;
} }
auto const&im = *m_types[static_cast<std::size_t>(m_selectedType)]; auto const&im = *m_types[m_selectedType];
if (sctx.project->exists(im.itemPath(m_itemName))) { 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"}); oxLogError(ox::Error{1, "New file error: file already exists"});
return; return;
} }
auto const [path, err] = if (auto const err = im.write(sctx, path, m_selectedTemplate)) {
im.write(sctx, m_itemName, m_selectedTemplate);
if (err) {
oxLogError(err); oxLogError(err);
return; return;
} }

View File

@ -30,6 +30,7 @@ class NewMenu final: public 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::String m_path;
ox::Vector<ox::UPtr<studio::ItemMaker>> m_types; ox::Vector<ox::UPtr<studio::ItemMaker>> m_types;
size_t m_selectedType = 0; size_t m_selectedType = 0;
size_t m_selectedTemplate = 0; size_t m_selectedTemplate = 0;
@ -38,6 +39,8 @@ class NewMenu final: public Popup {
public: public:
NewMenu() noexcept; NewMenu() noexcept;
void openPath(ox::StringParam path) noexcept;
void open() noexcept override; void open() noexcept override;
void close() noexcept override; void close() noexcept override;

View File

@ -19,7 +19,8 @@ class ProjectExplorer: public Widget {
public: public:
// slots // slots
ox::Signal<ox::Error(ox::StringView const&)> fileChosen; ox::Signal<ox::Error(ox::StringViewCR)> fileChosen;
ox::Signal<ox::Error(ox::StringViewCR)> addItem;
explicit ProjectExplorer(turbine::Context &ctx) noexcept; explicit ProjectExplorer(turbine::Context &ctx) noexcept;

View File

@ -4,6 +4,8 @@
#include <imgui.h> #include <imgui.h>
#include <studio/imguiutil.hpp>
#include "projectexplorer.hpp" #include "projectexplorer.hpp"
#include "projecttreemodel.hpp" #include "projecttreemodel.hpp"
@ -30,10 +32,14 @@ bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
if (!m_children.empty()) { if (!m_children.empty()) {
if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) { if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) {
drawDirContextMenu();
for (auto const&child : m_children) { for (auto const&child : m_children) {
updated = child->draw(ctx) || updated; updated = child->draw(ctx) || updated;
} }
ImGui::TreePop(); ImGui::TreePop();
} else {
ig::IDStackItem const idStackItem{m_name};
drawDirContextMenu();
} }
} else { } else {
auto const path = fullPath(); auto const path = fullPath();
@ -58,6 +64,15 @@ void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> childr
m_children = std::move(children); 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 { ox::BasicString<255> ProjectTreeModel::fullPath() const noexcept {
if (m_parent) { if (m_parent) {
return m_parent->fullPath() + "/" + ox::StringView(m_name); return m_parent->fullPath() + "/" + ox::StringView(m_name);

View File

@ -31,6 +31,8 @@ class ProjectTreeModel {
void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept; void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept;
private: private:
void drawDirContextMenu() const noexcept;
[[nodiscard]] [[nodiscard]]
ox::BasicString<255> fullPath() const noexcept; ox::BasicString<255> fullPath() const noexcept;

View File

@ -52,6 +52,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
m_aboutPopup(m_tctx) { m_aboutPopup(m_tctx) {
turbine::setApplicationData(m_tctx, &m_sctx); turbine::setApplicationData(m_tctx, &m_sctx);
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile); m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
m_newProject.finished.connect(this, &StudioUI::createOpenProject); m_newProject.finished.connect(this, &StudioUI::createOpenProject);
m_newMenu.finished.connect(this, &StudioUI::openFile); m_newMenu.finished.connect(this, &StudioUI::openFile);
ImGui::GetIO().IniFilename = nullptr; 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 { ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
std::error_code ec; std::error_code ec;
std::filesystem::create_directories(toStdStringView(path), ec); std::filesystem::create_directories(toStdStringView(path), ec);

View File

@ -83,6 +83,8 @@ class StudioUI: public ox::SignalHandler {
void handleKeyInput() noexcept; void handleKeyInput() noexcept;
ox::Error addFile(ox::StringViewCR path) noexcept;
ox::Error createOpenProject(ox::StringViewCR path) noexcept; ox::Error createOpenProject(ox::StringViewCR path) noexcept;
ox::Error openProjectPath(ox::StringParam path) noexcept; ox::Error openProjectPath(ox::StringParam path) noexcept;

View File

@ -128,6 +128,16 @@ class ItemMaker {
return m_templates; 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]] [[nodiscard]]
ox::String itemPath(ox::StringViewCR pName) const noexcept { ox::String itemPath(ox::StringViewCR pName) const noexcept {
return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt); return ox::sfmt("/{}/{}.{}", m_parentDir, pName, m_fileExt);
@ -142,12 +152,40 @@ class ItemMaker {
/** /**
* Returns path of the file created. * Returns path of the file created.
* @param ctx * @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 pName
* @param pTemplateIdx * @param pTemplateIdx
* @return path of file or error in Result * @return path of file or error in Result
*/ */
virtual ox::Result<ox::String> write( ox::Error write(
StudioContext &ctx, ox::StringViewCR pName, size_t pTemplateIdx) const noexcept = 0; 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<typename T> template<typename T>
@ -205,20 +243,19 @@ class ItemMakerT final: public ItemMaker {
return ox::ModelTypeVersion_v<T>; return ox::ModelTypeVersion_v<T>;
} }
ox::Result<ox::String> write( ox::Error writeItem(
StudioContext &sctx, StudioContext &ctx,
ox::StringViewCR pName, ox::StringViewCR pPath,
size_t const pTemplateIdx) const noexcept override { size_t const pTemplateIdx) const noexcept override {
auto const path = itemPath(pName); createUuidMapping(keelCtx(ctx.tctx), pPath, ox::UUID::generate().unwrap());
createUuidMapping(keelCtx(sctx.tctx), path, ox::UUID::generate().unwrap());
auto const&templates = itemTemplates(); auto const&templates = itemTemplates();
auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0; auto const tmplIdx = pTemplateIdx < templates.size() ? pTemplateIdx : 0;
OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem( OX_REQUIRE_M(tmpl, templates[tmplIdx]->getItem(
keelCtx(sctx), typeName(), typeVersion())); keelCtx(ctx), typeName(), typeVersion()));
auto item = tmpl.template get<T>(); auto item = tmpl.template get<T>();
OX_RETURN_ERROR(sctx.project->writeObj(path, *item, m_fmt)); OX_RETURN_ERROR(ctx.project->writeObj(pPath, *item, m_fmt));
tmpl.free(); tmpl.free();
return path; return {};
} }
}; };