mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
feat: Save opened projects as recent entries (#1105)
This PR does two things : - save opened projects as recent entries - refactor stuff about recent entries in a separate file. The reason is that I felt like welcome_screen.cpp was really big ( 685 lines before this, 500 now). What do you think ? --------- Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
@@ -195,6 +195,11 @@ namespace hex {
|
||||
EVENT_DEF(EventStoreContentRemoved, const std::fs::path&);
|
||||
EVENT_DEF(EventImHexClosing);
|
||||
|
||||
/**
|
||||
* @brief Called when a project has been loaded
|
||||
*/
|
||||
EVENT_DEF(EventProjectOpened);
|
||||
|
||||
EVENT_DEF_NO_LOG(EventFrameBegin);
|
||||
EVENT_DEF_NO_LOG(EventFrameEnd);
|
||||
EVENT_DEF_NO_LOG(EventSetTaskBarIconState, u32, u32, u32);
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
resetPath.release();
|
||||
EventManager::post<EventProjectOpened>();
|
||||
EventManager::post<RequestUpdateWindowTitle>();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -27,6 +27,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/content/hashes.cpp
|
||||
source/content/global_actions.cpp
|
||||
source/content/themes.cpp
|
||||
source/content/recent.cpp
|
||||
|
||||
source/content/providers/file_provider.cpp
|
||||
source/content/providers/gdb_provider.cpp
|
||||
|
||||
81
plugins/builtin/include/content/recent.hpp
Normal file
81
plugins/builtin/include/content/recent.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
|
||||
namespace hex::plugin::builtin::recent {
|
||||
|
||||
/**
|
||||
* @brief Structure used to represent a recent other
|
||||
*/
|
||||
struct RecentEntry {
|
||||
|
||||
/**
|
||||
* @brief Name that should be used to display the entry to the user
|
||||
*/
|
||||
std::string displayName;
|
||||
|
||||
/**
|
||||
* @brief type of this entry. Might be a provider id (e.g. hex.builtin.provider.file)
|
||||
* or "project" in case of a project
|
||||
*/
|
||||
std::string type;
|
||||
|
||||
/**
|
||||
* @brief path of this entry file
|
||||
*/
|
||||
std::fs::path entryFilePath;
|
||||
|
||||
/**
|
||||
* @brief Entire json data of the recent entry (include the fields above)
|
||||
* Used for custom settings set by the providers
|
||||
*/
|
||||
nlohmann::json data;
|
||||
|
||||
bool operator==(const RecentEntry &other) const {
|
||||
return HashFunction()(*this) == HashFunction()(other);
|
||||
}
|
||||
|
||||
std::size_t getHash() const {
|
||||
return HashFunction()(*this);
|
||||
}
|
||||
|
||||
struct HashFunction {
|
||||
std::size_t operator()(const RecentEntry& provider) const {
|
||||
return
|
||||
(std::hash<std::string>()(provider.displayName)) ^
|
||||
(std::hash<std::string>()(provider.type) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
void registerEventHandlers();
|
||||
|
||||
/**
|
||||
* @brief Scan the files in ImHexPath::Recent to get the recent entries, and delete duplicates.
|
||||
*/
|
||||
void updateRecentEntries();
|
||||
|
||||
/**
|
||||
* @brief Load a recent entry in ImHex. The entry might be a provider of a project
|
||||
* @param recentEntry entry to load
|
||||
*/
|
||||
void loadRecentEntry(const RecentEntry &recentEntry);
|
||||
|
||||
/**
|
||||
* @brief Draw the recent providers in the welcome screen
|
||||
*/
|
||||
void draw();
|
||||
|
||||
/**
|
||||
* @brief Draw the "open recent" item in the "File" menu
|
||||
*/
|
||||
void drawFileMenuItem();
|
||||
|
||||
}
|
||||
237
plugins/builtin/source/content/recent.cpp
Normal file
237
plugins/builtin/source/content/recent.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/task.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
#include <content/recent.hpp>
|
||||
#include <content/popups/popup_notification.hpp>
|
||||
#include <fonts/codicons_font.h>
|
||||
|
||||
namespace hex::plugin::builtin::recent {
|
||||
|
||||
constexpr static auto MaxRecentEntries = 5;
|
||||
|
||||
static std::atomic<bool> s_recentEntriesUpdating = false;
|
||||
static std::list<RecentEntry> s_recentEntries;
|
||||
|
||||
void registerEventHandlers() {
|
||||
// Save every opened provider as a "recent" shortcut
|
||||
(void)EventManager::subscribe<EventProviderOpened>([](prv::Provider *provider) {
|
||||
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", 1) == 1) {
|
||||
auto fileName = hex::format("{:%y%m%d_%H%M%S}.json", fmt::gmtime(std::chrono::system_clock::now()));
|
||||
|
||||
// do not save to recents if the provider is part of a project
|
||||
if (ProjectFile::hasPath()) return;
|
||||
|
||||
// The recent provider is saved to every "recent" directory
|
||||
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
|
||||
wolv::io::File recentFile(recentPath / fileName, wolv::io::File::Mode::Create);
|
||||
if (!recentFile.isValid())
|
||||
continue;
|
||||
|
||||
{
|
||||
auto path = ProjectFile::getPath();
|
||||
ProjectFile::clearPath();
|
||||
|
||||
if (auto settings = provider->storeSettings(); !settings.is_null())
|
||||
recentFile.writeString(settings.dump(4));
|
||||
|
||||
ProjectFile::setPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRecentEntries();
|
||||
});
|
||||
|
||||
// save opened projects as a "recent" shortcut
|
||||
(void)EventManager::subscribe<EventProjectOpened>([] {
|
||||
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", 1) == 1) {
|
||||
auto fileName = hex::format("{:%y%m%d_%H%M%S}.json", fmt::gmtime(std::chrono::system_clock::now()));
|
||||
|
||||
// The recent provider is saved to every "recent" directory
|
||||
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
|
||||
wolv::io::File recentFile(recentPath / fileName, wolv::io::File::Mode::Create);
|
||||
if (!recentFile.isValid())
|
||||
continue;
|
||||
|
||||
std::string displayName = hex::format("[Project] {}", wolv::util::toUTF8String(ProjectFile::getPath().filename()));
|
||||
|
||||
nlohmann::json recentEntry {
|
||||
{"type", "project"},
|
||||
{"displayName", displayName},
|
||||
{"path", ProjectFile::getPath()}
|
||||
};
|
||||
|
||||
recentFile.writeString(recentEntry.dump(4));
|
||||
}
|
||||
}
|
||||
|
||||
updateRecentEntries();
|
||||
});
|
||||
}
|
||||
|
||||
void updateRecentEntries() {
|
||||
TaskManager::createBackgroundTask("Updating recent files", [](auto&){
|
||||
if (s_recentEntriesUpdating)
|
||||
return;
|
||||
|
||||
s_recentEntriesUpdating = true;
|
||||
ON_SCOPE_EXIT { s_recentEntriesUpdating = false; };
|
||||
|
||||
s_recentEntries.clear();
|
||||
|
||||
// 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
|
||||
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);
|
||||
});
|
||||
|
||||
std::unordered_set<RecentEntry, RecentEntry::HashFunction> uniqueProviders;
|
||||
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < MaxRecentEntries; i++) {
|
||||
auto &path = recentFilePaths[i];
|
||||
try {
|
||||
auto jsonData = nlohmann::json::parse(wolv::io::File(path, wolv::io::File::Mode::Read).readString());
|
||||
uniqueProviders.insert(RecentEntry {
|
||||
.displayName = jsonData.at("displayName"),
|
||||
.type = jsonData.at("type"),
|
||||
.entryFilePath = path,
|
||||
.data = jsonData
|
||||
});
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
// Delete all recent provider files that are not in the list
|
||||
for (const auto &path : recentFilePaths) {
|
||||
bool found = false;
|
||||
for (const auto &provider : uniqueProviders) {
|
||||
if (path == provider.entryFilePath) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
wolv::io::fs::remove(path);
|
||||
}
|
||||
|
||||
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentEntries));
|
||||
});
|
||||
}
|
||||
|
||||
void loadRecentEntry(const RecentEntry &recentEntry) {
|
||||
if (recentEntry.type == "project") {
|
||||
std::fs::path projectPath = recentEntry.data["path"].get<std::string>();
|
||||
ProjectFile::load(projectPath);
|
||||
return;
|
||||
}
|
||||
auto *provider = ImHexApi::Provider::createProvider(recentEntry.type, true);
|
||||
if (provider != nullptr) {
|
||||
provider->loadSettings(recentEntry.data);
|
||||
|
||||
if (!provider->open() || !provider->isAvailable()) {
|
||||
PopupError::open(hex::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
|
||||
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
return;
|
||||
}
|
||||
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
|
||||
updateRecentEntries();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void draw() {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 9);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::UnderlinedText(s_recentEntries.empty() ? "" : "hex.builtin.welcome.start.recent"_lang);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
|
||||
{
|
||||
if (!s_recentEntriesUpdating) {
|
||||
auto it = s_recentEntries.begin();
|
||||
while (it != s_recentEntries.end()) {
|
||||
const auto &recentEntry = *it;
|
||||
bool shouldRemove = false;
|
||||
|
||||
ImGui::PushID(&recentEntry);
|
||||
ON_SCOPE_EXIT { ImGui::PopID(); };
|
||||
|
||||
const char* icon;
|
||||
if (recentEntry.type == "project") {
|
||||
icon = ICON_VS_PROJECT;
|
||||
} else {
|
||||
icon = ICON_VS_FILE_BINARY;
|
||||
}
|
||||
if (ImGui::BulletHyperlink(hex::format("{} {}", icon, recentEntry.displayName).c_str())) {
|
||||
loadRecentEntry(recentEntry);
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect right click on recent provider
|
||||
std::string popupID = std::string("RecentEntryMenu.")+std::to_string(recentEntry.getHash());
|
||||
if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup(popupID.c_str());
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(popupID.c_str())) {
|
||||
if (ImGui::MenuItem("Remove")) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// handle deletion from vector and on disk
|
||||
if (shouldRemove) {
|
||||
wolv::io::fs::remove(recentEntry.entryFilePath);
|
||||
it = s_recentEntries.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawFileMenuItem() {
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file" }, 1200, [] {
|
||||
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !recent::s_recentEntriesUpdating && !s_recentEntries.empty())) {
|
||||
// Copy to avoid changing list while iteration
|
||||
auto recentEntries = s_recentEntries;
|
||||
for (auto &recentEntry : recentEntries) {
|
||||
if (ImGui::MenuItem(recentEntry.displayName.c_str())) {
|
||||
loadRecentEntry(recentEntry);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.clear_recent"_lang)) {
|
||||
s_recentEntries.clear();
|
||||
|
||||
// Remove all recent files
|
||||
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent))
|
||||
for (const auto &entry : std::fs::directory_iterator(recentPath))
|
||||
std::fs::remove(entry.path());
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <content/popups/popup_notification.hpp>
|
||||
#include <content/popups/popup_question.hpp>
|
||||
#include <content/recent.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
@@ -34,39 +35,12 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
constexpr static auto MaxRecentProviders = 5;
|
||||
|
||||
static ImGui::Texture s_bannerTexture, s_backdropTexture;
|
||||
|
||||
static std::fs::path s_safetyBackupPath;
|
||||
|
||||
static std::string s_tipOfTheDay;
|
||||
|
||||
struct RecentProvider {
|
||||
std::string displayName;
|
||||
std::string type;
|
||||
std::fs::path filePath;
|
||||
|
||||
nlohmann::json data;
|
||||
|
||||
bool operator==(const RecentProvider &other) const {
|
||||
return HashFunction()(*this) == HashFunction()(other);
|
||||
}
|
||||
|
||||
std::size_t getHash() const {
|
||||
return HashFunction()(*this);
|
||||
}
|
||||
|
||||
struct HashFunction {
|
||||
std::size_t operator()(const RecentProvider& provider) const {
|
||||
return
|
||||
(std::hash<std::string>()(provider.displayName)) ^
|
||||
(std::hash<std::string>()(provider.type) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class PopupRestoreBackup : public Popup<PopupRestoreBackup> {
|
||||
private:
|
||||
std::fs::path m_logFilePath;
|
||||
@@ -129,82 +103,6 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
};
|
||||
|
||||
static std::atomic<bool> s_recentProvidersUpdating = false;
|
||||
static std::list<RecentProvider> s_recentProviders;
|
||||
|
||||
static void updateRecentProviders() {
|
||||
TaskManager::createBackgroundTask("Updating recent files", [](auto&){
|
||||
if (s_recentProvidersUpdating)
|
||||
return;
|
||||
|
||||
s_recentProvidersUpdating = true;
|
||||
ON_SCOPE_EXIT { s_recentProvidersUpdating = false; };
|
||||
|
||||
s_recentProviders.clear();
|
||||
|
||||
// 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
|
||||
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);
|
||||
});
|
||||
|
||||
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
|
||||
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < MaxRecentProviders; i++) {
|
||||
auto &path = recentFilePaths[i];
|
||||
try {
|
||||
auto jsonData = nlohmann::json::parse(wolv::io::File(path, wolv::io::File::Mode::Read).readString());
|
||||
uniqueProviders.insert(RecentProvider {
|
||||
.displayName = jsonData.at("displayName"),
|
||||
.type = jsonData.at("type"),
|
||||
.filePath = path,
|
||||
.data = jsonData
|
||||
});
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
// Delete all recent provider files that are not in the list
|
||||
for (const auto &path : recentFilePaths) {
|
||||
bool found = false;
|
||||
for (const auto &provider : uniqueProviders) {
|
||||
if (path == provider.filePath) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
wolv::io::fs::remove(path);
|
||||
}
|
||||
|
||||
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
|
||||
});
|
||||
}
|
||||
|
||||
static void loadRecentProvider(const RecentProvider &recentProvider) {
|
||||
auto *provider = ImHexApi::Provider::createProvider(recentProvider.type, true);
|
||||
if (provider != nullptr) {
|
||||
provider->loadSettings(recentProvider.data);
|
||||
|
||||
if (!provider->open() || !provider->isAvailable()) {
|
||||
PopupError::open(hex::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
|
||||
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
|
||||
return;
|
||||
}
|
||||
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
|
||||
updateRecentProviders();
|
||||
}
|
||||
}
|
||||
|
||||
static void loadDefaultLayout() {
|
||||
LayoutManager::loadString(std::string(romfs::get("layouts/default.hexlyt").string()));
|
||||
}
|
||||
@@ -267,48 +165,8 @@ namespace hex::plugin::builtin {
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 9);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::UnderlinedText(s_recentProviders.empty() ? "" : "hex.builtin.welcome.start.recent"_lang);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
|
||||
{
|
||||
if (!s_recentProvidersUpdating) {
|
||||
auto it = s_recentProviders.begin();
|
||||
while(it != s_recentProviders.end()){
|
||||
const auto &recentProvider = *it;
|
||||
bool shouldRemove = false;
|
||||
|
||||
ImGui::PushID(&recentProvider);
|
||||
ON_SCOPE_EXIT { ImGui::PopID(); };
|
||||
|
||||
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
|
||||
loadRecentProvider(recentProvider);
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect right click on recent provider
|
||||
std::string popupID = std::string("RecentProviderMenu.")+std::to_string(recentProvider.getHash());
|
||||
if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup(popupID.c_str());
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup(popupID.c_str())) {
|
||||
if (ImGui::MenuItem("Remove")) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// handle deletion from vector and on disk
|
||||
if (shouldRemove) {
|
||||
wolv::io::fs::remove(recentProvider.filePath);
|
||||
it = s_recentProviders.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// draw recent entries
|
||||
recent::draw();
|
||||
|
||||
if (ImHexApi::System::getInitArguments().contains("update-available")) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, ImGui::GetTextLineHeightWithSpacing() * 5);
|
||||
@@ -481,7 +339,8 @@ namespace hex::plugin::builtin {
|
||||
* should only be called once, at startup
|
||||
*/
|
||||
void createWelcomeScreen() {
|
||||
updateRecentProviders();
|
||||
recent::registerEventHandlers();
|
||||
recent::updateRecentEntries();
|
||||
|
||||
(void)EventManager::subscribe<EventFrameBegin>(drawWelcomeScreen);
|
||||
|
||||
@@ -534,32 +393,6 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Save every opened provider as a "recent" shortcut
|
||||
(void)EventManager::subscribe<EventProviderOpened>([](prv::Provider *provider) {
|
||||
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", 1) == 1) {
|
||||
auto fileName = hex::format("{:%y%m%d_%H%M%S}.json", fmt::gmtime(std::chrono::system_clock::now()));
|
||||
// The recent provider is saved to every "recent" directory
|
||||
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
|
||||
wolv::io::File recentFile(recentPath / fileName, wolv::io::File::Mode::Create);
|
||||
if (!recentFile.isValid())
|
||||
continue;
|
||||
|
||||
{
|
||||
auto path = ProjectFile::getPath();
|
||||
ProjectFile::clearPath();
|
||||
|
||||
if (auto settings = provider->storeSettings(); !settings.is_null())
|
||||
recentFile.writeString(settings.dump(4));
|
||||
|
||||
ProjectFile::setPath(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRecentProviders();
|
||||
});
|
||||
|
||||
EventManager::subscribe<EventProviderCreated>([](auto) {
|
||||
if (!isAnyViewOpen())
|
||||
loadDefaultLayout();
|
||||
@@ -592,29 +425,8 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
});
|
||||
|
||||
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.file" }, 1200, [] {
|
||||
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProvidersUpdating && !s_recentProviders.empty())) {
|
||||
// Copy to avoid changing list while iteration
|
||||
auto recentProviders = s_recentProviders;
|
||||
for (auto &recentProvider : recentProviders) {
|
||||
if (ImGui::MenuItem(recentProvider.displayName.c_str())) {
|
||||
loadRecentProvider(recentProvider);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("hex.builtin.menu.file.clear_recent"_lang)) {
|
||||
s_recentProviders.clear();
|
||||
|
||||
// Remove all recent files
|
||||
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent))
|
||||
for (const auto &entry : std::fs::directory_iterator(recentPath))
|
||||
std::fs::remove(entry.path());
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
});
|
||||
recent::drawFileMenuItem();
|
||||
|
||||
// Check for crash backup
|
||||
constexpr static auto CrashFileName = "crash.json";
|
||||
|
||||
Reference in New Issue
Block a user