feat: Added blocking tasks that show a full-screen modal when active
This commit is contained in:
parent
06c019387c
commit
19f9296a40
@ -22,7 +22,7 @@ namespace hex {
|
||||
class Task {
|
||||
public:
|
||||
Task() = default;
|
||||
Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
|
||||
Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
||||
|
||||
Task(const Task&) = delete;
|
||||
Task(Task &&other) noexcept;
|
||||
@ -57,6 +57,7 @@ namespace hex {
|
||||
void setInterruptCallback(std::function<void()> 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<bool> m_shouldInterrupt = false;
|
||||
std::atomic<bool> m_background = true;
|
||||
std::atomic<bool> m_blocking = false;
|
||||
|
||||
std::atomic<bool> m_interrupted = false;
|
||||
std::atomic<bool> m_finished = false;
|
||||
@ -162,6 +164,24 @@ namespace hex {
|
||||
*/
|
||||
static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void()> 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<void(Task &)> 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<void()> 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<std::shared_ptr<Task>>& getRunningTasks();
|
||||
static void runDeferredCalls();
|
||||
|
||||
private:
|
||||
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
|
||||
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task &)> function);
|
||||
};
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -63,8 +63,11 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
Task::Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> 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<void(Task &)> 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<void(Task&)> function) {
|
||||
TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, bool blocking, std::function<void(Task&)> function) {
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
// Construct new task
|
||||
auto task = std::make_shared<Task>(std::move(unlocalizedName), maxValue, background, std::move(function));
|
||||
auto task = std::make_shared<Task>(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<void(Task &)> 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<void()> 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<void(Task &)> 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<void()> 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<void(Task &)> 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<void()> 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<void()> &function) {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
@ -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))
|
||||
|
@ -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",
|
||||
|
@ -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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user