ux: Reduce file loading time to basically zero
This commit is contained in:
parent
b11dbe4fe1
commit
7b61268f22
@ -19,7 +19,7 @@ namespace hex {
|
|||||||
class Task {
|
class Task {
|
||||||
public:
|
public:
|
||||||
Task() = default;
|
Task() = default;
|
||||||
Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function);
|
Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
|
||||||
|
|
||||||
Task(const Task&) = delete;
|
Task(const Task&) = delete;
|
||||||
Task(Task &&other) noexcept;
|
Task(Task &&other) noexcept;
|
||||||
@ -28,6 +28,7 @@ namespace hex {
|
|||||||
void update(u64 value = 0);
|
void update(u64 value = 0);
|
||||||
void setMaxValue(u64 value);
|
void setMaxValue(u64 value);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isBackgroundTask() const;
|
||||||
[[nodiscard]] bool isFinished() const;
|
[[nodiscard]] bool isFinished() const;
|
||||||
[[nodiscard]] bool hadException() const;
|
[[nodiscard]] bool hadException() const;
|
||||||
[[nodiscard]] bool wasInterrupted() const;
|
[[nodiscard]] bool wasInterrupted() const;
|
||||||
@ -56,6 +57,7 @@ namespace hex {
|
|||||||
std::function<void()> m_interruptCallback;
|
std::function<void()> m_interruptCallback;
|
||||||
|
|
||||||
bool m_shouldInterrupt = false;
|
bool m_shouldInterrupt = false;
|
||||||
|
bool m_background = true;
|
||||||
|
|
||||||
bool m_interrupted = false;
|
bool m_interrupted = false;
|
||||||
bool m_finished = false;
|
bool m_finished = false;
|
||||||
@ -89,6 +91,7 @@ namespace hex {
|
|||||||
constexpr static auto NoProgress = 0;
|
constexpr static auto NoProgress = 0;
|
||||||
|
|
||||||
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
|
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
|
||||||
|
static TaskHolder createBackgroundTask(std::string name, std::function<void(Task &)> function);
|
||||||
static void collectGarbage();
|
static void collectGarbage();
|
||||||
|
|
||||||
static size_t getRunningTaskCount();
|
static size_t getRunningTaskCount();
|
||||||
|
@ -9,11 +9,11 @@ namespace hex {
|
|||||||
|
|
||||||
std::mutex TaskManager::s_deferredCallsMutex;
|
std::mutex TaskManager::s_deferredCallsMutex;
|
||||||
|
|
||||||
std::list<std::shared_ptr<Task>> TaskManager::s_tasks;
|
std::list<std::shared_ptr<Task>> TaskManager::s_tasks, s_backgroundTasks;
|
||||||
std::list<std::function<void()>> TaskManager::s_deferredCalls;
|
std::list<std::function<void()>> TaskManager::s_deferredCalls;
|
||||||
|
|
||||||
Task::Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function)
|
Task::Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function)
|
||||||
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue) {
|
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue), m_background(background) {
|
||||||
this->m_thread = std::thread([this, func = std::move(function)] {
|
this->m_thread = std::thread([this, func = std::move(function)] {
|
||||||
try {
|
try {
|
||||||
func(*this);
|
func(*this);
|
||||||
@ -83,6 +83,12 @@ namespace hex {
|
|||||||
this->m_interruptCallback = std::move(callback);
|
this->m_interruptCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Task::isBackgroundTask() const {
|
||||||
|
std::scoped_lock lock(this->m_mutex);
|
||||||
|
|
||||||
|
return this->m_background;
|
||||||
|
}
|
||||||
|
|
||||||
bool Task::isFinished() const {
|
bool Task::isFinished() const {
|
||||||
std::scoped_lock lock(this->m_mutex);
|
std::scoped_lock lock(this->m_mutex);
|
||||||
|
|
||||||
@ -164,13 +170,20 @@ namespace hex {
|
|||||||
|
|
||||||
|
|
||||||
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
|
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
|
||||||
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, std::move(function)));
|
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, false, std::move(function)));
|
||||||
|
|
||||||
return TaskHolder(s_tasks.back());
|
return TaskHolder(s_tasks.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskHolder TaskManager::createBackgroundTask(std::string name, std::function<void(Task &)> function) {
|
||||||
|
s_backgroundTasks.emplace_back(std::make_shared<Task>(std::move(name), 0, true, std::move(function)));
|
||||||
|
|
||||||
|
return TaskHolder(s_backgroundTasks.back());
|
||||||
|
}
|
||||||
|
|
||||||
void TaskManager::collectGarbage() {
|
void TaskManager::collectGarbage() {
|
||||||
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
|
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
|
||||||
|
std::erase_if(s_backgroundTasks, [](const auto &task) { return task->isFinished(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {
|
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {
|
||||||
|
@ -52,6 +52,7 @@ namespace hex {
|
|||||||
|
|
||||||
ImGui::Texture m_logoTexture = { nullptr };
|
ImGui::Texture m_logoTexture = { nullptr };
|
||||||
|
|
||||||
|
std::mutex m_popupMutex;
|
||||||
std::list<std::string> m_popupsToOpen;
|
std::list<std::string> m_popupsToOpen;
|
||||||
std::vector<int> m_pressedKeys;
|
std::vector<int> m_pressedKeys;
|
||||||
|
|
||||||
|
@ -144,6 +144,8 @@ namespace hex {
|
|||||||
});
|
});
|
||||||
|
|
||||||
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
|
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
|
||||||
|
std::scoped_lock lock(this->m_popupMutex);
|
||||||
|
|
||||||
this->m_popupsToOpen.push_back(name);
|
this->m_popupsToOpen.push_back(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -424,15 +426,17 @@ namespace hex {
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(this->m_popupMutex);
|
||||||
|
this->m_popupsToOpen.remove_if([](const auto &name) {
|
||||||
|
if (ImGui::IsPopupOpen(name.c_str()))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
ImGui::OpenPopup(name.c_str());
|
||||||
|
|
||||||
this->m_popupsToOpen.remove_if([](const auto &name) {
|
return false;
|
||||||
if (ImGui::IsPopupOpen(name.c_str()))
|
});
|
||||||
return true;
|
}
|
||||||
else
|
|
||||||
ImGui::OpenPopup(name.c_str());
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
TaskManager::runDeferredCalls();
|
TaskManager::runDeferredCalls();
|
||||||
|
|
||||||
|
@ -106,19 +106,22 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
void ViewPatternData::drawContent() {
|
void ViewPatternData::drawContent() {
|
||||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||||
auto provider = ImHexApi::Provider::get();
|
if (ImHexApi::Provider::isValid()) {
|
||||||
if (ImHexApi::Provider::isValid() && provider->isReadable() && !ProviderExtraData::get(provider).patternLanguage.runtime->isRunning()) {
|
auto provider = ImHexApi::Provider::get();
|
||||||
|
auto &runtime = ProviderExtraData::get(provider).patternLanguage.runtime;
|
||||||
|
|
||||||
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
|
if (provider->isReadable() && runtime != nullptr && !runtime->isRunning()) {
|
||||||
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) {
|
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
|
||||||
ImGui::TableHeadersRow();
|
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) {
|
||||||
if (!sortedPatterns.empty()) {
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
for (auto &patterns : sortedPatterns)
|
if (!sortedPatterns.empty()) {
|
||||||
patterns->accept(this->m_patternDrawer);
|
for (auto &patterns : sortedPatterns)
|
||||||
|
patterns->accept(this->m_patternDrawer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,58 +114,60 @@ namespace hex::plugin::builtin {
|
|||||||
patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>();
|
patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>();
|
||||||
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider);
|
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider);
|
||||||
|
|
||||||
if (!this->m_autoLoadPatterns)
|
TaskManager::createBackgroundTask("Analyzing file content", [this, provider, &data = patternLanguageData](auto &) {
|
||||||
return;
|
if (!this->m_autoLoadPatterns)
|
||||||
|
return;
|
||||||
|
|
||||||
// Copy over current pattern source code to the new provider
|
// Copy over current pattern source code to the new provider
|
||||||
if (!this->m_syncPatternSourceCode) {
|
if (!this->m_syncPatternSourceCode) {
|
||||||
patternLanguageData.sourceCode = this->m_textEditor.GetText();
|
data.sourceCode = this->m_textEditor.GetText();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &runtime = patternLanguageData.runtime;
|
auto &runtime = data.runtime;
|
||||||
|
|
||||||
auto mimeType = magic::getMIMEType(provider);
|
auto mimeType = magic::getMIMEType(provider);
|
||||||
|
|
||||||
bool foundCorrectType = false;
|
bool foundCorrectType = false;
|
||||||
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
|
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
|
||||||
hex::unused(runtime);
|
hex::unused(runtime);
|
||||||
|
|
||||||
if (value == mimeType) {
|
if (value == mimeType) {
|
||||||
foundCorrectType = true;
|
foundCorrectType = true;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||||
|
});
|
||||||
|
|
||||||
|
this->m_possiblePatternFiles.clear();
|
||||||
|
|
||||||
|
std::error_code errorCode;
|
||||||
|
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
|
||||||
|
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
|
||||||
|
foundCorrectType = false;
|
||||||
|
if (!entry.is_regular_file())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fs::File file(entry.path(), fs::File::Mode::Read);
|
||||||
|
if (!file.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
|
||||||
|
|
||||||
|
if (foundCorrectType)
|
||||||
|
this->m_possiblePatternFiles.push_back(entry.path());
|
||||||
|
|
||||||
|
runtime->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
|
||||||
|
|
||||||
|
if (!this->m_possiblePatternFiles.empty()) {
|
||||||
|
this->m_selectedPatternFile = 0;
|
||||||
|
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
|
||||||
|
this->m_acceptPatternWindowOpen = true;
|
||||||
}
|
}
|
||||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this->m_possiblePatternFiles.clear();
|
|
||||||
|
|
||||||
std::error_code errorCode;
|
|
||||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
|
|
||||||
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
|
|
||||||
foundCorrectType = false;
|
|
||||||
if (!entry.is_regular_file())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fs::File file(entry.path(), fs::File::Mode::Read);
|
|
||||||
if (!file.isValid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
|
|
||||||
|
|
||||||
if (foundCorrectType)
|
|
||||||
this->m_possiblePatternFiles.push_back(entry.path());
|
|
||||||
|
|
||||||
runtime->reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
|
|
||||||
|
|
||||||
if (!this->m_possiblePatternFiles.empty()) {
|
|
||||||
this->m_selectedPatternFile = 0;
|
|
||||||
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
|
|
||||||
this->m_acceptPatternWindowOpen = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
|
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
|
||||||
|
@ -52,40 +52,49 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::atomic<bool> s_recentProvidersUpdating = false;
|
||||||
static std::list<RecentProvider> s_recentProviders;
|
static std::list<RecentProvider> s_recentProviders;
|
||||||
|
|
||||||
static void updateRecentProviders() {
|
static void updateRecentProviders() {
|
||||||
s_recentProviders.clear();
|
TaskManager::createBackgroundTask("Updating recent files", [](auto&){
|
||||||
|
if (s_recentProvidersUpdating)
|
||||||
|
return;
|
||||||
|
|
||||||
// Query all recent providers
|
s_recentProvidersUpdating = true;
|
||||||
std::vector<std::fs::path> recentFilePaths;
|
ON_SCOPE_EXIT { s_recentProvidersUpdating = false; };
|
||||||
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
|
|
||||||
for (const auto &entry : std::fs::directory_iterator(folder)) {
|
s_recentProviders.clear();
|
||||||
if (entry.is_regular_file())
|
|
||||||
recentFilePaths.push_back(entry.path());
|
// Query all recent providers
|
||||||
|
std::vector<std::fs::path> recentFilePaths;
|
||||||
|
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
|
||||||
|
for (const auto &entry : std::fs::directory_iterator(folder)) {
|
||||||
|
if (entry.is_regular_file())
|
||||||
|
recentFilePaths.push_back(entry.path());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Sort recent provider files by last modified time
|
// Sort recent provider files by last modified time
|
||||||
std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) {
|
std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) {
|
||||||
return std::fs::last_write_time(a) > std::fs::last_write_time(b);
|
return std::fs::last_write_time(a) > std::fs::last_write_time(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
|
||||||
|
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
|
||||||
|
auto &path = recentFilePaths[i];
|
||||||
|
try {
|
||||||
|
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
|
||||||
|
uniqueProviders.insert(RecentProvider {
|
||||||
|
.displayName = jsonData["displayName"],
|
||||||
|
.type = jsonData["type"],
|
||||||
|
.filePath = path,
|
||||||
|
.data = jsonData
|
||||||
|
});
|
||||||
|
} catch (...) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
|
||||||
});
|
});
|
||||||
|
|
||||||
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
|
|
||||||
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
|
|
||||||
auto &path = recentFilePaths[i];
|
|
||||||
try {
|
|
||||||
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
|
|
||||||
uniqueProviders.insert(RecentProvider {
|
|
||||||
.displayName = jsonData["displayName"],
|
|
||||||
.type = jsonData["type"],
|
|
||||||
.filePath = path,
|
|
||||||
.data = jsonData
|
|
||||||
});
|
|
||||||
} catch (...) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadRecentProvider(const RecentProvider &recentProvider) {
|
static void loadRecentProvider(const RecentProvider &recentProvider) {
|
||||||
@ -227,13 +236,15 @@ namespace hex::plugin::builtin {
|
|||||||
ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang);
|
ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang);
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
|
||||||
{
|
{
|
||||||
for (const auto &recentProvider : s_recentProviders) {
|
if (!s_recentProvidersUpdating) {
|
||||||
ImGui::PushID(&recentProvider);
|
for (const auto &recentProvider : s_recentProviders) {
|
||||||
ON_SCOPE_EXIT { ImGui::PopID(); };
|
ImGui::PushID(&recentProvider);
|
||||||
|
ON_SCOPE_EXIT { ImGui::PopID(); };
|
||||||
|
|
||||||
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
|
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
|
||||||
loadRecentProvider(recentProvider);
|
loadRecentProvider(recentProvider);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,7 +511,7 @@ namespace hex::plugin::builtin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] {
|
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] {
|
||||||
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProviders.empty())) {
|
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProvidersUpdating && !s_recentProviders.empty())) {
|
||||||
// Copy to avoid changing list while iteration
|
// Copy to avoid changing list while iteration
|
||||||
auto recentProviders = s_recentProviders;
|
auto recentProviders = s_recentProviders;
|
||||||
for (auto &recentProvider : recentProviders) {
|
for (auto &recentProvider : recentProviders) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user