mirror of https://github.com/WerWolv/ImHex.git
feature: User now can add custom directories (#444)
* feat: user directories * ux: show setting categories in order they were created * feat: add descriptable setting categories
This commit is contained in:
parent
60a717365c
commit
26f998ecb6
|
@ -46,11 +46,27 @@ namespace hex {
|
|||
Callback callback;
|
||||
};
|
||||
|
||||
struct Category {
|
||||
std::string name;
|
||||
size_t slot = 0;
|
||||
|
||||
bool operator<(const Category &other) const {
|
||||
return name < other.name;
|
||||
}
|
||||
|
||||
operator const std::string &() const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
void load();
|
||||
void store();
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback);
|
||||
|
||||
void addCategoryDescrition(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription);
|
||||
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value);
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
|
||||
|
@ -60,9 +76,11 @@ namespace hex {
|
|||
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
|
||||
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue = {});
|
||||
|
||||
std::map<std::string, std::vector<Entry>> &getEntries();
|
||||
std::map<Category, std::vector<Entry>> &getEntries();
|
||||
std::map<std::string, std::string> &getCategoryDescriptions();
|
||||
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
|
||||
nlohmann::json &getSettingsData();
|
||||
std::vector<std::string> getStringArray(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
|
||||
}
|
||||
|
||||
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
|
||||
|
@ -369,4 +387,4 @@ namespace hex {
|
|||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,10 +41,23 @@ namespace hex {
|
|||
}
|
||||
}
|
||||
|
||||
static auto getCategoryEntry(const std::string &unlocalizedCategory) {
|
||||
auto &entries = getEntries();
|
||||
const size_t curSlot = entries.size();
|
||||
auto found = entries.find(Category { unlocalizedCategory });
|
||||
|
||||
if (found == entries.end()) {
|
||||
auto [iter, _] = entries.emplace(Category { unlocalizedCategory, curSlot }, std::vector<Entry> {});
|
||||
return iter;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback) {
|
||||
log::info("Registered new integer setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getEntries()[unlocalizedCategory.c_str()].emplace_back(Entry { unlocalizedName.c_str(), callback });
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName.c_str(), callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
|
@ -57,7 +70,7 @@ namespace hex {
|
|||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback) {
|
||||
log::info("Registered new string setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getEntries()[unlocalizedCategory].emplace_back(Entry { unlocalizedName, callback });
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
|
@ -67,6 +80,23 @@ namespace hex {
|
|||
json[unlocalizedCategory][unlocalizedName] = std::string(defaultValue);
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback) {
|
||||
log::info("Registered new string array setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
if (!json.contains(unlocalizedCategory))
|
||||
json[unlocalizedCategory] = nlohmann::json::object();
|
||||
if (!json[unlocalizedCategory].contains(unlocalizedName) || !json[unlocalizedCategory][unlocalizedName].is_array())
|
||||
json[unlocalizedCategory][unlocalizedName] = defaultValue;
|
||||
}
|
||||
|
||||
void addCategoryDescrition(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription) {
|
||||
getCategoryDescriptions()[unlocalizedCategory] = unlocalizedCategoryDescription;
|
||||
}
|
||||
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value) {
|
||||
auto &json = getSettingsData();
|
||||
|
||||
|
@ -141,12 +171,18 @@ namespace hex {
|
|||
}
|
||||
|
||||
|
||||
std::map<std::string, std::vector<Entry>> &getEntries() {
|
||||
static std::map<std::string, std::vector<Entry>> entries;
|
||||
std::map<Category, std::vector<Entry>> &getEntries() {
|
||||
static std::map<Category, std::vector<Entry>> entries;
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> &getCategoryDescriptions() {
|
||||
static std::map<std::string, std::string> descriptions;
|
||||
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
|
||||
auto &settings = getSettingsData();
|
||||
|
||||
|
@ -162,6 +198,15 @@ namespace hex {
|
|||
return settings;
|
||||
}
|
||||
|
||||
std::vector<std::string> getStringArray(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
|
||||
auto setting = getSetting(unlocalizedCategory, unlocalizedName);
|
||||
if (setting.is_array()) {
|
||||
return setting;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -496,4 +541,4 @@ namespace hex {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/paths_mac.h>
|
||||
|
||||
#include <xdg.hpp>
|
||||
|
@ -13,8 +13,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
|
@ -38,9 +36,17 @@ namespace hex {
|
|||
|
||||
std::vector<fs::path> getPath(ImHexPath path, bool listNonExisting) {
|
||||
std::vector<fs::path> result;
|
||||
const auto exePath = getExecutablePath();
|
||||
const std::string settingName { "hex.builtin.setting.folders" };
|
||||
auto userDirs = ContentRegistry::Settings::getStringArray(settingName, settingName);
|
||||
|
||||
auto addUserDirs = [&userDirs](auto &paths) {
|
||||
std::transform(userDirs.begin(), userDirs.end(), std::back_inserter(paths), [](auto &item) {
|
||||
return std::move(item);
|
||||
});
|
||||
};
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
const auto exePath = getExecutablePath();
|
||||
const auto parentDir = fs::path(exePath).parent_path();
|
||||
|
||||
fs::path appDataDir;
|
||||
|
@ -57,21 +63,25 @@ namespace hex {
|
|||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "patterns").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "includes").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "magic").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "python").string();
|
||||
});
|
||||
|
@ -82,6 +92,7 @@ namespace hex {
|
|||
});
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "yara").string();
|
||||
});
|
||||
|
@ -94,11 +105,13 @@ namespace hex {
|
|||
});
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "constants").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "encodings").string();
|
||||
});
|
||||
|
@ -113,7 +126,6 @@ namespace hex {
|
|||
}
|
||||
#elif defined(OS_MACOS)
|
||||
// Get path to special directories
|
||||
const auto exePath = getExecutablePath();
|
||||
const fs::path applicationSupportDir(getMacApplicationSupportDirectoryPath());
|
||||
|
||||
std::vector<fs::path> paths = { exePath, applicationSupportDir };
|
||||
|
@ -167,28 +179,31 @@ namespace hex {
|
|||
for (auto &dir : dataDirs)
|
||||
dir = dir / "imhex";
|
||||
|
||||
const auto exePath = getExecutablePath();
|
||||
|
||||
if (!exePath.empty())
|
||||
dataDirs.emplace(dataDirs.begin(), fs::path(exePath.data()).parent_path());
|
||||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "patterns").string(); });
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "includes").string(); });
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "magic").string(); });
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p).string(); });
|
||||
break;
|
||||
case ImHexPath::Plugins:
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "plugins").string(); });
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "yara").string(); });
|
||||
break;
|
||||
case ImHexPath::Config:
|
||||
|
@ -198,9 +213,11 @@ namespace hex {
|
|||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "resources").string(); });
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "constants").string(); });
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "encodings").string(); });
|
||||
break;
|
||||
case ImHexPath::Logs:
|
||||
|
@ -221,4 +238,4 @@ namespace hex {
|
|||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <hex/api/localization.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <fonts/codicons_font.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -219,6 +221,50 @@ namespace hex::plugin::builtin {
|
|||
|
||||
return false;
|
||||
});
|
||||
|
||||
static const std::string dirsSetting { "hex.builtin.setting.folders" };
|
||||
|
||||
ContentRegistry::Settings::addCategoryDescrition(dirsSetting, "hex.builtin.setting.folders.description");
|
||||
|
||||
ContentRegistry::Settings::add(dirsSetting, dirsSetting, std::vector<std::string> {}, [](auto name, nlohmann::json &setting) {
|
||||
static std::vector<std::string> folders = setting;
|
||||
static int currentItemIndex = 0;
|
||||
|
||||
if (!ImGui::BeginListBox("", ImVec2(-38, -FLT_MIN))) {
|
||||
return false;
|
||||
} else {
|
||||
for (size_t n = 0; n < folders.size(); n++) {
|
||||
const bool isSelected = (currentItemIndex == n);
|
||||
if (ImGui::Selectable(folders.at(n).c_str(), isSelected)) { currentItemIndex = n; }
|
||||
if (isSelected) { ImGui::SetItemDefaultFocus(); }
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
if (ImGui::IconButton(ICON_VS_NEW_FOLDER, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton), ImVec2(30, 30))) {
|
||||
hex::openFileBrowser("Select include folder", hex::DialogMode::Folder, {}, [&](fs::path path) {
|
||||
auto pathStr = path.string();
|
||||
|
||||
if (std::find(folders.begin(), folders.end(), pathStr) == folders.end()) {
|
||||
folders.emplace_back(pathStr);
|
||||
ContentRegistry::Settings::write(dirsSetting, dirsSetting, folders);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::IconButton(ICON_VS_REMOVE_CLOSE, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton), ImVec2(30, 30))) {
|
||||
if (!folders.empty()) {
|
||||
folders.erase(std::next(folders.begin(), currentItemIndex));
|
||||
ContentRegistry::Settings::write(dirsSetting, dirsSetting, folders);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,29 @@ namespace hex::plugin::builtin {
|
|||
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.settings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) {
|
||||
if (ImGui::BeginTabBar("settings")) {
|
||||
for (auto &[category, entries] : ContentRegistry::Settings::getEntries()) {
|
||||
auto &entries = ContentRegistry::Settings::getEntries();
|
||||
|
||||
std::vector<std::decay_t<decltype(entries)>::const_iterator> sortedCategories;
|
||||
|
||||
for (auto it = entries.cbegin(); it != entries.cend(); it++) {
|
||||
sortedCategories.emplace_back(std::move(it));
|
||||
}
|
||||
|
||||
std::sort(sortedCategories.begin(), sortedCategories.end(), [](auto &item0, auto &item1) {
|
||||
return item0->first.slot < item1->first.slot;
|
||||
});
|
||||
|
||||
const auto &descriptions = ContentRegistry::Settings::getCategoryDescriptions();
|
||||
|
||||
for (auto &it : sortedCategories) {
|
||||
auto &[category, entries] = *it;
|
||||
if (ImGui::BeginTabItem(LangEntry(category))) {
|
||||
ImGui::TextUnformatted(LangEntry(category));
|
||||
const std::string &categoryDesc = descriptions.count(category) ? descriptions.at(category) : category.name;
|
||||
ImGui::TextUnformatted(LangEntry(categoryDesc));
|
||||
ImGui::Separator();
|
||||
|
||||
for (auto &[name, callback] : entries) {
|
||||
if (callback(LangEntry(name), ContentRegistry::Settings::getSettingsData()[category][name]))
|
||||
if (callback(LangEntry(name), ContentRegistry::Settings::getSettingsData()[category.name][name]))
|
||||
EventManager::post<EventSettingsChanged>();
|
||||
}
|
||||
|
||||
|
@ -51,4 +67,4 @@ namespace hex::plugin::builtin {
|
|||
this->getWindowOpenState() = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -674,6 +674,8 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.builtin.setting.hex_editor.grey_zeros", "Grey out zeros" },
|
||||
{ "hex.builtin.setting.hex_editor.uppercase_hex", "Upper case Hex characters" },
|
||||
{ "hex.builtin.setting.hex_editor.extra_info", "Display extra information" },
|
||||
{ "hex.builtin.setting.folders", "Folders" },
|
||||
{ "hex.builtin.setting.folders.description", "Specify additional search paths for patterns, scripts, rules and more" },
|
||||
|
||||
{ "hex.builtin.provider.file.path", "File path" },
|
||||
{ "hex.builtin.provider.file.size", "Size" },
|
||||
|
|
Loading…
Reference in New Issue