#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 = m_value == 1; if (ImGui::Checkbox(name.data(), &enabled)) { m_value = enabled ? 1 : 0; return true; } return false; } void load(const nlohmann::json &data) override { if (data.is_number()) m_value = data.get(); } nlohmann::json store() override { return 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 (m_value > 200) return "hex.builtin.setting.interface.fps.unlocked"_lang; else if (m_value < 15) return "hex.builtin.setting.interface.fps.native"_lang; else return "%d FPS"; }(); if (ImGui::SliderInt(name.data(), &m_value, 14, 201, format.c_str(), ImGuiSliderFlags_AlwaysClamp)) { return true; } return false; } void load(const nlohmann::json &data) override { if (data.is_number()) m_value = data.get(); } nlohmann::json store() override { return 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(-40_scaled, 280_scaled))) { return false; } else { for (size_t n = 0; n < m_paths.size(); n++) { const bool isSelected = (m_itemIndex == n); if (ImGui::Selectable(wolv::util::toUTF8String(m_paths[n]).c_str(), isSelected)) { m_itemIndex = n; } if (isSelected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndListBox(); } ImGui::SameLine(); ImGui::BeginGroup(); if (ImGuiExt::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(m_paths.begin(), m_paths.end(), path) == m_paths.end()) { m_paths.emplace_back(path); ImHexApi::System::setAdditionalFolderPaths(m_paths); result = true; } }); } ImGuiExt::InfoTooltip("hex.builtin.setting.folders.add_folder"_lang); if (ImGuiExt::IconButton(ICON_VS_REMOVE_CLOSE, ImGui::GetStyleColorVec4(ImGuiCol_Text), ImVec2(30, 30))) { if (!m_paths.empty()) { m_paths.erase(std::next(m_paths.begin(), m_itemIndex)); ImHexApi::System::setAdditionalFolderPaths(m_paths); result = true; } } ImGuiExt::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 pathStrings = data; for (const auto &pathString : pathStrings) { m_paths.emplace_back(pathString); } ImHexApi::System::setAdditionalFolderPaths(m_paths); } } nlohmann::json store() override { std::vector pathStrings; for (const auto &path : m_paths) { pathStrings.push_back(wolv::io::fs::toNormalizedPathString(path)); } return pathStrings; } private: u32 m_itemIndex = 0; std::vector m_paths; }; class ScalingWidget : public ContentRegistry::Settings::Widgets::Widget { public: bool draw(const std::string &name) override { auto format = [this] -> std::string { if (m_value == 0) return "hex.builtin.setting.interface.scaling.native"_lang + hex::format(" (x{:.1f})", ImHexApi::System::getNativeScale()); else return "x%.1f"; }(); if (ImGui::SliderFloat(name.data(), &m_value, 0, 10, format.c_str(), ImGuiSliderFlags_AlwaysClamp)) { return true; } return false; } void load(const nlohmann::json &data) override { if (data.is_number()) m_value = data.get(); } nlohmann::json store() override { return m_value; } private: float m_value = 0; }; class AutoBackupWidget : public ContentRegistry::Settings::Widgets::Widget { public: bool draw(const std::string &name) override { auto format = [this] -> std::string { auto value = m_value * 30; if (value == 0) return "hex.ui.common.off"_lang; else if (value < 60) return hex::format("hex.builtin.setting.general.auto_backup_time.format.simple"_lang, value); else return hex::format("hex.builtin.setting.general.auto_backup_time.format.extended"_lang, value / 60, value % 60); }(); if (ImGui::SliderInt(name.data(), &m_value, 0, (30 * 60) / 30, format.c_str(), ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) { return true; } return false; } void load(const nlohmann::json &data) override { if (data.is_number()) m_value = data.get(); } nlohmann::json store() override { return m_value; } private: int m_value = 0; }; class KeybindingWidget : public ContentRegistry::Settings::Widgets::Widget { public: KeybindingWidget(View *view, const Shortcut &shortcut) : m_view(view), m_shortcut(shortcut), m_drawShortcut(shortcut), m_defaultShortcut(shortcut) {} bool draw(const std::string &name) override { std::string label; if (!m_editing) label = m_drawShortcut.toString(); else label = "..."; if (label.empty()) label = "???"; if (m_hasDuplicate) ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_LoggerError)); ImGui::PushID(this); if (ImGui::Button(label.c_str(), ImVec2(250_scaled, 0))) { m_editing = !m_editing; if (m_editing) ShortcutManager::pauseShortcuts(); else ShortcutManager::resumeShortcuts(); } ImGui::SameLine(); if (m_hasDuplicate) ImGui::PopStyleColor(); bool settingChanged = false; ImGui::BeginDisabled(m_drawShortcut == m_defaultShortcut); if (ImGuiExt::IconButton(ICON_VS_X, ImGui::GetStyleColorVec4(ImGuiCol_Text))) { m_hasDuplicate = !ShortcutManager::updateShortcut(m_shortcut, m_defaultShortcut, m_view); m_drawShortcut = m_defaultShortcut; if (!m_hasDuplicate) { m_shortcut = m_defaultShortcut; settingChanged = true; } } ImGui::EndDisabled(); if (!ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { m_editing = false; ShortcutManager::resumeShortcuts(); } ImGui::SameLine(); ImGuiExt::TextFormatted("{}", name); ImGui::PopID(); if (m_editing) { if (this->detectShortcut()) { m_editing = false; ShortcutManager::resumeShortcuts(); settingChanged = true; if (!m_hasDuplicate) { } } } return settingChanged; } void load(const nlohmann::json &data) override { std::set keys; for (const auto &key : data.get>()) keys.insert(Key(Keys(key))); if (keys.empty()) return; auto newShortcut = Shortcut(keys); m_hasDuplicate = !ShortcutManager::updateShortcut(m_shortcut, newShortcut, m_view); m_shortcut = std::move(newShortcut); m_drawShortcut = m_shortcut; } nlohmann::json store() override { std::vector keys; for (const auto &key : m_shortcut.getKeys()) { if (key != CurrentView) keys.push_back(key.getKeyCode()); } return keys; } private: bool detectShortcut() { if (const auto &shortcut = ShortcutManager::getPreviousShortcut(); shortcut.has_value()) { auto keys = m_shortcut.getKeys(); std::erase_if(keys, [](Key key) { return key != AllowWhileTyping && key != CurrentView; }); for (const auto &key : shortcut->getKeys()) { keys.insert(key); } auto newShortcut = Shortcut(std::move(keys)); m_hasDuplicate = !ShortcutManager::updateShortcut(m_shortcut, newShortcut, m_view); m_drawShortcut = std::move(newShortcut); if (!m_hasDuplicate) { m_shortcut = m_drawShortcut; log::info("Changed shortcut to {}", shortcut->toString()); } else { log::warn("Changing shortcut failed as it overlapped with another one", shortcut->toString()); } return true; } return false; } private: View *m_view = nullptr; Shortcut m_shortcut, m_drawShortcut, m_defaultShortcut; bool m_editing = false; bool m_hasDuplicate = false; }; class ToolbarIconsWidget : public ContentRegistry::Settings::Widgets::Widget { private: struct MenuItemSorter { bool operator()(const auto *a, const auto *b) const { return a->toolbarIndex < b->toolbarIndex; } }; public: bool draw(const std::string &) override { bool changed = false; // Top level layout table if (ImGui::BeginTable("##top_level", 2, ImGuiTableFlags_None, ImGui::GetContentRegionAvail())) { ImGui::TableSetupColumn("##left", ImGuiTableColumnFlags_WidthStretch, 0.3F); ImGui::TableSetupColumn("##right", ImGuiTableColumnFlags_WidthStretch, 0.7F); ImGui::TableNextRow(); ImGui::TableNextColumn(); // Draw list of menu items that can be added to the toolbar if (ImGui::BeginTable("##all_icons", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 280_scaled))) { ImGui::TableNextRow(); ImGui::TableNextColumn(); // Loop over all available menu items for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { // Filter out items without icon, separators, submenus and items that are already in the toolbar if (menuItem.icon.glyph.empty()) continue; const auto &unlocalizedName = menuItem.unlocalizedNames.back(); if (menuItem.unlocalizedNames.size() > 2) continue; if (unlocalizedName.get() == ContentRegistry::Interface::impl::SeparatorValue) continue; if (unlocalizedName.get() == ContentRegistry::Interface::impl::SubMenuValue) continue; if (menuItem.toolbarIndex != -1) continue; ImGui::TableNextRow(); ImGui::TableNextColumn(); // Draw the menu item ImGui::Selectable(hex::format("{} {}", menuItem.icon.glyph, Lang(unlocalizedName)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns); // Handle draggin the menu item to the toolbar box if (ImGui::BeginDragDropSource()) { auto ptr = &menuItem; ImGui::SetDragDropPayload("MENU_ITEM_PAYLOAD", &ptr, sizeof(void*)); ImGuiExt::TextFormatted("{} {}", menuItem.icon.glyph, Lang(unlocalizedName)); ImGui::EndDragDropSource(); } } ImGui::EndTable(); } // Handle dropping menu items from the toolbar box if (ImGui::BeginDragDropTarget()) { if (auto payload = ImGui::AcceptDragDropPayload("TOOLBAR_ITEM_PAYLOAD"); payload != nullptr) { auto &menuItem = *static_cast(payload->Data); menuItem->toolbarIndex = -1; changed = true; } ImGui::EndDragDropTarget(); } ImGui::TableNextColumn(); // Draw toolbar icon box ImGuiExt::BeginSubWindow("hex.builtin.setting.toolbar.icons"_lang, ImGui::GetContentRegionAvail()); { if (ImGui::BeginTable("##icons", 6, ImGuiTableFlags_SizingStretchSame, ImGui::GetContentRegionAvail())) { ImGui::TableNextRow(); // Find all menu items that are in the toolbar and sort them by their toolbar index std::set toolbarItems; for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItemsMutable()) { if (menuItem.toolbarIndex == -1) continue; toolbarItems.emplace(&menuItem); } // Loop over all toolbar items for (auto &menuItem : toolbarItems) { // Filter out items without icon, separators, submenus and items that are not in the toolbar if (menuItem->icon.glyph.empty()) continue; const auto &unlocalizedName = menuItem->unlocalizedNames.back(); if (menuItem->unlocalizedNames.size() > 2) continue; if (unlocalizedName.get() == ContentRegistry::Interface::impl::SubMenuValue) continue; if (menuItem->toolbarIndex == -1) continue; ImGui::TableNextColumn(); // Draw the toolbar item ImGui::InvisibleButton(unlocalizedName.get().c_str(), ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x)); // Handle dragging the toolbar item around if (ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("TOOLBAR_ITEM_PAYLOAD", &menuItem, sizeof(void*)); ImGuiExt::TextFormatted("{} {}", menuItem->icon.glyph, Lang(unlocalizedName)); ImGui::EndDragDropSource(); } // Handle dropping toolbar items onto each other to reorder them if (ImGui::BeginDragDropTarget()) { if (auto payload = ImGui::AcceptDragDropPayload("TOOLBAR_ITEM_PAYLOAD"); payload != nullptr) { auto &otherMenuItem = *static_cast(payload->Data); std::swap(menuItem->toolbarIndex, otherMenuItem->toolbarIndex); changed = true; } ImGui::EndDragDropTarget(); } // Handle right clicking toolbar items to open the color selection popup if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) ImGui::OpenPopup(unlocalizedName.get().c_str()); // Draw the color selection popup if (ImGui::BeginPopup(unlocalizedName.get().c_str())) { constexpr static std::array Colors = { ImGuiCustomCol_ToolbarGray, ImGuiCustomCol_ToolbarRed, ImGuiCustomCol_ToolbarYellow, ImGuiCustomCol_ToolbarGreen, ImGuiCustomCol_ToolbarBlue, ImGuiCustomCol_ToolbarPurple, ImGuiCustomCol_ToolbarBrown }; // Draw all the color buttons for (auto color : Colors) { ImGui::PushID(&color); if (ImGui::ColorButton(hex::format("##color{}", u32(color)).c_str(), ImGuiExt::GetCustomColorVec4(color), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker, ImVec2(20, 20))) { menuItem->icon.color = color; ImGui::CloseCurrentPopup(); changed = true; } ImGui::PopID(); ImGui::SameLine(); } ImGui::EndPopup(); } auto min = ImGui::GetItemRectMin(); auto max = ImGui::GetItemRectMax(); auto iconSize = ImGui::CalcTextSize(menuItem->icon.glyph.c_str()); auto text = Lang(unlocalizedName).get(); if (text.ends_with("...")) text = text.substr(0, text.size() - 3); auto textSize = ImGui::CalcTextSize(text.c_str()); // Draw icon and text of the toolbar item auto drawList = ImGui::GetWindowDrawList(); drawList->AddText((min + ((max - min) - iconSize) / 2) - ImVec2(0, 10_scaled), ImGuiExt::GetCustomColorU32(ImGuiCustomCol(menuItem->icon.color)), menuItem->icon.glyph.c_str()); drawList->AddText((min + ((max - min)) / 2) + ImVec2(-textSize.x / 2, 5_scaled), ImGui::GetColorU32(ImGuiCol_Text), text.c_str()); } ImGui::EndTable(); } } ImGuiExt::EndSubWindow(); // Handle dropping menu items onto the toolbar box if (ImGui::BeginDragDropTarget()) { if (auto payload = ImGui::AcceptDragDropPayload("MENU_ITEM_PAYLOAD"); payload != nullptr) { auto &menuItem = *static_cast(payload->Data); menuItem->toolbarIndex = m_currIndex; m_currIndex += 1; changed = true; } ImGui::EndDragDropTarget(); } ImGui::EndTable(); } return changed; } nlohmann::json store() override { std::map> items; for (const auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItems()) { if (menuItem.toolbarIndex != -1) items.emplace(menuItem.toolbarIndex, std::make_pair(menuItem.unlocalizedNames.back().get(), menuItem.icon.color)); } return items; } void load(const nlohmann::json &data) override { if (data.is_null()) return; auto toolbarItems = data.get>>(); if (toolbarItems.empty()) return; for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItemsMutable()) menuItem.toolbarIndex = -1; for (auto &[priority, menuItem] : ContentRegistry::Interface::impl::getMenuItemsMutable()) { for (const auto &[index, value] : toolbarItems) { const auto &[name, color] = value; if (menuItem.unlocalizedNames.back().get() == name) { menuItem.toolbarIndex = index; menuItem.icon.color = ImGuiCustomCol(color); break; } } } m_currIndex = toolbarItems.size(); } private: i32 m_currIndex = 0; }; class FontFilePicker : public ContentRegistry::Settings::Widgets::FilePicker { public: bool draw(const std::string &name) { bool changed = false; const auto &fonts = hex::getFonts(); bool customFont = false; std::string pathPreview = ""; if (m_path.empty()) { pathPreview = "Default Font"; } else if (fonts.contains(m_path)) { pathPreview = fonts.at(m_path); } else { pathPreview = wolv::util::toUTF8String(m_path.filename()); customFont = true; } if (ImGui::BeginCombo(name.c_str(), pathPreview.c_str())) { if (ImGui::Selectable("Default Font", m_path.empty())) { m_path.clear(); changed = true; } if (ImGui::Selectable("Custom Font", customFont)) { changed = fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, [this](const std::fs::path &path) { m_path = path; }); } for (const auto &[path, fontName] : fonts) { if (ImGui::Selectable(fontName.c_str(), m_path == path)) { m_path = path; changed = true; } } ImGui::EndCombo(); } return changed; } }; bool getDefaultBorderlessWindowMode() { bool result = false; #if defined (OS_WINDOWS) || defined(OS_MACOS) result = true; #endif // Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right. // This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it. // If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1 if (ImHexApi::System::isBorderlessWindowModeEnabled()) { const bool isIntelGPU = hex::containsIgnoreCase(ImHexApi::System::getGPUVendor(), "Intel"); result = !isIntelGPU; if (isIntelGPU) log::warn("Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer"); } return result; } } void registerSettings() { namespace Widgets = ContentRegistry::Settings::Widgets; /* General */ { ContentRegistry::Settings::add("hex.builtin.setting.general", "", "hex.builtin.setting.general.show_tips", false); ContentRegistry::Settings::add("hex.builtin.setting.general", "", "hex.builtin.setting.general.save_recent_providers", true); ContentRegistry::Settings::add("hex.builtin.setting.general", "", "hex.builtin.setting.general.auto_backup_time"); ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.auto_load_patterns", true); ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.sync_pattern_source", false); ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.network_interface", false); #if !defined(OS_WEB) ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.server_contact"); ContentRegistry::Settings::add("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.upload_crash_logs", true); #endif } /* Interface */ { auto themeNames = ThemeManager::getThemeNames(); std::vector themeJsons = { }; for (const auto &themeName : themeNames) themeJsons.emplace_back(themeName); themeNames.emplace(themeNames.begin(), ThemeManager::NativeTheme); themeJsons.emplace(themeJsons.begin(), ThemeManager::NativeTheme); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.color", themeNames, themeJsons, "Dark").setChangedCallback([](auto &widget) { auto dropDown = static_cast(&widget); if (dropDown->getValue() == ThemeManager::NativeTheme) ImHexApi::System::enableSystemThemeDetection(true); else { ImHexApi::System::enableSystemThemeDetection(false); ThemeManager::changeTheme(dropDown->getValue()); } }); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.scaling_factor").requiresRestart(); #if defined (OS_WEB) ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.crisp_scaling", false) .setChangedCallback([](Widgets::Widget &widget) { auto checkBox = static_cast(&widget); EM_ASM({ var canvas = document.getElementById('canvas'); if ($0) canvas.style.imageRendering = 'pixelated'; else canvas.style.imageRendering = 'smooth'; }, checkBox->isChecked()); }); #endif ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.pattern_data_row_bg", false); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.always_show_provider_tabs", false); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.style", "hex.builtin.setting.interface.show_header_command_palette", true); std::vector languageNames; std::vector languageCodes; for (auto &[languageCode, languageName] : LocalizationManager::getSupportedLanguages()) { languageNames.emplace_back(languageName); languageCodes.emplace_back(languageCode); } ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "hex.builtin.setting.interface.language", languageNames, languageCodes, "en-US"); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.language", "hex.builtin.setting.interface.wiki_explain_language", "en"); ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.window", "hex.builtin.setting.interface.fps"); #if defined (OS_LINUX) constexpr static auto MultiWindowSupportEnabledDefault = 0; #else constexpr static auto MultiWindowSupportEnabledDefault = 1; #endif ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.window", "hex.builtin.setting.interface.multi_windows", MultiWindowSupportEnabledDefault).requiresRestart(); #if !defined(OS_WEB) ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.window", "hex.builtin.setting.interface.native_window_decorations", !getDefaultBorderlessWindowMode()).requiresRestart(); #endif ContentRegistry::Settings::add("hex.builtin.setting.interface", "hex.builtin.setting.interface.window", "hex.builtin.setting.interface.restore_window_pos", false); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.highlight_color", ImColor(0x80, 0x80, 0xC0, 0x60)); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.sync_scrolling", false); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.byte_padding", 0, 0, 50); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.char_padding", 0, 0, 50); ContentRegistry::Settings::add("hex.builtin.setting.hex_editor", "", "hex.builtin.setting.hex_editor.pattern_parent_highlighting", true); } /* Fonts */ { ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.glyphs", "hex.builtin.setting.font.load_all_unicode_chars", false) .requiresRestart(); auto customFontEnabledSetting = ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.custom_font_enable", false).requiresRestart(); const auto customFontsEnabled = [customFontEnabledSetting] { auto &customFontsEnabled = static_cast(customFontEnabledSetting.getWidget()); return customFontsEnabled.isChecked(); }; auto customFontPathSetting = ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_path") .requiresRestart() .setEnabledCallback(customFontsEnabled); const auto customFontSettingsEnabled = [customFontEnabledSetting, customFontPathSetting] { auto &customFontsEnabled = static_cast(customFontEnabledSetting.getWidget()); auto &fontPath = static_cast(customFontPathSetting.getWidget()); return customFontsEnabled.isChecked() && !fontPath.getPath().empty(); }; ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.custom_font_info") .setEnabledCallback(customFontsEnabled); ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_size", 13, 0, 100) .requiresRestart() .setEnabledCallback(customFontSettingsEnabled); ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_bold", false) .requiresRestart() .setEnabledCallback(customFontSettingsEnabled); ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_italic", false) .requiresRestart() .setEnabledCallback(customFontSettingsEnabled); ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_antialias", true) .requiresRestart() .setEnabledCallback(customFontSettingsEnabled); } /* Folders */ { ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.folders", "hex.builtin.setting.folders.description"); ContentRegistry::Settings::add("hex.builtin.setting.folders", "", "hex.builtin.setting.folders.description"); } /* Proxy */ { HttpRequest::setProxyUrl(ContentRegistry::Settings::read("hex.builtin.setting.proxy", "hex.builtin.setting.proxy.url", "")); ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.proxy", "hex.builtin.setting.proxy.description"); auto proxyEnabledSetting = ContentRegistry::Settings::add("hex.builtin.setting.proxy", "", "hex.builtin.setting.proxy.enable", false) .setChangedCallback([](Widgets::Widget &widget) { auto checkBox = static_cast(&widget); HttpRequest::setProxyState(checkBox->isChecked()); }); ContentRegistry::Settings::add("hex.builtin.setting.proxy", "", "hex.builtin.setting.proxy.url", "") .setEnabledCallback([proxyEnabledSetting] { auto &checkBox = static_cast(proxyEnabledSetting.getWidget()); return checkBox.isChecked(); }) .setChangedCallback([](Widgets::Widget &widget) { auto textBox = static_cast(&widget); HttpRequest::setProxyUrl(textBox->getValue()); }); } /* Experiments */ { ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.experiments", "hex.builtin.setting.experiments.description"); EventImHexStartupFinished::subscribe([]{ for (const auto &[name, experiment] : ContentRegistry::Experiments::impl::getExperiments()) { ContentRegistry::Settings::add("hex.builtin.setting.experiments", "", experiment.unlocalizedName, false) .setTooltip(Lang(experiment.unlocalizedDescription)) .setChangedCallback([name](Widgets::Widget &widget) { auto checkBox = static_cast(&widget); ContentRegistry::Experiments::enableExperiement(name, checkBox->isChecked()); }); } }); } /* Shorcuts */ { EventImHexStartupFinished::subscribe([]{ for (const auto &shortcutEntry : ShortcutManager::getGlobalShortcuts()) { ContentRegistry::Settings::add("hex.builtin.setting.shortcuts", "hex.builtin.setting.shortcuts.global", shortcutEntry.unlocalizedName, nullptr, shortcutEntry.shortcut); } for (auto &[viewName, view] : ContentRegistry::Views::impl::getEntries()) { for (const auto &shortcutEntry : ShortcutManager::getViewShortcuts(view.get())) { ContentRegistry::Settings::add("hex.builtin.setting.shortcuts", viewName, shortcutEntry.unlocalizedName, view.get(), shortcutEntry.shortcut); } } }); } /* Toolbar icons */ { ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.toolbar", "hex.builtin.setting.toolbar.description"); ContentRegistry::Settings::add("hex.builtin.setting.toolbar", "", "hex.builtin.setting.toolbar.icons"); } } static void loadLayoutSettings() { const bool locked = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.layout_locked", false); LayoutManager::lockLayout(locked); } static void loadThemeSettings() { auto theme = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.color", ThemeManager::NativeTheme); if (theme == ThemeManager::NativeTheme) { ImHexApi::System::enableSystemThemeDetection(true); } else { ImHexApi::System::enableSystemThemeDetection(false); ThemeManager::changeTheme(theme); } auto borderlessWindowMode = !ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.native_window_decorations", !getDefaultBorderlessWindowMode()); ImHexApi::System::impl::setBorderlessWindowMode(borderlessWindowMode); } static void loadFolderSettings() { auto folderPathStrings = ContentRegistry::Settings::read>("hex.builtin.setting.folders", "hex.builtin.setting.folders", { }); std::vector paths; for (const auto &pathString : folderPathStrings) { paths.emplace_back(pathString); } ImHexApi::System::setAdditionalFolderPaths(paths); } void loadSettings() { loadLayoutSettings(); loadThemeSettings(); loadFolderSettings(); } }