From 117eb1e2a7bf2c4c648fd5b23a243d299e67428d Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sat, 18 Jan 2025 23:34:43 +0100 Subject: [PATCH] feat: Added more granular font settings Fixes #1260 --- lib/libimhex/include/hex/api/imhex_api.hpp | 20 +- lib/libimhex/source/api/content_registry.cpp | 2 + lib/libimhex/source/api/imhex_api.cpp | 12 + main/gui/source/window/window.cpp | 29 +- plugins/builtin/romfs/lang/de_DE.json | 11 - plugins/builtin/romfs/lang/en_US.json | 12 - plugins/builtin/romfs/lang/es_ES.json | 11 - plugins/builtin/romfs/lang/hu_HU.json | 11 - plugins/builtin/romfs/lang/it_IT.json | 11 - plugins/builtin/romfs/lang/ja_JP.json | 11 - plugins/builtin/romfs/lang/ko_KR.json | 11 - plugins/builtin/romfs/lang/pt_BR.json | 11 - plugins/builtin/romfs/lang/zh_CN.json | 12 - plugins/builtin/romfs/lang/zh_TW.json | 11 - .../source/content/settings_entries.cpp | 134 +------ .../content/views/view_pattern_editor.cpp | 8 + .../source/content/views/view_settings.cpp | 1 - plugins/fonts/CMakeLists.txt | 1 + plugins/fonts/include/font_atlas.hpp | 253 +++++++++++++ plugins/fonts/include/font_settings.hpp | 59 +++ plugins/fonts/include/fonts/fonts.hpp | 11 + plugins/fonts/romfs/lang/de_DE.json | 18 + plugins/fonts/romfs/lang/en_US.json | 21 ++ plugins/fonts/romfs/lang/es_ES.json | 17 + plugins/fonts/romfs/lang/hu_HU.json | 17 + plugins/fonts/romfs/lang/it_IT.json | 17 + plugins/fonts/romfs/lang/ja_JP.json | 17 + plugins/fonts/romfs/lang/ko_KR.json | 17 + plugins/fonts/romfs/lang/pt_BR.json | 17 + plugins/fonts/romfs/lang/zh_CN.json | 17 + plugins/fonts/romfs/lang/zh_TW.json | 18 + plugins/fonts/source/font_loader.cpp | 337 ++---------------- plugins/fonts/source/font_settings.cpp | 206 +++++++++++ plugins/fonts/source/library_fonts.cpp | 97 ++++- plugins/ui/romfs/lang/en_US.json | 1 + plugins/ui/source/ui/hex_editor.cpp | 3 + .../content/pl_visualizers/timestamp.cpp | 6 +- 37 files changed, 878 insertions(+), 590 deletions(-) create mode 100644 plugins/fonts/include/font_atlas.hpp create mode 100644 plugins/fonts/include/font_settings.hpp create mode 100644 plugins/fonts/include/fonts/fonts.hpp create mode 100644 plugins/fonts/romfs/lang/de_DE.json create mode 100644 plugins/fonts/romfs/lang/en_US.json create mode 100644 plugins/fonts/romfs/lang/es_ES.json create mode 100644 plugins/fonts/romfs/lang/hu_HU.json create mode 100644 plugins/fonts/romfs/lang/it_IT.json create mode 100644 plugins/fonts/romfs/lang/ja_JP.json create mode 100644 plugins/fonts/romfs/lang/ko_KR.json create mode 100644 plugins/fonts/romfs/lang/pt_BR.json create mode 100644 plugins/fonts/romfs/lang/zh_CN.json create mode 100644 plugins/fonts/romfs/lang/zh_TW.json create mode 100644 plugins/fonts/source/font_settings.cpp diff --git a/lib/libimhex/include/hex/api/imhex_api.hpp b/lib/libimhex/include/hex/api/imhex_api.hpp index 67f439a8e..6e9917382 100644 --- a/lib/libimhex/include/hex/api/imhex_api.hpp +++ b/lib/libimhex/include/hex/api/imhex_api.hpp @@ -753,10 +753,8 @@ namespace hex { const std::vector& getFonts(); - void setFontSize(float size); - void setFontAtlas(ImFontAtlas *fontAtlas); + std::map& getFontDefinitions(); - void setFonts(ImFont *bold, ImFont *italic); } GlyphRange glyph(const char *glyph); @@ -769,20 +767,8 @@ namespace hex { constexpr static float DefaultFontSize = 13.0; - ImFont* Bold(); - ImFont* Italic(); - - /** - * @brief Gets the current font size - * @return The current font size - */ - float getFontSize(); - - /** - * @brief Gets the current font atlas - * @return Current font atlas - */ - ImFontAtlas* getFontAtlas(); + void registerFont(const UnlocalizedString &fontName); + ImFont* getFont(const UnlocalizedString &fontName); } diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 36b4d2c9e..f4de78a12 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -223,6 +223,8 @@ namespace hex { log::error("Failed to load setting [{} / {}]: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what()); } }); + + runOnChangeHandlers(unlocalizedCategory, unlocalizedName, getSetting(unlocalizedCategory, unlocalizedName, entry->widget->store())); } return entry->widget.get(); diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 41cd703ee..71c41e21f 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -1010,6 +1010,10 @@ namespace hex { s_italicFont = italic; } + static AutoReset> s_fontDefinitions; + std::map& getFontDefinitions() { + return *s_fontDefinitions; + } } @@ -1082,6 +1086,14 @@ namespace hex { return impl::s_fontAtlas; } + void registerFont(const UnlocalizedString &fontName) { + (*impl::s_fontDefinitions)[fontName] = nullptr; + } + + ImFont* getFont(const UnlocalizedString &fontName) { + return (*impl::s_fontDefinitions)[fontName]; + } + ImFont* Bold() { return impl::s_boldFont; } diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index 914a8d77f..be87772cd 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -296,6 +296,23 @@ namespace hex { } void Window::frameBegin() { + // Create font textures if necessary + { + const auto &fontDefinitions = ImHexApi::Fonts::impl::getFontDefinitions(); + auto ¤tFont = ImGui::GetIO().Fonts; + for (const auto &[name, font] : fontDefinitions) { + // If the texture for this atlas has been built already, don't do it again + if (font->ContainerAtlas->TexID != 0) + continue; + + currentFont = font->ContainerAtlas; + ImGui_ImplOpenGL3_CreateFontsTexture(); + } + + // Make the first font in the list the default UI font + currentFont = fontDefinitions.begin()->second->ContainerAtlas; + } + // Start new ImGui Frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); @@ -1060,17 +1077,8 @@ namespace hex { void Window::initImGui() { IMGUI_CHECKVERSION(); - auto fonts = ImHexApi::Fonts::getFontAtlas(); - - if (fonts == nullptr) { - fonts = IM_NEW(ImFontAtlas)(); - - fonts->AddFontDefault(); - fonts->Build(); - } - // Initialize ImGui and all other ImGui extensions - GImGui = ImGui::CreateContext(fonts); + GImGui = ImGui::CreateContext(); GImPlot = ImPlot::CreateContext(); ImPlot3D::GImPlot3D = ImPlot3D::CreateContext(); GImNodes = ImNodes::CreateContext(); @@ -1105,7 +1113,6 @@ namespace hex { auto scale = ImHexApi::System::getGlobalScale(); style.ScaleAllSizes(scale); io.DisplayFramebufferScale = ImVec2(scale, scale); - io.Fonts->SetTexID(fonts->TexID); style.WindowMenuButtonPosition = ImGuiDir_None; style.IndentSpacing = 10.0F; diff --git a/plugins/builtin/romfs/lang/de_DE.json b/plugins/builtin/romfs/lang/de_DE.json index a6f087f1a..ee6307cd2 100644 --- a/plugins/builtin/romfs/lang/de_DE.json +++ b/plugins/builtin/romfs/lang/de_DE.json @@ -448,17 +448,6 @@ "hex.builtin.setting.folders.add_folder": "Neuer Ordner hinzufügen", "hex.builtin.setting.folders.description": "Gib zusätzliche Orderpfade an, in welchen Pattern, Scripts, Yara Rules und anderes gesucht wird", "hex.builtin.setting.folders.remove_folder": "Ausgewählter Ordner von Liste entfernen", - "hex.builtin.setting.font": "Schriftart", - "hex.builtin.setting.font.custom_font": "Schriftart", - "hex.builtin.setting.font.custom_font_info": "Die folgenden Einstellungen sind nur verfügbar wenn eine benutzerdefinierte Schriftart ausgewählt ist.", - "hex.builtin.setting.font.font_antialias": "Schrift Anti-Aliasing", - "hex.builtin.setting.font.font_bold": "Fett", - "hex.builtin.setting.font.font_italic": "Kursiv", - "hex.builtin.setting.font.font_path": "Eigene Schriftart", - "hex.builtin.setting.font.font_size": "Schriftgrösse", - "hex.builtin.setting.font.font_size.tooltip": "Die Schriftgrösse kann nur mit einer benutzerdefinierten Schriftart geändert werden.\n\nDer Grund dafür ist, dass die Standard Schriftart von ImHex eine Bitmap Schriftart ist, welche nicht skaliert werden kann.", - "hex.builtin.setting.font.glyphs": "Glyphen", - "hex.builtin.setting.font.load_all_unicode_chars": "Alle Unicode Zeichen laden", "hex.builtin.setting.general": "Allgemein", "hex.builtin.setting.general.auto_backup_time": "Periodisches Projekt Backup", "hex.builtin.setting.general.auto_backup_time.format.extended": "Alle {0}min {1}s", diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 63b5a065d..4e9a7c63b 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -460,17 +460,6 @@ "hex.builtin.setting.folders.add_folder": "Add new folder", "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.custom_font_info": "The following settings are only available when a custom font has been selected.", - "hex.builtin.setting.font.font_bold": "Bold", - "hex.builtin.setting.font.font_italic": "Italic", - "hex.builtin.setting.font.font_antialias": "Antialiasing", - "hex.builtin.setting.font.font_path": "Font", - "hex.builtin.setting.font.pixel_perfect_default_font": "Use a Pixel-Perfect default font", - "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", @@ -487,7 +476,6 @@ "hex.builtin.setting.general.show_tips": "Show tips on startup", "hex.builtin.setting.general.sync_pattern_source": "Sync pattern source code between providers", "hex.builtin.setting.general.upload_crash_logs": "Upload crash reports", - "hex.builtin.setting.font.load_all_unicode_chars": "Load all unicode characters", "hex.builtin.setting.hex_editor": "Hex Editor", "hex.builtin.setting.hex_editor.byte_padding": "Extra byte cell padding", "hex.builtin.setting.hex_editor.bytes_per_row": "Bytes per row", diff --git a/plugins/builtin/romfs/lang/es_ES.json b/plugins/builtin/romfs/lang/es_ES.json index 0856f190b..c65ab003e 100644 --- a/plugins/builtin/romfs/lang/es_ES.json +++ b/plugins/builtin/romfs/lang/es_ES.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "Añadir nueva carpeta", "hex.builtin.setting.folders.description": "Especifica rutas de búsqueda adicionales para patterns, scripts, Yara Rules y más", "hex.builtin.setting.folders.remove_folder": "Eliminar la carpeta seleccionada de la lista", - "hex.builtin.setting.font": "Fuente", - "hex.builtin.setting.font.custom_font": "", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "", - "hex.builtin.setting.font.font_bold": "", - "hex.builtin.setting.font.font_italic": "", - "hex.builtin.setting.font.font_path": "Ruta de Fuente Personalizada", - "hex.builtin.setting.font.font_size": "Tamaño de Fuente", - "hex.builtin.setting.font.font_size.tooltip": "El tamaño de la fuente de letra puede únicamente ajustarse cuando arriba se ha seleccionado una fuente personalizada.\n\nEsto se debe a que ImHex usa una fuente de mapa de bits pixel-perfect por defecto. Escalarla por un factor no entero sólamente causará que se vuelva borrosa.", - "hex.builtin.setting.font.glyphs": "", - "hex.builtin.setting.font.load_all_unicode_chars": "Cargar todos los caracteres unicode", "hex.builtin.setting.general": "General", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/romfs/lang/hu_HU.json b/plugins/builtin/romfs/lang/hu_HU.json index fc41dee84..6f3e7dcc3 100644 --- a/plugins/builtin/romfs/lang/hu_HU.json +++ b/plugins/builtin/romfs/lang/hu_HU.json @@ -440,16 +440,6 @@ "hex.builtin.setting.folders.add_folder": "Új mappa hozzáadása", "hex.builtin.setting.folders.description": "Adj meg további keresési útvonalakat mintákhoz, szkriptekhez, Yara szabályokhoz és egyebekhez", "hex.builtin.setting.folders.remove_folder": "A kijelölt mappa eltávolítása a listáról", - "hex.builtin.setting.font": "Betűtípus", - "hex.builtin.setting.font.glyphs": "Szimbólumok", - "hex.builtin.setting.font.custom_font": "Saját betűtípus", - "hex.builtin.setting.font.custom_font_info": "Az alábbi beállítások csak egyéni betűtípusok esetén érhetők el.", - "hex.builtin.setting.font.font_bold": "Félkövér", - "hex.builtin.setting.font.font_italic": "Dőlt", - "hex.builtin.setting.font.font_antialias": "Élsimítás", - "hex.builtin.setting.font.font_path": "Egyéni betűtípus útvonal", - "hex.builtin.setting.font.font_size": "Betűméret", - "hex.builtin.setting.font.font_size.tooltip": "A betűméretet csak akkor lehet módosítani, ha egy egyéni betűtípus van kiválasztva.", "hex.builtin.setting.general": "Általános", "hex.builtin.setting.general.patterns": "Sablonok", "hex.builtin.setting.general.network": "Hálózat", @@ -463,7 +453,6 @@ "hex.builtin.setting.general.show_tips": "Tippek mutatása indításkor", "hex.builtin.setting.general.sync_pattern_source": "Sablon kód szinkronizálása források között", "hex.builtin.setting.general.upload_crash_logs": "Hibajelentések feltöltése", - "hex.builtin.setting.font.load_all_unicode_chars": "Minden Unicode karakter betöltése", "hex.builtin.setting.hex_editor": "Hex szerkesztő", "hex.builtin.setting.hex_editor.byte_padding": "Extra bájt cella kitöltés", "hex.builtin.setting.hex_editor.bytes_per_row": "Bájtok soronként", diff --git a/plugins/builtin/romfs/lang/it_IT.json b/plugins/builtin/romfs/lang/it_IT.json index 228f6e335..7af3101f1 100644 --- a/plugins/builtin/romfs/lang/it_IT.json +++ b/plugins/builtin/romfs/lang/it_IT.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "", "hex.builtin.setting.folders.description": "", "hex.builtin.setting.folders.remove_folder": "", - "hex.builtin.setting.font": "", - "hex.builtin.setting.font.custom_font": "", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "", - "hex.builtin.setting.font.font_bold": "", - "hex.builtin.setting.font.font_italic": "", - "hex.builtin.setting.font.font_path": "", - "hex.builtin.setting.font.font_size": "", - "hex.builtin.setting.font.font_size.tooltip": "", - "hex.builtin.setting.font.glyphs": "", - "hex.builtin.setting.font.load_all_unicode_chars": "", "hex.builtin.setting.general": "Generali", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/romfs/lang/ja_JP.json b/plugins/builtin/romfs/lang/ja_JP.json index d92d02e43..8aedbd8ad 100644 --- a/plugins/builtin/romfs/lang/ja_JP.json +++ b/plugins/builtin/romfs/lang/ja_JP.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "フォルダを追加…", "hex.builtin.setting.folders.description": "パターン、スクリプト、ルールなどのための検索パスを指定して追加できます。", "hex.builtin.setting.folders.remove_folder": "選択中のフォルダをリストから消去", - "hex.builtin.setting.font": "フォント", - "hex.builtin.setting.font.custom_font": "", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "", - "hex.builtin.setting.font.font_bold": "", - "hex.builtin.setting.font.font_italic": "", - "hex.builtin.setting.font.font_path": "フォントファイルのパス", - "hex.builtin.setting.font.font_size": "フォントサイズ", - "hex.builtin.setting.font.font_size.tooltip": "", - "hex.builtin.setting.font.glyphs": "", - "hex.builtin.setting.font.load_all_unicode_chars": "", "hex.builtin.setting.general": "基本", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/romfs/lang/ko_KR.json b/plugins/builtin/romfs/lang/ko_KR.json index 98cf5d826..3aee5f4f7 100644 --- a/plugins/builtin/romfs/lang/ko_KR.json +++ b/plugins/builtin/romfs/lang/ko_KR.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "새 폴더 추가", "hex.builtin.setting.folders.description": "패턴, 스크립트, YARA 규칙 등에 대한 추가 검색 경로를 지정합니다", "hex.builtin.setting.folders.remove_folder": "목록에서 현재 선택된 폴더 제거", - "hex.builtin.setting.font": "글꼴", - "hex.builtin.setting.font.custom_font": "사용자 정의 글꼴", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "안티에일리어싱", - "hex.builtin.setting.font.font_bold": "굵게", - "hex.builtin.setting.font.font_italic": "기울임꼴", - "hex.builtin.setting.font.font_path": "사용자 정의 글꼴 경로", - "hex.builtin.setting.font.font_size": "글꼴 크기", - "hex.builtin.setting.font.font_size.tooltip": "글꼴 크기는 위에서 사용자 지정 글꼴을 선택한 경우에만 조정할 수 있습니다.\n\n이는 ImHex가 기본적으로 픽셀 완벽한 비트맵 글꼴을 사용하기 때문입니다. 정수가 아닌 계수로 크기를 조정하면 글꼴이 흐릿해질 뿐입니다.", - "hex.builtin.setting.font.glyphs": "글리프", - "hex.builtin.setting.font.load_all_unicode_chars": "모든 유니코드 문자 불러오기", "hex.builtin.setting.general": "일반", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/romfs/lang/pt_BR.json b/plugins/builtin/romfs/lang/pt_BR.json index afb1e2ea0..2ec81e8be 100644 --- a/plugins/builtin/romfs/lang/pt_BR.json +++ b/plugins/builtin/romfs/lang/pt_BR.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "Adicionar nova pasta", "hex.builtin.setting.folders.description": "Especifique caminhos de pesquisa adicionais para padrões, scripts, regras Yara e muito mais", "hex.builtin.setting.folders.remove_folder": "Remover a pasta atualmente selecionada da lista", - "hex.builtin.setting.font": "Fonte", - "hex.builtin.setting.font.custom_font": "", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "", - "hex.builtin.setting.font.font_bold": "", - "hex.builtin.setting.font.font_italic": "", - "hex.builtin.setting.font.font_path": "Caminho da Fonte Customizada", - "hex.builtin.setting.font.font_size": "Tamanho da Fonte", - "hex.builtin.setting.font.font_size.tooltip": "", - "hex.builtin.setting.font.glyphs": "", - "hex.builtin.setting.font.load_all_unicode_chars": "", "hex.builtin.setting.general": "General", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/romfs/lang/zh_CN.json b/plugins/builtin/romfs/lang/zh_CN.json index 1e023a790..7a8d7a7d5 100644 --- a/plugins/builtin/romfs/lang/zh_CN.json +++ b/plugins/builtin/romfs/lang/zh_CN.json @@ -500,18 +500,6 @@ "hex.builtin.setting.folders.add_folder": "添加新的目录", "hex.builtin.setting.folders.description": "为模式、脚本和规则等指定额外的搜索路径", "hex.builtin.setting.folders.remove_folder": "从列表中移除当前目录", - "hex.builtin.setting.font": "字体", - "hex.builtin.setting.font.custom_font": "自定义字体", - "hex.builtin.setting.font.custom_font_info": "仅当选择自定义字体时,以下设置才可用。", - "hex.builtin.setting.font.font_antialias": "抗锯齿", - "hex.builtin.setting.font.font_bold": "粗体", - "hex.builtin.setting.font.font_italic": "斜体", - "hex.builtin.setting.font.font_path": "自定义字体路径", - "hex.builtin.setting.font.font_size": "字体大小", - "hex.builtin.setting.font.font_size.tooltip": "仅当选择了自定义字体时才能调整字体大小。\n\n这是因为 ImHex 默认使用像素完美的位图字体,用任何非整数因子缩放它只会导致它变得模糊。", - "hex.builtin.setting.font.glyphs": "字形", - "hex.builtin.setting.font.load_all_unicode_chars": "加载所有 Unicode 字符", - "hex.builtin.setting.font.pixel_perfect_default_font": "使用Pixel-Perfect默认字体", "hex.builtin.setting.general": "通用", "hex.builtin.setting.general.auto_backup_time": "定期备份项目", "hex.builtin.setting.general.auto_backup_time.format.extended": "每 {0}分 {1}秒", diff --git a/plugins/builtin/romfs/lang/zh_TW.json b/plugins/builtin/romfs/lang/zh_TW.json index 0bcce490f..190a2e769 100644 --- a/plugins/builtin/romfs/lang/zh_TW.json +++ b/plugins/builtin/romfs/lang/zh_TW.json @@ -447,17 +447,6 @@ "hex.builtin.setting.folders.add_folder": "新增資料夾", "hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more", "hex.builtin.setting.folders.remove_folder": "從列表中移除目前選擇的資料夾", - "hex.builtin.setting.font": "字體", - "hex.builtin.setting.font.custom_font": "", - "hex.builtin.setting.font.custom_font_info": "", - "hex.builtin.setting.font.font_antialias": "", - "hex.builtin.setting.font.font_bold": "", - "hex.builtin.setting.font.font_italic": "", - "hex.builtin.setting.font.font_path": "自訂字型路徑", - "hex.builtin.setting.font.font_size": "字體大小", - "hex.builtin.setting.font.font_size.tooltip": "需要在上方選擇自訂字型後才能調整字體大小。\n\n因為 ImHex 預設使用像素完美點陣圖字型,若以任何非整數的倍率放大,將使其模糊。", - "hex.builtin.setting.font.glyphs": "", - "hex.builtin.setting.font.load_all_unicode_chars": "載入所有 unicode 字元", "hex.builtin.setting.general": "一般", "hex.builtin.setting.general.auto_backup_time": "", "hex.builtin.setting.general.auto_backup_time.format.extended": "", diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 9518daf7c..27851c936 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace hex::plugin::builtin { @@ -650,98 +651,6 @@ namespace hex::plugin::builtin { i32 m_currIndex = 0; }; - class FontFilePicker : public ContentRegistry::Settings::Widgets::FilePicker { - public: - bool draw(const std::string &name) override { - bool changed = false; - - const auto &fonts = hex::getFonts(); - const bool pixelPerfectFont = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", true); - - bool customFont = false; - std::string pathPreview = ""; - if (m_path.empty() && pixelPerfectFont) { - pathPreview = "Pixel-Perfect Default Font (Proggy Clean)"; - } else if (m_path.empty() && !pixelPerfectFont) { - pathPreview = "Smooth Default Font (JetbrainsMono)"; - } else if (fonts.contains(m_path)) { - pathPreview = fonts.at(m_path); - } else { - pathPreview = wolv::util::toUTF8String(m_path.filename()); - customFont = true; - } - - bool pixelPerfectFontSelected = false; - if (ImGui::BeginCombo(name.c_str(), pathPreview.c_str())) { - if (ImGui::Selectable("Pixel-Perfect Default Font (Proggy Clean)", m_path.empty() && pixelPerfectFont)) { - m_path.clear(); - changed = true; - pixelPerfectFontSelected = true; - } - - if (ImGui::Selectable("Smooth Default Font (JetbrainsMono)", m_path.empty() && !pixelPerfectFont)) { - 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(limitStringLength(fontName, 50).c_str(), m_path == path)) { - m_path = path; - changed = true; - } - ImGui::SetItemTooltip("%s", fontName.c_str()); - } - - ImGui::EndCombo(); - } - - if (changed) { - m_pixelPerfectFont = pixelPerfectFontSelected; - } - - return changed; - } - - bool isPixelPerfectFontSelected() const { - return m_pixelPerfectFont; - } - - private: - bool m_pixelPerfectFont = false; - }; - - class SliderPoints : public ContentRegistry::Settings::Widgets::SliderFloat { - public: - SliderPoints(float defaultValue, float min, float max) : SliderFloat(defaultValue, min, max) { } - bool draw(const std::string &name) override { - float value = pixelsToPoints(m_value); - float min = pixelsToPoints(m_min); - float max = pixelsToPoints(m_max); - - auto changed = ImGui::SliderFloat(name.c_str(), &value, min, max, "%.0f pt"); - - m_value = pointsToPixels(value); - - return changed; - } - - private: - static float pixelsToPoints(float pixels) { - return pixels * (72_scaled / 96.0F); - } - - static float pointsToPixels(float points) { - return points / (72_scaled / 96.0F); - } - }; - - bool getDefaultBorderlessWindowMode() { bool result; @@ -919,47 +828,6 @@ namespace hex::plugin::builtin { "none"); } - /* Fonts */ - { - const auto scaleWarningHandler = [](auto&) { - s_showScalingWarning = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", true); - }; - ContentRegistry::Settings::onChange("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", scaleWarningHandler); - - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.glyphs", "hex.builtin.setting.font.load_all_unicode_chars", false) - .requiresRestart(); - - auto customFontPathSetting = ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_path") - .requiresRestart() - .setChangedCallback([scaleWarningHandler](Widgets::Widget &widget) { - scaleWarningHandler(widget); - - auto &fontPicker = static_cast(widget); - ContentRegistry::Settings::write("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", fontPicker.isPixelPerfectFontSelected()); - }); - - const auto fontCustomizationEnabled = [customFontPathSetting ] { - auto &fontPicker = static_cast(customFontPathSetting.getWidget()); - - return !fontPicker.isPixelPerfectFontSelected(); - }; - - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.custom_font_info"); - - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_size", 16, 2, 100) - .requiresRestart() - .setEnabledCallback(fontCustomizationEnabled); - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_bold", false) - .requiresRestart() - .setEnabledCallback(fontCustomizationEnabled); - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_italic", false) - .requiresRestart() - .setEnabledCallback(fontCustomizationEnabled); - ContentRegistry::Settings::add("hex.builtin.setting.font", "hex.builtin.setting.font.custom_font", "hex.builtin.setting.font.font_antialias", true) - .requiresRestart() - .setEnabledCallback(fontCustomizationEnabled); - } - /* Folders */ { ContentRegistry::Settings::setCategoryDescription("hex.builtin.setting.folders", "hex.builtin.setting.folders.description"); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 85b74daf6..f7647eaf5 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -32,6 +32,7 @@ #include #include +#include #include namespace hex::plugin::builtin { @@ -338,7 +339,11 @@ namespace hex::plugin::builtin { if (name.contains(textEditorView) || name.contains(consoleView)) m_focusedSubWindowName = name; } + + ImGui::PushFont(fonts::CodeEditor()); m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, false); + ImGui::PopFont(); + m_textEditorHoverBox = ImRect(windowPosition,windowPosition+textEditorSize); m_consoleHoverBox = ImRect(ImVec2(windowPosition.x,windowPosition.y+textEditorSize.y),windowPosition+availableSize); TextEditor::FindReplaceHandler *findReplaceHandler = m_textEditor.GetFindReplaceHandler(); @@ -1073,7 +1078,10 @@ namespace hex::plugin::builtin { m_consoleNeedsUpdate = false; } + ImGui::PushFont(fonts::CodeEditor()); m_consoleEditor.Render("##console", size, true); + ImGui::PopFont(); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y + 1_scaled); } diff --git a/plugins/builtin/source/content/views/view_settings.cpp b/plugins/builtin/source/content/views/view_settings.cpp index 4b1d622a9..51858e5cc 100644 --- a/plugins/builtin/source/content/views/view_settings.cpp +++ b/plugins/builtin/source/content/views/view_settings.cpp @@ -33,7 +33,6 @@ namespace hex::plugin::builtin { try { auto defaultValue = widget->store(); widget->load(ContentRegistry::Settings::impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue)); - widget->onChanged(); } catch (const std::exception &e) { log::error("Failed to load setting [{} / {}]: {}", unlocalizedCategory.get(), unlocalizedName.get(), e.what()); } diff --git a/plugins/fonts/CMakeLists.txt b/plugins/fonts/CMakeLists.txt index 0f4cfda12..d1512fe4f 100644 --- a/plugins/fonts/CMakeLists.txt +++ b/plugins/fonts/CMakeLists.txt @@ -9,6 +9,7 @@ add_imhex_plugin( source/library_fonts.cpp source/font_loader.cpp source/fonts.cpp + source/font_settings.cpp INCLUDES include LIBRARY_PLUGIN diff --git a/plugins/fonts/include/font_atlas.hpp b/plugins/fonts/include/font_atlas.hpp new file mode 100644 index 000000000..2f877e121 --- /dev/null +++ b/plugins/fonts/include/font_atlas.hpp @@ -0,0 +1,253 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + + +namespace hex::fonts { + + class Font { + public: + Font() = default; + + float getDescent() const { + return m_font->Descent; + } + + private: + explicit Font(ImFont *font) : m_font(font) { } + + private: + friend class FontAtlas; + + ImFont *m_font; + }; + + class FontAtlas { + public: + FontAtlas() : m_fontAtlas(IM_NEW(ImFontAtlas)) { + enableUnicodeCharacters(false); + + // Set the default configuration for the font atlas + m_config.OversampleH = m_config.OversampleV = 1; + m_config.PixelSnapH = true; + m_config.MergeMode = false; + + // Make sure the font atlas doesn't get too large, otherwise weaker GPUs might reject it + m_fontAtlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; + m_fontAtlas->TexDesiredWidth = 4096; + } + + FontAtlas(const FontAtlas &) = delete; + FontAtlas &operator=(const FontAtlas &) = delete; + + FontAtlas(FontAtlas &&other) noexcept { + this->m_fontAtlas = other.m_fontAtlas; + other.m_fontAtlas = nullptr; + + this->m_fontSizes = std::move(other.m_fontSizes); + this->m_config = other.m_config; + this->m_glyphRange = std::move(other.m_glyphRange); + this->m_fontData = std::move(other.m_fontData); + } + + FontAtlas &operator=(FontAtlas &&other) noexcept { + this->m_fontAtlas = other.m_fontAtlas; + other.m_fontAtlas = nullptr; + + this->m_fontSizes = std::move(other.m_fontSizes); + this->m_config = other.m_config; + this->m_glyphRange = std::move(other.m_glyphRange); + this->m_fontData = std::move(other.m_fontData); + + return *this; + } + + ~FontAtlas() { + if (m_fontAtlas != nullptr) + IM_DELETE(m_fontAtlas); + } + + Font addDefaultFont() { + ImFontConfig config = m_config; + config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; + config.SizePixels = std::floor(getAdjustedFontSize(ImHexApi::System::getGlobalScale() * 13.0F)); + + auto font = m_fontAtlas->AddFontDefault(&config); + m_fontSizes.emplace_back(false, config.SizePixels); + + m_config.MergeMode = true; + + return Font(font); + } + + Font addFontFromMemory(const std::vector &fontData, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { + auto &storedFontData = m_fontData.emplace_back(fontData); + + ImFontConfig config = m_config; + config.FontDataOwnedByAtlas = false; + + config.GlyphOffset = { offset.x, offset.y }; + auto font = m_fontAtlas->AddFontFromMemoryTTF(storedFontData.data(), int(storedFontData.size()), getAdjustedFontSize(fontSize), &config, !glyphRange.empty() ? glyphRange.Data : m_glyphRange.Data); + m_fontSizes.emplace_back(scalable, fontSize); + + m_config.MergeMode = true; + + return Font(font); + } + + Font addFontFromRomFs(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { + auto data = romfs::get(path).span(); + return addFontFromMemory({ data.begin(), data.end() }, fontSize, scalable, offset, glyphRange); + } + + Font addFontFromFile(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { + wolv::io::File file(path, wolv::io::File::Mode::Read); + + auto data = file.readVector(); + return addFontFromMemory(data, fontSize, scalable, offset, glyphRange); + } + + void setBold(bool enabled) { + if (enabled) + m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; + else + m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Bold; + } + + void setItalic(bool enabled) { + if (enabled) + m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique; + else + m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Oblique; + } + + void setAntiAliasing(bool enabled) { + if (enabled) + m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; + else + m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; + } + + void enableUnicodeCharacters(bool enabled) { + ImFontGlyphRangesBuilder glyphRangesBuilder; + + { + constexpr static std::array controlCodeRange = { 0x0001, 0x001F, 0 }; + constexpr static std::array extendedAsciiRange = { 0x007F, 0x00FF, 0 }; + constexpr static std::array latinExtendedARange = { 0x0100, 0x017F, 0 }; + + glyphRangesBuilder.AddRanges(controlCodeRange.data()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesDefault()); + glyphRangesBuilder.AddRanges(extendedAsciiRange.data()); + glyphRangesBuilder.AddRanges(latinExtendedARange.data()); + } + + if (enabled) { + constexpr static std::array fullRange = { 0x0180, 0xFFEF, 0 }; + + glyphRangesBuilder.AddRanges(fullRange.data()); + } else { + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesJapanese()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesChineseFull()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesCyrillic()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesKorean()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesThai()); + glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesVietnamese()); + glyphRangesBuilder.AddText("⌘⌥⌃⇧⏎⇥⌫⇪"); + } + + m_glyphRange.clear(); + glyphRangesBuilder.BuildRanges(&m_glyphRange); + } + + bool build() const { + return m_fontAtlas->Build(); + } + + [[nodiscard]] ImFontAtlas* getAtlas() { + auto result = m_fontAtlas; + + return result; + } + + float calculateFontDescend(const ImHexApi::Fonts::Font &font, float fontSize) const { + auto atlas = std::make_unique(); + auto cfg = m_config; + + // Calculate the expected font size + auto size = fontSize; + if (font.defaultSize.has_value()) + size = font.defaultSize.value() * std::max(1.0F, std::floor(ImHexApi::System::getGlobalScale())); + else + size = std::max(1.0F, std::floor(size / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize; + + cfg.MergeMode = false; + cfg.SizePixels = size; + cfg.FontDataOwnedByAtlas = false; + + // Construct a range that only contains the first glyph of the font + ImVector queryRange; + { + auto firstGlyph = font.glyphRanges.empty() ? m_glyphRange.front() : font.glyphRanges.front().begin; + queryRange.push_back(firstGlyph); + queryRange.push_back(firstGlyph); + } + queryRange.push_back(0x00); + + // Build the font atlas with the query range + auto newFont = atlas->AddFontFromMemoryTTF(const_cast(font.fontData.data()), int(font.fontData.size()), 0, &cfg, queryRange.Data); + atlas->Build(); + + return newFont->Descent; + } + + void reset() { + /*IM_DELETE(m_fontAtlas); + m_fontAtlas = IM_NEW(ImFontAtlas);*/ + + m_fontData.clear(); + m_config.MergeMode = false; + } + + void updateFontScaling(float newScaling) { + for (int i = 0; i < m_fontAtlas->ConfigData.size(); i += 1) { + const auto &[scalable, fontSize] = m_fontSizes[i]; + auto &configData = m_fontAtlas->ConfigData[i]; + + if (!scalable) { + configData.SizePixels = fontSize * std::floor(newScaling); + } else { + configData.SizePixels = fontSize * newScaling; + } + } + } + + float getAdjustedFontSize(float fontSize) const { + // Since macOS reports half the framebuffer size that's actually available, + // we'll multiply all font sizes by that and then divide the global font scale + // by the same amount to get super crisp font rendering. + return fontSize * hex::ImHexApi::System::getBackingScaleFactor(); + } + + private: + ImFontAtlas* m_fontAtlas; + std::vector> m_fontSizes; + ImFontConfig m_config; + ImVector m_glyphRange; + + std::list> m_fontData; + }; + +} \ No newline at end of file diff --git a/plugins/fonts/include/font_settings.hpp b/plugins/fonts/include/font_settings.hpp new file mode 100644 index 000000000..f98130019 --- /dev/null +++ b/plugins/fonts/include/font_settings.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +namespace hex::fonts { + + class FontFilePicker : public ContentRegistry::Settings::Widgets::FilePicker { + public: + bool draw(const std::string &name) override; + + bool isPixelPerfectFontSelected() const; + const std::string& getSelectedFontName() const; + + void load(const nlohmann::json& data) override; + nlohmann::json store() override; + + private: + bool updateSelectedFontName(); + + private: + std::string m_selectedFontName; + bool m_pixelPerfectFont = false; + }; + + class SliderPoints : public ContentRegistry::Settings::Widgets::SliderFloat { + public: + SliderPoints(float defaultValue, float min, float max) : SliderFloat(defaultValue, min, max) { } + bool draw(const std::string &name) override; + }; + + + class FontSelector : public ContentRegistry::Settings::Widgets::Widget { + public: + FontSelector() : m_fontSize(16, 2, 100), m_bold(false), m_italic(false), m_antiAliased(true) { } + + bool draw(const std::string &name) override; + + nlohmann::json store() override; + void load(const nlohmann::json& data) override; + + [[nodiscard]] const std::fs::path& getFontPath() const; + [[nodiscard]] bool isPixelPerfectFont() const; + [[nodiscard]] float getFontSize() const; + [[nodiscard]] bool isBold() const; + [[nodiscard]] bool isItalic() const; + [[nodiscard]] bool isAntiAliased() const; + + private: + bool drawPopup(); + + private: + FontFilePicker m_fontFilePicker; + SliderPoints m_fontSize; + ContentRegistry::Settings::Widgets::Checkbox m_bold, m_italic, m_antiAliased; + }; + + ContentRegistry::Settings::Widgets::Widget::Interface& addFontSettingsWidget(UnlocalizedString name); + +} \ No newline at end of file diff --git a/plugins/fonts/include/fonts/fonts.hpp b/plugins/fonts/include/fonts/fonts.hpp new file mode 100644 index 000000000..cd8ea02c1 --- /dev/null +++ b/plugins/fonts/include/fonts/fonts.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace hex::fonts { + + const static auto Default = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.default"); }; + const static auto HexEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.hex_editor"); }; + const static auto CodeEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.code_editor"); }; + +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/de_DE.json b/plugins/fonts/romfs/lang/de_DE.json new file mode 100644 index 000000000..a16fd3edc --- /dev/null +++ b/plugins/fonts/romfs/lang/de_DE.json @@ -0,0 +1,18 @@ +{ + "code": "de-DE", + "country": "Germany", + "fallback": false, + "language": "German", + "translations": { + "hex.fonts.setting.font": "Schriftart", + "hex.fonts.setting.font.custom_font": "Schriftart", + "hex.fonts.setting.font.custom_font_info": "Die folgenden Einstellungen sind nur verfügbar wenn eine benutzerdefinierte Schriftart ausgewählt ist.", + "hex.fonts.setting.font.font_antialias": "Schrift Anti-Aliasing", + "hex.fonts.setting.font.font_bold": "Fett", + "hex.fonts.setting.font.font_italic": "Kursiv", + "hex.fonts.setting.font.font_path": "Schriftart", + "hex.fonts.setting.font.font_size": "Schriftgrösse", + "hex.fonts.setting.font.glyphs": "Glyphen", + "hex.fonts.setting.font.load_all_unicode_chars": "Alle Unicode Zeichen laden" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/en_US.json b/plugins/fonts/romfs/lang/en_US.json new file mode 100644 index 000000000..f0586ae6f --- /dev/null +++ b/plugins/fonts/romfs/lang/en_US.json @@ -0,0 +1,21 @@ +{ + "code": "en-US", + "language": "English", + "country": "United States", + "fallback": true, + "translations": { + "hex.fonts.setting.font": "Font", + "hex.fonts.setting.font.glyphs": "Glyphs", + "hex.fonts.setting.font.custom_font": "Font", + "hex.fonts.setting.font.custom_font_info": "The following settings are only available when a custom font has been selected.", + "hex.fonts.setting.font.font_bold": "Bold", + "hex.fonts.setting.font.font_italic": "Italic", + "hex.fonts.setting.font.font_antialias": "Antialiasing", + "hex.fonts.setting.font.font_path": "Font", + "hex.fonts.setting.font.font_size": "Font Size", + "hex.fonts.setting.font.load_all_unicode_chars": "Load all unicode glyphs", + "hex.fonts.font.default": "General UI Font", + "hex.fonts.font.hex_editor": "Hex Editor Font", + "hex.fonts.font.code_editor": "Code Editor Font" + } +} diff --git a/plugins/fonts/romfs/lang/es_ES.json b/plugins/fonts/romfs/lang/es_ES.json new file mode 100644 index 000000000..fdbef0bf5 --- /dev/null +++ b/plugins/fonts/romfs/lang/es_ES.json @@ -0,0 +1,17 @@ +{ + "code": "es_ES", + "country": "Spain", + "fallback": false, + "language": "Spanish", + "translations": { + "hex.fonts.setting.font": "Fuente", + "hex.fonts.setting.font.custom_font": "", + "hex.fonts.setting.font.font_antialias": "", + "hex.fonts.setting.font.font_bold": "", + "hex.fonts.setting.font.font_italic": "", + "hex.fonts.setting.font.font_path": "Fuente", + "hex.fonts.setting.font.font_size": "Tamaño de Fuente", + "hex.fonts.setting.font.glyphs": "", + "hex.fonts.setting.font.load_all_unicode_chars": "Cargar todos los caracteres unicode" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/hu_HU.json b/plugins/fonts/romfs/lang/hu_HU.json new file mode 100644 index 000000000..acc6d19f0 --- /dev/null +++ b/plugins/fonts/romfs/lang/hu_HU.json @@ -0,0 +1,17 @@ +{ + "code": "hu_HU", + "language": "Hungarian", + "country": "Hungary", + "fallback": false, + "translations": { + "hex.fonts.setting.font": "Betűtípus", + "hex.fonts.setting.font.glyphs": "Szimbólumok", + "hex.fonts.setting.font.custom_font": "Saját betűtípus", + "hex.fonts.setting.font.font_bold": "Félkövér", + "hex.fonts.setting.font.font_italic": "Dőlt", + "hex.fonts.setting.font.font_antialias": "Élsimítás", + "hex.fonts.setting.font.font_path": "Betűtípus", + "hex.fonts.setting.font.font_size": "Betűméret", + "hex.fonts.setting.font.load_all_unicode_chars": "Minden Unicode karakter betöltése" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/it_IT.json b/plugins/fonts/romfs/lang/it_IT.json new file mode 100644 index 000000000..f51b5326e --- /dev/null +++ b/plugins/fonts/romfs/lang/it_IT.json @@ -0,0 +1,17 @@ +{ + "code": "it-IT", + "country": "Italy", + "fallback": false, + "language": "Italian", + "translations": { + "hex.fonts.setting.font": "", + "hex.fonts.setting.font.custom_font": "", + "hex.fonts.setting.font.font_antialias": "", + "hex.fonts.setting.font.font_bold": "", + "hex.fonts.setting.font.font_italic": "", + "hex.fonts.setting.font.font_path": "", + "hex.fonts.setting.font.font_size": "", + "hex.fonts.setting.font.glyphs": "", + "hex.fonts.setting.font.load_all_unicode_chars": "" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/ja_JP.json b/plugins/fonts/romfs/lang/ja_JP.json new file mode 100644 index 000000000..ed5b819f1 --- /dev/null +++ b/plugins/fonts/romfs/lang/ja_JP.json @@ -0,0 +1,17 @@ +{ + "code": "ja-JP", + "country": "Japan", + "fallback": false, + "language": "Japanese", + "translations": { + "hex.fonts.setting.font": "フォント", + "hex.fonts.setting.font.custom_font": "", + "hex.fonts.setting.font.font_antialias": "", + "hex.fonts.setting.font.font_bold": "", + "hex.fonts.setting.font.font_italic": "", + "hex.fonts.setting.font.font_path": "フォント", + "hex.fonts.setting.font.font_size": "フォントサイズ", + "hex.fonts.setting.font.glyphs": "", + "hex.fonts.setting.font.load_all_unicode_chars": "" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/ko_KR.json b/plugins/fonts/romfs/lang/ko_KR.json new file mode 100644 index 000000000..74b7aef79 --- /dev/null +++ b/plugins/fonts/romfs/lang/ko_KR.json @@ -0,0 +1,17 @@ +{ + "code": "ko-KR", + "country": "Korea", + "fallback": false, + "language": "Korean", + "translations": { + "hex.fonts.setting.font": "글꼴", + "hex.fonts.setting.font.custom_font": "사용자 정의 글꼴", + "hex.fonts.setting.font.font_antialias": "안티에일리어싱", + "hex.fonts.setting.font.font_bold": "굵게", + "hex.fonts.setting.font.font_italic": "기울임꼴", + "hex.fonts.setting.font.font_path": "글꼴", + "hex.fonts.setting.font.font_size": "글꼴 크기", + "hex.fonts.setting.font.glyphs": "글리프", + "hex.fonts.setting.font.load_all_unicode_chars": "모든 유니코드 문자 불러오기" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/pt_BR.json b/plugins/fonts/romfs/lang/pt_BR.json new file mode 100644 index 000000000..e7c0514e4 --- /dev/null +++ b/plugins/fonts/romfs/lang/pt_BR.json @@ -0,0 +1,17 @@ +{ + "code": "pt-BR", + "country": "Brazil", + "fallback": false, + "language": "Portuguese", + "translations": { + "hex.fonts.setting.font": "Fonte", + "hex.fonts.setting.font.custom_font": "", + "hex.fonts.setting.font.font_antialias": "", + "hex.fonts.setting.font.font_bold": "", + "hex.fonts.setting.font.font_italic": "", + "hex.fonts.setting.font.font_path": "Fonte", + "hex.fonts.setting.font.font_size": "Tamanho da Fonte", + "hex.fonts.setting.font.glyphs": "", + "hex.fonts.setting.font.load_all_unicode_chars": "" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/zh_CN.json b/plugins/fonts/romfs/lang/zh_CN.json new file mode 100644 index 000000000..0d622c88b --- /dev/null +++ b/plugins/fonts/romfs/lang/zh_CN.json @@ -0,0 +1,17 @@ +{ + "code": "zh-CN", + "country": "China", + "fallback": false, + "language": "Chinese (Simplified)", + "translations": { + "hex.fonts.setting.font": "字体", + "hex.fonts.setting.font.custom_font": "自定义字体", + "hex.fonts.setting.font.font_antialias": "抗锯齿", + "hex.fonts.setting.font.font_bold": "粗体", + "hex.fonts.setting.font.font_italic": "斜体", + "hex.fonts.setting.font.font_path": "字体", + "hex.fonts.setting.font.font_size": "字体大小", + "hex.fonts.setting.font.glyphs": "字形", + "hex.fonts.setting.font.load_all_unicode_chars": "加载所有 Unicode 字符" + } +} \ No newline at end of file diff --git a/plugins/fonts/romfs/lang/zh_TW.json b/plugins/fonts/romfs/lang/zh_TW.json new file mode 100644 index 000000000..5ed779f71 --- /dev/null +++ b/plugins/fonts/romfs/lang/zh_TW.json @@ -0,0 +1,18 @@ +{ + "code": "zh-TW", + "country": "Taiwan", + "fallback": false, + "language": "Chinese (Traditional)", + "translations": { + "hex.fonts.setting.font": "字體", + "hex.fonts.setting.font.custom_font": "", + "hex.fonts.setting.font.custom_font_info": "", + "hex.fonts.setting.font.font_antialias": "", + "hex.fonts.setting.font.font_bold": "", + "hex.fonts.setting.font.font_italic": "", + "hex.fonts.setting.font.font_path": "字體", + "hex.fonts.setting.font.font_size": "字體大小", + "hex.fonts.setting.font.glyphs": "", + "hex.fonts.setting.font.load_all_unicode_chars": "載入所有 unicode 字元" + } +} \ No newline at end of file diff --git a/plugins/fonts/source/font_loader.cpp b/plugins/fonts/source/font_loader.cpp index 1645088c1..11255e10a 100644 --- a/plugins/fonts/source/font_loader.cpp +++ b/plugins/fonts/source/font_loader.cpp @@ -1,326 +1,66 @@ #include #include -#include -#include - -#include #include #include #include -#include -#include -#include #include #include #include -#include -#include - -#include #include +#include + namespace hex::fonts { - namespace { - - class Font { - public: - Font() = default; - - float getDescent() const { - return m_font->Descent; - } - - private: - explicit Font(ImFont *font) : m_font(font) { } - - private: - friend class FontAtlas; - - ImFont *m_font; - }; - - class FontAtlas { - public: - FontAtlas() : m_fontAtlas(IM_NEW(ImFontAtlas)) { - enableUnicodeCharacters(false); - - // Set the default configuration for the font atlas - m_config.OversampleH = m_config.OversampleV = 1; - m_config.PixelSnapH = true; - m_config.MergeMode = false; - - // Make sure the font atlas doesn't get too large, otherwise weaker GPUs might reject it - m_fontAtlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight; - m_fontAtlas->TexDesiredWidth = 4096; - } - - ~FontAtlas() { - IM_DELETE(m_fontAtlas); - } - - Font addDefaultFont() { - ImFontConfig config = m_config; - config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - config.SizePixels = std::floor(getAdjustedFontSize(ImHexApi::System::getGlobalScale() * 13.0F)); - - auto font = m_fontAtlas->AddFontDefault(&config); - m_fontSizes.emplace_back(false, config.SizePixels); - - m_config.MergeMode = true; - - return Font(font); - } - - Font addFontFromMemory(const std::vector &fontData, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { - auto &storedFontData = m_fontData.emplace_back(fontData); - - ImFontConfig config = m_config; - config.FontDataOwnedByAtlas = false; - - config.GlyphOffset = { offset.x, offset.y }; - auto font = m_fontAtlas->AddFontFromMemoryTTF(storedFontData.data(), int(storedFontData.size()), getAdjustedFontSize(fontSize), &config, !glyphRange.empty() ? glyphRange.Data : m_glyphRange.Data); - m_fontSizes.emplace_back(scalable, fontSize); - - m_config.MergeMode = true; - - return Font(font); - } - - Font addFontFromRomFs(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { - auto data = romfs::get(path).span(); - return addFontFromMemory({ data.begin(), data.end() }, fontSize, scalable, offset, glyphRange); - } - - Font addFontFromFile(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector &glyphRange = {}) { - wolv::io::File file(path, wolv::io::File::Mode::Read); - - auto data = file.readVector(); - return addFontFromMemory(data, fontSize, scalable, offset, glyphRange); - } - - void setBold(bool enabled) { - if (enabled) - m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; - else - m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Bold; - } - - void setItalic(bool enabled) { - if (enabled) - m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique; - else - m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Oblique; - } - - void setAntiAliasing(bool enabled) { - if (enabled) - m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - else - m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting; - } - - void enableUnicodeCharacters(bool enabled) { - ImFontGlyphRangesBuilder glyphRangesBuilder; - - { - constexpr static std::array controlCodeRange = { 0x0001, 0x001F, 0 }; - constexpr static std::array extendedAsciiRange = { 0x007F, 0x00FF, 0 }; - constexpr static std::array latinExtendedARange = { 0x0100, 0x017F, 0 }; - - glyphRangesBuilder.AddRanges(controlCodeRange.data()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesDefault()); - glyphRangesBuilder.AddRanges(extendedAsciiRange.data()); - glyphRangesBuilder.AddRanges(latinExtendedARange.data()); - } - - if (enabled) { - constexpr static std::array fullRange = { 0x0180, 0xFFEF, 0 }; - - glyphRangesBuilder.AddRanges(fullRange.data()); - } else { - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesJapanese()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesChineseFull()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesCyrillic()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesKorean()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesThai()); - glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesVietnamese()); - glyphRangesBuilder.AddText("⌘⌥⌃⇧⏎⇥⌫⇪"); - } - - m_glyphRange.clear(); - glyphRangesBuilder.BuildRanges(&m_glyphRange); - } - - bool build() const { - return m_fontAtlas->Build(); - } - - [[nodiscard]] ImFontAtlas* getAtlas() { - auto result = m_fontAtlas; - - return result; - } - - float calculateFontDescend(const ImHexApi::Fonts::Font &font, float fontSize) const { - auto atlas = std::make_unique(); - auto cfg = m_config; - - // Calculate the expected font size - auto size = fontSize; - if (font.defaultSize.has_value()) - size = font.defaultSize.value() * std::max(1.0F, std::floor(ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize)); - else - size = std::max(1.0F, std::floor(size / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize; - - cfg.MergeMode = false; - cfg.SizePixels = size; - cfg.FontDataOwnedByAtlas = false; - - // Construct a range that only contains the first glyph of the font - ImVector queryRange; - { - auto firstGlyph = font.glyphRanges.empty() ? m_glyphRange.front() : font.glyphRanges.front().begin; - queryRange.push_back(firstGlyph); - queryRange.push_back(firstGlyph); - } - queryRange.push_back(0x00); - - // Build the font atlas with the query range - auto newFont = atlas->AddFontFromMemoryTTF(const_cast(font.fontData.data()), int(font.fontData.size()), 0, &cfg, queryRange.Data); - atlas->Build(); - - return newFont->Descent; - } - - void reset() { - IM_DELETE(m_fontAtlas); - m_fontAtlas = IM_NEW(ImFontAtlas); - - m_fontData.clear(); - m_config.MergeMode = false; - } - - void updateFontScaling(float newScaling) { - for (int i = 0; i < m_fontAtlas->ConfigData.size(); i += 1) { - const auto &[scalable, fontSize] = m_fontSizes[i]; - auto &configData = m_fontAtlas->ConfigData[i]; - - if (!scalable) { - configData.SizePixels = fontSize * std::floor(newScaling); - } else { - configData.SizePixels = fontSize * newScaling; - } - } - } - - private: - float getAdjustedFontSize(float fontSize) const { - // Since macOS reports half the framebuffer size that's actually available, - // we'll multiply all font sizes by that and then divide the global font scale - // by the same amount to get super crisp font rendering. - return fontSize * hex::ImHexApi::System::getBackingScaleFactor(); - } - - private: - ImFontAtlas* m_fontAtlas; - std::vector> m_fontSizes; - ImFontConfig m_config; - ImVector m_glyphRange; - - std::list> m_fontData; - }; - - std::fs::path findCustomFontPath() { - // Find the custom font file specified in the settings - auto fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", ""); - 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)); - fontFile.clear(); - } - - log::info("Loading custom font from {}", wolv::util::toUTF8String(fontFile)); - } - - // If no custom font has been specified, search for a file called "font.ttf" in one of the resource folders - if (fontFile.empty()) { - for (const auto &dir : paths::Resources.read()) { - auto path = dir / "font.ttf"; - if (wolv::io::fs::exists(path)) { - log::info("Loading custom font from {}", wolv::util::toUTF8String(path)); - - fontFile = path; - break; - } - } - } - - return fontFile; + bool buildFontAtlas(FontAtlas *fontAtlas, std::fs::path fontPath, bool pixelPerfectFont, float fontSize, bool loadUnicodeCharacters, bool bold, bool italic, bool antialias) { + if (fontAtlas == nullptr) { + return false; } - float getFontSize() { - const auto pixelPerfectFont = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", true); - - if (pixelPerfectFont) - return 13.0F * ImHexApi::System::getGlobalScale(); - else - return float(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13)) * ImHexApi::System::getGlobalScale(); - } - - } - - bool buildFontAtlasImpl(bool loadUnicodeCharacters) { - static FontAtlas fontAtlas; - fontAtlas.reset(); + fontAtlas->reset(); // 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.font", "hex.builtin.setting.font.load_all_unicode_chars", false) && + ContentRegistry::Settings::read("hex.fonts.setting.font", "hex.builtin.fonts.font.load_all_unicode_chars", false) && ImHexApi::System::getGPUVendor() != "VMware, Inc."; if (!loadUnicodeCharacters) shouldLoadUnicode = false; - fontAtlas.enableUnicodeCharacters(shouldLoadUnicode); + fontAtlas->enableUnicodeCharacters(shouldLoadUnicode); - auto pixelPerfectFont = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.pixel_perfect_default_font", true); - - std::fs::path customFontPath; // If a custom font is set in the settings, load the rest of the settings as well if (!pixelPerfectFont) { - fontAtlas.setBold(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_bold", false)); - fontAtlas.setItalic(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_italic", false)); - fontAtlas.setAntiAliasing(ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_antialias", true)); - - customFontPath = findCustomFontPath(); + fontAtlas->setBold(bold); + fontAtlas->setItalic(italic); + fontAtlas->setAntiAliasing(antialias); + } else { + fontPath.clear(); } - ImHexApi::Fonts::impl::setFontSize(getFontSize()); - - const auto fontSize = ImHexApi::Fonts::getFontSize(); // Try to load the custom font if one was set std::optional defaultFont; - if (!customFontPath.empty()) { - defaultFont = fontAtlas.addFontFromFile(customFontPath, fontSize, true, ImVec2()); - if (!fontAtlas.build()) { - log::error("Failed to load custom font '{}'! Falling back to default font", wolv::util::toUTF8String(customFontPath)); + if (!fontPath.empty()) { + defaultFont = fontAtlas->addFontFromFile(fontPath, fontSize, true, ImVec2()); + if (!fontAtlas->build()) { + log::error("Failed to load custom font '{}'! Falling back to default font", wolv::util::toUTF8String(fontPath)); defaultFont.reset(); } } // If there's no custom font set, or it failed to load, fall back to the default font if (!defaultFont.has_value()) { - if (pixelPerfectFont) - defaultFont = fontAtlas.addDefaultFont(); - else - defaultFont = fontAtlas.addFontFromRomFs("fonts/JetBrainsMono.ttf", fontSize, true, ImVec2()); + if (pixelPerfectFont) { + defaultFont = fontAtlas->addDefaultFont(); + fontSize = std::floor(fontAtlas->getAdjustedFontSize(ImHexApi::System::getGlobalScale() * 13.0F)); + } else + defaultFont = fontAtlas->addFontFromRomFs("fonts/JetBrainsMono.ttf", fontSize, true, ImVec2()); - if (!fontAtlas.build()) { + if (!fontAtlas->build()) { log::fatal("Failed to load default font!"); return false; } @@ -345,48 +85,25 @@ namespace hex::fonts { glyphRanges.push_back(glyphRange); // Calculate the glyph offset for the font - ImVec2 offset = { font.offset.x, font.offset.y - (defaultFont->getDescent() - fontAtlas.calculateFontDescend(font, fontSize)) }; + const ImVec2 offset = { font.offset.x, font.offset.y - (defaultFont->getDescent() - fontAtlas->calculateFontDescend(font, fontSize)) }; // Load the font - fontAtlas.addFontFromMemory(font.fontData, font.defaultSize.value_or(fontSize), !font.defaultSize.has_value(), offset, glyphRanges.back()); + fontAtlas->addFontFromMemory(font.fontData, font.defaultSize.value_or(fontSize), !font.defaultSize.has_value(), offset, glyphRanges.back()); } } - EventDPIChanged::subscribe([](float, float newScaling) { - fontAtlas.updateFontScaling(newScaling); - - if (fontAtlas.build()) { - ImGui_ImplOpenGL3_DestroyFontsTexture(); - ImGui_ImplOpenGL3_CreateFontsTexture(); - ImHexApi::Fonts::impl::setFontAtlas(fontAtlas.getAtlas()); - } - }); - // Build the font atlas - const bool result = fontAtlas.build(); - if (result) { - // Set the font atlas if the build was successful - ImHexApi::Fonts::impl::setFontAtlas(fontAtlas.getAtlas()); + if (fontAtlas->build()) { return true; } // If the build wasn't successful and Unicode characters are enabled, try again without them // If they were disabled already, something went wrong, and we can't recover from it if (!shouldLoadUnicode) { - // Reset Unicode loading and scaling factor settings back to default to make sure the user can still use the application - ContentRegistry::Settings::write("hex.builtin.setting.font", "hex.builtin.setting.font.load_all_unicode_chars", false); - - ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.scaling_factor", 1.0F); - ImHexApi::System::impl::setGlobalScale(1.0F); - return false; } else { - return buildFontAtlasImpl(false); + return buildFontAtlas(fontAtlas, fontPath, pixelPerfectFont, fontSize, false, bold, italic, antialias); } } - bool buildFontAtlas() { - return buildFontAtlasImpl(true); - } - } \ No newline at end of file diff --git a/plugins/fonts/source/font_settings.cpp b/plugins/fonts/source/font_settings.cpp new file mode 100644 index 000000000..288486249 --- /dev/null +++ b/plugins/fonts/source/font_settings.cpp @@ -0,0 +1,206 @@ +#include + +#include +#include +#include + +#include + +namespace hex::fonts { + constexpr static auto PixelPerfectName = "Pixel-Perfect Default Font (Proggy Clean)"; + constexpr static auto SmoothName = "Smooth Default Font (JetbrainsMono)"; + constexpr static auto CustomName = "Custom Font"; + + bool FontFilePicker::draw(const std::string &name) { + bool changed = false; + + const bool pixelPerfectFont = isPixelPerfectFontSelected(); + bool customFont = updateSelectedFontName(); + + if (ImGui::BeginCombo(name.c_str(), m_selectedFontName.c_str())) { + if (ImGui::Selectable(PixelPerfectName, m_path.empty() && pixelPerfectFont)) { + m_path.clear(); + m_pixelPerfectFont = true; + changed = true; + } + + if (ImGui::Selectable(SmoothName, m_path.empty() && !pixelPerfectFont)) { + m_path.clear(); + m_pixelPerfectFont = false; + changed = true; + } + + if (ImGui::Selectable(CustomName, customFont)) { + changed = fs::openFileBrowser(fs::DialogMode::Open, { { "TTF Font", "ttf" }, { "OTF Font", "otf" } }, [this](const std::fs::path &path) { + m_path = path; + m_pixelPerfectFont = false; + }); + } + + for (const auto &[path, fontName] : hex::getFonts()) { + if (ImGui::Selectable(limitStringLength(fontName, 50).c_str(), m_path == path)) { + m_path = path; + m_pixelPerfectFont = false; + changed = true; + } + ImGui::SetItemTooltip("%s", fontName.c_str()); + } + + ImGui::EndCombo(); + } + + return changed; + } + + bool FontFilePicker::isPixelPerfectFontSelected() const { + return m_pixelPerfectFont; + } + + const std::string& FontFilePicker::getSelectedFontName() const { + return m_selectedFontName; + } + + void FontFilePicker::load(const nlohmann::json& data) { + FilePicker::load(data["path"]); + m_pixelPerfectFont = data["pixel_perfect_font"]; + + updateSelectedFontName(); + } + + nlohmann::json FontFilePicker::store() { + nlohmann::json data = nlohmann::json::object(); + data["path"] = FilePicker::store(); + data["pixel_perfect_font"] = m_pixelPerfectFont; + + return data; + } + + bool FontFilePicker::updateSelectedFontName() { + const auto &fonts = hex::getFonts(); + bool customFont = false; + const bool pixelPerfectFont = isPixelPerfectFontSelected(); + + if (m_path.empty() && pixelPerfectFont) { + m_selectedFontName = PixelPerfectName; + } else if (m_path.empty() && !pixelPerfectFont) { + m_selectedFontName = SmoothName; + } else if (fonts.contains(m_path)) { + m_selectedFontName = fonts.at(m_path); + } else { + m_selectedFontName = wolv::util::toUTF8String(m_path.filename()); + customFont = true; + } + + return customFont; + } + + static float pixelsToPoints(float pixels) { + return pixels * (72_scaled / 96.0F); + } + + static float pointsToPixels(float points) { + return points / (72_scaled / 96.0F); + } + + bool SliderPoints::draw(const std::string &name) { + float value = pixelsToPoints(m_value); + float min = pixelsToPoints(m_min); + float max = pixelsToPoints(m_max); + + auto changed = ImGui::SliderFloat(name.c_str(), &value, min, max, "%.0f pt"); + + m_value = pointsToPixels(value); + + return changed; + } + + + bool FontSelector::draw(const std::string &name) { + ImGui::PushID(name.c_str()); + ON_SCOPE_EXIT { ImGui::PopID(); }; + + if (ImGui::Button(m_fontFilePicker.getSelectedFontName().c_str(), ImVec2(300_scaled, 0))) { + ImGui::OpenPopup("Fonts"); + } + + ImGui::SameLine(); + + ImGui::TextUnformatted(name.c_str()); + + ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); + return drawPopup(); + } + + nlohmann::json FontSelector::store() { + nlohmann::json json = nlohmann::json::object(); + + json["font_file"] = m_fontFilePicker.store(); + json["font_size"] = m_fontSize.store(); + json["bold"] = m_bold.store(); + json["italic"] = m_italic.store(); + json["antialiased"] = m_antiAliased.store(); + + return json; + } + void FontSelector::load(const nlohmann::json& data) { + m_fontFilePicker.load(data["font_file"]); + m_fontSize.load(data["font_size"]); + m_bold.load(data["bold"]); + m_italic.load(data["italic"]); + m_antiAliased.load(data["antialiased"]); + } + + bool FontSelector::drawPopup() { + bool changed = false; + if (ImGui::BeginPopup("Fonts")) { + m_fontFilePicker.draw("hex.fonts.setting.font.custom_font"_lang); + + ImGui::BeginDisabled(m_fontFilePicker.isPixelPerfectFontSelected()); + { + m_fontSize.draw("hex.fonts.setting.font.font_size"_lang); + m_bold.draw("hex.fonts.setting.font.font_bold"_lang); + m_italic.draw("hex.fonts.setting.font.font_italic"_lang); + m_antiAliased.draw("hex.fonts.setting.font.font_antialias"_lang); + } + ImGui::EndDisabled(); + + ImGui::NewLine(); + if (ImGui::Button("hex.ui.common.apply"_lang)) + changed = true; + + ImGui::EndPopup(); + } + + return changed; + } + + [[nodiscard]] const std::fs::path& FontSelector::getFontPath() const { + return m_fontFilePicker.getPath(); + } + + [[nodiscard]] bool FontSelector::isPixelPerfectFont() const { + return m_fontFilePicker.isPixelPerfectFontSelected(); + } + + [[nodiscard]] float FontSelector::getFontSize() const { + return m_fontSize.getValue(); + } + + [[nodiscard]] bool FontSelector::isBold() const { + return m_bold.isChecked(); + } + + [[nodiscard]] bool FontSelector::isItalic() const { + return m_italic.isChecked(); + } + + [[nodiscard]] bool FontSelector::isAntiAliased() const { + return m_antiAliased.isChecked(); + } + + + ContentRegistry::Settings::Widgets::Widget::Interface& addFontSettingsWidget(UnlocalizedString name) { + return ContentRegistry::Settings::add("hex.fonts.setting.font", "hex.fonts.setting.font.custom_font", std::move(name)); + } + +} diff --git a/plugins/fonts/source/library_fonts.cpp b/plugins/fonts/source/library_fonts.cpp index 20fb596dc..88ed6a7a0 100644 --- a/plugins/fonts/source/library_fonts.cpp +++ b/plugins/fonts/source/library_fonts.cpp @@ -4,16 +4,109 @@ #include #include +#include +#include +#include +#include namespace hex::fonts { + void registerFonts(); - bool buildFontAtlas(); + bool buildFontAtlas(FontAtlas *fontAtlas, std::fs::path fontPath, bool pixelPerfectFont, float fontSize, bool loadUnicodeCharacters, bool bold, bool italic, bool antialias); + + static AutoReset> s_fontAtlases; + + void loadFont(const ContentRegistry::Settings::Widgets::Widget &widget, const UnlocalizedString &name, ImFont **font) { + const auto &settings = static_cast(widget); + + FontAtlas atlas; + + const bool atlasBuilt = buildFontAtlas( + &atlas, + settings.getFontPath(), + settings.isPixelPerfectFont(), + settings.getFontSize(), + true, + settings.isBold(), + settings.isItalic(), + settings.isAntiAliased() + ); + + if (!atlasBuilt) { + buildFontAtlas( + &atlas, + "", + false, + settings.getFontSize(), + false, + settings.isBold(), + settings.isItalic(), + settings.isAntiAliased() + ); + + log::error("Failed to load font {}! Reverting back to default font!", name.get()); + } + + if (*font != nullptr) { + EventDPIChanged::unsubscribe(*font); + } + + *font = atlas.getAtlas()->Fonts[0]; + + s_fontAtlases->insert_or_assign(*font, std::move(atlas)); + (*s_fontAtlases)[*font] = std::move(atlas); + + EventDPIChanged::subscribe(*font, [&atlas, font](float, float newScaling) { + atlas.updateFontScaling(newScaling); + + if (atlas.build()) { + auto &io = ImGui::GetIO(); + auto prevFont = io.Fonts; + + { + io.Fonts = atlas.getAtlas(); + ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); + io.Fonts = prevFont; + } + + *font = atlas.getAtlas()->Fonts[0]; + } + }); + } + + bool setupFonts() { + ContentRegistry::Settings::add("hex.fonts.setting.font", "hex.fonts.setting.font.glyphs", "hex.fonts.setting.font.load_all_unicode_chars", false).requiresRestart(); + + for (auto &[name, font] : ImHexApi::Fonts::impl::getFontDefinitions()) { + auto &widget = addFontSettingsWidget(name) + .setChangedCallback([name, &font, firstLoad = true](auto &widget) mutable { + if (firstLoad) { + firstLoad = false; + return; + } + + loadFont(widget, name, &font); + }); + + loadFont(widget.getWidget(), name, &font); + } + + return true; + } + } IMHEX_LIBRARY_SETUP("Fonts") { hex::log::debug("Using romfs: '{}'", romfs::name()); + for (auto &path : romfs::list("lang")) + hex::ContentRegistry::Language::addLocalization(nlohmann::json::parse(romfs::get(path).string())); - hex::ImHexApi::System::addStartupTask("Loading fonts", true, hex::fonts::buildFontAtlas); + hex::ImHexApi::Fonts::registerFont("hex.fonts.font.default"); + hex::ImHexApi::Fonts::registerFont("hex.fonts.font.hex_editor"); + hex::ImHexApi::Fonts::registerFont("hex.fonts.font.code_editor"); + + hex::ImHexApi::System::addStartupTask("Loading fonts", true, hex::fonts::setupFonts); hex::fonts::registerFonts(); } \ No newline at end of file diff --git a/plugins/ui/romfs/lang/en_US.json b/plugins/ui/romfs/lang/en_US.json index aec032258..85905ee4e 100644 --- a/plugins/ui/romfs/lang/en_US.json +++ b/plugins/ui/romfs/lang/en_US.json @@ -7,6 +7,7 @@ "hex.ui.common.add": "Add", "hex.ui.common.address": "Address", "hex.ui.common.allow": "Allow", + "hex.ui.common.apply": "Apply", "hex.ui.common.begin": "Begin", "hex.ui.common.big": "Big", "hex.ui.common.big_endian": "Big Endian", diff --git a/plugins/ui/source/ui/hex_editor.cpp b/plugins/ui/source/ui/hex_editor.cpp index da4f4ea33..3b31393a2 100644 --- a/plugins/ui/source/ui/hex_editor.cpp +++ b/plugins/ui/source/ui/hex_editor.cpp @@ -12,6 +12,7 @@ #include #include +#include namespace hex::ui { @@ -1280,7 +1281,9 @@ namespace hex::ui { if (tableSize.y <= 0) tableSize.y = height; + ImGui::PushFont(fonts::HexEditor()); this->drawEditor(tableSize); + ImGui::PopFont(); if (tableSize.y > 0) this->drawFooter(footerSize); diff --git a/plugins/visualizers/source/content/pl_visualizers/timestamp.cpp b/plugins/visualizers/source/content/pl_visualizers/timestamp.cpp index a9fd1952d..50fddfaf7 100644 --- a/plugins/visualizers/source/content/pl_visualizers/timestamp.cpp +++ b/plugins/visualizers/source/content/pl_visualizers/timestamp.cpp @@ -22,8 +22,6 @@ namespace hex::plugin::visualizers { auto lastMonthDay = std::chrono::year_month_day_last(date.year(), date.month() / std::chrono::last); auto firstWeekDay = std::chrono::weekday(std::chrono::year_month_day(date.year(), date.month(), std::chrono::day(1))); - const auto scale = 1_scaled * (ImHexApi::Fonts::getFontSize() / ImHexApi::Fonts::DefaultFontSize); - // Draw calendar if (ImGui::BeginTable("##month_table", 2)) { ImGui::TableNextRow(); @@ -32,7 +30,7 @@ namespace hex::plugin::visualizers { // Draw centered month name and year ImGuiExt::TextFormattedCenteredHorizontal("{:%B %Y}", tm); - if (ImGui::BeginTable("##days_table", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX, ImVec2(160, 120) * scale)) { + if (ImGui::BeginTable("##days_table", 7, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX, scaled(ImVec2(160, 120)))) { constexpr static auto ColumnFlags = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoReorder | ImGuiTableColumnFlags_NoHide; ImGui::TableSetupColumn("M", ColumnFlags); ImGui::TableSetupColumn("T", ColumnFlags); @@ -68,7 +66,7 @@ namespace hex::plugin::visualizers { ImGui::TableNextColumn(); // Draw analog clock - const auto size = ImVec2(120, 120) * scale; + const auto size = scaled(ImVec2(120, 120)); if (ImGui::BeginChild("##clock", size + ImVec2(0, ImGui::GetTextLineHeightWithSpacing()))) { // Draw centered digital hour, minute and seconds ImGuiExt::TextFormattedCenteredHorizontal("{:%H:%M:%S}", tm);