diff --git a/lib/libimhex/include/hex/api/task_manager.hpp b/lib/libimhex/include/hex/api/task_manager.hpp index dfc9098d6..ed8ad2037 100644 --- a/lib/libimhex/include/hex/api/task_manager.hpp +++ b/lib/libimhex/include/hex/api/task_manager.hpp @@ -22,7 +22,7 @@ namespace hex { class Task { public: Task() = default; - Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function); + Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function); Task(const Task&) = delete; Task(Task &&other) noexcept; @@ -57,6 +57,7 @@ namespace hex { void setInterruptCallback(std::function callback); [[nodiscard]] bool isBackgroundTask() const; + [[nodiscard]] bool isBlocking() const; [[nodiscard]] bool isFinished() const; [[nodiscard]] bool hadException() const; [[nodiscard]] bool wasInterrupted() const; @@ -84,6 +85,7 @@ namespace hex { std::atomic m_shouldInterrupt = false; std::atomic m_background = true; + std::atomic m_blocking = false; std::atomic m_interrupted = false; std::atomic m_finished = false; @@ -162,6 +164,24 @@ namespace hex { */ static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function); + /** + * @brief Creates a new asynchronous task that shows a blocking modal window + * @param unlocalizedName Name of the task + * @param maxValue Maximum value of the task + * @param function Function to be executed + * @return A TaskHolder holding a weak reference to the task + */ + static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); + + /** + * @brief Creates a new asynchronous task that shows a blocking modal window + * @param unlocalizedName Name of the task + * @param maxValue Maximum value of the task + * @param function Function to be executed + * @return A TaskHolder holding a weak reference to the task + */ + static TaskHolder createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); + /** * @brief Creates a new synchronous task that will execute the given function at the start of the next frame * @param function Function to be executed @@ -202,12 +222,13 @@ namespace hex { static size_t getRunningTaskCount(); static size_t getRunningBackgroundTaskCount(); + static size_t getRunningBlockingTaskCount(); static const std::list>& getRunningTasks(); static void runDeferredCalls(); private: - static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function); + static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function); }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index 91b2c44c8..ba2e644d0 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -184,7 +184,7 @@ namespace ImGuiExt { void StyleCustomColorsLight(); void StyleCustomColorsClassic(); - void SmallProgressBar(float fraction, float yOffset = 0.0F); + void ProgressBar(float fraction, ImVec2 size_value = ImVec2(0, 0), float yOffset = 0.0F); inline void TextFormatted(std::string_view fmt, auto &&...args) { if constexpr (sizeof...(args) == 0) { diff --git a/lib/libimhex/source/api/task_manager.cpp b/lib/libimhex/source/api/task_manager.cpp index 717f920cd..c6c411036 100644 --- a/lib/libimhex/source/api/task_manager.cpp +++ b/lib/libimhex/source/api/task_manager.cpp @@ -63,8 +63,11 @@ namespace hex { } - Task::Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function) - : m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { } + Task::Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function) + : m_unlocalizedName(std::move(unlocalizedName)), + m_maxValue(maxValue), + m_function(std::move(function)), + m_background(background), m_blocking(blocking) { } Task::Task(hex::Task &&other) noexcept { { @@ -133,6 +136,11 @@ namespace hex { return m_background; } + bool Task::isBlocking() const { + return m_blocking; + } + + bool Task::isFinished() const { return m_finished; } @@ -327,11 +335,11 @@ namespace hex { s_tasksFinishedCallbacks.clear(); } - TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function) { + TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function function) { std::scoped_lock lock(s_queueMutex); // Construct new task - auto task = std::make_shared(std::move(unlocalizedName), maxValue, background, std::move(function)); + auto task = std::make_shared(std::move(unlocalizedName), maxValue, background, blocking, std::move(function)); s_tasks.emplace_back(task); @@ -346,12 +354,12 @@ namespace hex { TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating task {}", unlocalizedName.get()); - return createTask(std::move(unlocalizedName), maxValue, false, std::move(function)); + return createTask(std::move(unlocalizedName), maxValue, false, false, std::move(function)); } TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { log::debug("Creating task {}", unlocalizedName.get()); - return createTask(std::move(unlocalizedName), maxValue, false, + return createTask(std::move(unlocalizedName), maxValue, false, false, [function = std::move(function)](Task&) { function(); } @@ -360,12 +368,26 @@ namespace hex { TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function) { log::debug("Creating background task {}", unlocalizedName.get()); - return createTask(std::move(unlocalizedName), 0, true, std::move(function)); + return createTask(std::move(unlocalizedName), 0, true, false, std::move(function)); } TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function) { log::debug("Creating background task {}", unlocalizedName.get()); - return createTask(std::move(unlocalizedName), 0, true, + return createTask(std::move(unlocalizedName), 0, true, false, + [function = std::move(function)](Task&) { + function(); + } + ); + } + + TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { + log::debug("Creating blocking task {}", unlocalizedName.get()); + return createTask(std::move(unlocalizedName), maxValue, true, true, std::move(function)); + } + + TaskHolder TaskManager::createBlockingTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { + log::debug("Creating blocking task {}", unlocalizedName.get()); + return createTask(std::move(unlocalizedName), maxValue, true, true, [function = std::move(function)](Task&) { function(); } @@ -414,6 +436,14 @@ namespace hex { }); } + size_t TaskManager::getRunningBlockingTaskCount() { + std::scoped_lock lock(s_queueMutex); + + return std::ranges::count_if(s_tasks, [](const auto &task){ + return task->isBlocking(); + }); + } + void TaskManager::doLater(const std::function &function) { std::scoped_lock lock(s_deferredCallsMutex); diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 524a3130b..50fc6a64a 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -941,7 +941,7 @@ namespace ImGuiExt { return result; } - void SmallProgressBar(float fraction, float yOffset) { + void ProgressBar(float fraction, ImVec2 size_value, float yOffset) { ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return; @@ -950,7 +950,7 @@ namespace ImGuiExt { const ImGuiStyle &style = g.Style; ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset); - ImVec2 size = CalcItemSize(ImVec2(100, 5) * hex::ImHexApi::System::getGlobalScale(), 100, g.FontSize + style.FramePadding.y * 2.0F); + ImVec2 size = CalcItemSize(size_value, ImGui::GetContentRegionAvail().x, g.FontSize + style.FramePadding.y * 2.0F); ImRect bb(pos, pos + size); ItemSize(size, 0); if (!ItemAdd(bb, 0)) diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 4e9a7c63b..f6f336202 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -393,6 +393,7 @@ "hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks", "hex.builtin.popup.crash_recover.title": "Crash recovery", "hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and advert a crash", + "hex.builtin.popup.foreground_task.title": "Please Wait...", "hex.builtin.popup.blocking_task.title": "Running Task", "hex.builtin.popup.blocking_task.desc": "A task is currently executing.", "hex.builtin.popup.save_layout.title": "Save Layout", diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index 5bbff88e6..e102adcbe 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -51,6 +51,33 @@ namespace hex::plugin::builtin { break; } } + + if (TaskManager::getRunningBlockingTaskCount() > 0) { + auto tasks = TaskManager::getRunningTasks(); + ImGui::SetNextWindowSize(scaled({ 300, 200 }), ImGuiCond_Always); + ImGui::SetNextWindowPos(ImHexApi::System::getMainWindowPosition() + ImHexApi::System::getMainWindowSize() / 2, ImGuiCond_Always, ImVec2(0.5F, 0.5F)); + if (ImGui::BeginPopupModal("hex.builtin.popup.foreground_task.title"_lang, nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { + for (const auto &task : tasks) { + if (task->isBlocking()) { + ImGui::NewLine(); + ImGui::TextUnformatted(Lang(task->getUnlocalizedName())); + ImGui::NewLine(); + + ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("[-]").x) / 2); + ImGuiExt::TextSpinner(""); + + ImGui::NewLine(); + const auto progress = task->getMaxValue() == 0 ? -1 : float(task->getValue()) / float(task->getMaxValue()); + ImGuiExt::ProgressBar(progress, ImVec2(0, 10_scaled)); + break; + } + } + + ImGui::EndPopup(); + } else { + ImGui::OpenPopup("hex.builtin.popup.foreground_task.title"_lang); + } + } } static void drawDebugPopup() { @@ -257,7 +284,7 @@ namespace hex::plugin::builtin { { ImGuiExt::TextSpinner(hex::format("({})", taskCount).c_str()); ImGui::SameLine(); - ImGuiExt::SmallProgressBar(progress, (ImGui::GetCurrentWindowRead()->MenuBarHeight - 10_scaled) / 2.0); + ImGuiExt::ProgressBar(progress, scaled({ 100, 5 }), (ImGui::GetCurrentWindowRead()->MenuBarHeight - 10_scaled) / 2.0); ImGui::SameLine(); } const auto widgetEnd = ImGui::GetCursorPos(); @@ -284,7 +311,11 @@ namespace hex::plugin::builtin { ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); - ImGuiExt::SmallProgressBar(task->getMaxValue() == 0 ? -1 : (float(task->getValue()) / float(task->getMaxValue())), (ImGui::GetTextLineHeightWithSpacing() - 5_scaled) / 2); + ImGuiExt::ProgressBar( + task->getMaxValue() == 0 ? -1 : (float(task->getValue()) / float(task->getMaxValue())), + scaled({ 100, 5 }), + (ImGui::GetTextLineHeightWithSpacing() - 5_scaled) / 2 + ); ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));