[keel,studio] Add Make Copy option to ProjectExplorer
All checks were successful
Build / build (push) Successful in 3m46s
All checks were successful
Build / build (push) Successful in 3m46s
This commit is contained in:
parent
0abadc1850
commit
671dd86206
@ -15,6 +15,8 @@ constexpr auto K1HdrSz = 40;
|
|||||||
|
|
||||||
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept;
|
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept;
|
||||||
|
|
||||||
|
ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept;
|
||||||
|
|
||||||
ox::Error writeUuidHeader(ox::Writer_c auto &writer, ox::UUID const&uuid) noexcept {
|
ox::Error writeUuidHeader(ox::Writer_c auto &writer, ox::UUID const&uuid) noexcept {
|
||||||
OX_RETURN_ERROR(write(writer, "K1;"));
|
OX_RETURN_ERROR(write(writer, "K1;"));
|
||||||
OX_RETURN_ERROR(uuid.toString(writer));
|
OX_RETURN_ERROR(uuid.toString(writer));
|
||||||
|
@ -8,15 +8,25 @@ namespace keel {
|
|||||||
|
|
||||||
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept {
|
ox::Result<ox::UUID> readUuidHeader(ox::BufferView buff) noexcept {
|
||||||
if (buff.size() < K1HdrSz) [[unlikely]] {
|
if (buff.size() < K1HdrSz) [[unlikely]] {
|
||||||
return ox::Error(1, "Insufficient data to contain complete Keel header");
|
return ox::Error{1, "Insufficient data to contain complete Keel header"};
|
||||||
}
|
}
|
||||||
constexpr ox::StringView k1Hdr = "K1;";
|
constexpr ox::StringView k1Hdr = "K1;";
|
||||||
if (k1Hdr != ox::StringView(buff.data(), k1Hdr.bytes())) [[unlikely]] {
|
if (k1Hdr != ox::StringView{buff.data(), k1Hdr.bytes()}) [[unlikely]] {
|
||||||
return ox::Error(2, "No Keel asset header data");
|
return ox::Error{2, "No Keel asset header data"};
|
||||||
}
|
}
|
||||||
return ox::UUID::fromString(ox::StringView(&buff[k1Hdr.bytes()], 36));
|
return ox::UUID::fromString(ox::StringView(&buff[k1Hdr.bytes()], 36));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Result<ox::UUID> regenerateUuidHeader(ox::Buffer &buff) noexcept {
|
||||||
|
OX_RETURN_ERROR(readUuidHeader(buff));
|
||||||
|
OX_REQUIRE(id, ox::UUID::generate());
|
||||||
|
auto const str = id.toString();
|
||||||
|
for (size_t i = 0; i < ox::UUIDStr::cap(); ++i) {
|
||||||
|
buff[i + 3] = str[i];
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept {
|
ox::Result<ox::ModelObject> readAsset(ox::TypeStore &ts, ox::BufferView buff) noexcept {
|
||||||
std::size_t offset = 0;
|
std::size_t offset = 0;
|
||||||
if (!readUuidHeader(buff).error) {
|
if (!readUuidHeader(buff).error) {
|
||||||
|
@ -5,6 +5,7 @@ add_library(
|
|||||||
deleteconfirmation.cpp
|
deleteconfirmation.cpp
|
||||||
filedialogmanager.cpp
|
filedialogmanager.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
makecopypopup.cpp
|
||||||
newdir.cpp
|
newdir.cpp
|
||||||
newmenu.cpp
|
newmenu.cpp
|
||||||
newproject.cpp
|
newproject.cpp
|
||||||
|
70
src/olympic/studio/applib/src/makecopypopup.cpp
Normal file
70
src/olympic/studio/applib/src/makecopypopup.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "makecopypopup.hpp"
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
ox::Error MakeCopyPopup::open(ox::StringViewCR path) noexcept {
|
||||||
|
m_stage = Stage::Opening;
|
||||||
|
OX_REQUIRE(idx, ox::findIdx(path.rbegin(), path.rend(), '/'));
|
||||||
|
m_srcPath = path;
|
||||||
|
m_dirPath = substr(path, 0, idx + 1);
|
||||||
|
m_title = sfmt("Copy {}", path);
|
||||||
|
m_fileName = "";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCopyPopup::close() noexcept {
|
||||||
|
m_stage = Stage::Closed;
|
||||||
|
m_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MakeCopyPopup::isOpen() const noexcept {
|
||||||
|
return m_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCopyPopup::draw(StudioContext const &ctx, ImVec2 const &sz) noexcept {
|
||||||
|
switch (m_stage) {
|
||||||
|
case Stage::Closed:
|
||||||
|
break;
|
||||||
|
case Stage::Opening:
|
||||||
|
ImGui::OpenPopup(m_title.c_str());
|
||||||
|
m_stage = Stage::Open;
|
||||||
|
m_open = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case Stage::Open:
|
||||||
|
ig::centerNextWindow(ctx.tctx);
|
||||||
|
ImGui::SetNextWindowSize(sz);
|
||||||
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
|
if (ImGui::BeginPopupModal(m_title.c_str(), &m_open, modalFlags)) {
|
||||||
|
if (ImGui::IsWindowAppearing()) {
|
||||||
|
ImGui::SetKeyboardFocusHere();
|
||||||
|
}
|
||||||
|
ig::InputText("Name", m_fileName);
|
||||||
|
bool open = true;
|
||||||
|
switch (ig::PopupControlsOkCancel(open)) {
|
||||||
|
case ig::PopupResponse::None:
|
||||||
|
break;
|
||||||
|
case ig::PopupResponse::OK:
|
||||||
|
{
|
||||||
|
auto const p = sfmt("{}{}", m_dirPath, m_fileName);
|
||||||
|
if (!ctx.project->exists(p)) {
|
||||||
|
makeCopy.emit(m_srcPath, p);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ig::PopupResponse::Cancel:
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
42
src/olympic/studio/applib/src/makecopypopup.hpp
Normal file
42
src/olympic/studio/applib/src/makecopypopup.hpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/string.hpp>
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
#include <studio/imguiutil.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
class MakeCopyPopup {
|
||||||
|
private:
|
||||||
|
enum class Stage {
|
||||||
|
Closed,
|
||||||
|
Opening,
|
||||||
|
Open,
|
||||||
|
};
|
||||||
|
Stage m_stage = Stage::Closed;
|
||||||
|
bool m_open{};
|
||||||
|
ox::String m_title{"Copy File"};
|
||||||
|
ox::String m_srcPath;
|
||||||
|
ox::String m_dirPath;
|
||||||
|
ox::IString<255> m_fileName;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> makeCopy;
|
||||||
|
|
||||||
|
ox::Error open(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
void close() noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool isOpen() const noexcept;
|
||||||
|
|
||||||
|
void draw(StudioContext const &ctx, ImVec2 const &sz = {}) noexcept;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -42,6 +42,9 @@ void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
|||||||
if (ImGui::MenuItem("Rename")) {
|
if (ImGui::MenuItem("Rename")) {
|
||||||
renameItem.emit(path);
|
renameItem.emit(path);
|
||||||
}
|
}
|
||||||
|
if (ImGui::MenuItem("Make Copy")) {
|
||||||
|
makeCopy.emit(path);
|
||||||
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)> makeCopy;
|
||||||
ox::Signal<ox::Error(ox::StringViewCR src, ox::StringViewCR dst)> moveDir;
|
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;
|
||||||
|
|
||||||
|
@ -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.makeCopy.connect(this, &StudioUI::makeCopyDlg);
|
||||||
m_projectExplorer.moveDir.connect(this, &StudioUI::queueDirMove);
|
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);
|
||||||
@ -136,6 +137,7 @@ void StudioUI::draw() noexcept {
|
|||||||
p->draw(m_sctx);
|
p->draw(m_sctx);
|
||||||
}
|
}
|
||||||
m_closeFileConfirm.draw(m_sctx);
|
m_closeFileConfirm.draw(m_sctx);
|
||||||
|
m_copyFilePopup.draw(m_sctx, {250, 0});
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
handleKeyInput();
|
handleKeyInput();
|
||||||
@ -451,6 +453,7 @@ 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_copyFilePopup.makeCopy.connect(m_sctx.project, &Project::copyItem);
|
||||||
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);
|
||||||
@ -513,6 +516,10 @@ ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActi
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error StudioUI::makeCopyDlg(ox::StringViewCR path) noexcept {
|
||||||
|
return m_copyFilePopup.open(path);
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error StudioUI::handleCloseFileResponse(ig::PopupResponse const response) noexcept {
|
ox::Error StudioUI::handleCloseFileResponse(ig::PopupResponse const response) noexcept {
|
||||||
if (response == ig::PopupResponse::OK && m_activeEditor) {
|
if (response == ig::PopupResponse::OK && m_activeEditor) {
|
||||||
return closeCurrentFile();
|
return closeCurrentFile();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "aboutpopup.hpp"
|
#include "aboutpopup.hpp"
|
||||||
#include "deleteconfirmation.hpp"
|
#include "deleteconfirmation.hpp"
|
||||||
|
#include "makecopypopup.hpp"
|
||||||
#include "newdir.hpp"
|
#include "newdir.hpp"
|
||||||
#include "newmenu.hpp"
|
#include "newmenu.hpp"
|
||||||
#include "newproject.hpp"
|
#include "newproject.hpp"
|
||||||
@ -48,6 +49,7 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
DeleteConfirmation m_deleteConfirmation;
|
DeleteConfirmation m_deleteConfirmation;
|
||||||
NewDir m_newDirDialog;
|
NewDir m_newDirDialog;
|
||||||
ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
|
ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
|
||||||
|
MakeCopyPopup m_copyFilePopup;
|
||||||
RenameFile m_renameFile;
|
RenameFile m_renameFile;
|
||||||
NewProject m_newProject;
|
NewProject m_newProject;
|
||||||
AboutPopup m_aboutPopup;
|
AboutPopup m_aboutPopup;
|
||||||
@ -117,6 +119,8 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept;
|
ox::Error openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept;
|
||||||
|
|
||||||
|
ox::Error makeCopyDlg(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;
|
ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;
|
||||||
|
|
||||||
ox::Error closeCurrentFile() noexcept;
|
ox::Error closeCurrentFile() noexcept;
|
||||||
|
@ -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 copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
|
||||||
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 moveDir(ox::StringViewCR src, ox::StringViewCR dest) noexcept;
|
||||||
|
@ -97,6 +97,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::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
|
OX_REQUIRE_M(buff, loadBuff(src));
|
||||||
|
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
|
||||||
|
OX_RETURN_ERROR(writeBuff(dest, buff));
|
||||||
|
createUuidMapping(m_kctx, dest, id);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
ox::Error Project::moveItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
OX_RETURN_ERROR(m_fs.move(src, dest));
|
OX_RETURN_ERROR(m_fs.move(src, dest));
|
||||||
OX_RETURN_ERROR(keel::updatePath(m_kctx, src, dest));
|
OX_RETURN_ERROR(keel::updatePath(m_kctx, src, dest));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user