[studio] Add confirmation dialog for file deletion, move deletion to Project
All checks were successful
Build / build (push) Successful in 3m16s
All checks were successful
Build / build (push) Successful in 3m16s
This commit is contained in:
parent
6924147686
commit
643f95ec80
@ -2,6 +2,7 @@ add_library(
|
|||||||
StudioAppLib
|
StudioAppLib
|
||||||
aboutpopup.cpp
|
aboutpopup.cpp
|
||||||
clawviewer.cpp
|
clawviewer.cpp
|
||||||
|
deleteconfirmation.cpp
|
||||||
filedialogmanager.cpp
|
filedialogmanager.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
newmenu.cpp
|
newmenu.cpp
|
||||||
|
56
src/olympic/studio/applib/src/deleteconfirmation.cpp
Normal file
56
src/olympic/studio/applib/src/deleteconfirmation.cpp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <studio/imguiutil.hpp>
|
||||||
|
|
||||||
|
#include "deleteconfirmation.hpp"
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
DeleteConfirmation::DeleteConfirmation(StudioContext &ctx) noexcept:
|
||||||
|
m_ctx{ctx} {
|
||||||
|
setTitle("Delete Item");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteConfirmation::openPath(ox::StringViewCR path) noexcept {
|
||||||
|
open();
|
||||||
|
m_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteConfirmation::open() noexcept {
|
||||||
|
m_path = "";
|
||||||
|
m_stage = Stage::Opening;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteConfirmation::close() noexcept {
|
||||||
|
m_stage = Stage::Closed;
|
||||||
|
m_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteConfirmation::isOpen() const noexcept {
|
||||||
|
return m_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteConfirmation::draw(StudioContext &ctx) noexcept {
|
||||||
|
switch (m_stage) {
|
||||||
|
case Stage::Closed:
|
||||||
|
break;
|
||||||
|
case Stage::Opening:
|
||||||
|
ImGui::OpenPopup(title().c_str());
|
||||||
|
m_stage = Stage::Open;
|
||||||
|
m_open = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case Stage::Open:
|
||||||
|
drawWindow(ctx.tctx, m_open, [this] {
|
||||||
|
ImGui::Text("Are you sure you want to delete %s?", m_path.c_str());
|
||||||
|
if (ig::PopupControlsOkCancel(m_open) != ig::PopupResponse::None) {
|
||||||
|
deleteFile.emit(m_path);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
src/olympic/studio/applib/src/deleteconfirmation.hpp
Normal file
44
src/olympic/studio/applib/src/deleteconfirmation.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ox/std/string.hpp>
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
#include <studio/popup.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
class DeleteConfirmation final: public Popup {
|
||||||
|
private:
|
||||||
|
enum class Stage {
|
||||||
|
Closed,
|
||||||
|
Opening,
|
||||||
|
Open,
|
||||||
|
};
|
||||||
|
Stage m_stage = Stage::Closed;
|
||||||
|
bool m_open{};
|
||||||
|
ox::String m_path;
|
||||||
|
StudioContext &m_ctx;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ox::Signal<ox::Error(ox::StringViewCR path)> deleteFile;
|
||||||
|
|
||||||
|
DeleteConfirmation(StudioContext &ctx) noexcept;
|
||||||
|
|
||||||
|
void openPath(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
void open() noexcept override;
|
||||||
|
|
||||||
|
void close() noexcept override;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool isOpen() const noexcept override;
|
||||||
|
|
||||||
|
void draw(StudioContext &ctx) noexcept override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -42,9 +42,7 @@ void ProjectExplorer::draw(StudioContext &ctx) noexcept {
|
|||||||
ImGui::BeginChild("ProjectExplorer", ImVec2(300, viewport.y), true);
|
ImGui::BeginChild("ProjectExplorer", ImVec2(300, viewport.y), true);
|
||||||
ImGui::SetNextItemOpen(true);
|
ImGui::SetNextItemOpen(true);
|
||||||
if (m_treeModel) {
|
if (m_treeModel) {
|
||||||
if (m_treeModel->draw(ctx.tctx)) {
|
m_treeModel->draw(ctx.tctx);
|
||||||
oxLogError(refreshProjectTreeModel());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
@ -56,7 +54,7 @@ void ProjectExplorer::setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept {
|
|||||||
ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept {
|
ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept {
|
||||||
OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr));
|
OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr));
|
||||||
setModel(std::move(model));
|
setModel(std::move(model));
|
||||||
return ox::Error(0);
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ class ProjectExplorer: public Widget {
|
|||||||
// slots
|
// slots
|
||||||
ox::Signal<ox::Error(ox::StringViewCR)> fileChosen;
|
ox::Signal<ox::Error(ox::StringViewCR)> fileChosen;
|
||||||
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)> deleteItem;
|
||||||
|
|
||||||
explicit ProjectExplorer(turbine::Context &ctx) noexcept;
|
explicit ProjectExplorer(turbine::Context &ctx) noexcept;
|
||||||
|
|
||||||
|
@ -27,14 +27,13 @@ ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept:
|
|||||||
m_children(std::move(other.m_children)) {
|
m_children(std::move(other.m_children)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||||
bool updated = false;
|
|
||||||
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();
|
drawDirContextMenu();
|
||||||
for (auto const&child : m_children) {
|
for (auto const&child : m_children) {
|
||||||
updated = child->draw(ctx) || updated;
|
child->draw(ctx);
|
||||||
}
|
}
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
} else {
|
} else {
|
||||||
@ -50,14 +49,13 @@ bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
|||||||
}
|
}
|
||||||
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||||
if (ImGui::MenuItem("Delete")) {
|
if (ImGui::MenuItem("Delete")) {
|
||||||
updated = m_explorer.romFs()->remove(path).errCode == 0 || updated;
|
m_explorer.deleteItem.emit(path);
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept {
|
void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept {
|
||||||
@ -69,6 +67,9 @@ void ProjectTreeModel::drawDirContextMenu() const noexcept {
|
|||||||
if (ImGui::MenuItem("Add Item")) {
|
if (ImGui::MenuItem("Add Item")) {
|
||||||
m_explorer.addItem.emit(fullPath());
|
m_explorer.addItem.emit(fullPath());
|
||||||
}
|
}
|
||||||
|
if (ImGui::MenuItem("Add Directory")) {
|
||||||
|
m_explorer.addDir.emit(fullPath());
|
||||||
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,7 @@ class ProjectTreeModel {
|
|||||||
|
|
||||||
ProjectTreeModel(ProjectTreeModel &&other) noexcept;
|
ProjectTreeModel(ProjectTreeModel &&other) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
void draw(turbine::Context &ctx) const noexcept;
|
||||||
bool draw(turbine::Context &ctx) const noexcept;
|
|
||||||
|
|
||||||
void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept;
|
void setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept;
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
|||||||
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_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||||
|
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
||||||
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;
|
||||||
@ -348,6 +349,11 @@ ox::Error StudioUI::addFile(ox::StringViewCR path) noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Error StudioUI::deleteFile(ox::StringViewCR path) noexcept {
|
||||||
|
m_deleteConfirmation.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);
|
||||||
@ -362,8 +368,8 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|||||||
OX_RETURN_ERROR(
|
OX_RETURN_ERROR(
|
||||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||||
.moveTo(m_project));
|
.moveTo(m_project));
|
||||||
auto const sctx = applicationData<StudioContext>(m_tctx);
|
m_sctx.project = m_project.get();
|
||||||
sctx->project = m_project.get();
|
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||||
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_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);
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
#include <studio/module.hpp>
|
#include <studio/module.hpp>
|
||||||
#include <studio/project.hpp>
|
#include <studio/project.hpp>
|
||||||
#include <studio/task.hpp>
|
#include <studio/task.hpp>
|
||||||
|
|
||||||
#include "aboutpopup.hpp"
|
#include "aboutpopup.hpp"
|
||||||
|
#include "deleteconfirmation.hpp"
|
||||||
#include "newmenu.hpp"
|
#include "newmenu.hpp"
|
||||||
#include "newproject.hpp"
|
#include "newproject.hpp"
|
||||||
#include "projectexplorer.hpp"
|
#include "projectexplorer.hpp"
|
||||||
@ -38,12 +40,14 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
BaseEditor *m_activeEditor = nullptr;
|
BaseEditor *m_activeEditor = nullptr;
|
||||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||||
NewMenu m_newMenu;
|
NewMenu m_newMenu;
|
||||||
|
DeleteConfirmation m_deleteConfirmation{m_sctx};
|
||||||
NewProject m_newProject;
|
NewProject m_newProject;
|
||||||
AboutPopup m_aboutPopup;
|
AboutPopup m_aboutPopup;
|
||||||
ox::Array<Popup*, 3> const m_popups = {
|
ox::Array<Popup*, 4> const m_popups = {
|
||||||
&m_newMenu,
|
&m_newMenu,
|
||||||
&m_newProject,
|
&m_newProject,
|
||||||
&m_aboutPopup
|
&m_aboutPopup,
|
||||||
|
&m_deleteConfirmation,
|
||||||
};
|
};
|
||||||
bool m_showProjectExplorer = true;
|
bool m_showProjectExplorer = true;
|
||||||
|
|
||||||
@ -85,6 +89,8 @@ class StudioUI: public ox::SignalHandler {
|
|||||||
|
|
||||||
ox::Error addFile(ox::StringViewCR path) noexcept;
|
ox::Error addFile(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
|
ox::Error deleteFile(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;
|
||||||
|
@ -45,7 +45,7 @@ constexpr ox::StringView parentDir(ox::StringView path) noexcept {
|
|||||||
return substr(path, 0, extStart);
|
return substr(path, 0, extStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Project {
|
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_ctx;
|
||||||
@ -91,6 +91,8 @@ class Project {
|
|||||||
|
|
||||||
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
ox::Result<ox::FileStat> stat(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
|
ox::Error deleteItem(ox::StringViewCR path) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool exists(ox::StringViewCR path) const noexcept;
|
bool exists(ox::StringViewCR path) const noexcept;
|
||||||
|
|
||||||
|
@ -69,6 +69,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::deleteItem(ox::StringViewCR path) noexcept {
|
||||||
|
auto const err = m_fs.remove(path);
|
||||||
|
if (!err) {
|
||||||
|
fileDeleted.emit(path);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
bool Project::exists(ox::StringViewCR path) const noexcept {
|
bool Project::exists(ox::StringViewCR path) const noexcept {
|
||||||
return m_fs.stat(path).error == 0;
|
return m_fs.stat(path).error == 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user