Merge commit '7b7d59cf63d77cf7ab6daf6ed7122eef97954555'
Some checks failed
Build / build (push) Failing after 59s

This commit is contained in:
2025-01-19 13:39:31 -06:00
30 changed files with 521 additions and 120 deletions

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/string.hpp>
#include <ox/model/def.hpp>
namespace studio {
struct FileRef {
static constexpr auto TypeName = "net.drinkingtea.studio.FileRef";
static constexpr auto TypeVersion = 1;
ox::String path;
};
OX_MODEL_BEGIN(FileRef)
OX_MODEL_FIELD(path)
OX_MODEL_END()
}

View File

@@ -63,6 +63,7 @@ auto dragDropSource(auto const&cb) noexcept {
if (ig::DragDropSource const tgt; tgt) [[unlikely]] {
return cb();
}
return ox::Error{};
} else {
if (ig::DragDropSource const tgt; tgt) [[unlikely]] {
cb();
@@ -175,9 +176,16 @@ enum class PopupResponse {
Cancel,
};
PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen);
PopupResponse PopupControlsOkCancel(
float popupWidth,
bool &popupOpen,
ox::CStringViewCR ok = "OK",
ox::CStringViewCR cancel = "Cancel");
PopupResponse PopupControlsOkCancel(bool &popupOpen);
PopupResponse PopupControlsOkCancel(
bool &popupOpen,
ox::CStringViewCR ok = "OK",
ox::CStringViewCR cancel = "Cancel");
[[nodiscard]]
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});

View File

@@ -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<ox::String> 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<typename T>
@@ -205,20 +243,19 @@ class ItemMakerT final: public ItemMaker {
return ox::ModelTypeVersion_v<T>;
}
ox::Result<ox::String> 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<T>();
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 {};
}
};

View File

@@ -19,6 +19,7 @@ namespace studio {
enum class ProjectEvent {
None,
DirAdded,
FileAdded,
// FileRecognized is triggered for all matching files upon a new
// subscription to a section of the project and upon the addition of a file.
@@ -45,7 +46,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
return substr(path, 0, extStart);
}
class Project {
class Project: public ox::SignalHandler {
private:
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
keel::Context &m_ctx;
@@ -91,6 +92,8 @@ class Project {
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
ox::Error deleteItem(ox::StringViewCR path) noexcept;
[[nodiscard]]
bool exists(ox::StringViewCR path) const noexcept;
@@ -118,6 +121,7 @@ class Project {
// signals
public:
ox::Signal<ox::Error(ProjectEvent, ox::StringViewCR)> fileEvent;
ox::Signal<ox::Error(ox::StringViewCR)> dirAdded;
ox::Signal<ox::Error(ox::StringViewCR)> fileAdded;
// FileRecognized is triggered for all matching files upon a new
// subscription to a section of the project and upon the addition of a
@@ -175,6 +179,9 @@ ox::Error Project::subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&s
switch (e) {
case ProjectEvent::None:
break;
case ProjectEvent::DirAdded:
connect(this, &Project::dirAdded, tgt, slot);
break;
case ProjectEvent::FileAdded:
connect(this, &Project::fileAdded, tgt, slot);
break;

View File

@@ -6,6 +6,7 @@
#include <studio/configio.hpp>
#include <studio/context.hpp>
#include <studio/dragdrop.hpp>
#include <studio/editor.hpp>
#include <studio/filedialog.hpp>
#include <studio/imguiutil.hpp>

View File

@@ -54,18 +54,22 @@ bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz) noexcept {
return ImGui::Button(lbl.c_str(), btnSz);
}
PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) {
PopupResponse PopupControlsOkCancel(
float popupWidth,
bool &popupOpen,
ox::CStringViewCR ok,
ox::CStringViewCR cancel) {
auto out = PopupResponse::None;
constexpr auto btnSz = ImVec2{50, BtnSz.y};
ImGui::Separator();
ImGui::SetCursorPosX(popupWidth - 118);
if (ImGui::Button("OK", btnSz)) {
if (ImGui::Button(ok.c_str(), btnSz)) {
ImGui::CloseCurrentPopup();
popupOpen = false;
out = PopupResponse::OK;
}
ImGui::SameLine();
if (ImGui::IsKeyDown(ImGuiKey_Escape) || ImGui::Button("Cancel", btnSz)) {
if (ImGui::IsKeyDown(ImGuiKey_Escape) || ImGui::Button(cancel.c_str(), btnSz)) {
ImGui::CloseCurrentPopup();
popupOpen = false;
out = PopupResponse::Cancel;
@@ -73,8 +77,11 @@ PopupResponse PopupControlsOkCancel(float popupWidth, bool &popupOpen) {
return out;
}
PopupResponse PopupControlsOkCancel(bool &popupOpen) {
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen);
PopupResponse PopupControlsOkCancel(
bool &popupOpen,
ox::CStringViewCR ok,
ox::CStringViewCR cancel) {
return PopupControlsOkCancel(ImGui::GetContentRegionAvail().x + 17, popupOpen, ok, cancel);
}
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz) {

View File

@@ -59,16 +59,41 @@ ox::Error Project::mkdir(ox::StringViewCR path) const noexcept {
auto const [stat, err] = m_fs.stat(path);
if (err) {
OX_RETURN_ERROR(m_fs.mkdir(path, true));
fileUpdated.emit(path, {});
dirAdded.emit(path);
}
return stat.fileType == ox::FileType::Directory ?
ox::Error{} : ox::Error(1, "path exists as normal file");
ox::Error{} : ox::Error{1, "path exists as normal file"};
}
ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
return m_fs.stat(path);
}
ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
OX_REQUIRE(stat, m_fs.stat(path));
if (stat.fileType == ox::FileType::Directory) {
bool partialRemoval{};
OX_REQUIRE(members, m_fs.ls(path));
for (auto const&p : members) {
partialRemoval = m_fs.remove(ox::sfmt("{}/{}", path, p)) || partialRemoval;
}
if (partialRemoval) {
return ox::Error{1, "failed to remove one or more directory members"};
}
auto const err = m_fs.remove(path);
if (!err) {
fileDeleted.emit(path);
}
return err;
} else {
auto const err = m_fs.remove(path);
if (!err) {
fileDeleted.emit(path);
}
return err;
}
}
bool Project::exists(ox::StringViewCR path) const noexcept {
return m_fs.stat(path).error == 0;
}