1
0
mirror of synced 2025-02-17 18:59:21 +01:00

impr: Rewrote entire settings API and UI (#1378)

This commit is contained in:
Nik 2023-10-21 23:07:33 +02:00 committed by GitHub
parent f114239f51
commit 7fe9a768d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 818 additions and 781 deletions

View File

@ -14,16 +14,18 @@
#include <string_view>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
#if defined(OS_WEB)
#include <jthread.hpp>
#endif
#include <nlohmann/json_fwd.hpp>
#include <nlohmann/json.hpp>
using ImGuiDataType = int;
using ImGuiInputTextFlags = int;
struct ImColor;
namespace hex {
@ -47,127 +49,237 @@ namespace hex {
/* Settings Registry. Allows adding of new entries into the ImHex preferences window. */
namespace Settings {
namespace Widgets {
class Widget {
public:
virtual ~Widget() = default;
virtual bool draw(const std::string &name) = 0;
virtual void load(const nlohmann::json &data) = 0;
virtual nlohmann::json store() = 0;
class Interface {
public:
friend class Widget;
Interface& requiresRestart() {
this->m_requiresRestart = true;
return *this;
}
Interface& setEnabledCallback(std::function<bool()> callback) {
this->m_enabledCallback = std::move(callback);
return *this;
}
Interface& setChangedCallback(std::function<void(Widget&)> callback) {
this->m_changedCallback = std::move(callback);
return *this;
}
Interface& setTooltip(const std::string &tooltip) {
this->m_tooltip = tooltip;
return *this;
}
[[nodiscard]]
Widget& getWidget() const {
return *this->m_widget;
}
private:
explicit Interface(Widget *widget) : m_widget(widget) {}
Widget *m_widget;
bool m_requiresRestart = false;
std::function<bool()> m_enabledCallback;
std::function<void(Widget&)> m_changedCallback;
std::optional<std::string> m_tooltip;
};
[[nodiscard]]
bool doesRequireRestart() const {
return this->m_interface.m_requiresRestart;
}
[[nodiscard]]
bool isEnabled() const {
return !this->m_interface.m_enabledCallback || this->m_interface.m_enabledCallback();
}
[[nodiscard]]
const std::optional<std::string>& getTooltip() const {
return this->m_interface.m_tooltip;
}
void onChanged() {
if (this->m_interface.m_changedCallback)
this->m_interface.m_changedCallback(*this);
}
[[nodiscard]]
Interface& getInterface() {
return this->m_interface;
}
private:
Interface m_interface = Interface(this);
};
class Checkbox : public Widget {
public:
explicit Checkbox(bool defaultValue) : m_value(defaultValue) { }
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] bool isChecked() const { return this->m_value; }
private:
bool m_value;
};
class SliderInteger : public Widget {
public:
SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) { }
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] i32 getValue() const { return this->m_value; }
private:
int m_value;
i32 m_min, m_max;
};
class SliderFloat : public Widget {
public:
SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), m_max(max) { }
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] float getValue() const { return this->m_value; }
private:
float m_value;
float m_min, m_max;
};
class ColorPicker : public Widget {
public:
explicit ColorPicker(ImColor defaultColor);
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]] ImColor getColor() const;
private:
std::array<float, 4> m_value;
};
class DropDown : public Widget {
public:
explicit DropDown(const std::vector<std::string> &items, const std::vector<nlohmann::json> &settingsValues) : m_items(items), m_settingsValues(settingsValues) { }
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]]
const nlohmann::json& getValue() const;
private:
std::vector<std::string> m_items;
std::vector<nlohmann::json> m_settingsValues;
int m_value = 0;
};
class TextBox : public Widget {
public:
explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) { }
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]]
const std::string& getValue() const { return this->m_value; }
private:
std::string m_value;
};
class FilePicker : public Widget {
public:
bool draw(const std::string &name) override;
void load(const nlohmann::json &data) override;
nlohmann::json store() override;
[[nodiscard]]
std::fs::path getPath() const { return this->m_value; }
private:
std::string m_value;
};
}
namespace impl {
using Callback = std::function<bool(const std::string &, nlohmann::json &)>;
struct Entry {
std::string name;
bool requiresRestart;
Callback callback;
std::string unlocalizedName;
std::unique_ptr<Widgets::Widget> widget;
};
struct SubCategory {
std::string unlocalizedName;
std::vector<Entry> entries;
};
struct Category {
std::string name;
size_t slot = 0;
bool operator<(const Category &other) const {
return name < other.name;
}
explicit operator const std::string &() const {
return name;
}
std::string unlocalizedName;
std::string unlocalizedDescription;
std::vector<SubCategory> subCategories;
};
void load();
void store();
void clear();
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);
std::vector<Category> &getSettings();
nlohmann::json& getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue);
nlohmann::json &getSettingsData();
Widgets::Widget* add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget);
}
template<std::derived_from<Widgets::Widget> T>
Widgets::Widget::Interface& add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, auto && ... args) {
return impl::add(
unlocalizedCategory,
unlocalizedSubCategory,
unlocalizedName,
std::make_unique<T>(std::forward<decltype(args)>(args)...)
)->getInterface();
}
/**
* @brief Adds a new integer setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a new string setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a new string list setting entry
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @param callback The callback that will be called when the settings item in the preferences window is rendered
* @param requiresRestart Whether the setting requires a restart to take effect
*/
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const impl::Callback &callback, bool requiresRestart = false);
/**
* @brief Adds a description to a given category
* @param unlocalizedCategory The name of the category
* @param unlocalizedCategoryDescription The description of the category
*/
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription);
/**
* @brief Writes a integer value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value);
/**
* @brief Writes a string value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
/**
* @brief Writes a string list value to the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param value The value to write
*/
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &value);
/**
* @brief Reads an integer value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
i64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue);
/**
* @brief Reads a string value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
/**
* @brief Reads a string list value from the settings file
* @param unlocalizedCategory The category of the setting
* @param unlocalizedName The name of the setting
* @param defaultValue The default value of the setting
* @return The value of the setting
*/
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue = {});
void setCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedDescription);
nlohmann::json read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue);
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &value);
}
/* Command Palette Command Registry. Allows adding of new commands to the command palette */

View File

@ -8,6 +8,7 @@
#include <filesystem>
#include <thread>
#if defined(OS_WEB)
#include <jthread.hpp>
#include <emscripten.h>
@ -26,23 +27,14 @@ namespace hex {
namespace impl {
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) {
nlohmann::json& getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue) {
auto &settings = getSettingsData();
if (!settings.contains(unlocalizedCategory)) return {};
if (!settings[unlocalizedCategory].contains(unlocalizedName)) return {};
if (!settings.contains(unlocalizedCategory))
settings[unlocalizedCategory] = {};
if (!settings[unlocalizedCategory].contains(unlocalizedName))
settings[unlocalizedCategory][unlocalizedName] = defaultValue;
return settings[unlocalizedCategory][unlocalizedName];
}
@ -119,135 +111,258 @@ namespace hex {
}
#endif
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;
template<typename T>
static T* insertOrGetEntry(std::vector<T> &vector, const std::string &unlocalizedName) {
T *foundEntry = nullptr;
for (auto &entry : vector) {
if (entry.unlocalizedName == unlocalizedName) {
foundEntry = &entry;
break;
}
}
return found;
if (foundEntry == nullptr) {
if (unlocalizedName.empty())
foundEntry = &*vector.emplace(vector.begin(), unlocalizedName);
else
foundEntry = &vector.emplace_back(unlocalizedName);
}
return foundEntry;
}
std::vector<Category> &getSettings() {
static std::vector<impl::Category> categories;
return categories;
}
Widgets::Widget* add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget) {
auto category = impl::insertOrGetEntry(impl::getSettings(), unlocalizedCategory);
auto subCategory = impl::insertOrGetEntry(category->subCategories, unlocalizedSubCategory);
auto entry = impl::insertOrGetEntry(subCategory->entries, unlocalizedName);
entry->widget = std::move(widget);
entry->widget->load(getSetting(unlocalizedCategory, unlocalizedName, entry->widget->store()));
return entry->widget.get();
}
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new integer setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
void setCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedDescription) {
auto category = impl::insertOrGetEntry(impl::getSettings(), unlocalizedCategory);
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
if (!json[unlocalizedCategory].contains(unlocalizedName) || !json[unlocalizedCategory][unlocalizedName].is_number())
json[unlocalizedCategory][unlocalizedName] = int(defaultValue);
category->unlocalizedDescription = unlocalizedDescription;
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new string setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
nlohmann::json read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue) {
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
if (setting.is_number() && defaultValue.is_boolean())
setting = setting.get<int>() != 0;
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
if (!json[unlocalizedCategory].contains(unlocalizedName) || !json[unlocalizedCategory][unlocalizedName].is_string())
json[unlocalizedCategory][unlocalizedName] = std::string(defaultValue);
return setting;
}
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const impl::Callback &callback, bool requiresRestart) {
log::debug("Registered new string array setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
impl::getCategoryEntry(unlocalizedCategory)->second.emplace_back(impl::Entry { unlocalizedName, requiresRestart, callback });
auto &json = impl::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 write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &value) {
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
}
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription) {
impl::getCategoryDescriptions()[unlocalizedCategory] = unlocalizedCategoryDescription;
}
namespace Widgets {
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value) {
auto &json = impl::getSettingsData();
bool Checkbox::draw(const std::string &name) {
return ImGui::Checkbox(name.c_str(), &this->m_value);
}
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
void Checkbox::load(const nlohmann::json &data) {
if (data.is_number()) {
this->m_value = data.get<int>() != 0;
} else if (data.is_boolean()) {
this->m_value = data.get<bool>();
} else {
log::warn("Invalid data type loaded from settings for checkbox!");
}
}
json[unlocalizedCategory][unlocalizedName] = value;
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value) {
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
json[unlocalizedCategory][unlocalizedName] = value;
}
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &value) {
auto &json = impl::getSettingsData();
if (!json.contains(unlocalizedCategory))
json[unlocalizedCategory] = nlohmann::json::object();
json[unlocalizedCategory][unlocalizedName] = value;
}
nlohmann::json Checkbox::store() {
return this->m_value;
}
i64 read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue) {
auto &json = impl::getSettingsData();
bool SliderInteger::draw(const std::string &name) {
return ImGui::SliderInt(name.c_str(), &this->m_value, this->m_min, this->m_max);
}
if (!json.contains(unlocalizedCategory))
return defaultValue;
if (!json[unlocalizedCategory].contains(unlocalizedName))
return defaultValue;
void SliderInteger::load(const nlohmann::json &data) {
if (data.is_number_integer()) {
this->m_value = data.get<int>();
} else {
log::warn("Invalid data type loaded from settings for slider!");
}
}
if (!json[unlocalizedCategory][unlocalizedName].is_number())
json[unlocalizedCategory][unlocalizedName] = defaultValue;
nlohmann::json SliderInteger::store() {
return this->m_value;
}
return json[unlocalizedCategory][unlocalizedName].get<i64>();
}
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue) {
auto &json = impl::getSettingsData();
bool SliderFloat::draw(const std::string &name) {
return ImGui::SliderFloat(name.c_str(), &this->m_value, this->m_min, this->m_max);
}
if (!json.contains(unlocalizedCategory))
return defaultValue;
if (!json[unlocalizedCategory].contains(unlocalizedName))
return defaultValue;
void SliderFloat::load(const nlohmann::json &data) {
if (data.is_number()) {
this->m_value = data.get<float>();
} else {
log::warn("Invalid data type loaded from settings for slider!");
}
}
if (!json[unlocalizedCategory][unlocalizedName].is_string())
json[unlocalizedCategory][unlocalizedName] = defaultValue;
nlohmann::json SliderFloat::store() {
return this->m_value;
}
return json[unlocalizedCategory][unlocalizedName].get<std::string>();
}
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue) {
auto &json = impl::getSettingsData();
ColorPicker::ColorPicker(ImColor defaultColor) {
this->m_value = {
defaultColor.Value.x,
defaultColor.Value.y,
defaultColor.Value.z,
defaultColor.Value.w
};
}
if (!json.contains(unlocalizedCategory))
return defaultValue;
if (!json[unlocalizedCategory].contains(unlocalizedName))
return defaultValue;
bool ColorPicker::draw(const std::string &name) {
return ImGui::ColorEdit4(name.c_str(), this->m_value.data(), ImGuiColorEditFlags_NoInputs);
}
if (!json[unlocalizedCategory][unlocalizedName].is_array())
json[unlocalizedCategory][unlocalizedName] = defaultValue;
void ColorPicker::load(const nlohmann::json &data) {
if (data.is_number()) {
ImColor color(data.get<u32>());
this->m_value = { color.Value.x, color.Value.y, color.Value.z, color.Value.w };
} else {
log::warn("Invalid data type loaded from settings for color picker!");
}
}
if (!json[unlocalizedCategory][unlocalizedName].array().empty() && !json[unlocalizedCategory][unlocalizedName][0].is_string())
json[unlocalizedCategory][unlocalizedName] = defaultValue;
nlohmann::json ColorPicker::store() {
ImColor color(this->m_value[0], this->m_value[1], this->m_value[2], this->m_value[3]);
return ImU32(color);
}
ImColor ColorPicker::getColor() const {
return { this->m_value[0], this->m_value[1], this->m_value[2], this->m_value[3] };
}
bool DropDown::draw(const std::string &name) {
const char *preview = "";
if (size_t(this->m_value) < this->m_items.size())
preview = this->m_items[this->m_value].c_str();
bool changed = false;
if (ImGui::BeginCombo(name.c_str(), LangEntry(preview))) {
int index = 0;
for (const auto &item : this->m_items) {
bool selected = (index == this->m_value);
if (ImGui::Selectable(LangEntry(item), selected)) {
this->m_value = index;
changed = true;
}
if (selected)
ImGui::SetItemDefaultFocus();
index += 1;
}
ImGui::EndCombo();
}
return changed;
}
void DropDown::load(const nlohmann::json &data) {
this->m_value = 0;
int index = 0;
for (const auto &item : this->m_settingsValues) {
if (item == data) {
this->m_value = index;
break;
}
index += 1;
}
}
nlohmann::json DropDown::store() {
if (size_t(this->m_value) >= this->m_items.size())
return nullptr;
return this->m_settingsValues[this->m_value];
}
const nlohmann::json& DropDown::getValue() const {
return this->m_settingsValues[this->m_value];
}
bool TextBox::draw(const std::string &name) {
return ImGui::InputText(name.c_str(), this->m_value);
}
void TextBox::load(const nlohmann::json &data) {
if (data.is_string()) {
this->m_value = data.get<std::string>();
} else {
log::warn("Invalid data type loaded from settings for text box!");
}
}
nlohmann::json TextBox::store() {
return this->m_value;
}
bool FilePicker::draw(const std::string &name) {
bool changed = false;
if (ImGui::InputText("##font_path", this->m_value)) {
changed = true;
}
ImGui::SameLine();
if (ImGui::IconButton(ICON_VS_FOLDER_OPENED, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
return fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } },
[&](const std::fs::path &path) {
this->m_value = wolv::util::toUTF8String(path);
});
}
ImGui::SameLine();
ImGui::TextFormatted("{}", name);
return changed;
}
void FilePicker::load(const nlohmann::json &data) {
if (data.is_string()) {
this->m_value = data.get<std::string>();
} else {
log::warn("Invalid data type loaded from settings for file picker!");
}
}
nlohmann::json FilePicker::store() {
return this->m_value;
}
return json[unlocalizedCategory][unlocalizedName].get<std::vector<std::string>>();
}
}

View File

@ -71,7 +71,7 @@ namespace hex::init {
ImHexApi::System::impl::addInitArgument("update-available", latestVersion.data());
// Check if there is a telemetry uuid
std::string uuid = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.uuid", "");
std::string uuid = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.uuid", "").get<std::string>();
if(uuid.empty()) {
// Generate a new uuid
uuid = wolv::hash::generateUUID();
@ -182,7 +182,7 @@ namespace hex::init {
// Load font related settings
{
std::fs::path fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "");
std::fs::path fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "").get<std::string>();
if (!fontFile.empty()) {
if (!wolv::io::fs::exists(fontFile) || !wolv::io::fs::isRegularFile(fontFile)) {
log::warn("Custom font file {} not found! Falling back to default font.", wolv::util::toUTF8String(fontFile));
@ -210,7 +210,7 @@ namespace hex::init {
// If a custom font has been loaded now, also load the font size
float fontSize = defaultFontSize;
if (!fontFile.empty()) {
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13) * ImHexApi::System::getGlobalScale();
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13).get<int>() * ImHexApi::System::getGlobalScale();
}
ImHexApi::System::impl::setFontSize(fontSize);
@ -310,7 +310,7 @@ namespace hex::init {
IM_DELETE(fonts);
// Disable unicode support in settings
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.load_all_unicode_chars", false);
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.font.load_all_unicode_chars", false);
// Try to load the font atlas again
return loadFontsImpl(false);
@ -331,7 +331,7 @@ namespace hex::init {
// Check if unicode support is enabled in the settings and that the user doesn't use the No GPU version on Windows
// The Mesa3D software renderer on Windows identifies itself as "VMware, Inc."
bool shouldLoadUnicode =
ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.load_all_unicode_chars", false) &&
ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.font.load_all_unicode_chars", false) &&
ImHexApi::System::getGPUVendor() != "VMware, Inc.";
return loadFontsImpl(shouldLoadUnicode);
@ -361,7 +361,7 @@ namespace hex::init {
ImHexApi::System::getCustomFontPath().clear();
ImHexApi::Messaging::impl::getHandlers().clear();
ContentRegistry::Settings::impl::getEntries().clear();
ContentRegistry::Settings::impl::getSettings().clear();
ContentRegistry::Settings::impl::getSettingsData().clear();
ContentRegistry::CommandPaletteCommands::impl::getEntries().clear();
@ -567,33 +567,11 @@ namespace hex::init {
}
bool configureUIScale() {
float interfaceScaling;
switch (ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling", 0)) {
default:
case 0:
interfaceScaling = ImHexApi::System::getNativeScale();
break;
case 1:
interfaceScaling = 0.5F;
break;
case 2:
interfaceScaling = 1.0F;
break;
case 3:
interfaceScaling = 1.5F;
break;
case 4:
interfaceScaling = 2.0F;
break;
case 5:
interfaceScaling = 3.0F;
break;
case 6:
interfaceScaling = 4.0F;
break;
}
int interfaceScaling = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling", 0).get<float>() * 10;
if (interfaceScaling == 0)
interfaceScaling = ImHexApi::System::getNativeScale();
ImHexApi::System::impl::setGlobalScale(interfaceScaling);
ImHexApi::System::impl::setGlobalScale(interfaceScaling / 10.0F);
return true;
}
@ -608,7 +586,7 @@ namespace hex::init {
return true;
}
// run all exit taks, and print to console
// Run all exit tasks, and print to console
void runExitTasks() {
for (const auto &[name, task, async] : init::getExitTasks()) {
task();

View File

@ -1037,7 +1037,7 @@ namespace hex {
io.FontGlobalScale = 1.0F;
if (glfwGetPrimaryMonitor() != nullptr) {
bool multiWindowEnabled = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.multi_windows", 0) != 0;
bool multiWindowEnabled = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.multi_windows", false);
if (multiWindowEnabled)
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;

View File

@ -14,7 +14,7 @@ namespace hex::plugin::builtin {
PopupTelemetryRequest()
: hex::Popup<PopupTelemetryRequest>("hex.builtin.common.question", false) {
// Check if there is a telemetry uuid
this->m_uuid = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.uuid", "");
this->m_uuid = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.uuid", "").get<std::string>();
if(this->m_uuid.empty()) {
// Generate a new uuid
this->m_uuid = wolv::hash::generateUUID();

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "Allgemein",
"hex.builtin.setting.general.auto_load_patterns": "Automatisches Laden unterstützter Pattern",
"hex.builtin.setting.general.server_contact": "Update checks und Statistiken zulassen",
"hex.builtin.setting.general.load_all_unicode_chars": "Alle Unicode Zeichen laden",
"hex.builtin.setting.font.load_all_unicode_chars": "Alle Unicode Zeichen laden",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "",
"hex.builtin.setting.general.show_tips": "Tipps beim Start anzeigen",

View File

@ -516,13 +516,17 @@
"hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more",
"hex.builtin.setting.folders.remove_folder": "Remove currently selected folder from list",
"hex.builtin.setting.font": "Font",
"hex.builtin.setting.font.glyphs": "Glyphs",
"hex.builtin.setting.font.custom_font": "Custom Font",
"hex.builtin.setting.font.font_path": "Custom Font Path",
"hex.builtin.setting.font.font_size": "Font Size",
"hex.builtin.setting.font.font_size.tooltip": "The font size can only be adjusted when a custom font has been selected above.\n\nThis is because ImHex uses a pixel-perfect bitmap font by default. Scaling it by any non-integer factor will only cause it to become blurry.",
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.patterns": "Patterns",
"hex.builtin.setting.general.network": "Network",
"hex.builtin.setting.general.auto_load_patterns": "Auto-load supported pattern",
"hex.builtin.setting.general.server_contact": "Enable update checks and usage statistics",
"hex.builtin.setting.general.load_all_unicode_chars": "Load all unicode characters",
"hex.builtin.setting.font.load_all_unicode_chars": "Load all unicode characters",
"hex.builtin.setting.general.network_interface": "Enable network interface",
"hex.builtin.setting.general.save_recent_providers": "Save recently used providers",
"hex.builtin.setting.general.show_tips": "Show tips on startup",
@ -533,7 +537,7 @@
"hex.builtin.setting.hex_editor.bytes_per_row": "Bytes per row",
"hex.builtin.setting.hex_editor.char_padding": "Extra character cell padding",
"hex.builtin.setting.hex_editor.highlight_color": "Selection highlight color",
"hex.builtin.setting.hex_editor.sync_scrolling": "Synchronize editor position",
"hex.builtin.setting.hex_editor.sync_scrolling": "Synchronize editor scroll position",
"hex.builtin.setting.imhex": "ImHex",
"hex.builtin.setting.imhex.recent_files": "Recent Files",
"hex.builtin.setting.interface": "Interface",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.auto_load_patterns": "Cargar automáticamente patterns soportados",
"hex.builtin.setting.general.server_contact": "",
"hex.builtin.setting.general.load_all_unicode_chars": "Cargar todos los caracteres unicode",
"hex.builtin.setting.font.load_all_unicode_chars": "Cargar todos los caracteres unicode",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "Guardar proveedores recientemente utilizados",
"hex.builtin.setting.general.show_tips": "Mostrar consejos al inicio",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "Generali",
"hex.builtin.setting.general.auto_load_patterns": "Auto-caricamento del pattern supportato",
"hex.builtin.setting.general.server_contact": "",
"hex.builtin.setting.general.load_all_unicode_chars": "",
"hex.builtin.setting.font.load_all_unicode_chars": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "",
"hex.builtin.setting.general.show_tips": "Mostra consigli all'avvio",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "基本",
"hex.builtin.setting.general.auto_load_patterns": "対応するパターンを自動で読み込む",
"hex.builtin.setting.general.server_contact": "",
"hex.builtin.setting.general.load_all_unicode_chars": "",
"hex.builtin.setting.font.load_all_unicode_chars": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "",
"hex.builtin.setting.general.show_tips": "起動時に豆知識を表示",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "일반",
"hex.builtin.setting.general.auto_load_patterns": "지원하는 패턴 자동으로 로드",
"hex.builtin.setting.general.server_contact": "",
"hex.builtin.setting.general.load_all_unicode_chars": "",
"hex.builtin.setting.font.load_all_unicode_chars": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "",
"hex.builtin.setting.general.show_tips": "시작 시 팁 표시",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.auto_load_patterns": "Padrão compatível com carregamento automático",
"hex.builtin.setting.general.server_contact": "",
"hex.builtin.setting.general.load_all_unicode_chars": "",
"hex.builtin.setting.font.load_all_unicode_chars": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.save_recent_providers": "",
"hex.builtin.setting.general.show_tips": "Mostrar dicas na inicialização",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "通用",
"hex.builtin.setting.general.auto_load_patterns": "自动加载支持的模式",
"hex.builtin.setting.general.server_contact": "启用更新检查和使用统计",
"hex.builtin.setting.general.load_all_unicode_chars": "加载所有 Unicode 字符",
"hex.builtin.setting.font.load_all_unicode_chars": "加载所有 Unicode 字符",
"hex.builtin.setting.general.network_interface": "启动网络",
"hex.builtin.setting.general.save_recent_providers": "保存最近使用的提供者",
"hex.builtin.setting.general.show_tips": "在启动时显示每日提示",

View File

@ -516,7 +516,7 @@
"hex.builtin.setting.general": "一般",
"hex.builtin.setting.general.auto_load_patterns": "自動載入支援的模式",
"hex.builtin.setting.general.server_contact": "啟用檢查更新和使用統計",
"hex.builtin.setting.general.load_all_unicode_chars": "載入所有 unicode 字元",
"hex.builtin.setting.font.load_all_unicode_chars": "載入所有 unicode 字元",
"hex.builtin.setting.general.network_interface": "啟用網路介面",
"hex.builtin.setting.general.save_recent_providers": "儲存近期使用過的提供者",
"hex.builtin.setting.general.show_tips": "啟動時顯示提示",

View File

@ -54,7 +54,7 @@ namespace hex::plugin::builtin {
void registerBackgroundServices() {
EventManager::subscribe<EventSettingsChanged>([]{
networkInterfaceServiceEnabled = bool(ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.network_interface", 0));
networkInterfaceServiceEnabled = bool(ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.network_interface", false));
});
ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.network_interface"_lang, handleNetworkInterfaceService);

View File

@ -27,7 +27,7 @@ namespace hex::plugin::builtin::recent {
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) {
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", true)) {
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
@ -59,7 +59,7 @@ namespace hex::plugin::builtin::recent {
// 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) {
if (ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", true)) {
auto fileName = hex::format("{:%y%m%d_%H%M%S}.json", fmt::gmtime(std::chrono::system_clock::now()));
auto projectFileName = ProjectFile::getPath().filename();

View File

@ -1,12 +1,10 @@
#include <hex/api/imhex_api.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/api/theme_manager.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/http_requests.hpp>
#include <hex/helpers/logger.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
@ -14,230 +12,234 @@
#include <nlohmann/json.hpp>
#include <ui/pattern_drawer.hpp>
#include <wolv/utils/guards.hpp>
namespace {
std::vector<std::fs::path> userFolders;
void loadUserFoldersFromSetting(const std::vector<std::string> &paths) {
userFolders.clear();
for (const auto &path : paths) {
userFolders.emplace_back(
reinterpret_cast<const char8_t*>(path.data()),
reinterpret_cast<const char8_t*>(path.data() + path.size())
);
}
}
}
#include <wolv/utils/string.hpp>
namespace hex::plugin::builtin {
namespace {
/*
Values of this setting:
0 - do not check for updates on startup
1 - check for updates on startup
2 - default value - ask the user if he wants to check for updates. This value should only be encountered on the first startup.
*/
class ServerContactWidget : public ContentRegistry::Settings::Widgets::Widget {
public:
bool draw(const std::string &name) override {
bool enabled = this->m_value == 1;
if (ImGui::Checkbox(name.data(), &enabled)) {
this->m_value = enabled ? 1 : 0;
return true;
}
return false;
}
void load(const nlohmann::json &data) override {
if (data.is_number())
this->m_value = data.get<int>();
}
nlohmann::json store() override {
return this->m_value;
}
private:
u32 m_value = 2;
};
class FPSWidget : public ContentRegistry::Settings::Widgets::Widget {
public:
bool draw(const std::string &name) override {
auto format = [this] -> std::string {
if (this->m_value > 200)
return "hex.builtin.setting.interface.fps.unlocked"_lang;
else if (this->m_value < 15)
return "hex.builtin.setting.interface.fps.native"_lang;
else
return "%d FPS";
}();
if (ImGui::SliderInt(name.data(), &this->m_value, 14, 201, format.c_str(), ImGuiSliderFlags_AlwaysClamp)) {
return true;
}
return false;
}
void load(const nlohmann::json &data) override {
if (data.is_number())
this->m_value = data.get<int>();
}
nlohmann::json store() override {
return this->m_value;
}
private:
int m_value = 60;
};
class UserFolderWidget : public ContentRegistry::Settings::Widgets::Widget {
public:
bool draw(const std::string &) override {
bool result = false;
if (!ImGui::BeginListBox("", ImVec2(-38, -10))) {
return false;
} else {
for (size_t n = 0; n < this->m_paths.size(); n++) {
const bool isSelected = (this->m_itemIndex == n);
if (ImGui::Selectable(wolv::util::toUTF8String(this->m_paths[n]).c_str(), isSelected)) {
this->m_itemIndex = n;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndListBox();
}
ImGui::SameLine();
ImGui::BeginGroup();
if (ImGui::IconButton(ICON_VS_NEW_FOLDER, ImGui::GetStyleColorVec4(ImGuiCol_Text), ImVec2(30, 30))) {
fs::openFileBrowser(fs::DialogMode::Folder, {}, [&](const std::fs::path &path) {
if (std::find(this->m_paths.begin(), this->m_paths.end(), path) == this->m_paths.end()) {
this->m_paths.emplace_back(path);
ImHexApi::System::setAdditionalFolderPaths(this->m_paths);
result = true;
}
});
}
ImGui::InfoTooltip("hex.builtin.setting.folders.add_folder"_lang);
if (ImGui::IconButton(ICON_VS_REMOVE_CLOSE, ImGui::GetStyleColorVec4(ImGuiCol_Text), ImVec2(30, 30))) {
if (!this->m_paths.empty()) {
this->m_paths.erase(std::next(this->m_paths.begin(), this->m_itemIndex));
ImHexApi::System::setAdditionalFolderPaths(this->m_paths);
result = true;
}
}
ImGui::InfoTooltip("hex.builtin.setting.folders.remove_folder"_lang);
ImGui::EndGroup();
return result;
}
void load(const nlohmann::json &data) override {
if (data.is_array()) {
std::vector<std::string> pathStrings = data;
for (const auto &pathString : pathStrings) {
this->m_paths.emplace_back(pathString);
}
}
}
nlohmann::json store() override {
std::vector<std::string> pathStrings;
for (const auto &path : this->m_paths) {
pathStrings.push_back(wolv::util::toUTF8String(path));
}
return pathStrings;
}
private:
u32 m_itemIndex = 0;
std::vector<std::fs::path> m_paths;
};
class ScalingWidget : public ContentRegistry::Settings::Widgets::Widget {
public:
bool draw(const std::string &name) override {
auto format = [this] -> std::string {
if (this->m_value == 0)
return "hex.builtin.setting.interface.scaling.native"_lang;
else
return "x%.1f";
}();
if (ImGui::SliderFloat(name.data(), &this->m_value, 0, 10, format.c_str(), ImGuiSliderFlags_AlwaysClamp)) {
return true;
}
return false;
}
void load(const nlohmann::json &data) override {
if (data.is_number())
this->m_value = data.get<float>();
}
nlohmann::json store() override {
return this->m_value;
}
private:
float m_value = 0;
};
}
void registerSettings() {
/* General */
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.show_tips", 1, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
namespace Widgets = ContentRegistry::Settings::Widgets;
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.auto_load_patterns", 1, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.sync_pattern_source", 0, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.load_all_unicode_chars", 0, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.save_recent_providers", 1, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.network_interface", 0, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
/*
Values of this setting :
0 - do not check for updates on startup
1 - check for updates on startup
2 - default value - ask the user if he wants to check for updates. This value should only be encountered on the first startup.
*/
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.server_contact", 2, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting) == 1;
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", 1, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting) == 1;
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
});
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.show_tips", true);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.save_recent_providers", true);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.auto_load_patterns", true);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.sync_pattern_source", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.network_interface", false);
ContentRegistry::Settings::add<ServerContactWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.server_contact");
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.upload_crash_logs", true);
/* Interface */
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", "Dark", [](auto name, nlohmann::json &setting) {
static auto selection = static_cast<std::string>(setting);
auto themeNames = ThemeManager::getThemeNames();
std::vector<nlohmann::json> themeJsons = { };
for (const auto &themeName : themeNames)
themeJsons.emplace_back(themeName);
const auto themeNames = ThemeManager::getThemeNames();
bool changed = false;
themeNames.emplace(themeNames.begin(), ThemeManager::NativeTheme);
themeJsons.emplace(themeJsons.begin(), ThemeManager::NativeTheme);
if (ImGui::BeginCombo(name.data(), selection.c_str())) {
if (ImGui::Selectable(ThemeManager::NativeTheme, selection == ThemeManager::NativeTheme)) {
selection = ThemeManager::NativeTheme;
setting = selection;
ImHexApi::System::enableSystemThemeDetection(true);
changed = true;
}
ContentRegistry::Settings::add<Widgets::DropDown>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.color",
themeNames,
themeJsons).setChangedCallback([](auto &widget) {
auto dropDown = static_cast<Widgets::DropDown *>(&widget);
for (const auto &themeName : themeNames) {
if (ImGui::Selectable(themeName.c_str(), selection == themeName)) {
selection = themeName;
setting = selection;
ImHexApi::System::enableSystemThemeDetection(false);
ThemeManager::changeTheme(selection);
changed = true;
}
}
if (dropDown->getValue() == ThemeManager::NativeTheme)
ImHexApi::System::enableSystemThemeDetection(true);
else {
ImHexApi::System::enableSystemThemeDetection(false);
ThemeManager::changeTheme(dropDown->getValue());
}
});
ImGui::EndCombo();
}
ContentRegistry::Settings::add<ScalingWidget>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.scaling").requiresRestart();
return changed;
});
std::vector<std::string> languageNames;
std::vector<nlohmann::json> languageCodes;
ContentRegistry::Settings::add(
"hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling", 0, [](auto name, nlohmann::json &setting) {
static int selection = static_cast<int>(setting);
for (auto &[languageCode, languageName] : LangEntry::getSupportedLanguages()) {
languageNames.emplace_back(languageName);
languageCodes.emplace_back(languageCode);
}
const char *scaling[] = {
"hex.builtin.setting.interface.scaling.native"_lang,
"hex.builtin.setting.interface.scaling.x0_5"_lang,
"hex.builtin.setting.interface.scaling.x1_0"_lang,
"hex.builtin.setting.interface.scaling.x1_5"_lang,
"hex.builtin.setting.interface.scaling.x2_0"_lang,
};
ContentRegistry::Settings::add<Widgets::DropDown>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.language", languageNames, languageCodes);
if (ImGui::Combo(name.data(), &selection, scaling, IM_ARRAYSIZE(scaling))) {
setting = selection;
return true;
}
return false;
},
true);
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "en-US", [](auto name, nlohmann::json &setting) {
auto &languages = LangEntry::getSupportedLanguages();
if (!languages.contains(setting.get<std::string>()))
setting = "en-US";
bool changed = false;
if (ImGui::BeginCombo(name.data(), languages.at(setting.get<std::string>()).c_str())) {
for (auto &[languageCode, languageName] : languages) {
if (ImGui::Selectable(languageName.c_str(), setting == languageCode)) {
setting = languageCode;
changed = true;
}
}
ImGui::EndCombo();
}
return changed;
});
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.wiki_explain_language", "en", [](auto name, nlohmann::json &setting) {
static auto lang = std::string(setting);
if (ImGui::InputText(name.data(), lang, ImGuiInputTextFlags_CharsNoBlank)) {
// Remove trailing null bytes
setting = std::string(lang.c_str());
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.fps", 60, [](auto name, nlohmann::json &setting) {
static int fps = static_cast<int>(setting);
auto format = [] -> std::string {
if (fps > 200)
return "hex.builtin.setting.interface.fps.unlocked"_lang;
else if (fps < 15)
return "hex.builtin.setting.interface.fps.native"_lang;
else
return "%d FPS";
}();
if (ImGui::SliderInt(name.data(), &fps, 14, 201, format.c_str(), ImGuiSliderFlags_AlwaysClamp)) {
setting = fps;
return true;
}
return false;
});
ContentRegistry::Settings::add<Widgets::TextBox>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.wiki_explain_language", "en");
ContentRegistry::Settings::add<FPSWidget>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.fps");
#if defined (OS_LINUX)
constexpr static auto MultiWindowSupportEnabledDefault = 0;
@ -245,245 +247,64 @@ namespace hex::plugin::builtin {
constexpr static auto MultiWindowSupportEnabledDefault = 1;
#endif
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.multi_windows", MultiWindowSupportEnabledDefault, [](auto name, nlohmann::json &setting) {
static bool enabled = static_cast<int>(setting);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.multi_windows", MultiWindowSupportEnabledDefault).requiresRestart();
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.interface", "", "hex.builtin.setting.interface.restore_window_pos", false);
if (ImGui::Checkbox(name.data(), &enabled)) {
setting = static_cast<int>(enabled);
return true;
}
return false;
}, true);
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color", 0x60C08080, [](auto name, nlohmann::json &setting) {
static auto color = static_cast<color_t>(setting);
std::array<float, 4> colorArray = {
((color >> 0) & 0x000000FF) / float(0xFF),
((color >> 8) & 0x000000FF) / float(0xFF),
((color >> 16) & 0x000000FF) / float(0xFF),
((color >> 24) & 0x000000FF) / float(0xFF)
};
if (ImGui::ColorEdit4(name.data(), colorArray.data(), ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoDragDrop | ImGuiColorEditFlags_NoInputs)) {
color =
(color_t(colorArray[0] * 0xFF) << 0) |
(color_t(colorArray[1] * 0xFF) << 8) |
(color_t(colorArray[2] * 0xFF) << 16) |
(color_t(colorArray[3] * 0xFF) << 24);
setting = color;
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.sync_scrolling", 0, [](auto name, nlohmann::json &setting) {
static bool syncScrolling = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &syncScrolling)) {
setting = static_cast<int>(syncScrolling);
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.byte_padding", 0, [](auto name, nlohmann::json &setting) {
static int padding = static_cast<int>(setting);
if (ImGui::SliderInt(name.data(), &padding, 0, 50)) {
setting = padding;
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.char_padding", 0, [](auto name, nlohmann::json &setting) {
static int padding = static_cast<int>(setting);
if (ImGui::SliderInt(name.data(), &padding, 0, 50)) {
setting = padding;
return true;
}
return false;
});
ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.restore_window_pos", 0, [](auto name, nlohmann::json &setting) {
static bool restoreWindowPos = static_cast<int>(setting);
if (ImGui::Checkbox(name.data(), &restoreWindowPos)) {
setting = static_cast<int>(restoreWindowPos);
return true;
}
return false;
});
ContentRegistry::Settings::add<Widgets::ColorPicker>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.highlight_color", ImColor(0x80, 0x80, 0xC0, 0x60));
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.sync_scrolling", false);
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.byte_padding", 0, 0, 50);
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.char_padding", 0, 0, 50);
/* Fonts */
static std::string fontPath;
ContentRegistry::Settings::add(
"hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "", [](auto name, nlohmann::json &setting) {
fontPath = static_cast<std::string>(setting);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.font", "hex.builtin.setting.font.glyphs", "hex.builtin.setting.font.load_all_unicode_chars", false);
auto fontPathSetting = ContentRegistry::Settings::add<Widgets::FilePicker>("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_path").requiresRestart();
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_size", 13, 0, 100)
.requiresRestart()
.setEnabledCallback([fontPathSetting]{
auto &filePicker = static_cast<Widgets::FilePicker &>(fontPathSetting.getWidget());
if (ImGui::InputText("##font_path", fontPath)) {
setting = fontPath;
return true;
}
ImGui::SameLine();
if (ImGui::IconButton(ICON_VS_FOLDER_OPENED, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
return fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } },
[&](const std::fs::path &path) {
fontPath = wolv::util::toUTF8String(path);
setting = fontPath;
});
}
ImGui::SameLine();
ImGui::TextFormatted("{}", name);
return false;
},
true);
ContentRegistry::Settings::add(
"hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13, [](auto name, nlohmann::json &setting) {
static int fontSize = static_cast<int>(setting);
ImGui::BeginDisabled(fontPath.empty());
ON_SCOPE_EXIT { ImGui::EndDisabled(); };
if (ImGui::SliderInt(name.data(), &fontSize, 0, 100, "%d", ImGuiSliderFlags_NoInput)) {
setting = fontSize;
return true;
}
if (fontPath.empty() && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetNextWindowSize(scaled(ImVec2(300, 0)));
ImGui::BeginTooltip();
ImGui::TextFormattedWrapped("{}", "hex.builtin.setting.font.font_size.tooltip"_lang);
ImGui::EndTooltip();
}
return false;
},
true);
return !filePicker.getPath().empty();
});
/* Folders */
static const std::string dirsSetting { "hex.builtin.setting.folders" };
ContentRegistry::Settings::addCategoryDescription(dirsSetting, "hex.builtin.setting.folders.description");
ContentRegistry::Settings::add(dirsSetting, dirsSetting, std::vector<std::string> {}, [](auto name, nlohmann::json &setting) {
hex::unused(name);
static size_t currentItemIndex = [&setting] {loadUserFoldersFromSetting(setting); return 0; }();
auto saveToSetting = [&setting] {
std::vector<std::string> folderStrings;
for (const auto &folder : userFolders) {
auto utfString = folder.u8string();
// JSON stores char8_t as array, char8_t is not supported as of now
folderStrings.emplace_back(reinterpret_cast<const char *>(&utfString.front()), reinterpret_cast<const char *>(std::next(&utfString.back())));
}
setting = folderStrings;
ImHexApi::System::setAdditionalFolderPaths(userFolders);
};
bool result = false;
if (!ImGui::BeginListBox("", ImVec2(-38, -FLT_MIN))) {
return false;
} else {
for (size_t n = 0; n < userFolders.size(); n++) {
const bool isSelected = (currentItemIndex == n);
if (ImGui::Selectable(wolv::util::toUTF8String(userFolders.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))) {
fs::openFileBrowser(fs::DialogMode::Folder, {}, [&](const std::fs::path &path) {
if (std::find(userFolders.begin(), userFolders.end(), path) == userFolders.end()) {
userFolders.emplace_back(path);
saveToSetting();
result = true;
}
});
}
ImGui::InfoTooltip("hex.builtin.setting.folders.add_folder"_lang);
if (ImGui::IconButton(ICON_VS_REMOVE_CLOSE, ImGui::GetCustomColorVec4(ImGuiCustomCol_DescButton), ImVec2(30, 30))) {
if (!userFolders.empty()) {
userFolders.erase(std::next(userFolders.begin(), currentItemIndex));
saveToSetting();
result = true;
}
}
ImGui::InfoTooltip("hex.builtin.setting.folders.remove_folder"_lang);
ImGui::EndGroup();
return result;
});
ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.folders", "hex.builtin.setting.folders.description");
ContentRegistry::Settings::add<UserFolderWidget>("hex.builtin.setting.folders", "", "hex.builtin.setting.folders.description");
/* Proxy */
static const std::string proxySetting { "hex.builtin.setting.proxy" };
HttpRequest::setProxy(ContentRegistry::Settings::read("hex.builtin.setting.proxy", "hex.builtin.setting.proxy.url", "").get<std::string>());
HttpRequest::setProxy(ContentRegistry::Settings::read(proxySetting, "hex.builtin.setting.proxy.url", ""));
ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.proxy", "hex.builtin.setting.proxy.description");
ContentRegistry::Settings::addCategoryDescription(proxySetting, "hex.builtin.setting.proxy.description");
auto proxyEnabledSetting = ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.proxy", "", "hex.builtin.setting.proxy.enable", false).setChangedCallback([](Widgets::Widget &widget) {
auto checkBox = static_cast<Widgets::Checkbox *>(&widget);
ContentRegistry::Settings::add(
proxySetting, "hex.builtin.setting.proxy.url", "", [](auto name, nlohmann::json &setting) {
static std::string proxyUrl = static_cast<std::string>(setting);
static bool enableProxy = !proxyUrl.empty();
if (checkBox->isChecked()) {
HttpRequest::setProxy(ContentRegistry::Settings::read("hex.builtin.setting.proxy", "hex.builtin.setting.proxy.url", "").get<std::string>());
} else {
HttpRequest::setProxy("");
}
});
bool result = false;
ContentRegistry::Settings::add<Widgets::TextBox>("hex.builtin.setting.proxy", "", "hex.builtin.setting.proxy.url", "")
.setEnabledCallback([proxyEnabledSetting] {
auto &checkBox = static_cast<Widgets::Checkbox &>(proxyEnabledSetting.getWidget());
if (ImGui::Checkbox("hex.builtin.setting.proxy.enable"_lang, &enableProxy)) {
setting = enableProxy ? proxyUrl : "";
HttpRequest::setProxy(enableProxy ? proxyUrl : "");
result = true;
}
return checkBox.isChecked();
})
.setChangedCallback([](Widgets::Widget &widget) {
auto textBox = static_cast<Widgets::TextBox *>(&widget);
ImGui::BeginDisabled(!enableProxy);
if (ImGui::InputText("##proxy_url", proxyUrl)) {
setting = proxyUrl;
HttpRequest::setProxy(proxyUrl);
result = true;
}
ImGui::EndDisabled();
ImGui::InfoTooltip("hex.builtin.setting.proxy.url.tooltip"_lang);
ImGui::SameLine();
ImGui::TextFormatted("{}", name);
return result;
},
false);
HttpRequest::setProxy(textBox->getValue());
});
}
static void loadThemeSettings() {
auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme);
auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme).get<std::string>();
if (theme == ThemeManager::NativeTheme)
ImHexApi::System::enableSystemThemeDetection(true);
@ -493,16 +314,20 @@ namespace hex::plugin::builtin {
}
}
static void loadFoldersSettings() {
auto directories = ContentRegistry::Settings::read("hex.builtin.setting.folders", "hex.builtin.setting.folders", std::vector<std::string> { });
static void loadFolderSettings() {
auto folderPathStrings = ContentRegistry::Settings::read("hex.builtin.setting.folders", "hex.builtin.setting.folders", std::vector<std::string> { });
loadUserFoldersFromSetting(directories);
ImHexApi::System::setAdditionalFolderPaths(userFolders);
std::vector<std::fs::path> paths;
for (const auto &pathString : folderPathStrings) {
paths.emplace_back(pathString);
}
ImHexApi::System::setAdditionalFolderPaths(paths);
}
void loadSettings() {
loadThemeSettings();
loadFoldersSettings();
loadFolderSettings();
}
}

View File

@ -656,7 +656,7 @@ namespace hex::plugin::builtin {
}*/
std::string getWikipediaApiUrl() {
auto setting = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.wiki_explain_language", "en");
std::string setting = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.wiki_explain_language", "en");
return "https://" + setting + ".wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext&redirects=10&formatversion=2";
}

View File

@ -22,7 +22,7 @@ namespace hex::plugin::builtin {
});
// Load settings
this->m_showPopup = bool(ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", 1));
this->m_showPopup = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", true);
}
ViewAchievements::~ViewAchievements() {
@ -381,7 +381,7 @@ namespace hex::plugin::builtin {
ImGui::BeginGroup();
{
if (ImGui::Checkbox("Show popup", &this->m_showPopup))
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", i64(this->m_showPopup));
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", this->m_showPopup);
}
ImGui::EndGroup();

View File

@ -1044,13 +1044,15 @@ namespace hex::plugin::builtin {
});
EventManager::subscribe<EventSettingsChanged>(this, [this] {
this->m_syncPatternSourceCode = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.sync_pattern_source", 0) == 1;
this->m_autoLoadPatterns = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.auto_load_patterns", 1) == 1;
this->m_syncPatternSourceCode = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.sync_pattern_source", false);
this->m_autoLoadPatterns = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.auto_load_patterns", true);
});
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
this->m_shouldAnalyze.get(provider) = true;
this->m_envVarEntries->push_back({ 0, "", 0, EnvVarType::Integer });
this->m_debuggerDrawer.get(provider) = std::make_unique<ui::PatternDrawer>();
});
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
@ -1069,10 +1071,6 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
this->m_debuggerDrawer.get(provider) = std::make_unique<ui::PatternDrawer>();
});
EventManager::subscribe<EventProviderClosed>(this, [this](prv::Provider *) {
if (this->m_syncPatternSourceCode && ImHexApi::Provider::getProviders().empty()) {
this->m_textEditor.SetText("");

View File

@ -36,66 +36,76 @@ namespace hex::plugin::builtin {
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.settings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) {
if (ImGui::BeginTabBar("settings")) {
auto &entries = ContentRegistry::Settings::impl::getEntries();
// Sort the categories by slot
auto sortedCategories = [&entries] {
std::vector<std::decay_t<decltype(entries)>::const_iterator> sortedCategories;
for (auto it = entries.cbegin(); it != entries.cend(); it++) {
sortedCategories.emplace_back(it);
}
std::sort(sortedCategories.begin(), sortedCategories.end(), [](auto &item0, auto &item1) {
return item0->first.slot < item1->first.slot;
});
return sortedCategories;
}();
// Get the description of the current category
const auto &descriptions = ContentRegistry::Settings::impl::getCategoryDescriptions();
auto &categories = ContentRegistry::Settings::impl::getSettings();
// Draw all categories
for (auto &iter : sortedCategories) {
auto &[category, settings] = *iter;
for (auto &category : categories) {
// Skip empty categories
if (category.subCategories.empty())
continue;
// For each category, create a new tab
if (ImGui::BeginTabItem(LangEntry(category.name))) {
const std::string &categoryDesc = descriptions.contains(category.name) ? descriptions.at(category.name) : category.name;
if (ImGui::BeginTabItem(LangEntry(category.unlocalizedName))) {
// Draw the category description
LangEntry descriptionEntry(categoryDesc);
ImGui::TextFormattedWrapped("{}", descriptionEntry);
ImGui::InfoTooltip(descriptionEntry);
ImGui::Separator();
if (!category.unlocalizedDescription.empty()) {
ImGui::TextFormattedWrapped("{}", LangEntry(category.unlocalizedDescription));
ImGui::NewLine();
}
bool firstSubCategory = true;
// Draw all settings of that category
for (auto &[name, requiresRestart, callback] : settings) {
// Get the current value of the setting
auto &setting = ContentRegistry::Settings::impl::getSettingsData()[category.name][name];
for (auto &subCategory : category.subCategories) {
// Execute the settings drawing callback
if (callback(LangEntry(name), setting)) {
// Handle a setting being changed
// Skip empty subcategories
if (subCategory.entries.empty())
continue;
// Print a debug message
log::debug("Setting [{}]: {} was changed to {}", category.name, name, [&] -> std::string {
if (setting.is_number())
return std::to_string(setting.get<int>());
else if (setting.is_string())
return setting.get<std::string>();
else
return "";
}());
if (!subCategory.unlocalizedName.empty())
ImGui::Header(LangEntry(subCategory.unlocalizedName), firstSubCategory);
// Post an event
EventManager::post<EventSettingsChanged>();
firstSubCategory = false;
// Request a restart if the setting requires it
if (requiresRestart)
this->m_restartRequested = true;
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, scaled({5, 5}));
if (ImGui::BeginTable("##subCategory", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
for (auto &setting : subCategory.entries) {
ImGui::BeginDisabled(!setting.widget->isEnabled());
bool settingChanged = setting.widget->draw(LangEntry(setting.unlocalizedName));
ImGui::EndDisabled();
if (auto tooltip = setting.widget->getTooltip(); tooltip.has_value() && ImGui::IsItemHovered())
ImGui::InfoTooltip(LangEntry(tooltip.value()));
auto &widget = setting.widget;
// Handle a setting being changed
if (settingChanged) {
auto newValue = widget->store();
// Write new value to settings
ContentRegistry::Settings::write(category.unlocalizedName, setting.unlocalizedName, newValue);
// Print a debug message
log::debug("Setting [{} / {}]: Value was changed to {}", category.unlocalizedName, setting.unlocalizedName, nlohmann::to_string(newValue));
// Signal that the setting was changed
EventManager::post<EventSettingsChanged>();
widget->onChanged();
// Request a restart if the setting requires it
if (widget->doesRequireRestart())
this->m_restartRequested = true;
}
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
}
ImGui::EndTabItem();

View File

@ -58,7 +58,7 @@ namespace hex::plugin::builtin {
m_restoreCallback(restoreCallback),
m_deleteCallback(deleteCallback) {
this->m_reportError = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", 1);
this->m_reportError = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", true);
}
void drawContent() override {
@ -104,7 +104,7 @@ namespace hex::plugin::builtin {
}
}
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", i64(this->m_reportError));
ContentRegistry::Settings::write("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", this->m_reportError);
this->close();
}
@ -394,7 +394,7 @@ namespace hex::plugin::builtin {
(void)EventManager::subscribe<EventSettingsChanged>([]() {
{
auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme);
auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme).get<std::string>();
if (theme != ThemeManager::NativeTheme) {
static std::string lastTheme;
@ -407,14 +407,14 @@ namespace hex::plugin::builtin {
}
{
auto language = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "en-US");
auto language = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "en-US").get<std::string>();
if (language != LangEntry::getSelectedLanguage())
LangEntry::loadLanguage(language);
}
{
auto targetFps = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.fps", 14);
auto targetFps = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.fps", 14).get<int>();
ImHexApi::System::setTargetFPS(targetFps);
}
@ -532,7 +532,7 @@ namespace hex::plugin::builtin {
auto chosenTip = chosenCategory[random()%chosenCategory.size()];
s_tipOfTheDay = chosenTip.get<std::string>();
bool showTipOfTheDay = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.show_tips", 1);
bool showTipOfTheDay = ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.show_tips", true);
if (showTipOfTheDay)
PopupTipOfTheDay::open();
}

View File

@ -81,6 +81,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
registerPatternLanguageVisualizers();
registerPatternLanguageInlineVisualizers();
registerCommandPaletteCommands();
registerThemes();
registerSettings();
loadSettings();
registerDataProcessorNodes();
@ -91,7 +92,6 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
registerViews();
registerThemeHandlers();
registerStyleHandlers();
registerThemes();
registerBackgroundServices();
registerNetworkEndpoints();
registerFileHandlers();

View File

@ -75,9 +75,9 @@ namespace hex::plugin::builtin::ui {
EventManager::subscribe<EventSettingsChanged>(this, [this] {
this->m_selectionColor = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.highlight_color", 0x60C08080);
this->m_syncScrolling = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.sync_scrolling", 0);
this->m_byteCellPadding = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.byte_padding", 0);
this->m_characterCellPadding = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.char_padding", 0);
this->m_syncScrolling = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.sync_scrolling", false);
this->m_byteCellPadding = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.byte_padding", false);
this->m_characterCellPadding = ContentRegistry::Settings::read("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.char_padding", false);
});
}

View File

@ -21,13 +21,13 @@ namespace hex::plugin::windows {
RegCreateKeyExA(HKEY_CURRENT_USER, ImHexContextMenuKey, 0x00, nullptr, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, nullptr, &imHexRootKey, nullptr);
RegSetValueA(imHexRootKey, nullptr, REG_SZ, "Open with ImHex", 0x00);
// Add Icon key to use first icon embedded in exe
// Add 'Icon' key to use first icon embedded in exe
std::array<char, MAX_PATH> imHexPath = { 0 };
GetModuleFileNameA(nullptr, imHexPath.data(), imHexPath.size());
auto iconValue = hex::format(R"("{}",0)", imHexPath.data());
RegSetKeyValueA(imHexRootKey, nullptr, "Icon", REG_SZ, iconValue.c_str(), iconValue.size() + 1);
// Add command key to pass file path as first argument to ImHex
// Add 'command' key to pass the right-clicked file path as first argument to ImHex
auto commandValue = hex::format(R"("{}" "%1")", imHexPath.data());
RegSetValueA(imHexRootKey, "command", REG_SZ, commandValue.c_str(), commandValue.size() + 1);
RegCloseKey(imHexRootKey);
@ -51,24 +51,19 @@ namespace hex::plugin::windows {
/* General */
ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.context_menu_entry", 0, [](auto name, nlohmann::json &setting) {
static bool enabled = hasImHexContextMenuEntry();
namespace Widgets = ContentRegistry::Settings::Widgets;
if (ImGui::Checkbox(name.data(), &enabled)) {
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.context_menu_entry", false)
.setChangedCallback([](auto &widget) {
auto checked = static_cast<Widgets::Checkbox &>(widget).isChecked();
if (enabled)
addImHexContextMenuEntry();
else
removeImHexContextMenuEntry();
if (checked)
addImHexContextMenuEntry();
else
removeImHexContextMenuEntry();
enabled = hasImHexContextMenuEntry();
setting = enabled;
return true;
}
return false;
});
widget.load(hasImHexContextMenuEntry());
});
}
}

View File

@ -22,7 +22,7 @@ namespace hex::plugin::windows {
static void detectSystemTheme() {
// Setup system theme change detector
EventManager::subscribe<EventOSThemeChanged>([] {
bool themeFollowSystem = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme) == ThemeManager::NativeTheme;
bool themeFollowSystem = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme).get<std::string>() == ThemeManager::NativeTheme;
if (!themeFollowSystem)
return;
@ -43,7 +43,7 @@ static void detectSystemTheme() {
});
EventManager::subscribe<EventWindowInitialized>([=] {
bool themeFollowSystem = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme) == ThemeManager::NativeTheme;
bool themeFollowSystem = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme).get<std::string>() == ThemeManager::NativeTheme;
if (themeFollowSystem)
EventManager::post<EventOSThemeChanged>();
@ -51,7 +51,7 @@ static void detectSystemTheme() {
}
static void checkBorderlessWindowOverride() {
bool borderlessWindowForced = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.force_borderless_window_mode", 0) != 0;
bool borderlessWindowForced = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.force_borderless_window_mode", false);
if (borderlessWindowForced)
ImHexApi::System::impl::setBorderlessWindowMode(true);