[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
|
||||
aboutpopup.cpp
|
||||
clawviewer.cpp
|
||||
deleteconfirmation.cpp
|
||||
filedialogmanager.cpp
|
||||
main.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::SetNextItemOpen(true);
|
||||
if (m_treeModel) {
|
||||
if (m_treeModel->draw(ctx.tctx)) {
|
||||
oxLogError(refreshProjectTreeModel());
|
||||
}
|
||||
m_treeModel->draw(ctx.tctx);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
@ -56,7 +54,7 @@ void ProjectExplorer::setModel(ox::UPtr<ProjectTreeModel> &&model) noexcept {
|
||||
ox::Error ProjectExplorer::refreshProjectTreeModel(ox::StringViewCR) noexcept {
|
||||
OX_REQUIRE_M(model, buildProjectTreeModel(*this, "Project", "/", nullptr));
|
||||
setModel(std::move(model));
|
||||
return ox::Error(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ class ProjectExplorer: public Widget {
|
||||
// slots
|
||||
ox::Signal<ox::Error(ox::StringViewCR)> fileChosen;
|
||||
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;
|
||||
|
||||
|
@ -27,14 +27,13 @@ ProjectTreeModel::ProjectTreeModel(ProjectTreeModel &&other) noexcept:
|
||||
m_children(std::move(other.m_children)) {
|
||||
}
|
||||
|
||||
bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||
bool updated = false;
|
||||
void ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||
constexpr auto dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
if (!m_children.empty()) {
|
||||
if (ImGui::TreeNodeEx(m_name.c_str(), dirFlags)) {
|
||||
drawDirContextMenu();
|
||||
for (auto const&child : m_children) {
|
||||
updated = child->draw(ctx) || updated;
|
||||
child->draw(ctx);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
} else {
|
||||
@ -50,14 +49,13 @@ bool ProjectTreeModel::draw(turbine::Context &ctx) const noexcept {
|
||||
}
|
||||
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
updated = m_explorer.romFs()->remove(path).errCode == 0 || updated;
|
||||
m_explorer.deleteItem.emit(path);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
void ProjectTreeModel::setChildren(ox::Vector<ox::UPtr<ProjectTreeModel>> children) noexcept {
|
||||
@ -69,6 +67,9 @@ void ProjectTreeModel::drawDirContextMenu() const noexcept {
|
||||
if (ImGui::MenuItem("Add Item")) {
|
||||
m_explorer.addItem.emit(fullPath());
|
||||
}
|
||||
if (ImGui::MenuItem("Add Directory")) {
|
||||
m_explorer.addDir.emit(fullPath());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,7 @@ class ProjectTreeModel {
|
||||
|
||||
ProjectTreeModel(ProjectTreeModel &&other) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool draw(turbine::Context &ctx) const noexcept;
|
||||
void draw(turbine::Context &ctx) const 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);
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||
m_projectExplorer.deleteItem.connect(this, &StudioUI::deleteFile);
|
||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
@ -348,6 +349,11 @@ ox::Error StudioUI::addFile(ox::StringViewCR path) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::deleteFile(ox::StringViewCR path) noexcept {
|
||||
m_deleteConfirmation.openPath(path);
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::createOpenProject(ox::StringViewCR path) noexcept {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(toStdStringView(path), ec);
|
||||
@ -362,8 +368,8 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
||||
OX_RETURN_ERROR(
|
||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||
.moveTo(m_project));
|
||||
auto const sctx = applicationData<StudioContext>(m_tctx);
|
||||
sctx->project = m_project.get();
|
||||
m_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()));
|
||||
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
|
@ -12,7 +12,9 @@
|
||||
#include <studio/module.hpp>
|
||||
#include <studio/project.hpp>
|
||||
#include <studio/task.hpp>
|
||||
|
||||
#include "aboutpopup.hpp"
|
||||
#include "deleteconfirmation.hpp"
|
||||
#include "newmenu.hpp"
|
||||
#include "newproject.hpp"
|
||||
#include "projectexplorer.hpp"
|
||||
@ -38,12 +40,14 @@ class StudioUI: public ox::SignalHandler {
|
||||
BaseEditor *m_activeEditor = nullptr;
|
||||
BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||
NewMenu m_newMenu;
|
||||
DeleteConfirmation m_deleteConfirmation{m_sctx};
|
||||
NewProject m_newProject;
|
||||
AboutPopup m_aboutPopup;
|
||||
ox::Array<Popup*, 3> const m_popups = {
|
||||
ox::Array<Popup*, 4> const m_popups = {
|
||||
&m_newMenu,
|
||||
&m_newProject,
|
||||
&m_aboutPopup
|
||||
&m_aboutPopup,
|
||||
&m_deleteConfirmation,
|
||||
};
|
||||
bool m_showProjectExplorer = true;
|
||||
|
||||
@ -85,6 +89,8 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
ox::Error addFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error deleteFile(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error createOpenProject(ox::StringViewCR 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);
|
||||
}
|
||||
|
||||
class Project {
|
||||
class Project: public ox::SignalHandler {
|
||||
private:
|
||||
ox::SmallMap<ox::String, ox::Optional<ox::ClawFormat>> m_typeFmt;
|
||||
keel::Context &m_ctx;
|
||||
@ -91,6 +91,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;
|
||||
|
||||
|
@ -69,6 +69,14 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
||||
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 {
|
||||
return m_fs.stat(path).error == 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user