[studio] Add ability to move directories
All checks were successful
Build / build (push) Successful in 3m29s
All checks were successful
Build / build (push) Successful in 3m29s
This commit is contained in:
parent
109e1898cc
commit
1207dadee8
@ -238,6 +238,9 @@ class AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ox::Error updateAssetId(ox::StringViewCR oldId, ox::StringViewCR newId) noexcept final {
|
ox::Error updateAssetId(ox::StringViewCR oldId, ox::StringViewCR newId) noexcept final {
|
||||||
|
if (!m_cache.contains(oldId)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
auto &o = m_cache[oldId];
|
auto &o = m_cache[oldId];
|
||||||
auto &n = m_cache[newId];
|
auto &n = m_cache[newId];
|
||||||
n = std::move(o);
|
n = std::move(o);
|
||||||
@ -246,11 +249,14 @@ class AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void gc() noexcept final {
|
void gc() noexcept final {
|
||||||
for (auto const&ack : m_cache.keys()) {
|
for (size_t i = 0; i < m_cache.keys().size();) {
|
||||||
|
auto const &ack = m_cache.keys()[i];
|
||||||
auto &ac = m_cache[ack];
|
auto &ac = m_cache[ack];
|
||||||
if (!ac->references()) {
|
if (!ac->references()) {
|
||||||
m_cache.erase(ack);
|
m_cache.erase(ack);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,6 +315,9 @@ class AssetManager {
|
|||||||
if (m_fileUpdated.contains(newId)) {
|
if (m_fileUpdated.contains(newId)) {
|
||||||
return ox::Error{1, "new asset ID already has an entry"};
|
return ox::Error{1, "new asset ID already has an entry"};
|
||||||
}
|
}
|
||||||
|
if (!m_fileUpdated.contains(oldId)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
auto &o = m_fileUpdated[oldId];
|
auto &o = m_fileUpdated[oldId];
|
||||||
auto &n = m_fileUpdated[newId];
|
auto &n = m_fileUpdated[newId];
|
||||||
n = std::move(o);
|
n = std::move(o);
|
||||||
|
@ -30,6 +30,10 @@ void ProjectExplorer::fileMoved(ox::StringViewCR src, ox::StringViewCR dst) cons
|
|||||||
moveItem.emit(src, dst);
|
moveItem.emit(src, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectExplorer::dirMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept {
|
||||||
|
moveDir.emit(src, dst);
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
||||||
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||||
if (ImGui::MenuItem("Delete")) {
|
if (ImGui::MenuItem("Delete")) {
|
||||||
|
@ -20,6 +20,7 @@ class ProjectExplorer final: public FileExplorer {
|
|||||||
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;
|
ox::Signal<ox::Error(ox::StringViewCR)> renameItem;
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveDir;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveItem;
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveItem;
|
||||||
|
|
||||||
explicit ProjectExplorer(keel::Context &kctx) noexcept;
|
explicit ProjectExplorer(keel::Context &kctx) noexcept;
|
||||||
@ -33,6 +34,8 @@ class ProjectExplorer final: public FileExplorer {
|
|||||||
|
|
||||||
void fileMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept override;
|
void fileMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept override;
|
||||||
|
|
||||||
|
void dirMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept override;
|
||||||
|
|
||||||
void fileContextMenu(ox::StringViewCR path) const noexcept override;
|
void fileContextMenu(ox::StringViewCR path) const noexcept override;
|
||||||
|
|
||||||
void dirContextMenu(ox::StringViewCR path) const noexcept override;
|
void dirContextMenu(ox::StringViewCR path) const noexcept override;
|
||||||
|
@ -59,6 +59,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
|||||||
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_projectExplorer.renameItem.connect(this, &StudioUI::renameFile);
|
||||||
|
m_projectExplorer.moveDir.connect(this, &StudioUI::queueDirMove);
|
||||||
m_projectExplorer.moveItem.connect(this, &StudioUI::queueFileMove);
|
m_projectExplorer.moveItem.connect(this, &StudioUI::queueFileMove);
|
||||||
m_renameFile.moveFile.connect(this, &StudioUI::queueFileMove);
|
m_renameFile.moveFile.connect(this, &StudioUI::queueFileMove);
|
||||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||||
@ -486,6 +487,11 @@ ox::Error StudioUI::closeFile(ox::StringViewCR path) noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error StudioUI::queueDirMove(ox::StringParam src, ox::StringParam dst) noexcept {
|
||||||
|
m_queuedDirMoves.emplace_back(std::move(src), std::move(dst));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error StudioUI::queueFileMove(ox::StringParam src, ox::StringParam dst) noexcept {
|
ox::Error StudioUI::queueFileMove(ox::StringParam src, ox::StringParam dst) noexcept {
|
||||||
m_queuedMoves.emplace_back(std::move(src), std::move(dst));
|
m_queuedMoves.emplace_back(std::move(src), std::move(dst));
|
||||||
return {};
|
return {};
|
||||||
@ -496,6 +502,10 @@ void StudioUI::procFileMoves() noexcept {
|
|||||||
oxLogError(m_project->moveItem(m.a, m.b));
|
oxLogError(m_project->moveItem(m.a, m.b));
|
||||||
}
|
}
|
||||||
m_queuedMoves.clear();
|
m_queuedMoves.clear();
|
||||||
|
for (auto const &m : m_queuedDirMoves) {
|
||||||
|
oxLogError(m_project->moveDir(m.a, m.b));
|
||||||
|
}
|
||||||
|
m_queuedDirMoves.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,8 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
BaseEditor *m_activeEditorOnLastDraw = nullptr;
|
||||||
BaseEditor *m_activeEditor = nullptr;
|
BaseEditor *m_activeEditor = nullptr;
|
||||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||||
ox::Vector<ox::Pair<ox::String, ox::String>> m_queuedMoves;
|
ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
|
||||||
|
ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
|
||||||
NewMenu m_newMenu{keelCtx(m_tctx)};
|
NewMenu m_newMenu{keelCtx(m_tctx)};
|
||||||
DeleteConfirmation m_deleteConfirmation;
|
DeleteConfirmation m_deleteConfirmation;
|
||||||
NewDir m_newDirDialog;
|
NewDir m_newDirDialog;
|
||||||
@ -113,6 +114,8 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error closeFile(ox::StringViewCR path) noexcept;
|
ox::Error closeFile(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
ox::Error queueDirMove(ox::StringParam src, ox::StringParam dst) noexcept;
|
||||||
|
|
||||||
ox::Error queueFileMove(ox::StringParam src, ox::StringParam dst) noexcept;
|
ox::Error queueFileMove(ox::StringParam src, ox::StringParam dst) noexcept;
|
||||||
|
|
||||||
void procFileMoves() noexcept;
|
void procFileMoves() noexcept;
|
||||||
|
@ -13,10 +13,12 @@ struct FileRef {
|
|||||||
static constexpr auto TypeName = "net.drinkingtea.studio.FileRef";
|
static constexpr auto TypeName = "net.drinkingtea.studio.FileRef";
|
||||||
static constexpr auto TypeVersion = 1;
|
static constexpr auto TypeVersion = 1;
|
||||||
ox::String path;
|
ox::String path;
|
||||||
|
bool isDir{};
|
||||||
};
|
};
|
||||||
|
|
||||||
OX_MODEL_BEGIN(FileRef)
|
OX_MODEL_BEGIN(FileRef)
|
||||||
OX_MODEL_FIELD(path)
|
OX_MODEL_FIELD(path)
|
||||||
|
OX_MODEL_FIELD(isDir)
|
||||||
OX_MODEL_END()
|
OX_MODEL_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,8 @@ class FileExplorer: public ox::SignalHandler {
|
|||||||
|
|
||||||
virtual void fileMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept;
|
virtual void fileMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept;
|
||||||
|
|
||||||
|
virtual void dirMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept;
|
||||||
|
|
||||||
void drawFileContextMenu(ox::CStringViewCR path) const noexcept;
|
void drawFileContextMenu(ox::CStringViewCR path) const noexcept;
|
||||||
|
|
||||||
void drawDirContextMenu(ox::CStringViewCR path) const noexcept;
|
void drawDirContextMenu(ox::CStringViewCR path) const noexcept;
|
||||||
|
@ -94,6 +94,8 @@ class Project: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
ox::Error moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
|
||||||
|
ox::Error moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
|
||||||
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
|
@ -43,6 +43,8 @@ void FileExplorer::fileDeleted(ox::StringViewCR) const noexcept {}
|
|||||||
|
|
||||||
void FileExplorer::fileMoved(ox::StringViewCR, ox::StringViewCR) const noexcept {}
|
void FileExplorer::fileMoved(ox::StringViewCR, ox::StringViewCR) const noexcept {}
|
||||||
|
|
||||||
|
void FileExplorer::dirMoved(ox::StringViewCR, ox::StringViewCR) const noexcept {}
|
||||||
|
|
||||||
void FileExplorer::drawFileContextMenu(ox::CStringViewCR path) const noexcept {
|
void FileExplorer::drawFileContextMenu(ox::CStringViewCR path) const noexcept {
|
||||||
ig::IDStackItem const idStackItem{path};
|
ig::IDStackItem const idStackItem{path};
|
||||||
fileContextMenu(path);
|
fileContextMenu(path);
|
||||||
@ -93,13 +95,23 @@ void FileTreeModel::draw(turbine::Context &tctx) const noexcept {
|
|||||||
}
|
}
|
||||||
ig::IDStackItem const idStackItem{m_name};
|
ig::IDStackItem const idStackItem{m_name};
|
||||||
m_explorer.drawDirContextMenu(m_fullPath);
|
m_explorer.drawDirContextMenu(m_fullPath);
|
||||||
|
if (m_explorer.fileDraggable()) {
|
||||||
|
std::ignore = ig::dragDropSource([this] {
|
||||||
|
ImGui::Text("%s", m_name.c_str());
|
||||||
|
return ig::setDragDropPayload("FileRef", FileRef{ox::String{m_fullPath}, true});
|
||||||
|
});
|
||||||
|
}
|
||||||
if (ig::DragDropTarget const dragDropTarget; dragDropTarget) {
|
if (ig::DragDropTarget const dragDropTarget; dragDropTarget) {
|
||||||
auto const [ref, err] = ig::getDragDropPayload<FileRef>("FileRef");
|
auto const [ref, err] = ig::getDragDropPayload<FileRef>("FileRef");
|
||||||
if (!err) {
|
if (!err) {
|
||||||
auto const name = substr(ref.path, find(ref.path.rbegin(), ref.path.rend(), '/').offset() + 1);
|
auto const name = substr(ref.path, find(ref.path.rbegin(), ref.path.rend(), '/').offset() + 1);
|
||||||
|
if (ref.isDir) {
|
||||||
|
m_explorer.dirMoved(ref.path, sfmt("{}/{}", m_fullPath, name));
|
||||||
|
} else {
|
||||||
m_explorer.fileMoved(ref.path, sfmt("{}/{}", m_fullPath, name));
|
m_explorer.fileMoved(ref.path, sfmt("{}/{}", m_fullPath, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (nodeOpen) {
|
if (nodeOpen) {
|
||||||
for (auto const&child : m_children) {
|
for (auto const&child : m_children) {
|
||||||
child->draw(tctx);
|
child->draw(tctx);
|
||||||
|
@ -26,6 +26,26 @@ static void generateTypes(ox::TypeStore &ts) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ox::Result<ox::Vector<ox::String>> listAllRecursive(ox::FileSystem &fs, ox::StringViewCR path) {
|
||||||
|
// there really isn't much recourse if this function fails, just log it and move on
|
||||||
|
auto [out, outErr] = fs.ls(path);
|
||||||
|
oxLogError(outErr);
|
||||||
|
for (auto const &p : out) {
|
||||||
|
auto const [stat, statErr] = fs.stat(path);
|
||||||
|
if (statErr) {
|
||||||
|
oxLogError(statErr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (stat.fileType == ox::FileType::Directory) {
|
||||||
|
OX_REQUIRE_M(l, listAllRecursive(fs, sfmt("{}/{}", path, p)));
|
||||||
|
for (auto &c : l) {
|
||||||
|
out.emplace_back(std::move(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
Project::Project(keel::Context &ctx, ox::String path, ox::StringViewCR projectDataDir):
|
||||||
m_kctx(ctx),
|
m_kctx(ctx),
|
||||||
@ -78,6 +98,21 @@ ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcep
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error Project::moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
|
OX_REQUIRE(files, listAllRecursive(m_fs, src));
|
||||||
|
OX_RETURN_ERROR(m_fs.move(src, dest));
|
||||||
|
fileMoved.emit(src, dest, ox::UUID{});
|
||||||
|
for (auto const &op : files) {
|
||||||
|
auto const name =
|
||||||
|
substr(op, ox::find(op.rbegin(), op.rend(), '/').offset() + 1);
|
||||||
|
auto const np = sfmt("{}/{}", dest, name);
|
||||||
|
OX_RETURN_ERROR(keel::updatePath(m_kctx, op, np));
|
||||||
|
OX_REQUIRE(uuid, keel::pathToUuid(m_kctx, dest));
|
||||||
|
fileMoved.emit(src, 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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user