diff --git a/src/olympic/studio/applib/src/font.cpp b/src/olympic/studio/applib/src/font.cpp index 93dc573e..5c1f9a25 100644 --- a/src/olympic/studio/applib/src/font.cpp +++ b/src/olympic/studio/applib/src/font.cpp @@ -5,7 +5,7 @@ namespace studio::files { -static constexpr ox::Array RobotoMedium_ttfData { +static const ox::Array 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, diff --git a/src/olympic/studio/applib/src/studioui.cpp b/src/olympic/studio/applib/src/studioui.cpp index fe6d96e0..038f1c2e 100644 --- a/src/olympic/studio/applib/src/studioui.cpp +++ b/src/olympic/studio/applib/src/studioui.cpp @@ -19,6 +19,11 @@ namespace studio { +static bool shutdownHandler(turbine::Context &ctx) { + auto sctx = turbine::applicationData(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(font.data()), static_cast(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 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(); diff --git a/src/olympic/studio/applib/src/studioui.hpp b/src/olympic/studio/applib/src/studioui.hpp index 8c8903fb..802f5bff 100644 --- a/src/olympic/studio/applib/src/studioui.hpp +++ b/src/olympic/studio/applib/src/studioui.hpp @@ -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; diff --git a/src/olympic/turbine/include/turbine/context.hpp b/src/olympic/turbine/include/turbine/context.hpp index c59b1619..f8da6152 100644 --- a/src/olympic/turbine/include/turbine/context.hpp +++ b/src/olympic/turbine/include/turbine/context.hpp @@ -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; diff --git a/src/olympic/turbine/include/turbine/turbine.hpp b/src/olympic/turbine/include/turbine/turbine.hpp index b8e2eb02..69d3274c 100644 --- a/src/olympic/turbine/include/turbine/turbine.hpp +++ b/src/olympic/turbine/include/turbine/turbine.hpp @@ -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; + } diff --git a/src/olympic/turbine/src/gba/context.hpp b/src/olympic/turbine/src/gba/context.hpp index 664faefc..5d2ba4a8 100644 --- a/src/olympic/turbine/src/gba/context.hpp +++ b/src/olympic/turbine/src/gba/context.hpp @@ -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); - } - }; } diff --git a/src/olympic/turbine/src/gba/turbine.cpp b/src/olympic/turbine/src/gba/turbine.cpp index 29cd246f..6caf007f 100644 --- a/src/olympic/turbine/src/gba/turbine.cpp +++ b/src/olympic/turbine/src/gba/turbine.cpp @@ -86,4 +86,7 @@ void requestShutdown(Context &ctx) noexcept { ctx.running = false; } +void setShutdownHandler(Context&, ShutdownHandler) noexcept { +} + } diff --git a/src/olympic/turbine/src/glfw/context.hpp b/src/olympic/turbine/src/glfw/context.hpp index 80d0f408..50e29ffc 100644 --- a/src/olympic/turbine/src/glfw/context.hpp +++ b/src/olympic/turbine/src/glfw/context.hpp @@ -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; diff --git a/src/olympic/turbine/src/glfw/turbine.cpp b/src/olympic/turbine/src/glfw/turbine.cpp index d3ccfaf4..3cce688b 100644 --- a/src/olympic/turbine/src/glfw/turbine.cpp +++ b/src/olympic/turbine/src/glfw/turbine.cpp @@ -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(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(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; } }