[keel,studio] Add ability to rename files
This commit is contained in:
parent
f7a7a66a6a
commit
cfa91d3d39
@ -9,7 +9,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <ox/event/signal.hpp>
|
#include <ox/event/signal.hpp>
|
||||||
#include <ox/fs/fs.hpp>
|
|
||||||
#include <ox/model/typenamecatcher.hpp>
|
#include <ox/model/typenamecatcher.hpp>
|
||||||
#include <ox/std/hashmap.hpp>
|
#include <ox/std/hashmap.hpp>
|
||||||
#include <ox/std/utility.hpp>
|
#include <ox/std/utility.hpp>
|
||||||
@ -190,6 +189,8 @@ class AssetManager {
|
|||||||
public:
|
public:
|
||||||
~AssetTypeManagerBase() override = default;
|
~AssetTypeManagerBase() override = default;
|
||||||
|
|
||||||
|
virtual ox::Error updateAssetId(ox::StringViewCR oldId, ox::StringViewCR newId) noexcept = 0;
|
||||||
|
|
||||||
virtual void gc() noexcept = 0;
|
virtual void gc() noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ class AssetManager {
|
|||||||
ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache;
|
ox::HashMap<ox::String, ox::UPtr<AssetContainer<T>>> m_cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AssetTypeManager(Loader &&loader) noexcept: m_loader(std::move(loader)) {}
|
explicit AssetTypeManager(Loader &&loader) noexcept: m_loader(std::move(loader)) {}
|
||||||
|
|
||||||
ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) const noexcept {
|
ox::Result<AssetRef<T>> getAsset(ox::StringViewCR assetId) const noexcept {
|
||||||
OX_REQUIRE(out, m_cache.at(assetId));
|
OX_REQUIRE(out, m_cache.at(assetId));
|
||||||
@ -236,6 +237,14 @@ class AssetManager {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error updateAssetId(ox::StringViewCR oldId, ox::StringViewCR newId) noexcept final {
|
||||||
|
auto &o = m_cache[oldId];
|
||||||
|
auto &n = m_cache[newId];
|
||||||
|
n = std::move(o);
|
||||||
|
m_cache.erase(oldId);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void gc() noexcept final {
|
void gc() noexcept final {
|
||||||
for (auto const&ack : m_cache.keys()) {
|
for (auto const&ack : m_cache.keys()) {
|
||||||
auto &ac = m_cache[ack];
|
auto &ac = m_cache[ack];
|
||||||
@ -244,10 +253,11 @@ class AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers;
|
ox::HashMap<ox::String, ox::UPtr<AssetTypeManagerBase>> m_assetTypeManagers;
|
||||||
ox::HashMap<ox::String, ox::Signal<ox::Error(ox::StringViewCR assetId)>> m_fileUpdated;
|
ox::HashMap<ox::String, ox::UPtr<ox::Signal<ox::Error(ox::StringViewCR assetId)>>> m_fileUpdated;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept {
|
ox::Result<AssetTypeManager<T>*> getTypeManager() noexcept {
|
||||||
@ -279,7 +289,7 @@ class AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error reloadAsset(ox::StringViewCR assetId) noexcept {
|
ox::Error reloadAsset(ox::StringViewCR assetId) noexcept {
|
||||||
m_fileUpdated[assetId].emit(assetId);
|
m_fileUpdated[assetId]->emit(assetId);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,15 +297,40 @@ class AssetManager {
|
|||||||
ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept {
|
ox::Result<AssetRef<T>> loadAsset(ox::StringViewCR assetId) noexcept {
|
||||||
OX_REQUIRE(m, getTypeManager<T>());
|
OX_REQUIRE(m, getTypeManager<T>());
|
||||||
OX_REQUIRE(out, m->loadAsset(assetId));
|
OX_REQUIRE(out, m->loadAsset(assetId));
|
||||||
m_fileUpdated[assetId].connect(m, &AssetTypeManager<T>::reloadAsset);
|
if (!m_fileUpdated.contains(assetId)) [[unlikely]] {
|
||||||
|
m_fileUpdated[assetId] = ox::make_unique<ox::Signal<ox::Error(ox::StringViewCR assetId)>>();
|
||||||
|
}
|
||||||
|
m_fileUpdated[assetId]->connect(m, &AssetTypeManager<T>::reloadAsset);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error updateAssetId(ox::StringViewCR oldId, ox::StringViewCR newId) noexcept {
|
||||||
|
gc();
|
||||||
|
if (m_fileUpdated.contains(newId)) {
|
||||||
|
return ox::Error{1, "new asset ID already has an entry"};
|
||||||
|
}
|
||||||
|
auto &o = m_fileUpdated[oldId];
|
||||||
|
auto &n = m_fileUpdated[newId];
|
||||||
|
n = std::move(o);
|
||||||
|
m_fileUpdated.erase(oldId);
|
||||||
|
for (auto &k : m_assetTypeManagers.keys()) {
|
||||||
|
auto const &tm = m_assetTypeManagers[k];
|
||||||
|
std::ignore = tm->updateAssetId(oldId, newId);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void gc() noexcept {
|
void gc() noexcept {
|
||||||
for (auto const&amk : m_assetTypeManagers.keys()) {
|
for (auto const&amk : m_assetTypeManagers.keys()) {
|
||||||
auto &am = m_assetTypeManagers[amk];
|
auto &am = m_assetTypeManagers[amk];
|
||||||
am->gc();
|
am->gc();
|
||||||
}
|
}
|
||||||
|
for (auto const&k : m_fileUpdated.keys()) {
|
||||||
|
auto &s = m_fileUpdated[k];
|
||||||
|
if (s->connectionCnt() == 0) {
|
||||||
|
m_fileUpdated.erase(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
|
@ -47,6 +47,8 @@ ox::Result<ox::CStringView> getPath(Context &ctx, ox::FileAddress const&fileAddr
|
|||||||
|
|
||||||
ox::Result<ox::CStringView> getPath(Context &ctx, ox::CStringViewCR fileId) noexcept;
|
ox::Result<ox::CStringView> getPath(Context &ctx, ox::CStringViewCR fileId) noexcept;
|
||||||
|
|
||||||
|
ox::Error updatePath(Context &ctx, ox::StringViewCR oldPath, ox::StringViewCR newPath) noexcept;
|
||||||
|
|
||||||
constexpr ox::Result<ox::UUID> uuidUrlToUuid(ox::StringViewCR uuidUrl) noexcept {
|
constexpr ox::Result<ox::UUID> uuidUrlToUuid(ox::StringViewCR uuidUrl) noexcept {
|
||||||
return ox::UUID::fromString(substr(uuidUrl, 7));
|
return ox::UUID::fromString(substr(uuidUrl, 7));
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,21 @@ ox::Result<ox::CStringView> getPath(Context &ctx, ox::CStringViewCR fileId) noex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error updatePath(Context &ctx, ox::StringViewCR oldPath, ox::StringViewCR newPath) noexcept {
|
||||||
|
#ifndef OX_BARE_METAL
|
||||||
|
if (auto const r = ctx.pathToUuid.at(oldPath); r.ok()) {
|
||||||
|
auto const ustr = r.value->toString();
|
||||||
|
ctx.pathToUuid[newPath] = *r.value;
|
||||||
|
ctx.pathToUuid.erase(oldPath);
|
||||||
|
ctx.uuidToPath[ustr] = newPath;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return ctx.assetManager.updateAssetId(oldPath, newPath);
|
||||||
|
#else
|
||||||
|
return ox::Error(1, "updating path is not supported on this platform");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
ox::Result<ox::CStringView> uuidUrlToPath(Context &ctx, ox::StringViewCR uuid) noexcept {
|
ox::Result<ox::CStringView> uuidUrlToPath(Context &ctx, ox::StringViewCR uuid) noexcept {
|
||||||
#ifndef OX_BARE_METAL
|
#ifndef OX_BARE_METAL
|
||||||
OX_REQUIRE_M(out, ctx.uuidToPath.at(substr(uuid, 7)));
|
OX_REQUIRE_M(out, ctx.uuidToPath.at(substr(uuid, 7)));
|
||||||
|
@ -39,10 +39,11 @@ static ox::Error pathToInode(
|
|||||||
auto const uuid = ox::substr(path, 7);
|
auto const uuid = ox::substr(path, 7);
|
||||||
OX_RETURN_ERROR(keel::uuidToPath(ctx, uuid).to<ox::String>().moveTo(path));
|
OX_RETURN_ERROR(keel::uuidToPath(ctx, uuid).to<ox::String>().moveTo(path));
|
||||||
}
|
}
|
||||||
OX_REQUIRE(s, dest.stat(path));
|
auto const s = dest.stat(path);
|
||||||
|
auto const inode = s.ok() ? s.value.inode : 0;
|
||||||
OX_RETURN_ERROR(typeVal->set(static_cast<int8_t>(ox::FileAddressType::Inode)));
|
OX_RETURN_ERROR(typeVal->set(static_cast<int8_t>(ox::FileAddressType::Inode)));
|
||||||
oxOutf("\tpath to inode: {} => {}\n", path, s.inode);
|
oxOutf("\tpath to inode: {} => {}\n", path, inode);
|
||||||
return data.set(2, s.inode);
|
return data.set(2, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ox::Error transformFileAddressesObj(
|
static ox::Error transformFileAddressesObj(
|
||||||
|
@ -9,6 +9,7 @@ add_library(
|
|||||||
newmenu.cpp
|
newmenu.cpp
|
||||||
newproject.cpp
|
newproject.cpp
|
||||||
projectexplorer.cpp
|
projectexplorer.cpp
|
||||||
|
renamefile.cpp
|
||||||
studioapp.cpp
|
studioapp.cpp
|
||||||
)
|
)
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
|
@ -31,6 +31,9 @@ void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
|||||||
if (ImGui::MenuItem("Delete")) {
|
if (ImGui::MenuItem("Delete")) {
|
||||||
deleteItem.emit(path);
|
deleteItem.emit(path);
|
||||||
}
|
}
|
||||||
|
if (ImGui::MenuItem("Rename")) {
|
||||||
|
renameItem.emit(path);
|
||||||
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ class ProjectExplorer final: public FileExplorer {
|
|||||||
ox::Signal<ox::Error(ox::StringViewCR)> addItem;
|
ox::Signal<ox::Error(ox::StringViewCR)> addItem;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> addDir;
|
ox::Signal<ox::Error(ox::StringViewCR)> addDir;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> deleteItem;
|
ox::Signal<ox::Error(ox::StringViewCR)> deleteItem;
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR)> renameItem;
|
||||||
|
|
||||||
explicit ProjectExplorer(keel::Context &kctx) noexcept;
|
explicit ProjectExplorer(keel::Context &kctx) noexcept;
|
||||||
|
|
||||||
|
63
src/olympic/studio/applib/src/renamefile.cpp
Normal file
63
src/olympic/studio/applib/src/renamefile.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <studio/imguiutil.hpp>
|
||||||
|
|
||||||
|
#include "renamefile.hpp"
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
RenameFile::RenameFile() {
|
||||||
|
setTitle("Rename File");
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error RenameFile::openPath(ox::StringParam path) noexcept {
|
||||||
|
m_oldPath = std::move(path);
|
||||||
|
OX_REQUIRE(idx, ox::findIdx(m_oldPath.rbegin(), m_oldPath.rend(), '/'));
|
||||||
|
m_name = substr(m_oldPath, idx + 1);
|
||||||
|
m_path = substr(m_oldPath, 0, idx + 1);
|
||||||
|
open();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameFile::open() noexcept {
|
||||||
|
m_stage = Stage::Opening;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameFile::close() noexcept {
|
||||||
|
m_stage = Stage::Closed;
|
||||||
|
m_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenameFile::isOpen() const noexcept {
|
||||||
|
return m_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameFile::draw(StudioContext &ctx) noexcept {
|
||||||
|
switch (m_stage) {
|
||||||
|
case Stage::Closed:
|
||||||
|
break;
|
||||||
|
case Stage::Opening:
|
||||||
|
ImGui::OpenPopup(title().c_str());
|
||||||
|
m_open = true;
|
||||||
|
m_stage = Stage::Open;
|
||||||
|
[[fallthrough]];
|
||||||
|
case Stage::Open:
|
||||||
|
setSize({250, 0});
|
||||||
|
drawWindow(ctx.tctx, m_open, [this] {
|
||||||
|
if (ImGui::IsWindowAppearing()) {
|
||||||
|
ImGui::SetKeyboardFocusHere();
|
||||||
|
}
|
||||||
|
ig::InputText("Name", m_name);
|
||||||
|
ImGui::Text("%s%s", m_path.c_str(), m_name.c_str());
|
||||||
|
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
|
||||||
|
moveFile.emit(m_oldPath, m_path + m_name);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
src/olympic/studio/applib/src/renamefile.hpp
Normal file
44
src/olympic/studio/applib/src/renamefile.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
#include <studio/popup.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
class RenameFile: public Popup {
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Stage {
|
||||||
|
Closed,
|
||||||
|
Opening,
|
||||||
|
Open,
|
||||||
|
};
|
||||||
|
Stage m_stage{};
|
||||||
|
ox::String m_oldPath;
|
||||||
|
ox::String m_path;
|
||||||
|
ox::IString<255> m_name;
|
||||||
|
bool m_open{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveFile;
|
||||||
|
|
||||||
|
RenameFile();
|
||||||
|
|
||||||
|
ox::Error openPath(ox::StringParam path) noexcept;
|
||||||
|
|
||||||
|
void open() noexcept override;
|
||||||
|
|
||||||
|
void close() noexcept override;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool isOpen() const noexcept override;
|
||||||
|
|
||||||
|
void draw(StudioContext &ctx) noexcept override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -47,20 +47,20 @@ OX_MODEL_BEGIN(StudioConfig)
|
|||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
|
StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexcept:
|
||||||
m_sctx(*this, ctx),
|
m_sctx{*this, ctx},
|
||||||
m_tctx(ctx),
|
m_tctx{ctx},
|
||||||
m_projectDataDir(std::move(projectDataDir)),
|
m_projectDataDir{std::move(projectDataDir)},
|
||||||
m_projectExplorer(keelCtx(m_tctx)),
|
m_projectExplorer{keelCtx(m_tctx)},
|
||||||
m_newProject(m_projectDataDir),
|
m_newProject{m_projectDataDir},
|
||||||
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.addDir.connect(this, &StudioUI::addDir);
|
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||||
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
||||||
|
m_projectExplorer.renameItem.connect(this, &StudioUI::renameFile);
|
||||||
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;
|
|
||||||
loadModules();
|
loadModules();
|
||||||
// open project and files
|
// open project and files
|
||||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_tctx));
|
||||||
@ -369,6 +369,14 @@ ox::Error StudioUI::deleteFile(ox::StringViewCR path) noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error StudioUI::renameFile(ox::StringViewCR path) noexcept {
|
||||||
|
return m_renameFile.openPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
ox::Error StudioUI::handleMoveFile(ox::StringViewCR, ox::UUID const&) noexcept {
|
||||||
|
return m_projectExplorer.refreshProjectTreeModel();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -386,10 +394,12 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|||||||
m_sctx.project = m_project.get();
|
m_sctx.project = m_project.get();
|
||||||
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
turbine::setWindowTitle(m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||||
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||||
|
m_renameFile.moveFile.connect(m_project.get(), &Project::moveItem);
|
||||||
m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir);
|
m_newDirDialog.newDir.connect(m_sctx.project, &Project::mkdir);
|
||||||
m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
m_project->dirAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||||
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||||
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||||
|
m_project->fileMoved.connect(this, &StudioUI::handleMoveFile);
|
||||||
m_openFiles.clear();
|
m_openFiles.clear();
|
||||||
m_editors.clear();
|
m_editors.clear();
|
||||||
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
studio::editConfig<StudioConfig>(keelCtx(m_tctx), [&](StudioConfig &config) {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "newmenu.hpp"
|
#include "newmenu.hpp"
|
||||||
#include "newproject.hpp"
|
#include "newproject.hpp"
|
||||||
#include "projectexplorer.hpp"
|
#include "projectexplorer.hpp"
|
||||||
|
#include "renamefile.hpp"
|
||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
@ -42,14 +43,16 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
NewMenu m_newMenu{keelCtx(m_tctx)};
|
NewMenu m_newMenu{keelCtx(m_tctx)};
|
||||||
DeleteConfirmation m_deleteConfirmation;
|
DeleteConfirmation m_deleteConfirmation;
|
||||||
NewDir m_newDirDialog;
|
NewDir m_newDirDialog;
|
||||||
|
RenameFile m_renameFile;
|
||||||
NewProject m_newProject;
|
NewProject m_newProject;
|
||||||
AboutPopup m_aboutPopup;
|
AboutPopup m_aboutPopup;
|
||||||
ox::Array<Popup*, 5> const m_popups = {
|
ox::Array<Popup*, 6> const m_popups = {
|
||||||
&m_newMenu,
|
&m_newMenu,
|
||||||
&m_newProject,
|
&m_newProject,
|
||||||
&m_aboutPopup,
|
&m_aboutPopup,
|
||||||
&m_deleteConfirmation,
|
&m_deleteConfirmation,
|
||||||
&m_newDirDialog,
|
&m_newDirDialog,
|
||||||
|
&m_renameFile,
|
||||||
};
|
};
|
||||||
bool m_showProjectExplorer = true;
|
bool m_showProjectExplorer = true;
|
||||||
|
|
||||||
@ -95,6 +98,10 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error deleteFile(ox::StringViewCR path) noexcept;
|
ox::Error deleteFile(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
ox::Error renameFile(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
ox::Error handleMoveFile(ox::StringViewCR path, ox::UUID const&id) 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;
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include <ox/claw/write.hpp>
|
#include <ox/claw/write.hpp>
|
||||||
#include <ox/event/signal.hpp>
|
#include <ox/event/signal.hpp>
|
||||||
#include <ox/fs/fs.hpp>
|
#include <ox/fs/fs.hpp>
|
||||||
#include <ox/mc/mc.hpp>
|
|
||||||
#include <ox/model/descwrite.hpp>
|
#include <ox/model/descwrite.hpp>
|
||||||
#include <ox/std/hashmap.hpp>
|
#include <ox/std/hashmap.hpp>
|
||||||
|
|
||||||
@ -26,6 +25,7 @@ enum class ProjectEvent {
|
|||||||
FileRecognized,
|
FileRecognized,
|
||||||
FileDeleted,
|
FileDeleted,
|
||||||
FileUpdated,
|
FileUpdated,
|
||||||
|
FileMoved,
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@ -49,7 +49,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
|
|||||||
class Project: public ox::SignalHandler {
|
class Project: public ox::SignalHandler {
|
||||||
private:
|
private:
|
||||||
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
|
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
|
||||||
keel::Context &m_ctx;
|
keel::Context &m_kctx;
|
||||||
ox::String m_path;
|
ox::String m_path;
|
||||||
ox::String m_projectDataDir;
|
ox::String m_projectDataDir;
|
||||||
ox::String m_typeDescPath;
|
ox::String m_typeDescPath;
|
||||||
@ -92,6 +92,8 @@ class Project: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
|
ox::Error moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
|
||||||
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
@ -114,7 +116,7 @@ class Project: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Result<ox::Buffer> loadBuff(ox::StringViewCR path) const noexcept;
|
ox::Result<ox::Buffer> loadBuff(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
ox::Error lsProcDir(ox::Vector<ox::String> *paths, ox::StringViewCR path) const noexcept;
|
ox::Error lsProcDir(ox::Vector<ox::String> &paths, ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
ox::Result<ox::Vector<ox::String>> listFiles(ox::StringViewCR path = "") const noexcept;
|
ox::Result<ox::Vector<ox::String>> listFiles(ox::StringViewCR path = "") const noexcept;
|
||||||
|
|
||||||
@ -128,7 +130,8 @@ class Project: public ox::SignalHandler {
|
|||||||
// file.
|
// file.
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> fileRecognized;
|
ox::Signal<ox::Error(ox::StringViewCR)> fileRecognized;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> fileDeleted;
|
ox::Signal<ox::Error(ox::StringViewCR)> fileDeleted;
|
||||||
ox::Signal<ox::Error(ox::StringView, ox::UUID)> fileUpdated;
|
ox::Signal<ox::Error(ox::StringViewCR path, ox::UUID const&id)> fileUpdated;
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR newPath, ox::UUID const&id)> fileMoved;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,8 +154,8 @@ ox::Error Project::writeObj(ox::StringViewCR path, T const&obj, ox::ClawFormat f
|
|||||||
if (!descExists) {
|
if (!descExists) {
|
||||||
OX_RETURN_ERROR(writeTypeStore());
|
OX_RETURN_ERROR(writeTypeStore());
|
||||||
}
|
}
|
||||||
OX_RETURN_ERROR(keel::reloadAsset(m_ctx, path));
|
OX_RETURN_ERROR(keel::reloadAsset(m_kctx, path));
|
||||||
OX_REQUIRE(uuid, pathToUuid(m_ctx, path));
|
OX_REQUIRE(uuid, pathToUuid(m_kctx, path));
|
||||||
fileUpdated.emit(path, uuid);
|
fileUpdated.emit(path, uuid);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -200,6 +203,9 @@ ox::Error Project::subscribe(ProjectEvent e, ox::SignalHandler *tgt, Functor &&s
|
|||||||
case ProjectEvent::FileUpdated:
|
case ProjectEvent::FileUpdated:
|
||||||
connect(this, &Project::fileUpdated, tgt, slot);
|
connect(this, &Project::fileUpdated, tgt, slot);
|
||||||
break;
|
break;
|
||||||
|
case ProjectEvent::FileMoved:
|
||||||
|
connect(this, &Project::fileMoved, tgt, slot);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,14 @@ static void generateTypes(ox::TypeStore &ts) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
||||||
m_ctx(ctx),
|
m_kctx(ctx),
|
||||||
m_path(std::move(path)),
|
m_path(std::move(path)),
|
||||||
m_projectDataDir(projectDataDir),
|
m_projectDataDir(projectDataDir),
|
||||||
m_typeDescPath(ox::sfmt("/{}/type_descriptors", m_projectDataDir)),
|
m_typeDescPath(ox::sfmt("/{}/type_descriptors", m_projectDataDir)),
|
||||||
m_typeStore(*m_ctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)),
|
m_typeStore(*m_kctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)),
|
||||||
m_fs(*m_ctx.rom) {
|
m_fs(*m_kctx.rom) {
|
||||||
oxTracef("studio", "Project: {}", m_path);
|
oxTracef("studio", "Project: {}", m_path);
|
||||||
generateTypes(m_typeStore);
|
generateTypes(m_typeStore);
|
||||||
if constexpr(ox::defines::Debug) {
|
if constexpr(ox::defines::Debug) {
|
||||||
@ -69,6 +70,14 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
|||||||
return m_fs.stat(path);
|
return m_fs.stat(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
|
OX_RETURN_ERROR(m_fs.move(src, dest));
|
||||||
|
OX_RETURN_ERROR(keel::updatePath(m_kctx, src, dest));
|
||||||
|
OX_REQUIRE(uuid, keel::pathToUuid(m_kctx, dest));
|
||||||
|
fileMoved.emit(dest, uuid);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
|
ox::Error Project::deleteItem(ox::StringViewCR path) noexcept {
|
||||||
OX_REQUIRE(stat, m_fs.stat(path));
|
OX_REQUIRE(stat, m_fs.stat(path));
|
||||||
if (stat.fileType == ox::FileType::Directory) {
|
if (stat.fileType == ox::FileType::Directory) {
|
||||||
@ -142,7 +151,7 @@ ox::Error Project::writeBuff(ox::StringViewCR path, ox::BufferView const&buff) n
|
|||||||
ox::Buffer outBuff;
|
ox::Buffer outBuff;
|
||||||
outBuff.reserve(buff.size() + HdrSz);
|
outBuff.reserve(buff.size() + HdrSz);
|
||||||
ox::BufferWriter writer(&outBuff);
|
ox::BufferWriter writer(&outBuff);
|
||||||
auto const [uuid, err] = pathToUuid(m_ctx, path);
|
auto const [uuid, err] = pathToUuid(m_kctx, path);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
OX_RETURN_ERROR(keel::writeUuidHeader(writer, uuid));
|
OX_RETURN_ERROR(keel::writeUuidHeader(writer, uuid));
|
||||||
}
|
}
|
||||||
@ -162,14 +171,14 @@ ox::Result<ox::Buffer> Project::loadBuff(ox::StringViewCR path) const noexcept {
|
|||||||
return m_fs.read(path);
|
return m_fs.read(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, ox::StringViewCR path) const noexcept {
|
ox::Error Project::lsProcDir(ox::Vector<ox::String> &paths, ox::StringViewCR path) const noexcept {
|
||||||
OX_REQUIRE(files, m_fs.ls(path));
|
OX_REQUIRE(files, m_fs.ls(path));
|
||||||
for (auto const&name : files) {
|
for (auto const&name : files) {
|
||||||
auto fullPath = ox::sfmt("{}/{}", path, name);
|
auto fullPath = ox::sfmt("{}/{}", path, name);
|
||||||
OX_REQUIRE(stat, m_fs.stat(ox::StringView(fullPath)));
|
OX_REQUIRE(stat, m_fs.stat(ox::StringView(fullPath)));
|
||||||
switch (stat.fileType) {
|
switch (stat.fileType) {
|
||||||
case ox::FileType::NormalFile:
|
case ox::FileType::NormalFile:
|
||||||
paths->emplace_back(std::move(fullPath));
|
paths.emplace_back(std::move(fullPath));
|
||||||
break;
|
break;
|
||||||
case ox::FileType::Directory:
|
case ox::FileType::Directory:
|
||||||
OX_RETURN_ERROR(lsProcDir(paths, fullPath));
|
OX_RETURN_ERROR(lsProcDir(paths, fullPath));
|
||||||
@ -183,7 +192,7 @@ ox::Error Project::lsProcDir(ox::Vector<ox::String> *paths, ox::StringViewCR pat
|
|||||||
|
|
||||||
ox::Result<ox::Vector<ox::String>> Project::listFiles(ox::StringViewCR path) const noexcept {
|
ox::Result<ox::Vector<ox::String>> Project::listFiles(ox::StringViewCR path) const noexcept {
|
||||||
ox::Vector<ox::String> paths;
|
ox::Vector<ox::String> paths;
|
||||||
OX_RETURN_ERROR(lsProcDir(&paths, path));
|
OX_RETURN_ERROR(lsProcDir(paths, path));
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +222,7 @@ ox::Error initGfx(Context &ctx) noexcept {
|
|||||||
//io.MouseDrawCursor = true;
|
//io.MouseDrawCursor = true;
|
||||||
ImGui_ImplGlfw_InitForOpenGL(ctx.window, true);
|
ImGui_ImplGlfw_InitForOpenGL(ctx.window, true);
|
||||||
ImGui_ImplOpenGL3_Init();
|
ImGui_ImplOpenGL3_Init();
|
||||||
|
io.IniFilename = nullptr;
|
||||||
themeImgui();
|
themeImgui();
|
||||||
#endif
|
#endif
|
||||||
return {};
|
return {};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user