[turbine,studio] Make Studio confirm with user before closing app if any unsaved changes
All checks were successful
Build / build (push) Successful in 1m15s
All checks were successful
Build / build (push) Successful in 1m15s
This commit is contained in:
parent
4770bb6a93
commit
e5dd448fe7
@ -5,7 +5,7 @@
|
||||
|
||||
namespace studio::files {
|
||||
|
||||
static constexpr ox::Array<uint8_t, 162588> RobotoMedium_ttfData {
|
||||
static const ox::Array<uint8_t, 162588> RobotoMedium_ttfData {
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x04,
|
||||
0x00, 0x10, 0x47, 0x50, 0x4f, 0x53, 0x7d, 0xaa, 0x71, 0x8c,
|
||||
0x00, 0x02, 0x08, 0xa8, 0x00, 0x00, 0x59, 0x0c, 0x47, 0x53,
|
||||
|
@ -19,6 +19,11 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
static bool shutdownHandler(turbine::Context &ctx) {
|
||||
auto sctx = turbine::applicationData<StudioContext>(ctx);
|
||||
return sctx->ui.handleShutdown();
|
||||
}
|
||||
|
||||
void navigateTo(StudioContext &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
||||
ctx.ui.navigateTo(std::move(filePath), std::move(navArgs));
|
||||
}
|
||||
@ -72,6 +77,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
io.Fonts->AddFontFromMemoryTTF(const_cast<uint8_t*>(font.data()), static_cast<int>(font.size()), 13, &fontCfg);
|
||||
}
|
||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||
@ -83,6 +89,7 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringParam projectDataDir) noexce
|
||||
m_renameFile.moveFile.connect(this, &StudioUI::queueFileMove);
|
||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
m_closeAppConfirm.response.connect(this, &StudioUI::handleCloseAppResponse);
|
||||
m_closeFileConfirm.response.connect(this, &StudioUI::handleCloseFileResponse);
|
||||
loadModules();
|
||||
// open project and files
|
||||
@ -158,6 +165,7 @@ void StudioUI::draw() noexcept {
|
||||
for (auto const p : m_popups) {
|
||||
p->draw(m_sctx);
|
||||
}
|
||||
m_closeAppConfirm.draw(m_sctx);
|
||||
m_closeFileConfirm.draw(m_sctx);
|
||||
m_copyFilePopup.draw(m_sctx);
|
||||
}
|
||||
@ -167,6 +175,16 @@ void StudioUI::draw() noexcept {
|
||||
procFileMoves();
|
||||
}
|
||||
|
||||
bool StudioUI::handleShutdown() noexcept {
|
||||
auto const out = ox::all_of(m_editors.begin(), m_editors.end(), [](ox::UPtr<BaseEditor> const &e) {
|
||||
return !e->unsavedChanges();
|
||||
});
|
||||
if (!out) {
|
||||
m_closeAppConfirm.open();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void StudioUI::drawMenu() noexcept {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
@ -267,6 +285,7 @@ void StudioUI::drawTabs() noexcept {
|
||||
}
|
||||
if (!open) {
|
||||
if (e->unsavedChanges()) {
|
||||
m_closeAppConfirm.open();
|
||||
m_closeFileConfirm.open();
|
||||
++it;
|
||||
} else {
|
||||
@ -384,6 +403,7 @@ void StudioUI::handleKeyInput() noexcept {
|
||||
} else if (ImGui::IsKeyPressed(ImGuiKey_W)) {
|
||||
if (m_activeEditor) {
|
||||
if (m_activeEditor->unsavedChanges()) {
|
||||
m_closeAppConfirm.open();
|
||||
m_closeFileConfirm.open();
|
||||
} else {
|
||||
oxLogError(closeCurrentFile());
|
||||
@ -548,6 +568,13 @@ ox::Error StudioUI::makeCopyDlg(ox::StringViewCR path) noexcept {
|
||||
return m_copyFilePopup.open(path);
|
||||
}
|
||||
|
||||
ox::Error StudioUI::handleCloseAppResponse(ig::PopupResponse const response) noexcept {
|
||||
if (response == ig::PopupResponse::OK && m_activeEditor) {
|
||||
turbine::requestShutdown(m_tctx);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error StudioUI::handleCloseFileResponse(ig::PopupResponse const response) noexcept {
|
||||
if (response == ig::PopupResponse::OK && m_activeEditor) {
|
||||
return closeCurrentFile();
|
||||
|
@ -49,6 +49,10 @@ class StudioUI: public ox::SignalHandler {
|
||||
DeleteConfirmation m_deleteConfirmation;
|
||||
NewDir m_newDirDialog;
|
||||
ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
|
||||
ig::QuestionPopup m_closeAppConfirm{
|
||||
"Close Application?",
|
||||
"There are files with unsaved changes. Close?"
|
||||
};
|
||||
MakeCopyPopup m_copyFilePopup;
|
||||
RenameFile m_renameFile;
|
||||
NewProject m_newProject;
|
||||
@ -80,6 +84,8 @@ class StudioUI: public ox::SignalHandler {
|
||||
return m_project.get();
|
||||
}
|
||||
|
||||
bool handleShutdown() noexcept;
|
||||
|
||||
protected:
|
||||
void draw() noexcept;
|
||||
|
||||
@ -128,6 +134,8 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
ox::Error makeCopyDlg(ox::StringViewCR path) noexcept;
|
||||
|
||||
ox::Error handleCloseAppResponse(ig::PopupResponse response) noexcept;
|
||||
|
||||
ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;
|
||||
|
||||
ox::Error closeCurrentFile() noexcept;
|
||||
|
@ -19,8 +19,6 @@ class Context;
|
||||
|
||||
void safeDelete(Context *p);
|
||||
|
||||
void shutdown(Context &ctx) noexcept;
|
||||
|
||||
keel::Context const&keelCtx(Context const&ctx) noexcept;
|
||||
|
||||
keel::Context &keelCtx(Context &ctx) noexcept;
|
||||
|
@ -29,4 +29,8 @@ TimeMs ticksMs(Context const&ctx) noexcept;
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept;
|
||||
|
||||
using ShutdownHandler = bool (*)(Context&);
|
||||
|
||||
void setShutdownHandler(Context &ctx, ShutdownHandler handler) noexcept;
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
namespace turbine {
|
||||
|
||||
class Context {
|
||||
class Context final {
|
||||
public:
|
||||
UpdateHandler updateHandler = [](Context&) -> int {return 0;};
|
||||
keel::Context keelCtx;
|
||||
@ -27,10 +27,6 @@ class Context {
|
||||
Context(Context const&other) noexcept = delete;
|
||||
Context(Context const&&other) noexcept = delete;
|
||||
|
||||
virtual inline ~Context() noexcept {
|
||||
shutdown(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -86,4 +86,7 @@ void requestShutdown(Context &ctx) noexcept {
|
||||
ctx.running = false;
|
||||
}
|
||||
|
||||
void setShutdownHandler(Context&, ShutdownHandler) noexcept {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class Context {
|
||||
uint64_t keysDown = 0;
|
||||
uint64_t prevFpsCheckTime = 0;
|
||||
uint64_t draws = 0;
|
||||
bool running{};
|
||||
ShutdownHandler shutdownHandler{};
|
||||
|
||||
Context() noexcept = default;
|
||||
|
||||
|
@ -68,9 +68,21 @@ static void tickFps(Context &ctx, uint64_t const nowMs) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown(Context &ctx) noexcept {
|
||||
if (ctx.window) {
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
#endif
|
||||
glfwDestroyWindow(ctx.window);
|
||||
ctx.window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error run(Context &ctx) noexcept {
|
||||
uint64_t sleepTime = 0;
|
||||
while (!glfwWindowShouldClose(ctx.window)) {
|
||||
ctx.running = true;
|
||||
while (ctx.running) {
|
||||
ctx.refreshWithinMs = 10 * 1000; // refresh within 10 seconds
|
||||
glfwPollEvents();
|
||||
auto const ticks = ticksMs(ctx);
|
||||
@ -92,22 +104,17 @@ ox::Error run(Context &ctx) noexcept {
|
||||
if (realSleepTime && ctx.mandatoryRefreshPeriodEnd <= ticks) {
|
||||
glfwWaitEventsTimeout(static_cast<double>(realSleepTime) / 1000);
|
||||
}
|
||||
if (glfwWindowShouldClose(ctx.window)) {
|
||||
if (ctx.shutdownHandler(ctx)) {
|
||||
break;
|
||||
}
|
||||
glfwSetWindowShouldClose(ctx.window, false);
|
||||
}
|
||||
}
|
||||
shutdown(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
void shutdown(Context &ctx) noexcept {
|
||||
if (ctx.window) {
|
||||
#if TURBINE_USE_IMGUI
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
#endif
|
||||
glfwDestroyWindow(ctx.window);
|
||||
ctx.window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TimeMs ticksMs(Context const&ctx) noexcept {
|
||||
using namespace std::chrono;
|
||||
auto const now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
@ -119,7 +126,11 @@ bool buttonDown(Context const&ctx, Key const key) noexcept {
|
||||
}
|
||||
|
||||
void requestShutdown(Context &ctx) noexcept {
|
||||
glfwSetWindowShouldClose(ctx.window, true);
|
||||
ctx.running = false;
|
||||
}
|
||||
|
||||
void setShutdownHandler(Context &ctx, ShutdownHandler const handler) noexcept {
|
||||
ctx.shutdownHandler = handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user