From fd61e757f0cd084ea813d0e0b51be4d86a4d2e76 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Tue, 23 Apr 2024 21:01:58 +0200 Subject: [PATCH] impr: Make unsaved changes popup behave more like in other applications --- .../include/content/global_actions.hpp | 4 +- .../content/popups/popup_unsaved_changes.hpp | 20 +++++--- plugins/builtin/romfs/lang/de_DE.json | 2 +- plugins/builtin/romfs/lang/en_US.json | 2 +- plugins/builtin/romfs/lang/ko_KR.json | 2 +- plugins/builtin/romfs/lang/zh_CN.json | 2 +- plugins/builtin/source/content/events.cpp | 34 +++++++++---- .../builtin/source/content/global_actions.cpp | 16 +++++-- plugins/ui/include/popups/popup_question.hpp | 48 +++++++++++++++++++ 9 files changed, 103 insertions(+), 27 deletions(-) diff --git a/plugins/builtin/include/content/global_actions.hpp b/plugins/builtin/include/content/global_actions.hpp index 6e511c66e..a1bc4e917 100644 --- a/plugins/builtin/include/content/global_actions.hpp +++ b/plugins/builtin/include/content/global_actions.hpp @@ -3,7 +3,7 @@ namespace hex::plugin::builtin { void openProject(); - void saveProject(); - void saveProjectAs(); + bool saveProject(); + bool saveProjectAs(); } diff --git a/plugins/builtin/include/content/popups/popup_unsaved_changes.hpp b/plugins/builtin/include/content/popups/popup_unsaved_changes.hpp index 175064c8e..4467ca7bd 100644 --- a/plugins/builtin/include/content/popups/popup_unsaved_changes.hpp +++ b/plugins/builtin/include/content/popups/popup_unsaved_changes.hpp @@ -11,10 +11,10 @@ namespace hex::plugin::builtin { class PopupUnsavedChanges : public Popup { public: - PopupUnsavedChanges(std::string message, std::function yesFunction, std::function noFunction) + PopupUnsavedChanges(std::string message, std::function yesFunction, std::function noFunction, std::function cancelFunction) : hex::Popup("hex.ui.common.question", false), m_message(std::move(message)), - m_yesFunction(std::move(yesFunction)), m_noFunction(std::move(noFunction)) { } + m_yesFunction(std::move(yesFunction)), m_noFunction(std::move(noFunction)), m_cancelFunction(std::move(cancelFunction)) { } void drawContent() override { ImGuiExt::TextFormattedWrapped("{}", m_message.c_str()); @@ -33,17 +33,23 @@ namespace hex::plugin::builtin { ImGui::Separator(); auto width = ImGui::GetWindowWidth(); - ImGui::SetCursorPosX(width / 9); - if (ImGui::Button("hex.ui.common.yes"_lang, ImVec2(width / 3, 0))) { + ImGui::SetCursorPosX((width / 10) * 0.5); + if (ImGui::Button("hex.ui.common.yes"_lang, ImVec2(width / 4, 0))) { m_yesFunction(); this->close(); } ImGui::SameLine(); - ImGui::SetCursorPosX(width / 9 * 5); - if (ImGui::Button("hex.ui.common.no"_lang, ImVec2(width / 3, 0)) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { + ImGui::SetCursorPosX((width / 10) * 3.75); + if (ImGui::Button("hex.ui.common.no"_lang, ImVec2(width / 4, 0)) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { m_noFunction(); this->close(); } + ImGui::SameLine(); + ImGui::SetCursorPosX((width / 10) * 7); + if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 4, 0)) || ImGui::IsKeyPressed(ImGuiKey_Escape)) { + m_cancelFunction(); + this->close(); + } ImGui::SetWindowPos((ImHexApi::System::getMainWindowSize() - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); } @@ -62,7 +68,7 @@ namespace hex::plugin::builtin { private: std::string m_message; - std::function m_yesFunction, m_noFunction; + std::function m_yesFunction, m_noFunction, m_cancelFunction; }; } \ No newline at end of file diff --git a/plugins/builtin/romfs/lang/de_DE.json b/plugins/builtin/romfs/lang/de_DE.json index be5bcaea3..d1b8612ba 100644 --- a/plugins/builtin/romfs/lang/de_DE.json +++ b/plugins/builtin/romfs/lang/de_DE.json @@ -360,7 +360,7 @@ "hex.builtin.oobe.tutorial_question": "Da dies das erste mal ist das du ImHex verwendest, möchtest du das Tutorial starten?", "hex.builtin.popup.blocking_task.desc": "Ein Task ist immer noch im Hintergrund am laufen.", "hex.builtin.popup.blocking_task.title": "Laufender Task", - "hex.builtin.popup.close_provider.desc": "Es wurden ungespeicherte Änderungen an einem oder mehreren Provider vorgenommen.\nBist du sicher, dass du diese schliessen willst?", + "hex.builtin.popup.close_provider.desc": "Einige Änderungen wurden noch nicht in einem Projekt gespeichert\nMöchtest du diese vor dem schliessen sichern?", "hex.builtin.popup.close_provider.title": "Provider schliessen?", "hex.builtin.popup.create_workspace.desc": "Gib einen Namen für den neuen Arbeitsbereich ein", "hex.builtin.popup.create_workspace.title": "Arbeitsbereich erstellen", diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index dd837ae3a..e2062f038 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -354,7 +354,7 @@ "hex.builtin.nodes.visualizer.image_rgba.header": "RGBA8 Image Visualizer", "hex.builtin.nodes.visualizer.layered_dist": "Layered Distribution", "hex.builtin.nodes.visualizer.layered_dist.header": "Layered Distribution", - "hex.builtin.popup.close_provider.desc": "There are unsaved changes made to one or more Providers\nthat haven't been saved to a Project yet.\n\nAre you sure you want to close them?", + "hex.builtin.popup.close_provider.desc": "Some changes haven't been saved to a Project yet.\n\nDo you want to save them before closing?", "hex.builtin.popup.close_provider.title": "Close Provider?", "hex.builtin.popup.docs_question.title": "Documentation query", "hex.builtin.popup.docs_question.no_answer": "The documentation didn't have an answer for this question", diff --git a/plugins/builtin/romfs/lang/ko_KR.json b/plugins/builtin/romfs/lang/ko_KR.json index e2c8a76a7..f1abae6b6 100644 --- a/plugins/builtin/romfs/lang/ko_KR.json +++ b/plugins/builtin/romfs/lang/ko_KR.json @@ -359,7 +359,7 @@ "hex.builtin.oobe.tutorial_question": "", "hex.builtin.popup.blocking_task.desc": "현재 작업을 실행 중입니다.", "hex.builtin.popup.blocking_task.title": "작업 실행 중", - "hex.builtin.popup.close_provider.desc": "이 공급자에 대해 아직 프로젝트에\n저장되지 않은 변경 사항이 있습니다.\n\n정말 종료하시겠습니까?", + "hex.builtin.popup.close_provider.desc": "", "hex.builtin.popup.close_provider.title": "공급자를 종료하시겠습니까?", "hex.builtin.popup.create_workspace.desc": "", "hex.builtin.popup.create_workspace.title": "", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index cd6407fd9..130300c2d 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -354,7 +354,7 @@ "hex.builtin.nodes.visualizer.image_rgba.header": "RGBA8 图像可视化", "hex.builtin.nodes.visualizer.layered_dist": "分层布局", "hex.builtin.nodes.visualizer.layered_dist.header": "分层布局", - "hex.builtin.popup.close_provider.desc": "对一个或多个提供者进行了未保存的更改\n尚未保存到项目中。\n\n并且您确定要关闭它们吗?", + "hex.builtin.popup.close_provider.desc": "", "hex.builtin.popup.close_provider.title": "关闭提供者?", "hex.builtin.popup.docs_question.title": "查找文档", "hex.builtin.popup.docs_question.no_answer": "文档中没有这个问题的答案", diff --git a/plugins/builtin/source/content/events.cpp b/plugins/builtin/source/content/events.cpp index 380d2d7b5..05e942b7e 100644 --- a/plugins/builtin/source/content/events.cpp +++ b/plugins/builtin/source/content/events.cpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -80,17 +81,30 @@ namespace hex::plugin::builtin { if (provider->isDirty()) { *shouldClose = false; PopupUnsavedChanges::open("hex.builtin.popup.close_provider.desc"_lang, - []{ - for (const auto &provider : ImHexApi::Provider::impl::getClosingProviders()) - ImHexApi::Provider::remove(provider, true); + []{ + const bool projectSaved = ProjectFile::hasPath() ? saveProject() : saveProjectAs(); + if (projectSaved) { + for (const auto &provider : ImHexApi::Provider::impl::getClosingProviders()) + ImHexApi::Provider::remove(provider, true); - if (imhexClosing) - ImHexApi::System::closeImHex(true); - }, - [] { - ImHexApi::Provider::impl::resetClosingProvider(); - imhexClosing = false; - } + if (imhexClosing) + ImHexApi::System::closeImHex(true); + } else { + ImHexApi::Provider::impl::resetClosingProvider(); + imhexClosing = false; + } + }, + [] { + for (const auto &provider : ImHexApi::Provider::impl::getClosingProviders()) + ImHexApi::Provider::remove(provider, true); + + if (imhexClosing) + ImHexApi::System::closeImHex(true); + }, + [] { + ImHexApi::Provider::impl::resetClosingProvider(); + imhexClosing = false; + } ); } }); diff --git a/plugins/builtin/source/content/global_actions.cpp b/plugins/builtin/source/content/global_actions.cpp index 90bb8d35b..c9b3f69a6 100644 --- a/plugins/builtin/source/content/global_actions.cpp +++ b/plugins/builtin/source/content/global_actions.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -18,18 +19,25 @@ namespace hex::plugin::builtin { }); } - void saveProject() { - if (ImHexApi::Provider::isValid() && ProjectFile::hasPath()) { + bool saveProject() { + if (!ImHexApi::Provider::isValid()) + return false; + + if (ProjectFile::hasPath()) { if (!ProjectFile::store()) { ui::ToastError::open("hex.builtin.popup.error.project.save"_lang); + return false; } else { log::debug("Project saved"); + return true; } + } else { + return saveProjectAs(); } } - void saveProjectAs() { - fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} }, + bool saveProjectAs() { + return fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} }, [](std::fs::path path) { if (path.extension() != ".hexproj") { path.replace_extension(".hexproj"); diff --git a/plugins/ui/include/popups/popup_question.hpp b/plugins/ui/include/popups/popup_question.hpp index 6884591e0..f5795cbe9 100644 --- a/plugins/ui/include/popups/popup_question.hpp +++ b/plugins/ui/include/popups/popup_question.hpp @@ -54,4 +54,52 @@ namespace hex::ui { std::function m_yesFunction, m_noFunction; }; + class PopupCancelableQuestion : public Popup { + public: + PopupCancelableQuestion(std::string message, std::function yesFunction, std::function noFunction) + : hex::Popup("hex.ui.common.question", false), + m_message(std::move(message)), + m_yesFunction(std::move(yesFunction)), m_noFunction(std::move(noFunction)) { } + + void drawContent() override { + ImGuiExt::TextFormattedWrapped("{}", m_message.c_str()); + ImGui::NewLine(); + ImGui::Separator(); + + auto width = ImGui::GetWindowWidth(); + ImGui::SetCursorPosX(width / 9); + if (ImGui::Button("hex.ui.common.yes"_lang, ImVec2(width / 3, 0))) { + m_yesFunction(); + this->close(); + } + ImGui::SameLine(); + if (ImGui::Button("hex.ui.common.no"_lang, ImVec2(width / 3, 0))) { + m_noFunction(); + this->close(); + } + ImGui::SameLine(); + if (ImGui::Button("hex.ui.common.cancel"_lang, ImVec2(width / 3, 0))) { + this->close(); + } + + ImGui::SetWindowPos((ImHexApi::System::getMainWindowSize() - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing); + } + + [[nodiscard]] ImGuiWindowFlags getFlags() const override { + return ImGuiWindowFlags_AlwaysAutoResize; + } + + [[nodiscard]] ImVec2 getMinSize() const override { + return scaled({ 400, 100 }); + } + + [[nodiscard]] ImVec2 getMaxSize() const override { + return scaled({ 600, 300 }); + } + + private: + std::string m_message; + std::function m_yesFunction, m_noFunction; + }; + } \ No newline at end of file