diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 880d67d11..3ebf79c3a 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -141,7 +141,7 @@ namespace hex { [[nodiscard]] bool isChecked() const { return m_value; } - private: + protected: bool m_value; }; @@ -155,7 +155,7 @@ namespace hex { [[nodiscard]] i32 getValue() const { return m_value; } - private: + protected: int m_value; i32 m_min, m_max; }; @@ -170,7 +170,7 @@ namespace hex { [[nodiscard]] float getValue() const { return m_value; } - private: + protected: float m_value; float m_min, m_max; }; @@ -186,7 +186,7 @@ namespace hex { [[nodiscard]] ImColor getColor() const; - private: + protected: std::array m_value{}; }; @@ -202,7 +202,7 @@ namespace hex { [[nodiscard]] const nlohmann::json& getValue() const; - private: + protected: std::vector m_items; std::vector m_settingsValues; nlohmann::json m_defaultItem; @@ -222,7 +222,7 @@ namespace hex { [[nodiscard]] const std::string& getValue() const { return m_value; } - private: + protected: std::string m_value; }; @@ -233,12 +233,12 @@ namespace hex { void load(const nlohmann::json &data) override; nlohmann::json store() override; - [[nodiscard]] std::fs::path getPath() const { - return m_value; + [[nodiscard]] const std::fs::path& getPath() const { + return m_path; } - private: - std::string m_value; + protected: + std::fs::path m_path; }; class Label : public Widget { diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index c428e7601..59b180358 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,9 @@ namespace hex { int executeCommand(const std::string &command); void openWebpage(std::string url); + extern "C" void registerFont(const char *fontName, const char *fontPath); + const std::map& getFonts(); + [[nodiscard]] std::string encodeByteString(const std::vector &bytes); [[nodiscard]] std::vector decodeByteString(const std::string &string); diff --git a/lib/libimhex/include/hex/helpers/utils_macos.hpp b/lib/libimhex/include/hex/helpers/utils_macos.hpp index 8aab9e5a8..83405a76f 100644 --- a/lib/libimhex/include/hex/helpers/utils_macos.hpp +++ b/lib/libimhex/include/hex/helpers/utils_macos.hpp @@ -14,6 +14,7 @@ void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode); + void enumerateFontsMacos(); } #endif \ No newline at end of file diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index f86c7cef5..cc128734e 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -413,16 +413,18 @@ namespace hex { bool FilePicker::draw(const std::string &name) { bool changed = false; - if (ImGui::InputText("##font_path", m_value)) { + + auto pathString = wolv::util::toUTF8String(m_path); + if (ImGui::InputText("##font_path", pathString)) { changed = true; } ImGui::SameLine(); if (ImGuiExt::IconButton("...", ImGui::GetStyleColorVec4(ImGuiCol_Text))) { - return fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, + changed = fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, [&](const std::fs::path &path) { - m_value = wolv::util::toUTF8String(path); + pathString = wolv::util::toUTF8String(path); }); } @@ -430,19 +432,23 @@ namespace hex { ImGuiExt::TextFormatted("{}", name); + if (changed) { + m_path = pathString; + } + return changed; } void FilePicker::load(const nlohmann::json &data) { if (data.is_string()) { - m_value = data.get(); + m_path = data.get(); } else { log::warn("Invalid data type loaded from settings for file picker!"); } } nlohmann::json FilePicker::store() { - return m_value; + return m_path; } bool Label::draw(const std::string& name) { diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 68e935d5e..cccea5962 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -677,14 +677,23 @@ namespace hex { return value; } - static std::optional fileToOpen; + static std::optional s_fileToOpen; extern "C" void openFile(const char *path) { log::info("Opening file: {0}", path); - fileToOpen = path; + s_fileToOpen = path; } std::optional getInitialFilePath() { - return fileToOpen; + return s_fileToOpen; + } + + static std::map s_fonts; + extern "C" void registerFont(const char *fontName, const char *fontPath) { + s_fonts[fontPath] = fontName; + } + + const std::map& getFonts() { + return s_fonts; } namespace { diff --git a/lib/libimhex/source/helpers/utils_macos.m b/lib/libimhex/source/helpers/utils_macos.m index 1c0d40fe3..686b3f022 100644 --- a/lib/libimhex/source/helpers/utils_macos.m +++ b/lib/libimhex/source/helpers/utils_macos.m @@ -3,8 +3,9 @@ #include #include #include - #include #include + #include + #include #include #include @@ -23,6 +24,7 @@ } void openFile(const char *path); + void registerFont(const char *fontName, const char *fontPath); void openWebpageMacos(const char *url) { CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL); @@ -64,6 +66,28 @@ return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; } + void enumerateFontsMacos(void) { + CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames(); + CFIndex count = CFArrayGetCount(fontDescriptors); + + for (CFIndex i = 0; i < count; i++) { + CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i); + + // Get font path + CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontNameAttribute : (__bridge NSString *)fontName }; + CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes); + CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute); + CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle); + + registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]); + + CFRelease(descriptor); + CFRelease(fontURL); + } + + CFRelease(fontDescriptors); + } + @interface HexDocument : NSDocument @end diff --git a/main/gui/source/window/linux_window.cpp b/main/gui/source/window/linux_window.cpp index f6e85c7f4..322e719d1 100644 --- a/main/gui/source/window/linux_window.cpp +++ b/main/gui/source/window/linux_window.cpp @@ -47,6 +47,31 @@ namespace hex { } // Hopefully one of these commands is installed } + void enumerateFonts() { + const std::array FontDirectories = { + "/usr/share/fonts", + "/usr/local/share/fonts", + "~/.fonts", + "~/.local/share/fonts" + }; + + for (const auto &directory : FontDirectories) { + if (!std::fs::exists(directory)) + continue; + + for (const auto &entry : std::fs::recursive_directory_iterator(directory)) { + if (!entry.exists()) + continue; + if (!entry.is_regular_file()) + continue; + if (entry.path().extension() != ".ttf" && entry.path().extension() != ".otf") + continue; + + registerFont(entry.path().stem().c_str(), entry.path().c_str()); + } + } + } + void Window::initNative() { log::impl::enableColorPrinting(); @@ -65,6 +90,8 @@ namespace hex { if (!isatty(STDOUT_FILENO)) { log::impl::redirectToFile(); } + + enumerateFonts(); } void Window::setupNativeWindow() { diff --git a/main/gui/source/window/macos_window.cpp b/main/gui/source/window/macos_window.cpp index 1db3971fd..7f691cc47 100644 --- a/main/gui/source/window/macos_window.cpp +++ b/main/gui/source/window/macos_window.cpp @@ -38,6 +38,8 @@ namespace hex { if (!isatty(STDOUT_FILENO)) { log::impl::redirectToFile(); } + + enumerateFontsMacos(); } void Window::setupNativeWindow() { diff --git a/main/gui/source/window/win_window.cpp b/main/gui/source/window/win_window.cpp index 14bc249dc..b2ca1ac78 100644 --- a/main/gui/source/window/win_window.cpp +++ b/main/gui/source/window/win_window.cpp @@ -268,6 +268,46 @@ namespace hex { dup2(unboundFd, stdFileDescriptor); } + void enumerateFonts() { + constexpr static auto FontRegistryPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"; + + static const std::array RegistryLocations = { + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER + }; + + for (const auto location : RegistryLocations) { + HKEY key; + if (RegOpenKeyExW(location, FontRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) { + continue; + } + + DWORD index = 0; + std::wstring valueName(0xFFF, L'\0'); + DWORD valueNameSize = valueName.size() * sizeof(wchar_t); + std::wstring valueData(0xFFF, L'\0'); + DWORD valueDataSize = valueData.size() * sizeof(wchar_t); + DWORD valueType; + + while (RegEnumValueW(key, index, valueName.data(), &valueNameSize, nullptr, &valueType, reinterpret_cast(valueData.data()), &valueDataSize) == ERROR_SUCCESS) { + if (valueType == REG_SZ) { + auto fontName = hex::utf16ToUtf8(valueName.c_str()); + auto fontPath = std::fs::path(valueData); + if (fontPath.is_relative()) + fontPath = std::fs::path("C:\\Windows\\Fonts") / fontPath; + + registerFont(fontName.c_str(), wolv::util::toUTF8String(fontPath).c_str()); + } + + valueNameSize = valueName.size(); + valueDataSize = valueData.size(); + index++; + } + + RegCloseKey(key); + } + } + void Window::initNative() { if (ImHexApi::System::isDebugBuild()) { @@ -301,6 +341,8 @@ namespace hex { if (std::fs::exists(path)) AddDllDirectory(path.c_str()); } + + enumerateFonts(); } class DropManager : public IDropTarget { @@ -486,7 +528,6 @@ namespace hex { }); ImGui::GetIO().ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent(); - } void Window::beginNativeWindowFrame() { diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 13d77d0a0..57351140f 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -603,6 +603,50 @@ namespace hex::plugin::builtin { 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; @@ -739,7 +783,7 @@ namespace hex::plugin::builtin { return customFontsEnabled.isChecked(); }; - auto customFontPathSetting = ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_path") + auto customFontPathSetting = ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_path") .requiresRestart() .setEnabledCallback(customFontsEnabled);