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:
Lukas Cone 2022-02-18 22:34:54 +01:00 committed by GitHub
parent 60a717365c
commit 26f998ecb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 20 deletions

View File

@ -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 {
}
};
}
}

View File

@ -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 {
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
});
}
}
}

View File

@ -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;
}
}
}

View File

@ -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" },