From 11c01e8538843b4a8a8d6d52009df57876e48432 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 18 Sep 2023 22:29:46 -0500 Subject: [PATCH 01/27] Add fontconfig dependency --- ACKNOWLEDGEMENTS.md | 1 + conanfile.py | 1 + scwx-qt/scwx-qt.cmake | 2 ++ 3 files changed, 4 insertions(+) diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index 1e70a3c7..4e6fffa0 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -18,6 +18,7 @@ Supercell Wx uses code from the following dependencies: | [CSS Color Parser](https://github.com/deanm/css-color-parser-js) | [MIT License](https://spdx.org/licenses/MIT.html) | Ported to C++ for MapLibre Native | | [Date](https://github.com/HowardHinnant/date) | [MIT License](https://spdx.org/licenses/MIT.html) | | [Dear ImGui](https://github.com/ocornut/imgui) | [MIT License](https://spdx.org/licenses/MIT.html) | +| [fontconfig](http://fontconfig.org/) | [MIT License](https://spdx.org/licenses/MIT.html) | | [FreeType](https://freetype.org/) | [Freetype Project License](https://spdx.org/licenses/FTL.html) | | [FreeType GL](https://github.com/rougier/freetype-gl) | [BSD 2-Clause with views sentence](https://spdx.org/licenses/BSD-2-Clause-Views.html) | | [GeographicLib](https://geographiclib.sourceforge.io/) | [MIT License](https://spdx.org/licenses/MIT.html) | diff --git a/conanfile.py b/conanfile.py index 4a803580..af9b5b90 100644 --- a/conanfile.py +++ b/conanfile.py @@ -4,6 +4,7 @@ class SupercellWxConan(ConanFile): settings = ("os", "compiler", "build_type", "arch") requires = ("boost/1.81.0", "cpr/1.9.3", + "fontconfig/2.14.2", "freetype/2.12.1", "geographiclib/1.52", "glew/2.2.0", diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index b3d73964..98647767 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -12,6 +12,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Boost) +find_package(Fontconfig) find_package(Freetype) find_package(geographiclib) find_package(glm) @@ -476,6 +477,7 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets Boost::timer qmaplibregl $<$:opengl32> + Fontconfig::Fontconfig freetype-gl GeographicLib::GeographicLib glm::glm From 3202476267838f151e2f3635dcf8ac1a3a8a6ca4 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 19 Sep 2023 23:15:02 -0500 Subject: [PATCH 02/27] Load bundled fonts into application font set --- .../scwx/qt/manager/resource_manager.cpp | 98 ++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 1e694905..fb747c44 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -6,9 +6,14 @@ #include #include +#include #include +#include +#include #include +#include +#include #include namespace scwx @@ -23,6 +28,9 @@ namespace ResourceManager static const std::string logPrefix_ = "scwx::qt::manager::resource_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); +static void InitializeFonts(); +static void InitializeFontCache(); +static void LoadFcApplicationFont(const std::string& fontFilename); static void LoadFonts(); static void LoadTextures(); @@ -31,6 +39,8 @@ static const std::unordered_map fontNames_ { {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; +static std::string fontCachePath_ {}; + static std::unordered_map fontIds_ {}; static std::unordered_map> fonts_ {}; @@ -38,11 +48,17 @@ void Initialize() { config::CountyDatabase::Initialize(); + InitializeFonts(); + LoadFonts(); LoadTextures(); } -void Shutdown() {} +void Shutdown() +{ + // Finalize Fontconfig + FcFini(); +} int FontId(types::Font font) { @@ -100,6 +116,38 @@ LoadImageResources(const std::vector& urlStrings) return images; } +static void InitializeFonts() +{ + // Create a directory to store local fonts + InitializeFontCache(); + + // Initialize Fontconfig + FcConfig* fcConfig = FcInitLoadConfigAndFonts(); + FcConfigSetCurrent(fcConfig); +} + +static void InitializeFontCache() +{ + std::string cachePath { + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + .toStdString() + + "/fonts"}; + + fontCachePath_ = cachePath + "/"; + + if (!std::filesystem::exists(cachePath)) + { + std::error_code error; + if (!std::filesystem::create_directories(cachePath, error)) + { + logger_->error("Unable to create font cache directory: \"{}\" ({})", + cachePath, + error.message()); + fontCachePath_.clear(); + } + } +} + static void LoadFonts() { for (auto& fontName : fontNames_) @@ -108,6 +156,8 @@ static void LoadFonts() QString::fromStdString(fontName.second)); fontIds_.emplace(fontName.first, fontId); + LoadFcApplicationFont(fontName.second); + auto font = util::Font::Create(fontName.second); fonts_.emplace(fontName.first, font); } @@ -116,6 +166,52 @@ static void LoadFonts() fontAtlas->AddFontDefault(); } +static void LoadFcApplicationFont(const std::string& fontFilename) +{ + // If the font cache failed to create, don't attempt to cache any fonts + if (fontCachePath_.empty()) + { + return; + } + + // Make a copy of the font in the cache (if it doesn't exist) + QFile fontFile(QString::fromStdString(fontFilename)); + QFileInfo fontFileInfo(fontFile); + + QFile cacheFile(QString::fromStdString(fontCachePath_) + + fontFileInfo.fileName()); + QFileInfo cacheFileInfo(cacheFile); + std::string cacheFilename = cacheFile.fileName().toStdString(); + + if (fontFile.exists()) + { + // If the file has not been cached, or the font file size has changed + if (!cacheFile.exists() || fontFileInfo.size() != cacheFileInfo.size()) + { + logger_->info("Caching font: {}", fontFilename); + if (!fontFile.copy(cacheFile.fileName())) + { + logger_->error("Could not cache font: {}", fontFilename); + return; + } + } + } + else + { + logger_->error("Font does not exist: {}", fontFilename); + return; + } + + // Load the file into fontconfig (FcConfigAppFontAddFile) + FcBool result = FcConfigAppFontAddFile( + nullptr, reinterpret_cast(cacheFilename.c_str())); + if (!result) + { + logger_->error("Could not load font into fontconfig database", + fontFilename); + } +} + static void LoadTextures() { util::TextureAtlas& textureAtlas = util::TextureAtlas::Instance(); From 7edd512ff7b2d76696e89ed8e263713b02d7775c Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Thu, 21 Sep 2023 23:35:26 -0500 Subject: [PATCH 03/27] Use a vector to store bundled font names to preserve initialization order --- scwx-qt/source/scwx/qt/manager/resource_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index fb747c44..21af6e43 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -34,7 +34,7 @@ static void LoadFcApplicationFont(const std::string& fontFilename); static void LoadFonts(); static void LoadTextures(); -static const std::unordered_map fontNames_ { +static const std::vector> fontNames_ { {types::Font::din1451alt, ":/res/fonts/din1451alt.ttf"}, {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; From d1a478ad12ffeb2ee2135298c3d4229fc49659f1 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 23 Sep 2023 00:44:35 -0500 Subject: [PATCH 04/27] Perform font matching from placefile --- .../scwx/qt/manager/placefile_manager.cpp | 32 ++++++++- .../scwx/qt/manager/resource_manager.cpp | 71 +++++++++++++++++++ .../scwx/qt/manager/resource_manager.hpp | 3 + wxdata/include/scwx/gr/placefile.hpp | 3 + 4 files changed, 106 insertions(+), 3 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp index 77344524..9f10a714 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp @@ -51,8 +51,10 @@ public: void ReadPlacefileSettings(); void WritePlacefileSettings(); + static void + LoadFontResources(const std::shared_ptr& placefile); static std::vector> - LoadResources(const std::shared_ptr& placefile); + LoadImageResources(const std::shared_ptr& placefile); boost::asio::thread_pool threadPool_ {1u}; @@ -587,7 +589,8 @@ void PlacefileManager::Impl::PlacefileRecord::Update() if (updatedPlacefile != nullptr) { // Load placefile resources - auto newImages = Impl::LoadResources(updatedPlacefile); + Impl::LoadFontResources(updatedPlacefile); + auto newImages = Impl::LoadImageResources(updatedPlacefile); // Check the name matches, in case the name updated if (name_ == name) @@ -684,8 +687,31 @@ std::shared_ptr PlacefileManager::Instance() return placefileManager; } +void PlacefileManager::Impl::LoadFontResources( + const std::shared_ptr& placefile) +{ + auto fonts = placefile->fonts(); + + for (auto& font : fonts) + { + units::font_size::pixels size {font.second->pixels_}; + std::vector styles {}; + + if (font.second->IsBold()) + { + styles.push_back("bold"); + } + if (font.second->IsItalic()) + { + styles.push_back("italic"); + } + + ResourceManager::LoadFontResource(font.second->face_, styles, size); + } +} + std::vector> -PlacefileManager::Impl::LoadResources( +PlacefileManager::Impl::LoadImageResources( const std::shared_ptr& placefile) { const auto iconFiles = placefile->icon_files(); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 21af6e43..10ad2557 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -34,6 +34,8 @@ static void LoadFcApplicationFont(const std::string& fontFilename); static void LoadFonts(); static void LoadTextures(); +static const std::string kFcTrueType_ {"TrueType"}; + static const std::vector> fontNames_ { {types::Font::din1451alt, ":/res/fonts/din1451alt.ttf"}, {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, @@ -44,6 +46,9 @@ static std::string fontCachePath_ {}; static std::unordered_map fontIds_ {}; static std::unordered_map> fonts_ {}; +static FcFontSet* fcFontSet_ {nullptr}; +static FcObjectSet* fcObjectSet_ {nullptr}; + void Initialize() { config::CountyDatabase::Initialize(); @@ -80,6 +85,72 @@ std::shared_ptr Font(types::Font font) return nullptr; } +void LoadFontResource(const std::string& family, + const std::vector& styles, + units::font_size::points size) +{ + const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); + const std::string fontString = + fmt::format("{}-{}:{}", family, size.value(), styleString); + + logger_->debug("LoadFontResource: {}", fontString); + + // Build fontconfig pattern + FcPattern* pattern = FcPatternCreate(); + + FcPatternAddString( + pattern, FC_FAMILY, reinterpret_cast(family.c_str())); + FcPatternAddDouble(pattern, FC_SIZE, size.value()); + FcPatternAddString(pattern, + FC_FONTFORMAT, + reinterpret_cast(kFcTrueType_.c_str())); + + if (!styles.empty()) + { + FcPatternAddString(pattern, + FC_STYLE, + reinterpret_cast(styleString.c_str())); + } + + // Perform font pattern match substitution + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + // Find matching font + FcResult result; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); + std::string fontFile {}; + + if (match != nullptr) + { + FcChar8* fcFamily; + FcChar8* fcStyle; + FcChar8* fcFile; + + // Match was found, get properties + if (FcPatternGetString(match, FC_FAMILY, 0, &fcFamily) == FcResultMatch && + FcPatternGetString(match, FC_STYLE, 0, &fcStyle) == FcResultMatch && + FcPatternGetString(match, FC_FILE, 0, &fcFile) == FcResultMatch) + { + fontFile = reinterpret_cast(fcFile); + + logger_->debug("Found matching font: {}:{} ({})", + reinterpret_cast(fcFamily), + reinterpret_cast(fcStyle), + fontFile); + } + } + + if (fontFile.empty()) + { + logger_->warn("Could not find matching font: {}", fontString); + } + + // Cleanup + FcPatternDestroy(match); + FcPatternDestroy(pattern); +} + std::shared_ptr LoadImageResource(const std::string& urlString) { diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp index 7b8003c9..45115fd7 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp @@ -22,6 +22,9 @@ void Shutdown(); int FontId(types::Font font); std::shared_ptr Font(types::Font font); +void LoadFontResource(const std::string& family, + const std::vector& styles, + units::font_size::points size); std::shared_ptr LoadImageResource(const std::string& urlString); std::vector> diff --git a/wxdata/include/scwx/gr/placefile.hpp b/wxdata/include/scwx/gr/placefile.hpp index 017cc112..e9b381fe 100644 --- a/wxdata/include/scwx/gr/placefile.hpp +++ b/wxdata/include/scwx/gr/placefile.hpp @@ -67,6 +67,9 @@ public: std::size_t pixels_ {}; std::int32_t flags_ {}; std::string face_ {}; + + bool IsBold() { return flags_ & 1; } + bool IsItalic() { return flags_ & 2; } }; struct DrawItem From 190bd95781fca7088fe8e7af25fa82b168a511df Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 24 Sep 2023 00:36:27 -0500 Subject: [PATCH 05/27] Add ImGui Font class and dedicated Font Manager --- scwx-qt/scwx-qt.cmake | 8 +- .../source/scwx/qt/manager/font_manager.cpp | 45 ++++++++++ .../source/scwx/qt/manager/font_manager.hpp | 31 +++++++ scwx-qt/source/scwx/qt/types/imgui_font.cpp | 83 +++++++++++++++++++ scwx-qt/source/scwx/qt/types/imgui_font.hpp | 40 +++++++++ 5 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 scwx-qt/source/scwx/qt/manager/font_manager.cpp create mode 100644 scwx-qt/source/scwx/qt/manager/font_manager.hpp create mode 100644 scwx-qt/source/scwx/qt/types/imgui_font.cpp create mode 100644 scwx-qt/source/scwx/qt/types/imgui_font.hpp diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 98647767..4b8cadda 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -74,7 +74,8 @@ set(SRC_GL_DRAW source/scwx/qt/gl/draw/draw_item.cpp source/scwx/qt/gl/draw/placefile_text.cpp source/scwx/qt/gl/draw/placefile_triangles.cpp source/scwx/qt/gl/draw/rectangle.cpp) -set(HDR_MANAGER source/scwx/qt/manager/placefile_manager.hpp +set(HDR_MANAGER source/scwx/qt/manager/font_manager.hpp + source/scwx/qt/manager/placefile_manager.hpp source/scwx/qt/manager/radar_product_manager.hpp source/scwx/qt/manager/radar_product_manager_notifier.hpp source/scwx/qt/manager/resource_manager.hpp @@ -82,7 +83,8 @@ set(HDR_MANAGER source/scwx/qt/manager/placefile_manager.hpp source/scwx/qt/manager/text_event_manager.hpp source/scwx/qt/manager/timeline_manager.hpp source/scwx/qt/manager/update_manager.hpp) -set(SRC_MANAGER source/scwx/qt/manager/placefile_manager.cpp +set(SRC_MANAGER source/scwx/qt/manager/font_manager.cpp + source/scwx/qt/manager/placefile_manager.cpp source/scwx/qt/manager/radar_product_manager.cpp source/scwx/qt/manager/radar_product_manager_notifier.cpp source/scwx/qt/manager/resource_manager.cpp @@ -158,6 +160,7 @@ set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp set(HDR_TYPES source/scwx/qt/types/alert_types.hpp source/scwx/qt/types/font_types.hpp source/scwx/qt/types/github_types.hpp + source/scwx/qt/types/imgui_font.hpp source/scwx/qt/types/map_types.hpp source/scwx/qt/types/qt_types.hpp source/scwx/qt/types/radar_product_record.hpp @@ -165,6 +168,7 @@ set(HDR_TYPES source/scwx/qt/types/alert_types.hpp source/scwx/qt/types/text_types.hpp) set(SRC_TYPES source/scwx/qt/types/alert_types.cpp source/scwx/qt/types/github_types.cpp + source/scwx/qt/types/imgui_font.cpp source/scwx/qt/types/map_types.cpp source/scwx/qt/types/radar_product_record.cpp source/scwx/qt/types/text_event_key.cpp diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp new file mode 100644 index 00000000..77dbf149 --- /dev/null +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -0,0 +1,45 @@ +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +static const std::string logPrefix_ = "scwx::qt::manager::font_manager"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +class FontManager::Impl +{ +public: + explicit Impl() {} + ~Impl() {} +}; + +FontManager::FontManager() : p(std::make_unique()) {} + +FontManager::~FontManager() {}; + +std::shared_ptr FontManager::Instance() +{ + static std::weak_ptr fontManagerReference_ {}; + static std::mutex instanceMutex_ {}; + + std::unique_lock lock(instanceMutex_); + + std::shared_ptr fontManager = fontManagerReference_.lock(); + + if (fontManager == nullptr) + { + fontManager = std::make_shared(); + fontManagerReference_ = fontManager; + } + + return fontManager; +} + +} // namespace manager +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp new file mode 100644 index 00000000..4699a4b5 --- /dev/null +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +class FontManager : public QObject +{ + Q_OBJECT + +public: + explicit FontManager(); + ~FontManager(); + + static std::shared_ptr Instance(); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace manager +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.cpp b/scwx-qt/source/scwx/qt/types/imgui_font.cpp new file mode 100644 index 00000000..b2b42acd --- /dev/null +++ b/scwx-qt/source/scwx/qt/types/imgui_font.cpp @@ -0,0 +1,83 @@ +// Disable strncpy warning +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace types +{ + +static const std::string logPrefix_ = "scwx::qt::types::imgui_font"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +class ImGuiFont::Impl +{ +public: + explicit Impl(const std::string& fontName, + const std::string& fontData, + units::pixels size) : + fontName_ {fontName}, size_ {size} + { + CreateImGuiFont(fontData); + } + + ~Impl() {} + + void CreateImGuiFont(const std::string& fontData); + + const std::string fontName_; + const units::pixels size_; + + ImFont* imFont_ {nullptr}; +}; + +ImGuiFont::ImGuiFont(const std::string& fontName, + const std::string& fontData, + units::pixels size) : + p(std::make_unique(fontName, fontData, size)) +{ +} +ImGuiFont::~ImGuiFont() = default; + +void ImGuiFont::Impl::CreateImGuiFont(const std::string& fontData) +{ + logger_->debug("Creating Font: {}", fontName_); + + ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); + ImFontConfig fontConfig {}; + + const float sizePixels = static_cast(size_.value()); + + // Do not transfer ownership of font data to ImGui, makes const_cast safe + fontConfig.FontDataOwnedByAtlas = false; + + // Assign name to font + strncpy(fontConfig.Name, fontName_.c_str(), sizeof(fontConfig.Name) - 1); + fontConfig.Name[sizeof(fontConfig.Name) - 1] = 0; + + imFont_ = fontAtlas->AddFontFromMemoryTTF( + const_cast(static_cast(fontData.c_str())), + static_cast(std::clamp( + fontData.size(), 0, std::numeric_limits::max())), + sizePixels, + &fontConfig); +} + +ImFont* ImGuiFont::font() +{ + return p->imFont_; +} + +} // namespace types +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.hpp b/scwx-qt/source/scwx/qt/types/imgui_font.hpp new file mode 100644 index 00000000..8c07cd95 --- /dev/null +++ b/scwx-qt/source/scwx/qt/types/imgui_font.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include + +struct ImFont; + +namespace scwx +{ +namespace qt +{ +namespace types +{ + +class ImGuiFont +{ +public: + explicit ImGuiFont(const std::string& fontName, + const std::string& fontData, + units::pixels size); + ~ImGuiFont(); + + ImGuiFont(const ImGuiFont&) = delete; + ImGuiFont& operator=(const ImGuiFont&) = delete; + + ImGuiFont(ImGuiFont&&) = delete; + ImGuiFont& operator=(ImGuiFont&&) = delete; + + ImFont* font(); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace types +} // namespace qt +} // namespace scwx From c807188b2b3f22b44434e5e6a32dd93446951635 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 24 Sep 2023 01:11:04 -0500 Subject: [PATCH 06/27] Refactoring fontconfig from Resource Manager to Font Manager --- .../source/scwx/qt/manager/font_manager.cpp | 202 +++++++++++++++++- .../source/scwx/qt/manager/font_manager.hpp | 9 +- .../scwx/qt/manager/placefile_manager.cpp | 3 +- .../scwx/qt/manager/resource_manager.cpp | 172 +-------------- .../scwx/qt/manager/resource_manager.hpp | 3 - 5 files changed, 206 insertions(+), 183 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 77dbf149..64b93d78 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -1,6 +1,13 @@ #include #include +#include + +#include +#include +#include +#include + namespace scwx { namespace qt @@ -11,33 +18,206 @@ namespace manager static const std::string logPrefix_ = "scwx::qt::manager::font_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); +static const std::string kFcTrueType_ {"TrueType"}; + +struct FontRecord +{ + std::string family_ {}; + std::string style_ {}; + std::string filename_ {}; +}; + class FontManager::Impl { public: - explicit Impl() {} - ~Impl() {} + explicit Impl() + { + InitializeFontCache(); + InitializeFontconfig(); + } + ~Impl() { FinalizeFontconfig(); } + + void FinalizeFontconfig(); + void InitializeFontCache(); + void InitializeFontconfig(); + + static FontRecord MatchFontFile(const std::string& family, + const std::vector& styles); + + std::string fontCachePath_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} FontManager::~FontManager() {}; -std::shared_ptr FontManager::Instance() +std::shared_ptr +FontManager::GetImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size) { - static std::weak_ptr fontManagerReference_ {}; - static std::mutex instanceMutex_ {}; + const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); + const std::string fontString = + fmt::format("{}-{}:{}", family, size.value(), styleString); - std::unique_lock lock(instanceMutex_); + logger_->debug("LoadFontResource: {}", fontString); - std::shared_ptr fontManager = fontManagerReference_.lock(); + FontRecord fontRecord = Impl::MatchFontFile(family, styles); - if (fontManager == nullptr) + // TODO: + Q_UNUSED(fontRecord); + + return nullptr; +} + +void FontManager::LoadApplicationFont(const std::string& filename) +{ + // If the font cache failed to create, don't attempt to cache any fonts + if (p->fontCachePath_.empty()) { - fontManager = std::make_shared(); - fontManagerReference_ = fontManager; + return; } - return fontManager; + // Make a copy of the font in the cache (if it doesn't exist) + QFile fontFile(QString::fromStdString(filename)); + QFileInfo fontFileInfo(fontFile); + + QFile cacheFile(QString::fromStdString(p->fontCachePath_) + + fontFileInfo.fileName()); + QFileInfo cacheFileInfo(cacheFile); + std::string cacheFilename = cacheFile.fileName().toStdString(); + + if (fontFile.exists()) + { + // If the file has not been cached, or the font file size has changed + if (!cacheFile.exists() || fontFileInfo.size() != cacheFileInfo.size()) + { + logger_->info("Caching font: {}", filename); + if (!fontFile.copy(cacheFile.fileName())) + { + logger_->error("Could not cache font: {}", filename); + return; + } + } + } + else + { + logger_->error("Font does not exist: {}", filename); + return; + } + + // Load the file into fontconfig + FcBool result = FcConfigAppFontAddFile( + nullptr, reinterpret_cast(cacheFilename.c_str())); + if (!result) + { + logger_->error("Could not load font into fontconfig database", filename); + } +} + +void FontManager::Impl::InitializeFontCache() +{ + std::string cachePath { + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + .toStdString() + + "/fonts"}; + + fontCachePath_ = cachePath + "/"; + + if (!std::filesystem::exists(cachePath)) + { + std::error_code error; + if (!std::filesystem::create_directories(cachePath, error)) + { + logger_->error("Unable to create font cache directory: \"{}\" ({})", + cachePath, + error.message()); + fontCachePath_.clear(); + } + } +} + +void FontManager::Impl::InitializeFontconfig() +{ + FcConfig* fcConfig = FcInitLoadConfigAndFonts(); + FcConfigSetCurrent(fcConfig); +} + +void FontManager::Impl::FinalizeFontconfig() +{ + FcFini(); +} + +FontRecord +FontManager::Impl::MatchFontFile(const std::string& family, + const std::vector& styles) +{ + const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); + const std::string fontString = fmt::format("{}:{}", family, styleString); + + // Build fontconfig pattern + FcPattern* pattern = FcPatternCreate(); + + FcPatternAddString( + pattern, FC_FAMILY, reinterpret_cast(family.c_str())); + FcPatternAddString(pattern, + FC_FONTFORMAT, + reinterpret_cast(kFcTrueType_.c_str())); + + if (!styles.empty()) + { + FcPatternAddString(pattern, + FC_STYLE, + reinterpret_cast(styleString.c_str())); + } + + // Perform font pattern match substitution + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + // Find matching font + FcResult result; + FcPattern* match = FcFontMatch(nullptr, pattern, &result); + FontRecord record {}; + + if (match != nullptr) + { + FcChar8* fcFamily; + FcChar8* fcStyle; + FcChar8* fcFile; + + // Match was found, get properties + if (FcPatternGetString(match, FC_FAMILY, 0, &fcFamily) == FcResultMatch && + FcPatternGetString(match, FC_STYLE, 0, &fcStyle) == FcResultMatch && + FcPatternGetString(match, FC_FILE, 0, &fcFile) == FcResultMatch) + { + record.family_ = reinterpret_cast(fcFamily); + record.style_ = reinterpret_cast(fcStyle); + record.filename_ = reinterpret_cast(fcFile); + + logger_->debug("Found matching font: {}:{} ({})", + record.family_, + record.style_, + record.filename_); + } + } + + if (record.filename_.empty()) + { + logger_->warn("Could not find matching font: {}", fontString); + } + + // Cleanup + FcPatternDestroy(match); + FcPatternDestroy(pattern); + + return record; +} + +FontManager& FontManager::Instance() +{ + static FontManager instance_ {}; + return instance_; } } // namespace manager diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 4699a4b5..0a4a812e 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -19,7 +19,14 @@ public: explicit FontManager(); ~FontManager(); - static std::shared_ptr Instance(); + std::shared_ptr + GetImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size); + + void LoadApplicationFont(const std::string& filename); + + static FontManager& Instance(); private: class Impl; diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp index 9f10a714..d721e5cb 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -706,7 +707,7 @@ void PlacefileManager::Impl::LoadFontResources( styles.push_back("italic"); } - ResourceManager::LoadFontResource(font.second->face_, styles, size); + FontManager::Instance().GetImGuiFont(font.second->face_, styles, size); } } diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 10ad2557..a622ea48 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,14 +7,9 @@ #include #include -#include #include -#include -#include #include -#include -#include #include namespace scwx @@ -28,42 +24,26 @@ namespace ResourceManager static const std::string logPrefix_ = "scwx::qt::manager::resource_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); -static void InitializeFonts(); -static void InitializeFontCache(); -static void LoadFcApplicationFont(const std::string& fontFilename); static void LoadFonts(); static void LoadTextures(); -static const std::string kFcTrueType_ {"TrueType"}; - static const std::vector> fontNames_ { {types::Font::din1451alt, ":/res/fonts/din1451alt.ttf"}, {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; -static std::string fontCachePath_ {}; - static std::unordered_map fontIds_ {}; static std::unordered_map> fonts_ {}; -static FcFontSet* fcFontSet_ {nullptr}; -static FcObjectSet* fcObjectSet_ {nullptr}; - void Initialize() { config::CountyDatabase::Initialize(); - InitializeFonts(); - LoadFonts(); LoadTextures(); } -void Shutdown() -{ - // Finalize Fontconfig - FcFini(); -} +void Shutdown() {} int FontId(types::Font font) { @@ -85,72 +65,6 @@ std::shared_ptr Font(types::Font font) return nullptr; } -void LoadFontResource(const std::string& family, - const std::vector& styles, - units::font_size::points size) -{ - const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); - const std::string fontString = - fmt::format("{}-{}:{}", family, size.value(), styleString); - - logger_->debug("LoadFontResource: {}", fontString); - - // Build fontconfig pattern - FcPattern* pattern = FcPatternCreate(); - - FcPatternAddString( - pattern, FC_FAMILY, reinterpret_cast(family.c_str())); - FcPatternAddDouble(pattern, FC_SIZE, size.value()); - FcPatternAddString(pattern, - FC_FONTFORMAT, - reinterpret_cast(kFcTrueType_.c_str())); - - if (!styles.empty()) - { - FcPatternAddString(pattern, - FC_STYLE, - reinterpret_cast(styleString.c_str())); - } - - // Perform font pattern match substitution - FcConfigSubstitute(nullptr, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - // Find matching font - FcResult result; - FcPattern* match = FcFontMatch(nullptr, pattern, &result); - std::string fontFile {}; - - if (match != nullptr) - { - FcChar8* fcFamily; - FcChar8* fcStyle; - FcChar8* fcFile; - - // Match was found, get properties - if (FcPatternGetString(match, FC_FAMILY, 0, &fcFamily) == FcResultMatch && - FcPatternGetString(match, FC_STYLE, 0, &fcStyle) == FcResultMatch && - FcPatternGetString(match, FC_FILE, 0, &fcFile) == FcResultMatch) - { - fontFile = reinterpret_cast(fcFile); - - logger_->debug("Found matching font: {}:{} ({})", - reinterpret_cast(fcFamily), - reinterpret_cast(fcStyle), - fontFile); - } - } - - if (fontFile.empty()) - { - logger_->warn("Could not find matching font: {}", fontString); - } - - // Cleanup - FcPatternDestroy(match); - FcPatternDestroy(pattern); -} - std::shared_ptr LoadImageResource(const std::string& urlString) { @@ -187,47 +101,17 @@ LoadImageResources(const std::vector& urlStrings) return images; } -static void InitializeFonts() -{ - // Create a directory to store local fonts - InitializeFontCache(); - - // Initialize Fontconfig - FcConfig* fcConfig = FcInitLoadConfigAndFonts(); - FcConfigSetCurrent(fcConfig); -} - -static void InitializeFontCache() -{ - std::string cachePath { - QStandardPaths::writableLocation(QStandardPaths::CacheLocation) - .toStdString() + - "/fonts"}; - - fontCachePath_ = cachePath + "/"; - - if (!std::filesystem::exists(cachePath)) - { - std::error_code error; - if (!std::filesystem::create_directories(cachePath, error)) - { - logger_->error("Unable to create font cache directory: \"{}\" ({})", - cachePath, - error.message()); - fontCachePath_.clear(); - } - } -} - static void LoadFonts() { + auto& fontManager = FontManager::Instance(); + for (auto& fontName : fontNames_) { int fontId = QFontDatabase::addApplicationFont( QString::fromStdString(fontName.second)); fontIds_.emplace(fontName.first, fontId); - LoadFcApplicationFont(fontName.second); + fontManager.LoadApplicationFont(fontName.second); auto font = util::Font::Create(fontName.second); fonts_.emplace(fontName.first, font); @@ -237,52 +121,6 @@ static void LoadFonts() fontAtlas->AddFontDefault(); } -static void LoadFcApplicationFont(const std::string& fontFilename) -{ - // If the font cache failed to create, don't attempt to cache any fonts - if (fontCachePath_.empty()) - { - return; - } - - // Make a copy of the font in the cache (if it doesn't exist) - QFile fontFile(QString::fromStdString(fontFilename)); - QFileInfo fontFileInfo(fontFile); - - QFile cacheFile(QString::fromStdString(fontCachePath_) + - fontFileInfo.fileName()); - QFileInfo cacheFileInfo(cacheFile); - std::string cacheFilename = cacheFile.fileName().toStdString(); - - if (fontFile.exists()) - { - // If the file has not been cached, or the font file size has changed - if (!cacheFile.exists() || fontFileInfo.size() != cacheFileInfo.size()) - { - logger_->info("Caching font: {}", fontFilename); - if (!fontFile.copy(cacheFile.fileName())) - { - logger_->error("Could not cache font: {}", fontFilename); - return; - } - } - } - else - { - logger_->error("Font does not exist: {}", fontFilename); - return; - } - - // Load the file into fontconfig (FcConfigAppFontAddFile) - FcBool result = FcConfigAppFontAddFile( - nullptr, reinterpret_cast(cacheFilename.c_str())); - if (!result) - { - logger_->error("Could not load font into fontconfig database", - fontFilename); - } -} - static void LoadTextures() { util::TextureAtlas& textureAtlas = util::TextureAtlas::Instance(); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp index 45115fd7..7b8003c9 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp @@ -22,9 +22,6 @@ void Shutdown(); int FontId(types::Font font); std::shared_ptr Font(types::Font font); -void LoadFontResource(const std::string& family, - const std::vector& styles, - units::font_size::points size); std::shared_ptr LoadImageResource(const std::string& urlString); std::vector> From e0aa327bb729e354a2372b0215b6eedec7d91044 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 25 Sep 2023 00:02:36 -0500 Subject: [PATCH 07/27] Add font selection skeleton --- scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 18 ++ scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 208 ++++++++++++++++++ 2 files changed, 226 insertions(+) diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index 9bb52e00..e53d033d 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace scwx @@ -84,6 +85,7 @@ public: explicit SettingsDialogImpl(SettingsDialog* self) : self_ {self}, radarSiteDialog_ {new RadarSiteDialog(self)}, + fontDialog_ {new QFontDialog(self)}, settings_ {std::initializer_list { &defaultRadarSite_, &fontSizes_, @@ -113,6 +115,9 @@ public: QColor(QString::fromStdString( paletteSettings.alert_color(phenomenon, false).GetDefault()))); } + + // Configure font dialog + fontDialog_->setOptions(QFontDialog::FontDialogOption::ScalableFonts); } ~SettingsDialogImpl() = default; @@ -146,6 +151,7 @@ public: SettingsDialog* self_; PlacefileSettingsWidget* placefileSettingsWidget_ {nullptr}; RadarSiteDialog* radarSiteDialog_; + QFontDialog* fontDialog_; settings::SettingsInterface defaultRadarSite_ {}; settings::SettingsInterface> fontSizes_ {}; @@ -232,6 +238,18 @@ void SettingsDialogImpl::ConnectSignals() } }); + QObject::connect(self_->ui->fontSelectButton, + &QAbstractButton::clicked, + self_, + [this]() { fontDialog_->show(); }); + + QObject::connect( + fontDialog_, + &QFontDialog::fontSelected, + self_, + [this](const QFont& font) + { logger_->debug("Selected font: {}", font.toString().toStdString()); }); + // Update the Radar Site dialog "map" location with the currently selected // radar site auto& defaultRadarSite = *defaultRadarSite_.GetSettingsVariable(); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index 15d5d1ec..4e40cdb3 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -438,6 +438,214 @@ + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Reset All Fonts + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Display Item: + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Tornado Warning expires in 15 minutes + + + Qt::AlignCenter + + + true + + + + + + + + + + ... + + + + + + + [Style] + + + + + + + [Font Name] + + + + + + + ... + + + + :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg + + + + + + + [Size] + + + + + + + Font: + + + + + + + Style: + + + + + + + Size: + + + + + + + Preview: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + From ad1646d72517ebc441af59c0a4180b41ee092758 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 25 Sep 2023 23:35:38 -0500 Subject: [PATCH 08/27] Initial setup for font settings --- .../scwx/qt/settings/settings_interface.hpp | 1 + .../source/scwx/qt/settings/text_settings.cpp | 80 ++++++++++++++++++- .../source/scwx/qt/settings/text_settings.hpp | 10 ++- scwx-qt/source/scwx/qt/types/text_types.cpp | 28 +++++++ scwx-qt/source/scwx/qt/types/text_types.hpp | 12 +++ scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 44 ++++++++++ scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 40 +++++----- 7 files changed, 192 insertions(+), 23 deletions(-) diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.hpp b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp index 030b8996..4ba1ea0e 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_interface.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp @@ -103,6 +103,7 @@ private: #ifdef SETTINGS_INTERFACE_IMPLEMENTATION template class SettingsInterface; +template class SettingsInterface; template class SettingsInterface; template class SettingsInterface; diff --git a/scwx-qt/source/scwx/qt/settings/text_settings.cpp b/scwx-qt/source/scwx/qt/settings/text_settings.cpp index b96f94f4..6d4ceafa 100644 --- a/scwx-qt/source/scwx/qt/settings/text_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/text_settings.cpp @@ -12,10 +12,34 @@ namespace settings static const std::string logPrefix_ = "scwx::qt::settings::text_settings"; +static const std::string kAlteDIN1451Mittelscrhift_ { + "Alte DIN 1451 Mittelschrift"}; +static const std::string kInconsolata_ {"Inconsolata"}; + +static const std::string kRegular_ {"Regular"}; + +static const std::unordered_map + kDefaultFontFamily_ { + {types::FontCategory::Default, kAlteDIN1451Mittelscrhift_}, + {types::FontCategory::Tooltip, kInconsolata_}}; +static const std::unordered_map + kDefaultFontStyle_ {{types::FontCategory::Default, kRegular_}, + {types::FontCategory::Tooltip, kRegular_}}; +static const std::unordered_map + kDefaultFontPointSize_ {{types::FontCategory::Default, 12.0}, + {types::FontCategory::Tooltip, 10.5}}; + class TextSettings::Impl { public: - explicit Impl() + struct FontData + { + SettingsVariable fontFamily_ {"font_family"}; + SettingsVariable fontStyle_ {"font_style"}; + SettingsVariable fontPointSize_ {"font_point_size"}; + }; + + explicit Impl(TextSettings* self) : self_ {self} { std::string defaultTooltipMethodValue = types::GetTooltipMethodName(types::TooltipMethod::ImGui); @@ -47,16 +71,24 @@ public: // No match found, invalid return false; }); + + InitializeFontVariables(); } ~Impl() {} + void InitializeFontVariables(); + + TextSettings* self_; + + std::unordered_map fontData_ {}; + SettingsVariable hoverTextWrap_ {"hover_text_wrap"}; SettingsVariable tooltipMethod_ {"tooltip_method"}; }; TextSettings::TextSettings() : - SettingsCategory("text"), p(std::make_unique()) + SettingsCategory("text"), p(std::make_unique(this)) { RegisterVariables({&p->hoverTextWrap_, &p->tooltipMethod_}); SetDefaults(); @@ -66,6 +98,50 @@ TextSettings::~TextSettings() = default; TextSettings::TextSettings(TextSettings&&) noexcept = default; TextSettings& TextSettings::operator=(TextSettings&&) noexcept = default; +void TextSettings::Impl::InitializeFontVariables() +{ + for (auto fontCategory : types::FontCategoryIterator()) + { + auto result = fontData_.emplace(fontCategory, FontData {}); + auto& pair = *result.first; + auto& font = pair.second; + + font.fontFamily_.SetDefault(kDefaultFontFamily_.at(fontCategory)); + font.fontStyle_.SetDefault(kDefaultFontStyle_.at(fontCategory)); + font.fontPointSize_.SetDefault(kDefaultFontPointSize_.at(fontCategory)); + + // String values must not be empty + font.fontFamily_.SetValidator([](const std::string& value) + { return !value.empty(); }); + font.fontStyle_.SetValidator([](const std::string& value) + { return !value.empty(); }); + + // Font point size must be between 6 and 72 + font.fontPointSize_.SetMinimum(6.0); + font.fontPointSize_.SetMaximum(72.0); + + // TODO: Variable registration + } +} + +SettingsVariable& +TextSettings::font_family(types::FontCategory fontCategory) const +{ + return p->fontData_.at(fontCategory).fontFamily_; +} + +SettingsVariable& +TextSettings::font_style(types::FontCategory fontCategory) const +{ + return p->fontData_.at(fontCategory).fontStyle_; +} + +SettingsVariable& +TextSettings::font_point_size(types::FontCategory fontCategory) const +{ + return p->fontData_.at(fontCategory).fontPointSize_; +} + SettingsVariable& TextSettings::hover_text_wrap() const { return p->hoverTextWrap_; diff --git a/scwx-qt/source/scwx/qt/settings/text_settings.hpp b/scwx-qt/source/scwx/qt/settings/text_settings.hpp index 42399f99..a0347b4f 100644 --- a/scwx-qt/source/scwx/qt/settings/text_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/text_settings.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -25,8 +26,15 @@ public: TextSettings(TextSettings&&) noexcept; TextSettings& operator=(TextSettings&&) noexcept; + SettingsVariable& + font_family(types::FontCategory fontCategory) const; + SettingsVariable& + font_style(types::FontCategory fontCategory) const; + SettingsVariable& + font_point_size(types::FontCategory fontCategory) const; + SettingsVariable& hover_text_wrap() const; - SettingsVariable& tooltip_method() const; + SettingsVariable& tooltip_method() const; static TextSettings& Instance(); diff --git a/scwx-qt/source/scwx/qt/types/text_types.cpp b/scwx-qt/source/scwx/qt/types/text_types.cpp index e708cf63..f994dab9 100644 --- a/scwx-qt/source/scwx/qt/types/text_types.cpp +++ b/scwx-qt/source/scwx/qt/types/text_types.cpp @@ -11,12 +11,40 @@ namespace qt namespace types { +static const std::unordered_map fontCategoryName_ { + {FontCategory::Default, "Default"}, + {FontCategory::Tooltip, "Tooltip"}, + {FontCategory::Unknown, "?"}}; + static const std::unordered_map tooltipMethodName_ { {TooltipMethod::ImGui, "ImGui"}, {TooltipMethod::QToolTip, "Native Tooltip"}, {TooltipMethod::QLabel, "Floating Label"}, {TooltipMethod::Unknown, "?"}}; +FontCategory GetFontCategory(const std::string& name) +{ + auto result = + std::find_if(fontCategoryName_.cbegin(), + fontCategoryName_.cend(), + [&](const std::pair& pair) -> bool + { return boost::iequals(pair.second, name); }); + + if (result != fontCategoryName_.cend()) + { + return result->first; + } + else + { + return FontCategory::Unknown; + } +} + +std::string GetFontCategoryName(FontCategory fontCategory) +{ + return fontCategoryName_.at(fontCategory); +} + TooltipMethod GetTooltipMethod(const std::string& name) { auto result = std::find_if( diff --git a/scwx-qt/source/scwx/qt/types/text_types.hpp b/scwx-qt/source/scwx/qt/types/text_types.hpp index 98025a86..07c1ea00 100644 --- a/scwx-qt/source/scwx/qt/types/text_types.hpp +++ b/scwx-qt/source/scwx/qt/types/text_types.hpp @@ -11,6 +11,16 @@ namespace qt namespace types { +enum class FontCategory +{ + Default, + Tooltip, + Unknown +}; +typedef scwx::util:: + Iterator + FontCategoryIterator; + enum class TooltipMethod { ImGui, @@ -22,6 +32,8 @@ typedef scwx::util:: Iterator TooltipMethodIterator; +FontCategory GetFontCategory(const std::string& name); +std::string GetFontCategoryName(FontCategory fontCategory); TooltipMethod GetTooltipMethod(const std::string& name); std::string GetTooltipMethodName(TooltipMethod tooltipMethod); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index e53d033d..c2449e9f 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace scwx @@ -86,6 +87,7 @@ public: self_ {self}, radarSiteDialog_ {new RadarSiteDialog(self)}, fontDialog_ {new QFontDialog(self)}, + fontCategoryModel_ {new QStandardItemModel(self)}, settings_ {std::initializer_list { &defaultRadarSite_, &fontSizes_, @@ -153,6 +155,8 @@ public: RadarSiteDialog* radarSiteDialog_; QFontDialog* fontDialog_; + QStandardItemModel* fontCategoryModel_; + settings::SettingsInterface defaultRadarSite_ {}; settings::SettingsInterface> fontSizes_ {}; settings::SettingsInterface gridWidth_ {}; @@ -173,6 +177,15 @@ public: settings::SettingsInterface> inactiveAlertColors_ {}; + std::unordered_map> + fontFamilies_ {}; + std::unordered_map> + fontStyles_ {}; + std::unordered_map> + fontPointSizes_ {}; + settings::SettingsInterface hoverTextWrap_ {}; settings::SettingsInterface tooltipMethod_ {}; @@ -663,6 +676,37 @@ void SettingsDialogImpl::SetupTextTab() { settings::TextSettings& textSettings = settings::TextSettings::Instance(); + self_->ui->fontListView->setModel(fontCategoryModel_); + for (const auto& fontCategory : types::FontCategoryIterator()) + { + // Add font category to list view + fontCategoryModel_->appendRow(new QStandardItem( + QString::fromStdString(types::GetFontCategoryName(fontCategory)))); + + // Create settings interface + auto fontFamilyResult = fontFamilies_.emplace( + fontCategory, settings::SettingsInterface {}); + auto fontStyleResult = fontStyles_.emplace( + fontCategory, settings::SettingsInterface {}); + auto fontSizeResult = fontPointSizes_.emplace( + fontCategory, settings::SettingsInterface {}); + + auto& fontFamily = (*fontFamilyResult.first).second; + auto& fontStyle = (*fontStyleResult.first).second; + auto& fontSize = (*fontSizeResult.first).second; + + // Add to settings list + settings_.push_back(&fontFamily); + settings_.push_back(&fontStyle); + settings_.push_back(&fontSize); + + // Set settings variables + fontFamily.SetSettingsVariable(textSettings.font_family(fontCategory)); + fontStyle.SetSettingsVariable(textSettings.font_style(fontCategory)); + fontSize.SetSettingsVariable(textSettings.font_point_size(fontCategory)); + } + self_->ui->fontListView->setCurrentIndex(fontCategoryModel_->index(0, 0)); + hoverTextWrap_.SetSettingsVariable(textSettings.hover_text_wrap()); hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox); hoverTextWrap_.SetResetButton(self_->ui->resetHoverTextWrapButton); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index 4e40cdb3..e921f38a 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -469,13 +469,6 @@ 0 - - - Reset All Fonts - - - - Qt::Horizontal @@ -488,10 +481,17 @@ - + + + + Reset All Fonts + + + + - + Display Item: @@ -581,17 +581,6 @@ - - - - ... - - - - :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg - - - @@ -640,6 +629,17 @@ + + + + ... + + + + :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg + + + From d82fb666f9bc4369631548e5d726854538b35bb6 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 26 Sep 2023 23:23:38 -0500 Subject: [PATCH 09/27] Register font subcategories with text settings --- .../scwx/qt/settings/settings_category.cpp | 85 ++++++++++++++++++- .../scwx/qt/settings/settings_category.hpp | 3 +- .../source/scwx/qt/settings/text_settings.cpp | 10 ++- test/data | 2 +- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/scwx-qt/source/scwx/qt/settings/settings_category.cpp b/scwx-qt/source/scwx/qt/settings/settings_category.cpp index 75daf41d..e6c929f8 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_category.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_category.cpp @@ -2,6 +2,8 @@ #include #include +#include + namespace scwx { namespace qt @@ -21,6 +23,8 @@ public: const std::string name_; + std::vector>> + subcategoryArrays_; std::vector variables_; }; @@ -41,6 +45,16 @@ std::string SettingsCategory::name() const void SettingsCategory::SetDefaults() { + // Set subcategory array defaults + for (auto& subcategoryArray : p->subcategoryArrays_) + { + for (auto& subcategory : subcategoryArray.second) + { + subcategory->SetDefaults(); + } + } + + // Set variable defaults for (auto& variable : p->variables_) { variable->SetValueToDefault(); @@ -57,6 +71,47 @@ bool SettingsCategory::ReadJson(const boost::json::object& json) { const boost::json::object& object = value->as_object(); + // Read subcategory arrays + for (auto& subcategoryArray : p->subcategoryArrays_) + { + const boost::json::value* arrayValue = + object.if_contains(subcategoryArray.first); + + if (arrayValue != nullptr && arrayValue->is_object()) + { + const boost::json::object& arrayObject = arrayValue->as_object(); + + for (auto& subcategory : subcategoryArray.second) + { + validated &= subcategory->ReadJson(arrayObject); + } + } + else + { + if (arrayValue == nullptr) + { + logger_->debug( + "Subcategory array key {} is not present, resetting to " + "defaults", + subcategoryArray.first); + } + else if (!arrayValue->is_object()) + { + logger_->warn( + "Invalid json for subcategory array key {}, resetting to " + "defaults", + p->name_); + } + + for (auto& subcategory : subcategoryArray.second) + { + subcategory->SetDefaults(); + } + validated = false; + } + } + + // Read variables for (auto& variable : p->variables_) { validated &= variable->ReadValue(object); @@ -66,8 +121,8 @@ bool SettingsCategory::ReadJson(const boost::json::object& json) { if (value == nullptr) { - logger_->warn("Key {} is not present, resetting to defaults", - p->name_); + logger_->debug("Key {} is not present, resetting to defaults", + p->name_); } else if (!value->is_object()) { @@ -86,6 +141,20 @@ void SettingsCategory::WriteJson(boost::json::object& json) const { boost::json::object object; + // Write subcategory arrays + for (auto& subcategoryArray : p->subcategoryArrays_) + { + boost::json::object arrayObject; + + for (auto& subcategory : subcategoryArray.second) + { + subcategory->WriteJson(arrayObject); + } + + object.insert_or_assign(subcategoryArray.first, arrayObject); + } + + // Write variables for (auto& variable : p->variables_) { variable->WriteValue(object); @@ -94,6 +163,18 @@ void SettingsCategory::WriteJson(boost::json::object& json) const json.insert_or_assign(p->name_, object); } +void SettingsCategory::RegisterSubcategoryArray( + const std::string& name, std::vector& subcategories) +{ + auto& newSubcategories = p->subcategoryArrays_.emplace_back( + name, std::vector {}); + + std::transform(subcategories.begin(), + subcategories.end(), + std::back_inserter(newSubcategories.second), + [](SettingsCategory& subcategory) { return &subcategory; }); +} + void SettingsCategory::RegisterVariables( std::initializer_list variables) { diff --git a/scwx-qt/source/scwx/qt/settings/settings_category.hpp b/scwx-qt/source/scwx/qt/settings/settings_category.hpp index cc07a7d7..d7c86abd 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_category.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_category.hpp @@ -50,7 +50,8 @@ public: */ virtual void WriteJson(boost::json::object& json) const; -protected: + void RegisterSubcategoryArray(const std::string& name, + std::vector& subcategories); void RegisterVariables(std::initializer_list variables); void RegisterVariables(std::vector variables); diff --git a/scwx-qt/source/scwx/qt/settings/text_settings.cpp b/scwx-qt/source/scwx/qt/settings/text_settings.cpp index 6d4ceafa..980abf40 100644 --- a/scwx-qt/source/scwx/qt/settings/text_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/text_settings.cpp @@ -82,6 +82,7 @@ public: TextSettings* self_; std::unordered_map fontData_ {}; + std::vector fontSettings_ {}; SettingsVariable hoverTextWrap_ {"hover_text_wrap"}; SettingsVariable tooltipMethod_ {"tooltip_method"}; @@ -120,8 +121,15 @@ void TextSettings::Impl::InitializeFontVariables() font.fontPointSize_.SetMinimum(6.0); font.fontPointSize_.SetMaximum(72.0); - // TODO: Variable registration + // Variable registration + auto& settings = fontSettings_.emplace_back( + SettingsCategory {types::GetFontCategoryName(fontCategory)}); + + settings.RegisterVariables( + {&font.fontFamily_, &font.fontStyle_, &font.fontPointSize_}); } + + self_->RegisterSubcategoryArray("fonts", fontSettings_); } SettingsVariable& diff --git a/test/data b/test/data index 33caca18..1685e404 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 33caca188b1007c643db75afa560fdfe348c0ee5 +Subproject commit 1685e4048ef4a9f34bc11ecbb8db4905dd0a2e19 From 4e5aa7b5e1b71f2ed4fbff1e7a02e52f59dd95f0 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Wed, 27 Sep 2023 23:47:37 -0500 Subject: [PATCH 10/27] Connecting font selection to settings dialog, in-work --- .../scwx/qt/settings/settings_interface.cpp | 129 ++++++++------- .../scwx/qt/settings/settings_variable.cpp | 6 + .../scwx/qt/settings/settings_variable.hpp | 8 + .../source/scwx/qt/settings/text_settings.cpp | 10 +- scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 153 ++++++++++++++++-- scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 34 +--- 6 files changed, 245 insertions(+), 95 deletions(-) diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.cpp b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp index dd729da0..49fe7513 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_interface.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,9 @@ public: ~Impl() {} + template + void SetWidgetText(U* widget, const T& currentValue); + void UpdateEditWidget(); void UpdateResetButton(); @@ -105,6 +109,11 @@ void SettingsInterface::SetEditWidget(QWidget* widget) p->editWidget_ = widget; + if (widget == nullptr) + { + return; + } + if (QLineEdit* lineEdit = dynamic_cast(widget)) { if constexpr (std::is_same_v) @@ -274,33 +283,36 @@ void SettingsInterface::SetResetButton(QAbstractButton* button) p->resetButton_ = button; - QObject::connect(p->resetButton_, - &QAbstractButton::clicked, - p->context_.get(), - [this]() - { - T defaultValue = p->variable_->GetDefault(); - - if (p->variable_->GetValue() == defaultValue) + if (p->resetButton_ != nullptr) + { + QObject::connect(p->resetButton_, + &QAbstractButton::clicked, + p->context_.get(), + [this]() { - // If the current value is default, reset the staged - // value - p->variable_->Reset(); - p->stagedValid_ = true; - p->UpdateEditWidget(); - p->UpdateResetButton(); - } - else - { - // Stage the default value - p->stagedValid_ = - p->variable_->StageValue(defaultValue); - p->UpdateEditWidget(); - p->UpdateResetButton(); - } - }); + T defaultValue = p->variable_->GetDefault(); - p->UpdateResetButton(); + if (p->variable_->GetValue() == defaultValue) + { + // If the current value is default, reset the + // staged value + p->variable_->Reset(); + p->stagedValid_ = true; + p->UpdateEditWidget(); + p->UpdateResetButton(); + } + else + { + // Stage the default value + p->stagedValid_ = + p->variable_->StageValue(defaultValue); + p->UpdateEditWidget(); + p->UpdateResetButton(); + } + }); + + p->UpdateResetButton(); + } } template @@ -317,6 +329,39 @@ void SettingsInterface::SetMapToValueFunction( p->mapToValue_ = function; } +template +template +void SettingsInterface::Impl::SetWidgetText(U* widget, const T& currentValue) +{ + if constexpr (std::is_integral_v) + { + widget->setText(QString::number(currentValue)); + } + else if constexpr (std::is_same_v) + { + if (mapFromValue_ != nullptr) + { + widget->setText(QString::fromStdString(mapFromValue_(currentValue))); + } + else + { + widget->setText(QString::fromStdString(currentValue)); + } + } + else if constexpr (std::is_same_v>) + { + if (mapFromValue_ != nullptr) + { + widget->setText(QString::fromStdString(mapFromValue_(currentValue))); + } + else + { + widget->setText(QString::fromStdString( + fmt::format("{}", fmt::join(currentValue, ", ")))); + } + } +} + template void SettingsInterface::Impl::UpdateEditWidget() { @@ -327,35 +372,11 @@ void SettingsInterface::Impl::UpdateEditWidget() if (QLineEdit* lineEdit = dynamic_cast(editWidget_)) { - if constexpr (std::is_integral_v) - { - lineEdit->setText(QString::number(currentValue)); - } - else if constexpr (std::is_same_v) - { - if (mapFromValue_ != nullptr) - { - lineEdit->setText( - QString::fromStdString(mapFromValue_(currentValue))); - } - else - { - lineEdit->setText(QString::fromStdString(currentValue)); - } - } - else if constexpr (std::is_same_v>) - { - if (mapFromValue_ != nullptr) - { - lineEdit->setText( - QString::fromStdString(mapFromValue_(currentValue))); - } - else - { - lineEdit->setText(QString::fromStdString( - fmt::format("{}", fmt::join(currentValue, ", ")))); - } - } + SetWidgetText(lineEdit, currentValue); + } + else if (QLabel* label = dynamic_cast(editWidget_)) + { + SetWidgetText(label, currentValue); } else if (QCheckBox* checkBox = dynamic_cast(editWidget_)) { diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp index 270a2eed..1f7661c0 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp @@ -239,6 +239,12 @@ std::optional SettingsVariable::GetStaged() const return p->staged_; } +template +T SettingsVariable::GetStagedOrValue() const +{ + return p->staged_.value_or(GetValue()); +} + template T SettingsVariable::GetDefault() const { diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp index c7999c0d..c05c543f 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp @@ -103,6 +103,14 @@ public: */ std::optional GetStaged() const; + /** + * Gets the staged value of the settings variable, if defined, otherwise the + * current value. + * + * @return Staged value or current value + */ + T GetStagedOrValue() const; + /** * Validate the value against the defined parameters of the settings * variable. diff --git a/scwx-qt/source/scwx/qt/settings/text_settings.cpp b/scwx-qt/source/scwx/qt/settings/text_settings.cpp index 980abf40..5bc9946b 100644 --- a/scwx-qt/source/scwx/qt/settings/text_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/text_settings.cpp @@ -79,6 +79,13 @@ public: void InitializeFontVariables(); + friend bool operator==(const FontData& lhs, const FontData& rhs) + { + return (lhs.fontFamily_ == rhs.fontFamily_ && + lhs.fontStyle_ == rhs.fontStyle_ && + lhs.fontPointSize_ == rhs.fontPointSize_); + } + TextSettings* self_; std::unordered_map fontData_ {}; @@ -168,7 +175,8 @@ TextSettings& TextSettings::Instance() bool operator==(const TextSettings& lhs, const TextSettings& rhs) { - return (lhs.p->hoverTextWrap_ == rhs.p->hoverTextWrap_ && + return (lhs.p->fontData_ == rhs.p->fontData_ && + lhs.p->hoverTextWrap_ == rhs.p->hoverTextWrap_ && lhs.p->tooltipMethod_ == rhs.p->tooltipMethod_); } diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index c2449e9f..c175fe7b 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -119,7 +120,10 @@ public: } // Configure font dialog - fontDialog_->setOptions(QFontDialog::FontDialogOption::ScalableFonts); + fontDialog_->setOptions( + QFontDialog::FontDialogOption::DontUseNativeDialog | + QFontDialog::FontDialogOption::ScalableFonts); + fontDialog_->setWindowModality(Qt::WindowModality::WindowModal); } ~SettingsDialogImpl() = default; @@ -133,6 +137,10 @@ public: void ShowColorDialog(QLineEdit* lineEdit, QFrame* frame = nullptr); void UpdateRadarDialogLocation(const std::string& id); + QFont GetSelectedFont(); + void SelectFontCategory(types::FontCategory fontCategory); + void UpdateFontDisplayData(); + void ApplyChanges(); void DiscardChanges(); void ResetToDefault(); @@ -157,6 +165,8 @@ public: QStandardItemModel* fontCategoryModel_; + types::FontCategory selectedFontCategory_ {types::FontCategory::Unknown}; + settings::SettingsInterface defaultRadarSite_ {}; settings::SettingsInterface> fontSizes_ {}; settings::SettingsInterface gridWidth_ {}; @@ -251,18 +261,6 @@ void SettingsDialogImpl::ConnectSignals() } }); - QObject::connect(self_->ui->fontSelectButton, - &QAbstractButton::clicked, - self_, - [this]() { fontDialog_->show(); }); - - QObject::connect( - fontDialog_, - &QFontDialog::fontSelected, - self_, - [this](const QFont& font) - { logger_->debug("Selected font: {}", font.toString().toStdString()); }); - // Update the Radar Site dialog "map" location with the currently selected // radar site auto& defaultRadarSite = *defaultRadarSite_.GetSettingsVariable(); @@ -270,6 +268,66 @@ void SettingsDialogImpl::ConnectSignals() [this](const std::string& newValue) { UpdateRadarDialogLocation(newValue); }); + QObject::connect( + self_->ui->fontListView->selectionModel(), + &QItemSelectionModel::selectionChanged, + self_, + [this](const QItemSelection& selected, const QItemSelection& deselected) + { + if (selected.size() == 0 && deselected.size() == 0) + { + // Items which stay selected but change their index are not + // included in selected and deselected. Thus, this signal might + // be emitted with both selected and deselected empty, if only + // the indices of selected items change. + return; + } + + if (selected.size() > 0) + { + QModelIndex selectedIndex = selected[0].indexes()[0]; + QVariant variantData = + self_->ui->fontListView->model()->data(selectedIndex); + if (variantData.typeId() == QMetaType::QString) + { + types::FontCategory fontCategory = + types::GetFontCategory(variantData.toString().toStdString()); + SelectFontCategory(fontCategory); + UpdateFontDisplayData(); + } + } + }); + + QObject::connect(self_->ui->fontSelectButton, + &QAbstractButton::clicked, + self_, + [this]() + { + fontDialog_->setCurrentFont(GetSelectedFont()); + fontDialog_->show(); + }); + + QObject::connect(fontDialog_, + &QFontDialog::fontSelected, + self_, + [this](const QFont& font) + { + logger_->debug("Selected font: {}", + font.toString().toStdString()); + + fontFamilies_.at(selectedFontCategory_) + .GetSettingsVariable() + ->StageValue(font.family().toStdString()); + fontStyles_.at(selectedFontCategory_) + .GetSettingsVariable() + ->StageValue(font.styleName().toStdString()); + fontPointSizes_.at(selectedFontCategory_) + .GetSettingsVariable() + ->StageValue(font.pointSizeF()); + + UpdateFontDisplayData(); + }); + QObject::connect( self_->ui->buttonBox, &QDialogButtonBox::clicked, @@ -706,6 +764,8 @@ void SettingsDialogImpl::SetupTextTab() fontSize.SetSettingsVariable(textSettings.font_point_size(fontCategory)); } self_->ui->fontListView->setCurrentIndex(fontCategoryModel_->index(0, 0)); + SelectFontCategory(*types::FontCategoryIterator().begin()); + UpdateFontDisplayData(); hoverTextWrap_.SetSettingsVariable(textSettings.hover_text_wrap()); hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox); @@ -861,6 +921,73 @@ void SettingsDialogImpl::UpdateRadarDialogLocation(const std::string& id) } } +QFont SettingsDialogImpl::GetSelectedFont() +{ + std::string fontFamily = fontFamilies_.at(selectedFontCategory_) + .GetSettingsVariable() + ->GetStagedOrValue(); + std::string fontStyle = fontStyles_.at(selectedFontCategory_) + .GetSettingsVariable() + ->GetStagedOrValue(); + units::font_size::points fontSize { + fontPointSizes_.at(selectedFontCategory_) + .GetSettingsVariable() + ->GetStagedOrValue()}; + + QFont font(QString::fromStdString(fontFamily)); + font.setStyleName(QString::fromStdString(fontStyle)); + font.setPointSizeF(fontSize.value()); + + return font; +} + +void SettingsDialogImpl::SelectFontCategory(types::FontCategory fontCategory) +{ + if (selectedFontCategory_ != types::FontCategory::Unknown && + selectedFontCategory_ != fontCategory) + { + auto& fontFamily = fontFamilies_.at(selectedFontCategory_); + auto& fontStyle = fontStyles_.at(selectedFontCategory_); + auto& fontSize = fontPointSizes_.at(selectedFontCategory_); + + fontFamily.SetResetButton(nullptr); + fontStyle.SetResetButton(nullptr); + fontSize.SetResetButton(nullptr); + + fontFamily.SetEditWidget(nullptr); + fontStyle.SetEditWidget(nullptr); + fontSize.SetEditWidget(nullptr); + } + + if (selectedFontCategory_ != fontCategory) + { + auto& fontFamily = fontFamilies_.at(fontCategory); + auto& fontStyle = fontStyles_.at(fontCategory); + auto& fontSize = fontPointSizes_.at(fontCategory); + + fontFamily.SetResetButton(self_->ui->resetFontButton); + fontStyle.SetResetButton(self_->ui->resetFontButton); + fontSize.SetResetButton(self_->ui->resetFontButton); + + fontFamily.SetEditWidget(self_->ui->fontNameLabel); + fontStyle.SetEditWidget(self_->ui->fontStyleLabel); + fontSize.SetEditWidget(self_->ui->fontSizeLabel); + } + + selectedFontCategory_ = fontCategory; +} + +void SettingsDialogImpl::UpdateFontDisplayData() +{ + QFont font = GetSelectedFont(); + + self_->ui->fontNameLabel->setText(font.family()); + self_->ui->fontStyleLabel->setText(font.styleName()); + self_->ui->fontSizeLabel->setText(QString::number(font.pointSizeF())); + + self_->ui->fontPreviewLabel->setFont(font); +} + void SettingsDialogImpl::ApplyChanges() { logger_->info("Applying settings changes"); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index e921f38a..de695341 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -102,7 +102,7 @@ - 0 + 2 @@ -468,36 +468,16 @@ 0 - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Reset All Fonts - - - - - - - + Display Item: + + + @@ -538,10 +518,10 @@ - QFrame::StyledPanel + QFrame::Panel - QFrame::Raised + QFrame::Plain From d3a3c3db36cbcb15eb1483cf7d32d01dd18e9255 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Thu, 28 Sep 2023 23:11:19 -0500 Subject: [PATCH 11/27] Cleaning up font selection in settings dialog --- .../scwx/qt/settings/settings_interface.cpp | 62 ++++++++++++----- .../scwx/qt/settings/settings_interface.hpp | 13 ++++ .../qt/settings/settings_interface_base.hpp | 10 ++- scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 66 ++++++++----------- scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 2 +- 5 files changed, 95 insertions(+), 58 deletions(-) diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.cpp b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp index 49fe7513..b7133537 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_interface.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp @@ -27,7 +27,7 @@ template class SettingsInterface::Impl { public: - explicit Impl() + explicit Impl(SettingsInterface* self) : self_ {self} { context_->moveToThread(QCoreApplication::instance()->thread()); } @@ -40,6 +40,8 @@ public: void UpdateEditWidget(); void UpdateResetButton(); + SettingsInterface* self_; + SettingsVariable* variable_ {nullptr}; bool stagedValid_ {true}; @@ -53,17 +55,27 @@ public: template SettingsInterface::SettingsInterface() : - SettingsInterfaceBase(), p(std::make_unique()) + SettingsInterfaceBase(), p(std::make_unique(this)) { } template SettingsInterface::~SettingsInterface() = default; template -SettingsInterface::SettingsInterface(SettingsInterface&&) noexcept = default; +SettingsInterface::SettingsInterface(SettingsInterface&& o) noexcept : + p {std::move(o.p)} +{ + p->self_ = this; +} + template SettingsInterface& -SettingsInterface::operator=(SettingsInterface&&) noexcept = default; +SettingsInterface::operator=(SettingsInterface&& o) noexcept +{ + p = std::move(o.p); + p->self_ = this; + return *this; +} template void SettingsInterface::SetSettingsVariable(SettingsVariable& variable) @@ -77,6 +89,27 @@ SettingsVariable* SettingsInterface::GetSettingsVariable() const return p->variable_; } +template +bool SettingsInterface::IsDefault() +{ + bool isDefault = false; + + const std::optional staged = p->variable_->GetStaged(); + const T defaultValue = p->variable_->GetDefault(); + const T value = p->variable_->GetValue(); + + if (staged.has_value()) + { + isDefault = (p->stagedValid_ && *staged == defaultValue); + } + else + { + isDefault = (value == defaultValue); + } + + return isDefault; +} + template bool SettingsInterface::Commit() { @@ -99,6 +132,14 @@ void SettingsInterface::StageDefault() p->UpdateResetButton(); } +template +void SettingsInterface::StageValue(const T& value) +{ + p->variable_->StageValue(value); + p->UpdateEditWidget(); + p->UpdateResetButton(); +} + template void SettingsInterface::SetEditWidget(QWidget* widget) { @@ -412,20 +453,9 @@ void SettingsInterface::Impl::UpdateEditWidget() template void SettingsInterface::Impl::UpdateResetButton() { - const std::optional staged = variable_->GetStaged(); - const T defaultValue = variable_->GetDefault(); - const T value = variable_->GetValue(); - if (resetButton_ != nullptr) { - if (staged.has_value()) - { - resetButton_->setVisible(!stagedValid_ || *staged != defaultValue); - } - else - { - resetButton_->setVisible(value != defaultValue); - } + resetButton_->setVisible(!self_->IsDefault()); } } diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.hpp b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp index 4ba1ea0e..f5f5bb4a 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_interface.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp @@ -45,6 +45,14 @@ public: */ SettingsVariable* GetSettingsVariable() const; + /** + * Gets whether the staged value (or current value, if none staged) is + * set to the default value. + * + * @return true if the settings variable is set to default, otherwise false. + */ + bool IsDefault() override; + /** * Sets the current value of the associated settings variable to the staged * value. @@ -64,6 +72,11 @@ public: */ void StageDefault() override; + /** + * Stages a value to the associated settings variable. + */ + void StageValue(const T& value); + /** * Sets the edit widget from the settings dialog. * diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface_base.hpp b/scwx-qt/source/scwx/qt/settings/settings_interface_base.hpp index 97ae442d..d0dc2ff2 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_interface_base.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_interface_base.hpp @@ -24,10 +24,18 @@ public: SettingsInterfaceBase(SettingsInterfaceBase&&) noexcept; SettingsInterfaceBase& operator=(SettingsInterfaceBase&&) noexcept; + /** + * Gets whether the staged value (or current value, if none staged) is + * set to the default value. + * + * @return true if the settings variable is set to default, otherwise false. + */ + virtual bool IsDefault() = 0; + /** * Sets the current value of the associated settings variable to the staged * value. - * + * * @return true if the staged value was committed, false if no staged value * is present. */ diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index c175fe7b..32464ecf 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -312,18 +312,24 @@ void SettingsDialogImpl::ConnectSignals() self_, [this](const QFont& font) { - logger_->debug("Selected font: {}", - font.toString().toStdString()); - fontFamilies_.at(selectedFontCategory_) - .GetSettingsVariable() - ->StageValue(font.family().toStdString()); + .StageValue(font.family().toStdString()); fontStyles_.at(selectedFontCategory_) - .GetSettingsVariable() - ->StageValue(font.styleName().toStdString()); + .StageValue(font.styleName().toStdString()); fontPointSizes_.at(selectedFontCategory_) - .GetSettingsVariable() - ->StageValue(font.pointSizeF()); + .StageValue(font.pointSizeF()); + + UpdateFontDisplayData(); + }); + + QObject::connect(self_->ui->resetFontButton, + &QAbstractButton::clicked, + self_, + [this]() + { + fontFamilies_.at(selectedFontCategory_).StageDefault(); + fontStyles_.at(selectedFontCategory_).StageDefault(); + fontPointSizes_.at(selectedFontCategory_).StageDefault(); UpdateFontDisplayData(); }); @@ -943,37 +949,6 @@ QFont SettingsDialogImpl::GetSelectedFont() void SettingsDialogImpl::SelectFontCategory(types::FontCategory fontCategory) { - if (selectedFontCategory_ != types::FontCategory::Unknown && - selectedFontCategory_ != fontCategory) - { - auto& fontFamily = fontFamilies_.at(selectedFontCategory_); - auto& fontStyle = fontStyles_.at(selectedFontCategory_); - auto& fontSize = fontPointSizes_.at(selectedFontCategory_); - - fontFamily.SetResetButton(nullptr); - fontStyle.SetResetButton(nullptr); - fontSize.SetResetButton(nullptr); - - fontFamily.SetEditWidget(nullptr); - fontStyle.SetEditWidget(nullptr); - fontSize.SetEditWidget(nullptr); - } - - if (selectedFontCategory_ != fontCategory) - { - auto& fontFamily = fontFamilies_.at(fontCategory); - auto& fontStyle = fontStyles_.at(fontCategory); - auto& fontSize = fontPointSizes_.at(fontCategory); - - fontFamily.SetResetButton(self_->ui->resetFontButton); - fontStyle.SetResetButton(self_->ui->resetFontButton); - fontSize.SetResetButton(self_->ui->resetFontButton); - - fontFamily.SetEditWidget(self_->ui->fontNameLabel); - fontStyle.SetEditWidget(self_->ui->fontStyleLabel); - fontSize.SetEditWidget(self_->ui->fontSizeLabel); - } - selectedFontCategory_ = fontCategory; } @@ -986,6 +961,17 @@ void SettingsDialogImpl::UpdateFontDisplayData() self_->ui->fontSizeLabel->setText(QString::number(font.pointSizeF())); self_->ui->fontPreviewLabel->setFont(font); + + if (selectedFontCategory_ != types::FontCategory::Unknown) + { + auto& fontFamily = fontFamilies_.at(selectedFontCategory_); + auto& fontStyle = fontStyles_.at(selectedFontCategory_); + auto& fontSize = fontPointSizes_.at(selectedFontCategory_); + + self_->ui->resetFontButton->setVisible(!fontFamily.IsDefault() || + !fontStyle.IsDefault() || + !fontSize.IsDefault()); + } } void SettingsDialogImpl::ApplyChanges() diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index de695341..8ed4a76f 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -446,7 +446,7 @@ QFrame::Plain - + From 2e9f5818cd6bb79cacb1321cf5ce3e5117573483 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 1 Oct 2023 22:07:36 -0500 Subject: [PATCH 12/27] Lock the ImGui font atlas when processing a frame --- scwx-qt/source/scwx/qt/manager/font_manager.cpp | 7 +++++++ scwx-qt/source/scwx/qt/manager/font_manager.hpp | 4 ++++ scwx-qt/source/scwx/qt/map/map_widget.cpp | 8 ++++++++ scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp | 8 ++++++++ 4 files changed, 27 insertions(+) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 64b93d78..1cc752fe 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -45,12 +45,19 @@ public: const std::vector& styles); std::string fontCachePath_ {}; + + std::shared_mutex imguiFontAtlasMutex_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} FontManager::~FontManager() {}; +std::shared_mutex& FontManager::imgui_font_atlas_mutex() +{ + return p->imguiFontAtlasMutex_; +} + std::shared_ptr FontManager::GetImGuiFont(const std::string& family, const std::vector& styles, diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 0a4a812e..c647c31a 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -2,6 +2,8 @@ #include +#include + #include namespace scwx @@ -19,6 +21,8 @@ public: explicit FontManager(); ~FontManager(); + std::shared_mutex& imgui_font_atlas_mutex(); + std::shared_ptr GetImGuiFont(const std::string& family, const std::vector& styles, diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 4f279ad3..89eabb3f 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -1028,6 +1029,10 @@ void MapWidget::paintGL() // Setup ImGui Frame ImGui::SetCurrentContext(p->imGuiContext_); + // Lock ImGui font atlas prior to new ImGui frame + std::shared_lock imguiFontAtlasLock { + manager::FontManager::Instance().imgui_font_atlas_mutex()}; + // Start ImGui Frame ImGui_ImplQt_NewFrame(this); ImGui_ImplOpenGL3_NewFrame(); @@ -1059,6 +1064,9 @@ void MapWidget::paintGL() ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + // Unlock ImGui font atlas after rendering + imguiFontAtlasLock.unlock(); + // Paint complete Q_EMIT WidgetPainted(); } diff --git a/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp b/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp index 18011b7f..04e692ac 100644 --- a/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -109,6 +110,10 @@ void ImGuiDebugWidget::paintGL() { ImGui::SetCurrentContext(p->currentContext_); + // Lock ImGui font atlas prior to new ImGui frame + std::shared_lock imguiFontAtlasLock { + manager::FontManager::Instance().imgui_font_atlas_mutex()}; + ImGui_ImplQt_NewFrame(this); ImGui_ImplOpenGL3_NewFrame(); @@ -131,6 +136,9 @@ void ImGuiDebugWidget::paintGL() ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Unlock ImGui font atlas after rendering + imguiFontAtlasLock.unlock(); } } // namespace ui From aead8e726423eed8fd3843a0796411f99472afa2 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 2 Oct 2023 00:10:57 -0500 Subject: [PATCH 13/27] Start of dynamic ImGui font loading --- .../source/scwx/qt/manager/font_manager.cpp | 106 +++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 1cc752fe..994d5a53 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -2,10 +2,13 @@ #include #include +#include #include #include #include +#include +#include #include namespace scwx @@ -27,6 +30,18 @@ struct FontRecord std::string filename_ {}; }; +template +struct FontRecordHash; + +template +struct FontRecordKeyEqual; + +template<> +struct FontRecordHash> +{ + size_t operator()(const std::pair& x) const; +}; + class FontManager::Impl { public: @@ -41,12 +56,24 @@ public: void InitializeFontCache(); void InitializeFontconfig(); + const std::vector& GetRawFontData(const std::string& filename); + static FontRecord MatchFontFile(const std::string& family, const std::vector& styles); std::string fontCachePath_ {}; std::shared_mutex imguiFontAtlasMutex_ {}; + + boost::unordered_flat_map, + std::shared_ptr, + FontRecordHash>> + imguiFonts_ {}; + std::shared_mutex imguiFontsMutex_ {}; + + boost::unordered_flat_map> + rawFontData_ {}; + std::mutex rawFontDataMutex_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} @@ -71,12 +98,69 @@ FontManager::GetImGuiFont(const std::string& family, FontRecord fontRecord = Impl::MatchFontFile(family, styles); - // TODO: - Q_UNUSED(fontRecord); + // Only allow whole pixels, and clamp to 6-72 pt + units::font_size::pixels pixels {size}; + int imFontSize = std::clamp(static_cast(pixels.value()), 8, 96); + + // Search for a loaded ImGui font + { + std::shared_lock imguiFontLock {p->imguiFontsMutex_}; + + // Search for the associated ImGui font + auto it = p->imguiFonts_.find(std::make_pair(fontRecord, imFontSize)); + if (it != p->imguiFonts_.end()) + { + return it->second; + } + + // No ImGui font was found, we need to create one + } + + // Get raw font data + const auto& rawFontData = p->GetRawFontData(fontRecord.filename_); + + // TODO: Create an ImGui font + // TODO: imguiFontLock could be acquired during a render loop, when the font + // atlas is already locked. Lock the font atlas first. Unless it's already + // locked. It might need to be reentrant? + // TODO: Search for font once more, to prevent loading the same font twice + Q_UNUSED(rawFontData); return nullptr; } +const std::vector& +FontManager::Impl::GetRawFontData(const std::string& filename) +{ + std::unique_lock rawFontDataLock {rawFontDataMutex_}; + + auto it = rawFontData_.find(filename); + if (it != rawFontData_.end()) + { + // Raw font data has already been loaded + return it->second; + } + + // Raw font data needs to be loaded + std::basic_ifstream ifs {filename, std::ios::binary}; + ifs.seekg(0, std::ios_base::end); + std::size_t dataSize = ifs.tellg(); + ifs.seekg(0, std::ios_base::beg); + + // Store the font data in a buffer + std::vector buffer {}; + buffer.reserve(dataSize); + std::copy(std::istreambuf_iterator(ifs), + std::istreambuf_iterator(), + std::back_inserter(buffer)); + + // Place the buffer in the cache + auto result = rawFontData_.emplace(filename, std::move(buffer)); + + // Return the cached buffer + return it->second; +} + void FontManager::LoadApplicationFont(const std::string& filename) { // If the font cache failed to create, don't attempt to cache any fonts @@ -227,6 +311,24 @@ FontManager& FontManager::Instance() return instance_; } +size_t FontRecordHash>::operator()( + const std::pair& x) const +{ + size_t seed = 0; + boost::hash_combine(seed, x.first.family_); + boost::hash_combine(seed, x.first.style_); + boost::hash_combine(seed, x.first.filename_); + boost::hash_combine(seed, x.second); + return seed; +} + +bool operator==(const FontRecord& lhs, const FontRecord& rhs) +{ + return lhs.family_ == rhs.family_ && // + lhs.style_ == rhs.style_ && // + lhs.filename_ == rhs.filename_; +} + } // namespace manager } // namespace qt } // namespace scwx From e4ab1e8c19a3f09114cf6b0f45a7ec8ea4f2a1b6 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 2 Oct 2023 23:57:19 -0500 Subject: [PATCH 14/27] Finish dynamic load GetImGuiFont function --- .../source/scwx/qt/manager/font_manager.cpp | 62 +++++++++++++------ .../source/scwx/qt/manager/font_manager.hpp | 3 +- scwx-qt/source/scwx/qt/types/imgui_font.cpp | 22 +++---- scwx-qt/source/scwx/qt/types/imgui_font.hpp | 7 ++- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 994d5a53..2b38168c 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -30,16 +30,15 @@ struct FontRecord std::string filename_ {}; }; +typedef std::pair> FontRecordPair; + template struct FontRecordHash; -template -struct FontRecordKeyEqual; - template<> -struct FontRecordHash> +struct FontRecordHash { - size_t operator()(const std::pair& x) const; + size_t operator()(const FontRecordPair& x) const; }; class FontManager::Impl @@ -65,9 +64,9 @@ public: std::shared_mutex imguiFontAtlasMutex_ {}; - boost::unordered_flat_map, + boost::unordered_flat_map, - FontRecordHash>> + FontRecordHash> imguiFonts_ {}; std::shared_mutex imguiFontsMutex_ {}; @@ -88,7 +87,8 @@ std::shared_mutex& FontManager::imgui_font_atlas_mutex() std::shared_ptr FontManager::GetImGuiFont(const std::string& family, const std::vector& styles, - units::font_size::points size) + units::font_size::points size, + bool loadIfNotFound) { const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); const std::string fontString = @@ -100,14 +100,16 @@ FontManager::GetImGuiFont(const std::string& family, // Only allow whole pixels, and clamp to 6-72 pt units::font_size::pixels pixels {size}; - int imFontSize = std::clamp(static_cast(pixels.value()), 8, 96); + units::font_size::pixels imFontSize { + std::clamp(static_cast(pixels.value()), 8, 96)}; + auto imguiFontKey = std::make_pair(fontRecord, imFontSize); // Search for a loaded ImGui font { std::shared_lock imguiFontLock {p->imguiFontsMutex_}; // Search for the associated ImGui font - auto it = p->imguiFonts_.find(std::make_pair(fontRecord, imFontSize)); + auto it = p->imguiFonts_.find(imguiFontKey); if (it != p->imguiFonts_.end()) { return it->second; @@ -116,17 +118,38 @@ FontManager::GetImGuiFont(const std::string& family, // No ImGui font was found, we need to create one } + // No font was found, return an empty shared pointer if not loading + if (!loadIfNotFound) + { + return nullptr; + } + // Get raw font data const auto& rawFontData = p->GetRawFontData(fontRecord.filename_); - // TODO: Create an ImGui font - // TODO: imguiFontLock could be acquired during a render loop, when the font - // atlas is already locked. Lock the font atlas first. Unless it's already - // locked. It might need to be reentrant? - // TODO: Search for font once more, to prevent loading the same font twice - Q_UNUSED(rawFontData); + // The font atlas mutex might already be locked within an ImGui render frame. + // Lock the font atlas mutex before the fonts mutex to prevent deadlock. + std::unique_lock imguiFontAtlasLock {p->imguiFontAtlasMutex_}; + std::unique_lock imguiFontsLock {p->imguiFontsMutex_}; - return nullptr; + // Search for the associated ImGui font again, to prevent loading the same + // font twice + auto it = p->imguiFonts_.find(imguiFontKey); + if (it != p->imguiFonts_.end()) + { + return it->second; + } + + // Create an ImGui font + std::shared_ptr imguiFont = + std::make_shared( + fontRecord.filename_, rawFontData, imFontSize); + + // Store the ImGui font + p->imguiFonts_.insert_or_assign(imguiFontKey, imguiFont); + + // Return the ImGui font + return imguiFont; } const std::vector& @@ -311,14 +334,13 @@ FontManager& FontManager::Instance() return instance_; } -size_t FontRecordHash>::operator()( - const std::pair& x) const +size_t FontRecordHash::operator()(const FontRecordPair& x) const { size_t seed = 0; boost::hash_combine(seed, x.first.family_); boost::hash_combine(seed, x.first.style_); boost::hash_combine(seed, x.first.filename_); - boost::hash_combine(seed, x.second); + boost::hash_combine(seed, x.second.value()); return seed; } diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index c647c31a..171da79c 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -26,7 +26,8 @@ public: std::shared_ptr GetImGuiFont(const std::string& family, const std::vector& styles, - units::font_size::points size); + units::font_size::points size, + bool loadIfNotFound = false); void LoadApplicationFont(const std::string& filename); diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.cpp b/scwx-qt/source/scwx/qt/types/imgui_font.cpp index b2b42acd..f7ba126c 100644 --- a/scwx-qt/source/scwx/qt/types/imgui_font.cpp +++ b/scwx-qt/source/scwx/qt/types/imgui_font.cpp @@ -23,9 +23,9 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class ImGuiFont::Impl { public: - explicit Impl(const std::string& fontName, - const std::string& fontData, - units::pixels size) : + explicit Impl(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size) : fontName_ {fontName}, size_ {size} { CreateImGuiFont(fontData); @@ -33,23 +33,23 @@ public: ~Impl() {} - void CreateImGuiFont(const std::string& fontData); + void CreateImGuiFont(const std::vector& fontData); - const std::string fontName_; - const units::pixels size_; + const std::string fontName_; + const units::font_size::pixels size_; ImFont* imFont_ {nullptr}; }; -ImGuiFont::ImGuiFont(const std::string& fontName, - const std::string& fontData, - units::pixels size) : +ImGuiFont::ImGuiFont(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size) : p(std::make_unique(fontName, fontData, size)) { } ImGuiFont::~ImGuiFont() = default; -void ImGuiFont::Impl::CreateImGuiFont(const std::string& fontData) +void ImGuiFont::Impl::CreateImGuiFont(const std::vector& fontData) { logger_->debug("Creating Font: {}", fontName_); @@ -66,7 +66,7 @@ void ImGuiFont::Impl::CreateImGuiFont(const std::string& fontData) fontConfig.Name[sizeof(fontConfig.Name) - 1] = 0; imFont_ = fontAtlas->AddFontFromMemoryTTF( - const_cast(static_cast(fontData.c_str())), + const_cast(static_cast(fontData.data())), static_cast(std::clamp( fontData.size(), 0, std::numeric_limits::max())), sizePixels, diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.hpp b/scwx-qt/source/scwx/qt/types/imgui_font.hpp index 8c07cd95..9a69ac6e 100644 --- a/scwx-qt/source/scwx/qt/types/imgui_font.hpp +++ b/scwx-qt/source/scwx/qt/types/imgui_font.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -17,9 +18,9 @@ namespace types class ImGuiFont { public: - explicit ImGuiFont(const std::string& fontName, - const std::string& fontData, - units::pixels size); + explicit ImGuiFont(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size); ~ImGuiFont(); ImGuiFont(const ImGuiFont&) = delete; From acc782b2bcc5ac7cc6b76640718d477b94b8836d Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Thu, 5 Oct 2023 21:18:58 -0500 Subject: [PATCH 15/27] Get ImGui font by font category --- .../source/scwx/qt/manager/font_manager.cpp | 40 ++++++++++++++++--- .../source/scwx/qt/manager/font_manager.hpp | 12 ++++-- .../scwx/qt/manager/placefile_manager.cpp | 2 +- scwx-qt/source/scwx/qt/map/map_widget.cpp | 16 ++++++++ 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 2b38168c..295d0cd8 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -64,6 +64,8 @@ public: std::shared_mutex imguiFontAtlasMutex_ {}; + std::uint64_t imguiFontsBuildCount_ {}; + boost::unordered_flat_map, FontRecordHash> @@ -73,6 +75,12 @@ public: boost::unordered_flat_map> rawFontData_ {}; std::mutex rawFontDataMutex_ {}; + + std::shared_ptr defaultFont_ {}; + boost::unordered_flat_map> + fontCategoryMap_ {}; + std::mutex fontCategoryMutex_ {}; }; FontManager::FontManager() : p(std::make_unique()) {} @@ -84,11 +92,30 @@ std::shared_mutex& FontManager::imgui_font_atlas_mutex() return p->imguiFontAtlasMutex_; } +std::uint64_t FontManager::imgui_fonts_build_count() const +{ + return p->imguiFontsBuildCount_; +} + std::shared_ptr -FontManager::GetImGuiFont(const std::string& family, - const std::vector& styles, - units::font_size::points size, - bool loadIfNotFound) +FontManager::GetImGuiFont(types::FontCategory fontCategory) +{ + std::unique_lock lock {p->fontCategoryMutex_}; + + auto it = p->fontCategoryMap_.find(fontCategory); + if (it != p->fontCategoryMap_.cend()) + { + return it->second; + } + + return p->defaultFont_; +} + +std::shared_ptr +FontManager::LoadImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size, + bool loadIfNotFound) { const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); const std::string fontString = @@ -148,6 +175,9 @@ FontManager::GetImGuiFont(const std::string& family, // Store the ImGui font p->imguiFonts_.insert_or_assign(imguiFontKey, imguiFont); + // Increment ImGui font build count + ++p->imguiFontsBuildCount_; + // Return the ImGui font return imguiFont; } @@ -181,7 +211,7 @@ FontManager::Impl::GetRawFontData(const std::string& filename) auto result = rawFontData_.emplace(filename, std::move(buffer)); // Return the cached buffer - return it->second; + return result.first->second; } void FontManager::LoadApplicationFont(const std::string& filename) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 171da79c..6cee1135 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -22,12 +23,15 @@ public: ~FontManager(); std::shared_mutex& imgui_font_atlas_mutex(); + std::uint64_t imgui_fonts_build_count() const; std::shared_ptr - GetImGuiFont(const std::string& family, - const std::vector& styles, - units::font_size::points size, - bool loadIfNotFound = false); + GetImGuiFont(types::FontCategory fontCategory); + std::shared_ptr + LoadImGuiFont(const std::string& family, + const std::vector& styles, + units::font_size::points size, + bool loadIfNotFound = true); void LoadApplicationFont(const std::string& filename); diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp index d721e5cb..52414031 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp @@ -707,7 +707,7 @@ void PlacefileManager::Impl::LoadFontResources( styles.push_back("italic"); } - FontManager::Instance().GetImGuiFont(font.second->face_, styles, size); + FontManager::Instance().LoadImGuiFont(font.second->face_, styles, size); } } diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 89eabb3f..96aca7a2 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -155,6 +155,7 @@ public: ImGuiContext* imGuiContext_; std::string imGuiContextName_; bool imGuiRendererInitialized_; + std::uint64_t imGuiFontsBuildCount_ {}; std::shared_ptr placefileManager_ { manager::PlacefileManager::Instance()}; @@ -981,9 +982,15 @@ void MapWidget::initializeGL() makeCurrent(); p->context_->gl().initializeOpenGLFunctions(); + // Lock ImGui font atlas prior to new ImGui frame + std::shared_lock imguiFontAtlasLock { + manager::FontManager::Instance().imgui_font_atlas_mutex()}; + // Initialize ImGui OpenGL3 backend ImGui::SetCurrentContext(p->imGuiContext_); ImGui_ImplOpenGL3_Init(); + p->imGuiFontsBuildCount_ = + manager::FontManager::Instance().imgui_fonts_build_count(); p->imGuiRendererInitialized_ = true; p->map_.reset( @@ -1038,6 +1045,15 @@ void MapWidget::paintGL() ImGui_ImplOpenGL3_NewFrame(); ImGui::NewFrame(); + // Update ImGui Fonts if required + std::uint64_t currentImGuiFontsBuildCount = + manager::FontManager::Instance().imgui_fonts_build_count(); + if (p->imGuiFontsBuildCount_ != currentImGuiFontsBuildCount) + { + ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); + } + // Update pixel ratio p->context_->set_pixel_ratio(pixelRatio()); From b66ca2cb094bcd3ed288cc88087025b6017b77ab Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Thu, 5 Oct 2023 21:55:43 -0500 Subject: [PATCH 16/27] Refactor settings to be managed by singletons --- scwx-qt/source/scwx/qt/main/main_window.cpp | 24 +++++---- .../scwx/qt/manager/settings_manager.cpp | 45 ++++++----------- .../scwx/qt/manager/settings_manager.hpp | 8 +-- .../scwx/qt/manager/timeline_manager.cpp | 4 +- scwx-qt/source/scwx/qt/map/alert_layer.cpp | 4 +- scwx-qt/source/scwx/qt/map/map_provider.cpp | 8 ++- scwx-qt/source/scwx/qt/map/map_widget.cpp | 8 +-- .../scwx/qt/settings/general_settings.cpp | 14 ++++-- .../scwx/qt/settings/general_settings.hpp | 7 +-- .../source/scwx/qt/settings/map_settings.cpp | 49 ++++++++++--------- .../source/scwx/qt/settings/map_settings.hpp | 7 +-- .../scwx/qt/settings/palette_settings.cpp | 16 ++++-- .../scwx/qt/settings/palette_settings.hpp | 7 +-- .../source/scwx/qt/settings/text_settings.cpp | 4 +- .../source/scwx/qt/ui/alert_dock_widget.cpp | 10 ++-- .../scwx/qt/ui/animation_dock_widget.cpp | 11 ++--- scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 10 ++-- scwx-qt/source/scwx/qt/util/font.cpp | 4 +- scwx-qt/source/scwx/qt/util/imgui.cpp | 6 +-- scwx-qt/source/scwx/qt/util/tooltip.cpp | 4 +- .../scwx/qt/manager/settings_manager.test.cpp | 21 +++++--- 21 files changed, 138 insertions(+), 133 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 4fff5ca0..756ca182 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -7,13 +7,14 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -89,10 +90,8 @@ public: elevationButtonsChanged_ {false}, resizeElevationButtons_ {false} { - mapProvider_ = - map::GetMapProvider(manager::SettingsManager::general_settings() - .map_provider() - .GetValue()); + mapProvider_ = map::GetMapProvider( + settings::GeneralSettings::Instance().map_provider().GetValue()); const map::MapProviderInfo& mapProviderInfo = map::GetMapProviderInfo(mapProvider_); @@ -230,7 +229,7 @@ MainWindow::MainWindow(QWidget* parent) : ui->actionAlerts->setVisible(false); ui->menuDebug->menuAction()->setVisible( - manager::SettingsManager::general_settings().debug_enabled().GetValue()); + settings::GeneralSettings::Instance().debug_enabled().GetValue()); // Configure Resource Explorer Dock ui->resourceExplorerDock->setVisible(false); @@ -306,7 +305,7 @@ MainWindow::MainWindow(QWidget* parent) : // Update Dialog p->updateDialog_ = new ui::UpdateDialog(this); - auto& mapSettings = manager::SettingsManager::map_settings(); + auto& mapSettings = settings::MapSettings::Instance(); for (size_t i = 0; i < p->maps_.size(); i++) { p->SelectRadarProduct(p->maps_.at(i), @@ -582,7 +581,7 @@ void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index) void MainWindowImpl::AsyncSetup() { - auto& generalSettings = manager::SettingsManager::general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); // Check for updates if (generalSettings.update_notifications_enabled().GetValue()) @@ -595,7 +594,7 @@ void MainWindowImpl::AsyncSetup() void MainWindowImpl::ConfigureMapLayout() { - auto& generalSettings = manager::SettingsManager::general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); const int64_t gridWidth = generalSettings.grid_width().GetValue(); const int64_t gridHeight = generalSettings.grid_height().GetValue(); @@ -646,7 +645,7 @@ void MainWindowImpl::ConfigureMapLayout() void MainWindowImpl::ConfigureMapStyles() { const auto& mapProviderInfo = map::GetMapProviderInfo(mapProvider_); - auto& mapSettings = manager::SettingsManager::map_settings(); + auto& mapSettings = settings::MapSettings::Instance(); for (std::size_t i = 0; i < maps_.size(); i++) { @@ -897,8 +896,7 @@ void MainWindowImpl::ConnectOtherSignals() { if (maps_[i] == activeMap_) { - auto& mapSettings = - manager::SettingsManager::map_settings(); + auto& mapSettings = settings::MapSettings::Instance(); mapSettings.map_style(i).StageValue(text.toStdString()); break; } @@ -1075,7 +1073,7 @@ void MainWindowImpl::UpdateMapStyle(const std::string& styleName) { if (maps_[i] == activeMap_) { - auto& mapSettings = manager::SettingsManager::map_settings(); + auto& mapSettings = settings::MapSettings::Instance(); mapSettings.map_style(i).StageValue(styleName); break; } diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp index 7c973ca4..1a2f6c04 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include #include #include @@ -96,8 +99,8 @@ void Shutdown() { bool dataChanged = false; - dataChanged |= general_settings().Shutdown(); - dataChanged |= map_settings().Shutdown(); + dataChanged |= settings::GeneralSettings::Instance().Shutdown(); + dataChanged |= settings::MapSettings::Instance().Shutdown(); dataChanged |= settings::UiSettings::Instance().Shutdown(); if (dataChanged) @@ -106,31 +109,13 @@ void Shutdown() } } -settings::GeneralSettings& general_settings() -{ - static settings::GeneralSettings generalSettings_; - return generalSettings_; -} - -settings::MapSettings& map_settings() -{ - static settings::MapSettings mapSettings_; - return mapSettings_; -} - -settings::PaletteSettings& palette_settings() -{ - static settings::PaletteSettings paletteSettings_; - return paletteSettings_; -} - static boost::json::value ConvertSettingsToJson() { boost::json::object settingsJson; - general_settings().WriteJson(settingsJson); - map_settings().WriteJson(settingsJson); - palette_settings().WriteJson(settingsJson); + settings::GeneralSettings::Instance().WriteJson(settingsJson); + settings::MapSettings::Instance().WriteJson(settingsJson); + settings::PaletteSettings::Instance().WriteJson(settingsJson); settings::TextSettings::Instance().WriteJson(settingsJson); settings::UiSettings::Instance().WriteJson(settingsJson); @@ -141,9 +126,9 @@ static void GenerateDefaultSettings() { logger_->info("Generating default settings"); - general_settings().SetDefaults(); - map_settings().SetDefaults(); - palette_settings().SetDefaults(); + settings::GeneralSettings::Instance().SetDefaults(); + settings::MapSettings::Instance().SetDefaults(); + settings::PaletteSettings::Instance().SetDefaults(); settings::TextSettings::Instance().SetDefaults(); settings::UiSettings::Instance().SetDefaults(); } @@ -154,9 +139,9 @@ static bool LoadSettings(const boost::json::object& settingsJson) bool jsonDirty = false; - jsonDirty |= !general_settings().ReadJson(settingsJson); - jsonDirty |= !map_settings().ReadJson(settingsJson); - jsonDirty |= !palette_settings().ReadJson(settingsJson); + jsonDirty |= !settings::GeneralSettings::Instance().ReadJson(settingsJson); + jsonDirty |= !settings::MapSettings::Instance().ReadJson(settingsJson); + jsonDirty |= !settings::PaletteSettings::Instance().ReadJson(settingsJson); jsonDirty |= !settings::TextSettings::Instance().ReadJson(settingsJson); jsonDirty |= !settings::UiSettings::Instance().ReadJson(settingsJson); @@ -169,7 +154,7 @@ static void ValidateSettings() bool settingsChanged = false; - auto& generalSettings = general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); // Validate map provider std::string mapProviderName = generalSettings.map_provider().GetValue(); diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp index ed05ca1e..5cc17b19 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp @@ -1,8 +1,6 @@ #pragma once -#include -#include -#include +#include namespace scwx { @@ -18,10 +16,6 @@ void ReadSettings(const std::string& settingsPath); void SaveSettings(); void Shutdown(); -settings::GeneralSettings& general_settings(); -settings::MapSettings& map_settings(); -settings::PaletteSettings& palette_settings(); - } // namespace SettingsManager } // namespace manager } // namespace qt diff --git a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp index 8e79a552..f7fc3f48 100644 --- a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include @@ -39,7 +39,7 @@ class TimelineManager::Impl public: explicit Impl(TimelineManager* self) : self_ {self} { - auto& generalSettings = SettingsManager::general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); loopDelay_ = std::chrono::milliseconds(generalSettings.loop_delay().GetValue()); diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.cpp b/scwx-qt/source/scwx/qt/map/alert_layer.cpp index f769231c..044cfb00 100644 --- a/scwx-qt/source/scwx/qt/map/alert_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/alert_layer.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #include #include #include @@ -394,7 +394,7 @@ static void AddAlertLayer(std::shared_ptr map, const QString& beforeLayer) { settings::PaletteSettings& paletteSettings = - manager::SettingsManager::palette_settings(); + settings::PaletteSettings::Instance(); QString sourceId = GetSourceId(phenomenon, alertActive); QString idSuffix = GetSuffix(phenomenon, alertActive); diff --git a/scwx-qt/source/scwx/qt/map/map_provider.cpp b/scwx-qt/source/scwx/qt/map/map_provider.cpp index 2a431614..df4bba08 100644 --- a/scwx-qt/source/scwx/qt/map/map_provider.cpp +++ b/scwx-qt/source/scwx/qt/map/map_provider.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -128,12 +128,10 @@ std::string GetMapProviderApiKey(MapProvider mapProvider) switch (mapProvider) { case MapProvider::Mapbox: - return manager::SettingsManager::general_settings() - .mapbox_api_key() - .GetValue(); + return settings::GeneralSettings::Instance().mapbox_api_key().GetValue(); case MapProvider::MapTiler: - return manager::SettingsManager::general_settings() + return settings::GeneralSettings::Instance() .maptiler_api_key() .GetValue(); diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 96aca7a2..adfcc467 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -13,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -81,8 +82,7 @@ public: prevBearing_ {0.0}, prevPitch_ {0.0} { - auto& generalSettings = - scwx::qt::manager::SettingsManager::general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); SetRadarSite(generalSettings.default_radar_site().GetValue()); @@ -1227,7 +1227,7 @@ void MapWidgetImpl::InitializeNewRadarProductView( auto radarProductView = context_->radar_product_view(); std::string colorTableFile = - manager::SettingsManager::palette_settings() + settings::PaletteSettings::Instance() .palette(colorPalette) .GetValue(); if (!colorTableFile.empty()) diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.cpp b/scwx-qt/source/scwx/qt/settings/general_settings.cpp index 8f2d3e74..bd94acef 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.cpp @@ -16,10 +16,10 @@ namespace settings static const std::string logPrefix_ = "scwx::qt::settings::general_settings"; -class GeneralSettingsImpl +class GeneralSettings::Impl { public: - explicit GeneralSettingsImpl() + explicit Impl() { std::string defaultDefaultAlertActionValue = types::GetAlertActionName(types::AlertAction::Go); @@ -102,7 +102,7 @@ public: { return !value.empty(); }); } - ~GeneralSettingsImpl() {} + ~Impl() {} SettingsVariable debugEnabled_ {"debug_enabled"}; SettingsVariable defaultAlertAction_ {"default_alert_action"}; @@ -120,7 +120,7 @@ public: }; GeneralSettings::GeneralSettings() : - SettingsCategory("general"), p(std::make_unique()) + SettingsCategory("general"), p(std::make_unique()) { RegisterVariables({&p->debugEnabled_, &p->defaultAlertAction_, @@ -221,6 +221,12 @@ bool GeneralSettings::Shutdown() return dataChanged; } +GeneralSettings& GeneralSettings::Instance() +{ + static GeneralSettings generalSettings_; + return generalSettings_; +} + bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs) { return (lhs.p->debugEnabled_ == rhs.p->debugEnabled_ && diff --git a/scwx-qt/source/scwx/qt/settings/general_settings.hpp b/scwx-qt/source/scwx/qt/settings/general_settings.hpp index 375d887e..ba58676f 100644 --- a/scwx-qt/source/scwx/qt/settings/general_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/general_settings.hpp @@ -13,8 +13,6 @@ namespace qt namespace settings { -class GeneralSettingsImpl; - class GeneralSettings : public SettingsCategory { public: @@ -41,13 +39,16 @@ public: SettingsVariable& maptiler_api_key() const; SettingsVariable& update_notifications_enabled() const; + static GeneralSettings& Instance(); + friend bool operator==(const GeneralSettings& lhs, const GeneralSettings& rhs); bool Shutdown(); private: - std::unique_ptr p; + class Impl; + std::unique_ptr p; }; } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/map_settings.cpp b/scwx-qt/source/scwx/qt/settings/map_settings.cpp index 8edd1c31..2d8e93b0 100644 --- a/scwx-qt/source/scwx/qt/settings/map_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/map_settings.cpp @@ -35,7 +35,7 @@ static const std::string kDefaultRadarProductGroupString_ = "L3"; static const std::array kDefaultRadarProduct_ { "N0B", "N0G", "N0C", "N0X"}; -class MapSettingsImpl +class MapSettings::Impl { public: struct MapData @@ -47,7 +47,7 @@ public: SettingsVariable radarProduct_ {kRadarProductName_}; }; - explicit MapSettingsImpl() + explicit Impl() { for (std::size_t i = 0; i < kCount_; i++) { @@ -101,7 +101,7 @@ public: } } - ~MapSettingsImpl() {} + ~Impl() {} void SetDefaults(std::size_t i) { @@ -111,12 +111,30 @@ public: map_[i].radarProduct_.SetValueToDefault(); } + friend void tag_invoke(boost::json::value_from_tag, + boost::json::value& jv, + const MapData& data) + { + jv = {{kMapStyleName_, data.mapStyle_.GetValue()}, + {kRadarSiteName_, data.radarSite_.GetValue()}, + {kRadarProductGroupName_, data.radarProductGroup_.GetValue()}, + {kRadarProductName_, data.radarProduct_.GetValue()}}; + } + + friend bool operator==(const MapData& lhs, const MapData& rhs) + { + return (lhs.mapStyle_ == rhs.mapStyle_ && // + lhs.radarSite_ == rhs.radarSite_ && + lhs.radarProductGroup_ == rhs.radarProductGroup_ && + lhs.radarProduct_ == rhs.radarProduct_); + } + std::array map_ {}; std::vector variables_ {}; }; MapSettings::MapSettings() : - SettingsCategory("maps"), p(std::make_unique()) + SettingsCategory("maps"), p(std::make_unique()) { RegisterVariables(p->variables_); SetDefaults(); @@ -161,7 +179,7 @@ bool MapSettings::Shutdown() // Commit settings that are managed separate from the settings dialog for (std::size_t i = 0; i < kCount_; ++i) { - MapSettingsImpl::MapData& mapRecordSettings = p->map_[i]; + Impl::MapData& mapRecordSettings = p->map_[i]; dataChanged |= mapRecordSettings.mapStyle_.Commit(); } @@ -184,7 +202,7 @@ bool MapSettings::ReadJson(const boost::json::object& json) if (i < mapArray.size() && mapArray.at(i).is_object()) { const boost::json::object& mapRecord = mapArray.at(i).as_object(); - MapSettingsImpl::MapData& mapRecordSettings = p->map_[i]; + Impl::MapData& mapRecordSettings = p->map_[i]; // Load JSON Elements validated &= mapRecordSettings.mapStyle_.ReadValue(mapRecord); @@ -234,14 +252,10 @@ void MapSettings::WriteJson(boost::json::object& json) const json.insert_or_assign(name(), object); } -void tag_invoke(boost::json::value_from_tag, - boost::json::value& jv, - const MapSettingsImpl::MapData& data) +MapSettings& MapSettings::Instance() { - jv = {{kMapStyleName_, data.mapStyle_.GetValue()}, - {kRadarSiteName_, data.radarSite_.GetValue()}, - {kRadarProductGroupName_, data.radarProductGroup_.GetValue()}, - {kRadarProductName_, data.radarProduct_.GetValue()}}; + static MapSettings mapSettings_; + return mapSettings_; } bool operator==(const MapSettings& lhs, const MapSettings& rhs) @@ -249,15 +263,6 @@ bool operator==(const MapSettings& lhs, const MapSettings& rhs) return (lhs.p->map_ == rhs.p->map_); } -bool operator==(const MapSettingsImpl::MapData& lhs, - const MapSettingsImpl::MapData& rhs) -{ - return (lhs.mapStyle_ == rhs.mapStyle_ && // - lhs.radarSite_ == rhs.radarSite_ && - lhs.radarProductGroup_ == rhs.radarProductGroup_ && - lhs.radarProduct_ == rhs.radarProduct_); -} - } // namespace settings } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/map_settings.hpp b/scwx-qt/source/scwx/qt/settings/map_settings.hpp index fd0c74dd..c8726491 100644 --- a/scwx-qt/source/scwx/qt/settings/map_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/map_settings.hpp @@ -13,8 +13,6 @@ namespace qt namespace settings { -class MapSettingsImpl; - class MapSettings : public SettingsCategory { public: @@ -52,10 +50,13 @@ public: */ void WriteJson(boost::json::object& json) const override; + static MapSettings& Instance(); + friend bool operator==(const MapSettings& lhs, const MapSettings& rhs); private: - std::unique_ptr p; + class Impl; + std::unique_ptr p; }; } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/palette_settings.cpp b/scwx-qt/source/scwx/qt/settings/palette_settings.cpp index 8edf2cf2..f041c078 100644 --- a/scwx-qt/source/scwx/qt/settings/palette_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/palette_settings.cpp @@ -72,10 +72,10 @@ static const std::map< static const std::string kDefaultKey_ {"???"}; static const awips::Phenomenon kDefaultPhenomenon_ {awips::Phenomenon::Marine}; -class PaletteSettingsImpl +class PaletteSettings::Impl { public: - explicit PaletteSettingsImpl() + explicit Impl() { for (const auto& name : kPaletteKeys_) { @@ -120,7 +120,7 @@ public: } } - ~PaletteSettingsImpl() {} + ~Impl() {} static bool ValidateColor(const std::string& value); @@ -132,14 +132,14 @@ public: std::vector variables_ {}; }; -bool PaletteSettingsImpl::ValidateColor(const std::string& value) +bool PaletteSettings::Impl::ValidateColor(const std::string& value) { static const std::regex re {"#[0-9A-Za-z]{8}"}; return std::regex_match(value, re); } PaletteSettings::PaletteSettings() : - SettingsCategory("palette"), p(std::make_unique()) + SettingsCategory("palette"), p(std::make_unique()) { RegisterVariables(p->variables_); SetDefaults(); @@ -200,6 +200,12 @@ const std::vector& PaletteSettings::alert_phenomena() return kAlertPhenomena_; } +PaletteSettings& PaletteSettings::Instance() +{ + static PaletteSettings paletteSettings_; + return paletteSettings_; +} + bool operator==(const PaletteSettings& lhs, const PaletteSettings& rhs) { return lhs.p->palette_ == rhs.p->palette_; diff --git a/scwx-qt/source/scwx/qt/settings/palette_settings.hpp b/scwx-qt/source/scwx/qt/settings/palette_settings.hpp index 948decf5..c0f7985a 100644 --- a/scwx-qt/source/scwx/qt/settings/palette_settings.hpp +++ b/scwx-qt/source/scwx/qt/settings/palette_settings.hpp @@ -14,8 +14,6 @@ namespace qt namespace settings { -class PaletteSettingsImpl; - class PaletteSettings : public SettingsCategory { public: @@ -34,11 +32,14 @@ public: static const std::vector& alert_phenomena(); + static PaletteSettings& Instance(); + friend bool operator==(const PaletteSettings& lhs, const PaletteSettings& rhs); private: - std::unique_ptr p; + class Impl; + std::unique_ptr p; }; } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/text_settings.cpp b/scwx-qt/source/scwx/qt/settings/text_settings.cpp index 5bc9946b..fdf41acf 100644 --- a/scwx-qt/source/scwx/qt/settings/text_settings.cpp +++ b/scwx-qt/source/scwx/qt/settings/text_settings.cpp @@ -169,8 +169,8 @@ SettingsVariable& TextSettings::tooltip_method() const TextSettings& TextSettings::Instance() { - static TextSettings TextSettings_; - return TextSettings_; + static TextSettings textSettings_; + return textSettings_; } bool operator==(const TextSettings& lhs, const TextSettings& rhs) diff --git a/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp index 5efca78a..e827469c 100644 --- a/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp @@ -1,10 +1,10 @@ #include "alert_dock_widget.hpp" #include "ui_alert_dock_widget.h" -#include #include #include #include +#include #include #include #include @@ -175,10 +175,10 @@ void AlertDockWidgetImpl::ConnectSignals() // If an item is selected if (selectedAlertKey_ != types::TextEventKey {}) { - types::AlertAction alertAction = types::GetAlertAction( - manager::SettingsManager::general_settings() - .default_alert_action() - .GetValue()); + types::AlertAction alertAction = + types::GetAlertAction(settings::GeneralSettings::Instance() + .default_alert_action() + .GetValue()); switch (alertAction) { diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp index 34b1001a..f19be01f 100644 --- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp @@ -1,7 +1,7 @@ #include "animation_dock_widget.hpp" #include "ui_animation_dock_widget.h" -#include +#include #include #include @@ -101,7 +101,7 @@ AnimationDockWidget::AnimationDockWidget(QWidget* parent) : maxDateTimer->start(15000); // Set loop defaults - auto& generalSettings = manager::SettingsManager::general_settings(); + auto& generalSettings = settings::GeneralSettings::Instance(); ui->loopTimeSpinBox->setValue(generalSettings.loop_time().GetValue()); ui->loopSpeedSpinBox->setValue(generalSettings.loop_speed().GetValue()); ui->loopDelaySpinBox->setValue(generalSettings.loop_delay().GetValue() * @@ -175,7 +175,7 @@ void AnimationDockWidgetImpl::ConnectSignals() self_, [this](int i) { - manager::SettingsManager::general_settings().loop_time().StageValue(i); + settings::GeneralSettings::Instance().loop_time().StageValue(i); Q_EMIT self_->LoopTimeChanged(std::chrono::minutes(i)); }); QObject::connect( @@ -184,8 +184,7 @@ void AnimationDockWidgetImpl::ConnectSignals() self_, [this](double d) { - manager::SettingsManager::general_settings().loop_speed().StageValue( - d); + settings::GeneralSettings::Instance().loop_speed().StageValue(d); Q_EMIT self_->LoopSpeedChanged(d); }); QObject::connect( @@ -194,7 +193,7 @@ void AnimationDockWidgetImpl::ConnectSignals() self_, [this](double d) { - manager::SettingsManager::general_settings().loop_delay().StageValue( + settings::GeneralSettings::Instance().loop_delay().StageValue( static_cast(d * 1000.0)); Q_EMIT self_->LoopDelayChanged(std::chrono::milliseconds( static_cast(d * 1000.0))); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index 32464ecf..44a36bf0 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -104,7 +106,7 @@ public: &tooltipMethod_}} { // Configure default alert phenomena colors - auto& paletteSettings = manager::SettingsManager::palette_settings(); + auto& paletteSettings = settings::PaletteSettings::Instance(); int index = 0; for (auto& phenomenon : settings::PaletteSettings::alert_phenomena()) @@ -384,7 +386,7 @@ void SettingsDialogImpl::SetupGeneralTab() } settings::GeneralSettings& generalSettings = - manager::SettingsManager::general_settings(); + settings::GeneralSettings::Instance(); defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site()); defaultRadarSite_.SetMapFromValueFunction( @@ -525,7 +527,7 @@ void SettingsDialogImpl::SetupGeneralTab() void SettingsDialogImpl::SetupPalettesColorTablesTab() { settings::PaletteSettings& paletteSettings = - manager::SettingsManager::palette_settings(); + settings::PaletteSettings::Instance(); // Palettes > Color Tables QGridLayout* colorTableLayout = @@ -617,7 +619,7 @@ void SettingsDialogImpl::SetupPalettesColorTablesTab() void SettingsDialogImpl::SetupPalettesAlertsTab() { settings::PaletteSettings& paletteSettings = - manager::SettingsManager::palette_settings(); + settings::PaletteSettings::Instance(); // Palettes > Alerts QGridLayout* alertsLayout = diff --git a/scwx-qt/source/scwx/qt/util/font.cpp b/scwx-qt/source/scwx/qt/util/font.cpp index 49fdee3a..74374163 100644 --- a/scwx-qt/source/scwx/qt/util/font.cpp +++ b/scwx-qt/source/scwx/qt/util/font.cpp @@ -5,8 +5,8 @@ #define _CRT_SECURE_NO_WARNINGS #include -#include #include +#include #include #include @@ -337,7 +337,7 @@ std::shared_ptr Font::Create(const std::string& resource) font->p->CreateImGuiFont( fontFile, fontData, - manager::SettingsManager::general_settings().font_sizes().GetValue()); + settings::GeneralSettings::Instance().font_sizes().GetValue()); font->p->atlas_ = ftgl::texture_atlas_new(512, 512, 1); ftgl::texture_font_t* textureFont = ftgl::texture_font_new_from_memory( diff --git a/scwx-qt/source/scwx/qt/util/imgui.cpp b/scwx-qt/source/scwx/qt/util/imgui.cpp index 9d1622ed..0afddf43 100644 --- a/scwx-qt/source/scwx/qt/util/imgui.cpp +++ b/scwx-qt/source/scwx/qt/util/imgui.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -48,7 +48,7 @@ void ImGui::Impl::Initialize() // Configure monospace font UpdateMonospaceFont(); - manager::SettingsManager::general_settings() + settings::GeneralSettings::Instance() .font_sizes() .RegisterValueChangedCallback([this](const std::vector&) { UpdateMonospaceFont(); }); @@ -61,7 +61,7 @@ void ImGui::Impl::UpdateMonospaceFont() // Get monospace font size std::size_t fontSize = 16; auto fontSizes = - manager::SettingsManager::general_settings().font_sizes().GetValue(); + settings::GeneralSettings::Instance().font_sizes().GetValue(); if (fontSizes.size() > 1) { fontSize = fontSizes[1]; diff --git a/scwx-qt/source/scwx/qt/util/tooltip.cpp b/scwx-qt/source/scwx/qt/util/tooltip.cpp index 5d7b4c6c..3e9432df 100644 --- a/scwx-qt/source/scwx/qt/util/tooltip.cpp +++ b/scwx-qt/source/scwx/qt/util/tooltip.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -100,7 +100,7 @@ void Show(const std::string& text, const QPointF& mouseGlobalPos) // Get monospace font size units::font_size::pixels fontSize {16}; auto fontSizes = - manager::SettingsManager::general_settings().font_sizes().GetValue(); + settings::GeneralSettings::Instance().font_sizes().GetValue(); if (fontSizes.size() > 1) { fontSize = units::font_size::pixels {fontSizes[1]}; diff --git a/test/source/scwx/qt/manager/settings_manager.test.cpp b/test/source/scwx/qt/manager/settings_manager.test.cpp index be576069..a4d69614 100644 --- a/test/source/scwx/qt/manager/settings_manager.test.cpp +++ b/test/source/scwx/qt/manager/settings_manager.test.cpp @@ -1,5 +1,10 @@ #include #include +#include +#include +#include +#include +#include #include #include @@ -39,10 +44,14 @@ void VerifyDefaults() settings::GeneralSettings defaultGeneralSettings {}; settings::MapSettings defaultMapSettings {}; settings::PaletteSettings defaultPaletteSettings {}; + settings::TextSettings defaultTextSettings {}; + settings::UiSettings defaultUiSettings {}; - EXPECT_EQ(defaultGeneralSettings, SettingsManager::general_settings()); - EXPECT_EQ(defaultMapSettings, SettingsManager::map_settings()); - EXPECT_EQ(defaultPaletteSettings, SettingsManager::palette_settings()); + EXPECT_EQ(defaultGeneralSettings, settings::GeneralSettings::Instance()); + EXPECT_EQ(defaultMapSettings, settings::MapSettings::Instance()); + EXPECT_EQ(defaultPaletteSettings, settings::PaletteSettings::Instance()); + EXPECT_EQ(defaultTextSettings, settings::TextSettings::Instance()); + EXPECT_EQ(defaultUiSettings, settings::UiSettings::Instance()); } void CompareFiles(const std::string& file1, const std::string& file2) @@ -86,11 +95,11 @@ TEST_F(SettingsManagerTest, SettingsKeax) SettingsManager::ReadSettings(filename); EXPECT_EQ( - SettingsManager::general_settings().default_radar_site().GetValue(), + settings::GeneralSettings::Instance().default_radar_site().GetValue(), "KEAX"); - for (size_t i = 0; i < SettingsManager::map_settings().count(); ++i) + for (size_t i = 0; i < settings::MapSettings::Instance().count(); ++i) { - EXPECT_EQ(SettingsManager::map_settings().radar_site(i).GetValue(), + EXPECT_EQ(settings::MapSettings::Instance().radar_site(i).GetValue(), "KEAX"); } } From 1f964c49f8ff74ad0f8fbe94da5a976293c612cf Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Fri, 6 Oct 2023 04:50:26 -0500 Subject: [PATCH 17/27] Check ImGui fonts before new frame --- scwx-qt/source/scwx/qt/map/map_widget.cpp | 27 ++++++++++++------- .../source/scwx/qt/ui/imgui_debug_widget.cpp | 27 ++++++++++++++++++- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index adfcc467..1e5397e6 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -124,6 +124,7 @@ public: std::shared_ptr layer, const std::string& before = {}); void ConnectSignals(); + void ImGuiCheckFonts(); void InitializeNewRadarProductView(const std::string& colorPalette); void RadarProductManagerConnect(); void RadarProductManagerDisconnect(); @@ -1043,17 +1044,9 @@ void MapWidget::paintGL() // Start ImGui Frame ImGui_ImplQt_NewFrame(this); ImGui_ImplOpenGL3_NewFrame(); + p->ImGuiCheckFonts(); ImGui::NewFrame(); - // Update ImGui Fonts if required - std::uint64_t currentImGuiFontsBuildCount = - manager::FontManager::Instance().imgui_fonts_build_count(); - if (p->imGuiFontsBuildCount_ != currentImGuiFontsBuildCount) - { - ImGui_ImplOpenGL3_DestroyFontsTexture(); - ImGui_ImplOpenGL3_CreateFontsTexture(); - } - // Update pixel ratio p->context_->set_pixel_ratio(pixelRatio()); @@ -1087,6 +1080,22 @@ void MapWidget::paintGL() Q_EMIT WidgetPainted(); } +void MapWidgetImpl::ImGuiCheckFonts() +{ + // Update ImGui Fonts if required + std::uint64_t currentImGuiFontsBuildCount = + manager::FontManager::Instance().imgui_fonts_build_count(); + + if (imGuiFontsBuildCount_ != currentImGuiFontsBuildCount || + !model::ImGuiContextModel::Instance().font_atlas()->IsBuilt()) + { + ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); + } + + imGuiFontsBuildCount_ = currentImGuiFontsBuildCount; +} + void MapWidgetImpl::RunMousePicking() { const QMapLibreGL::CustomLayerRenderParameters params = diff --git a/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp b/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp index 04e692ac..fcbed5d6 100644 --- a/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/imgui_debug_widget.cpp @@ -51,6 +51,8 @@ public: model::ImGuiContextModel::Instance().DestroyContext(contextName_); } + void ImGuiCheckFonts(); + ImGuiDebugWidget* self_; ImGuiContext* context_; std::string contextName_; @@ -59,6 +61,7 @@ public: std::set renderedSet_ {}; bool imGuiRendererInitialized_ {false}; + std::uint64_t imGuiFontsBuildCount_ {}; }; ImGuiDebugWidget::ImGuiDebugWidget(QWidget* parent) : @@ -103,6 +106,8 @@ void ImGuiDebugWidget::initializeGL() // Initialize ImGui OpenGL3 backend ImGui::SetCurrentContext(p->context_); ImGui_ImplOpenGL3_Init(); + p->imGuiFontsBuildCount_ = + manager::FontManager::Instance().imgui_fonts_build_count(); p->imGuiRendererInitialized_ = true; } @@ -116,7 +121,7 @@ void ImGuiDebugWidget::paintGL() ImGui_ImplQt_NewFrame(this); ImGui_ImplOpenGL3_NewFrame(); - + p->ImGuiCheckFonts(); ImGui::NewFrame(); if (!p->renderedSet_.contains(p->currentContext_)) @@ -141,6 +146,26 @@ void ImGuiDebugWidget::paintGL() imguiFontAtlasLock.unlock(); } +void ImGuiDebugWidgetImpl::ImGuiCheckFonts() +{ + // Update ImGui Fonts if required + std::uint64_t currentImGuiFontsBuildCount = + manager::FontManager::Instance().imgui_fonts_build_count(); + + if ((context_ == currentContext_ && + imGuiFontsBuildCount_ != currentImGuiFontsBuildCount) || + !model::ImGuiContextModel::Instance().font_atlas()->IsBuilt()) + { + ImGui_ImplOpenGL3_DestroyFontsTexture(); + ImGui_ImplOpenGL3_CreateFontsTexture(); + } + + if (context_ == currentContext_) + { + imGuiFontsBuildCount_ = currentImGuiFontsBuildCount; + } +} + } // namespace ui } // namespace qt } // namespace scwx From 67881d31d58f5371e5b5fa07ac1dd7a944d46941 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Fri, 6 Oct 2023 05:05:59 -0500 Subject: [PATCH 18/27] Make SettingsManager an object instead of a namespace --- scwx-qt/source/scwx/qt/main/main.cpp | 4 +- .../scwx/qt/manager/settings_manager.cpp | 76 ++++++++++++------- .../scwx/qt/manager/settings_manager.hpp | 28 +++++-- scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 2 +- .../scwx/qt/manager/settings_manager.test.cpp | 8 +- 5 files changed, 76 insertions(+), 42 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main.cpp b/scwx-qt/source/scwx/qt/main/main.cpp index b841b846..4c0bdac4 100644 --- a/scwx-qt/source/scwx/qt/main/main.cpp +++ b/scwx-qt/source/scwx/qt/main/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char* argv[]) // Initialize application scwx::qt::config::RadarSite::Initialize(); - scwx::qt::manager::SettingsManager::Initialize(); + scwx::qt::manager::SettingsManager::Instance().Initialize(); scwx::qt::manager::ResourceManager::Initialize(); // Run Qt main loop @@ -89,7 +89,7 @@ int main(int argc, char* argv[]) // Shutdown application scwx::qt::manager::ResourceManager::Shutdown(); - scwx::qt::manager::SettingsManager::Shutdown(); + scwx::qt::manager::SettingsManager::Instance().Shutdown(); // Shutdown AWS SDK Aws::ShutdownAPI(awsSdkOptions); diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp index 1a2f6c04..083453ac 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp @@ -21,21 +21,33 @@ namespace qt { namespace manager { -namespace SettingsManager -{ static const std::string logPrefix_ = "scwx::qt::manager::settings_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); -static boost::json::value ConvertSettingsToJson(); -static void GenerateDefaultSettings(); -static bool LoadSettings(const boost::json::object& settingsJson); -static void ValidateSettings(); +class SettingsManager::Impl +{ +public: + explicit Impl(SettingsManager* self) : self_ {self} {} + ~Impl() = default; -static bool initialized_ {false}; -static std::string settingsPath_ {}; + void ValidateSettings(); -void Initialize() + static boost::json::value ConvertSettingsToJson(); + static void GenerateDefaultSettings(); + static bool LoadSettings(const boost::json::object& settingsJson); + + SettingsManager* self_; + + bool initialized_ {false}; + std::string settingsPath_ {}; +}; + +SettingsManager::SettingsManager() : p(std::make_unique(this)) {} + +SettingsManager::~SettingsManager() {}; + +void SettingsManager::Initialize() { std::string appDataPath { QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) @@ -50,14 +62,14 @@ void Initialize() } } - settingsPath_ = appDataPath + "/settings.json"; - initialized_ = true; + p->settingsPath_ = appDataPath + "/settings.json"; + p->initialized_ = true; - ReadSettings(settingsPath_); - ValidateSettings(); + ReadSettings(p->settingsPath_); + p->ValidateSettings(); } -void ReadSettings(const std::string& settingsPath) +void SettingsManager::ReadSettings(const std::string& settingsPath) { boost::json::value settingsJson = nullptr; @@ -68,34 +80,34 @@ void ReadSettings(const std::string& settingsPath) if (settingsJson == nullptr || !settingsJson.is_object()) { - GenerateDefaultSettings(); - settingsJson = ConvertSettingsToJson(); + Impl::GenerateDefaultSettings(); + settingsJson = Impl::ConvertSettingsToJson(); util::json::WriteJsonFile(settingsPath, settingsJson); } else { - bool jsonDirty = LoadSettings(settingsJson.as_object()); + bool jsonDirty = Impl::LoadSettings(settingsJson.as_object()); if (jsonDirty) { - settingsJson = ConvertSettingsToJson(); + settingsJson = Impl::ConvertSettingsToJson(); util::json::WriteJsonFile(settingsPath, settingsJson); } }; } -void SaveSettings() +void SettingsManager::SaveSettings() { - if (initialized_) + if (p->initialized_) { logger_->info("Saving settings"); - boost::json::value settingsJson = ConvertSettingsToJson(); - util::json::WriteJsonFile(settingsPath_, settingsJson); + boost::json::value settingsJson = Impl::ConvertSettingsToJson(); + util::json::WriteJsonFile(p->settingsPath_, settingsJson); } } -void Shutdown() +void SettingsManager::Shutdown() { bool dataChanged = false; @@ -109,7 +121,7 @@ void Shutdown() } } -static boost::json::value ConvertSettingsToJson() +boost::json::value SettingsManager::Impl::ConvertSettingsToJson() { boost::json::object settingsJson; @@ -122,7 +134,7 @@ static boost::json::value ConvertSettingsToJson() return settingsJson; } -static void GenerateDefaultSettings() +void SettingsManager::Impl::GenerateDefaultSettings() { logger_->info("Generating default settings"); @@ -133,7 +145,8 @@ static void GenerateDefaultSettings() settings::UiSettings::Instance().SetDefaults(); } -static bool LoadSettings(const boost::json::object& settingsJson) +bool SettingsManager::Impl::LoadSettings( + const boost::json::object& settingsJson) { logger_->info("Loading settings"); @@ -148,7 +161,7 @@ static bool LoadSettings(const boost::json::object& settingsJson) return jsonDirty; } -static void ValidateSettings() +void SettingsManager::Impl::ValidateSettings() { logger_->debug("Validating settings"); @@ -185,11 +198,16 @@ static void ValidateSettings() if (settingsChanged) { - SaveSettings(); + self_->SaveSettings(); } } -} // namespace SettingsManager +SettingsManager& SettingsManager::Instance() +{ + static SettingsManager instance_ {}; + return instance_; +} + } // namespace manager } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp index 5cc17b19..e2115e93 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp @@ -1,6 +1,9 @@ #pragma once #include +#include + +#include namespace scwx { @@ -8,15 +11,28 @@ namespace qt { namespace manager { -namespace SettingsManager + +class SettingsManager : public QObject { + Q_OBJECT + Q_DISABLE_COPY_MOVE(SettingsManager) -void Initialize(); -void ReadSettings(const std::string& settingsPath); -void SaveSettings(); -void Shutdown(); +public: + explicit SettingsManager(); + ~SettingsManager(); + + void Initialize(); + void ReadSettings(const std::string& settingsPath); + void SaveSettings(); + void Shutdown(); + + static SettingsManager& Instance(); + +private: + class Impl; + std::unique_ptr p; +}; -} // namespace SettingsManager } // namespace manager } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index 44a36bf0..bfc17967 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -989,7 +989,7 @@ void SettingsDialogImpl::ApplyChanges() if (committed) { - manager::SettingsManager::SaveSettings(); + manager::SettingsManager::Instance().SaveSettings(); } } diff --git a/test/source/scwx/qt/manager/settings_manager.test.cpp b/test/source/scwx/qt/manager/settings_manager.test.cpp index a4d69614..54df04df 100644 --- a/test/source/scwx/qt/manager/settings_manager.test.cpp +++ b/test/source/scwx/qt/manager/settings_manager.test.cpp @@ -76,7 +76,7 @@ TEST_F(SettingsManagerTest, CreateJson) // Verify file doesn't exist prior to test start EXPECT_EQ(std::filesystem::exists(filename), false); - SettingsManager::ReadSettings(filename); + SettingsManager::Instance().ReadSettings(filename); EXPECT_EQ(std::filesystem::exists(filename), true); @@ -92,7 +92,7 @@ TEST_F(SettingsManagerTest, SettingsKeax) std::string filename(std::string(SCWX_TEST_DATA_DIR) + "/json/settings/settings-keax.json"); - SettingsManager::ReadSettings(filename); + SettingsManager::Instance().ReadSettings(filename); EXPECT_EQ( settings::GeneralSettings::Instance().default_radar_site().GetValue(), @@ -112,7 +112,7 @@ TEST_P(DefaultSettingsTest, DefaultSettings) std::filesystem::copy_file(sourceFile, filename); - SettingsManager::ReadSettings(filename); + SettingsManager::Instance().ReadSettings(filename); VerifyDefaults(); CompareFiles(filename, DEFAULT_SETTINGS_FILE); @@ -140,7 +140,7 @@ TEST_P(BadSettingsTest, BadSettings) std::filesystem::copy_file(sourceFile, filename); - SettingsManager::ReadSettings(filename); + SettingsManager::Instance().ReadSettings(filename); CompareFiles(filename, goodFile); From 3a4a32b97a869ed90478cf6905d6c05e53244d7c Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Fri, 6 Oct 2023 05:49:18 -0500 Subject: [PATCH 19/27] Load ImGui fonts on initialization and settings changes --- .../source/scwx/qt/manager/font_manager.cpp | 82 ++++++++++++++++++- .../source/scwx/qt/manager/font_manager.hpp | 2 + .../scwx/qt/manager/settings_manager.cpp | 2 + .../scwx/qt/manager/settings_manager.hpp | 3 + 4 files changed, 87 insertions(+), 2 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 295d0cd8..db35ec68 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -9,6 +11,7 @@ #include #include #include +#include #include namespace scwx @@ -44,22 +47,27 @@ struct FontRecordHash class FontManager::Impl { public: - explicit Impl() + explicit Impl(FontManager* self) : self_ {self} { InitializeFontCache(); InitializeFontconfig(); + ConnectSignals(); } ~Impl() { FinalizeFontconfig(); } + void ConnectSignals(); void FinalizeFontconfig(); void InitializeFontCache(); void InitializeFontconfig(); + void UpdateImGuiFont(types::FontCategory fontCategory); const std::vector& GetRawFontData(const std::string& filename); static FontRecord MatchFontFile(const std::string& family, const std::vector& styles); + FontManager* self_; + std::string fontCachePath_ {}; std::shared_mutex imguiFontAtlasMutex_ {}; @@ -81,12 +89,82 @@ public: std::shared_ptr> fontCategoryMap_ {}; std::mutex fontCategoryMutex_ {}; + + boost::unordered_flat_set dirtyFonts_ {}; + std::mutex dirtyFontsMutex_ {}; }; -FontManager::FontManager() : p(std::make_unique()) {} +FontManager::FontManager() : p(std::make_unique(this)) {} FontManager::~FontManager() {}; +void FontManager::Impl::ConnectSignals() +{ + auto& textSettings = settings::TextSettings::Instance(); + + for (auto fontCategory : types::FontCategoryIterator()) + { + textSettings.font_family(fontCategory) + .RegisterValueChangedCallback( + [this, fontCategory](const auto&) + { + std::unique_lock lock {dirtyFontsMutex_}; + dirtyFonts_.insert(fontCategory); + }); + textSettings.font_style(fontCategory) + .RegisterValueChangedCallback( + [this, fontCategory](const auto&) + { + std::unique_lock lock {dirtyFontsMutex_}; + dirtyFonts_.insert(fontCategory); + }); + textSettings.font_point_size(fontCategory) + .RegisterValueChangedCallback( + [this, fontCategory](const auto&) + { + std::unique_lock lock {dirtyFontsMutex_}; + dirtyFonts_.insert(fontCategory); + }); + } + + QObject::connect( + &SettingsManager::Instance(), + &SettingsManager::SettingsSaved, + self_, + [this]() + { + std::scoped_lock lock {dirtyFontsMutex_, fontCategoryMutex_}; + + for (auto fontCategory : dirtyFonts_) + { + UpdateImGuiFont(fontCategory); + } + + dirtyFonts_.clear(); + }); +} + +void FontManager::InitializeFonts() +{ + for (auto fontCategory : types::FontCategoryIterator()) + { + p->UpdateImGuiFont(fontCategory); + } +} + +void FontManager::Impl::UpdateImGuiFont(types::FontCategory fontCategory) +{ + auto& textSettings = settings::TextSettings::Instance(); + + auto family = textSettings.font_family(fontCategory).GetValue(); + auto styles = textSettings.font_style(fontCategory).GetValue(); + units::font_size::points size { + textSettings.font_point_size(fontCategory).GetValue()}; + + fontCategoryMap_.insert_or_assign( + fontCategory, self_->LoadImGuiFont(family, {styles}, size)); +} + std::shared_mutex& FontManager::imgui_font_atlas_mutex() { return p->imguiFontAtlasMutex_; diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 6cee1135..184cea18 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -17,6 +17,7 @@ namespace manager class FontManager : public QObject { Q_OBJECT + Q_DISABLE_COPY_MOVE(FontManager) public: explicit FontManager(); @@ -34,6 +35,7 @@ public: bool loadIfNotFound = true); void LoadApplicationFont(const std::string& filename); + void InitializeFonts(); static FontManager& Instance(); diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp index 083453ac..a4c0f4a3 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.cpp @@ -104,6 +104,8 @@ void SettingsManager::SaveSettings() boost::json::value settingsJson = Impl::ConvertSettingsToJson(); util::json::WriteJsonFile(p->settingsPath_, settingsJson); + + Q_EMIT SettingsSaved(); } } diff --git a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp index e2115e93..254ea4c8 100644 --- a/scwx-qt/source/scwx/qt/manager/settings_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/settings_manager.hpp @@ -28,6 +28,9 @@ public: static SettingsManager& Instance(); +signals: + void SettingsSaved(); + private: class Impl; std::unique_ptr p; From e37e64b3f281490b91ff02c618b5f1622cd4358b Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Fri, 6 Oct 2023 05:49:59 -0500 Subject: [PATCH 20/27] Initialize font manager fonts on startup --- scwx-qt/source/scwx/qt/manager/resource_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index a622ea48..e01e21ff 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -119,6 +119,8 @@ static void LoadFonts() ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); fontAtlas->AddFontDefault(); + + fontManager.InitializeFonts(); } static void LoadTextures() From 11ea4676cf335b600bbe1961025e8d218f3b4289 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 09:02:28 -0500 Subject: [PATCH 21/27] Use fonts defined by text settings for rendering --- .../source/scwx/qt/manager/font_manager.cpp | 16 +++++ .../source/scwx/qt/manager/font_manager.hpp | 3 + scwx-qt/source/scwx/qt/map/map_widget.cpp | 9 +++ scwx-qt/source/scwx/qt/util/imgui.cpp | 63 ++----------------- scwx-qt/source/scwx/qt/util/tooltip.cpp | 35 +++++------ 5 files changed, 47 insertions(+), 79 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index db35ec68..05d9e79e 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -189,6 +189,22 @@ FontManager::GetImGuiFont(types::FontCategory fontCategory) return p->defaultFont_; } +QFont FontManager::GetQFont(types::FontCategory fontCategory) +{ + auto& textSettings = settings::TextSettings::Instance(); + + auto family = textSettings.font_family(fontCategory).GetValue(); + auto styles = textSettings.font_style(fontCategory).GetValue(); + units::font_size::points size { + textSettings.font_point_size(fontCategory).GetValue()}; + + QFont font(QString::fromStdString(family)); + font.setStyleName(QString::fromStdString(styles)); + font.setPointSizeF(size.value()); + + return font; +} + std::shared_ptr FontManager::LoadImGuiFont(const std::string& family, const std::vector& styles, diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 184cea18..7b824861 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -5,6 +5,7 @@ #include +#include #include namespace scwx @@ -37,6 +38,8 @@ public: void LoadApplicationFont(const std::string& filename); void InitializeFonts(); + static QFont GetQFont(types::FontCategory fontCategory); + static FontManager& Instance(); private: diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 1e5397e6..a54c1a5e 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -1032,6 +1032,9 @@ void MapWidget::initializeGL() void MapWidget::paintGL() { + auto defaultFont = manager::FontManager::Instance().GetImGuiFont( + types::FontCategory::Default); + p->frameDraws_++; // Setup ImGui Frame @@ -1047,6 +1050,9 @@ void MapWidget::paintGL() p->ImGuiCheckFonts(); ImGui::NewFrame(); + // Set default font + ImGui::PushFont(defaultFont->font()); + // Update pixel ratio p->context_->set_pixel_ratio(pixelRatio()); @@ -1069,6 +1075,9 @@ void MapWidget::paintGL() p->lastItemPicked_ = false; } + // Pop default font + ImGui::PopFont(); + // Render ImGui Frame ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); diff --git a/scwx-qt/source/scwx/qt/util/imgui.cpp b/scwx-qt/source/scwx/qt/util/imgui.cpp index 0afddf43..46bc859e 100644 --- a/scwx-qt/source/scwx/qt/util/imgui.cpp +++ b/scwx-qt/source/scwx/qt/util/imgui.cpp @@ -1,10 +1,7 @@ #include -#include -#include +#include #include -#include - #include namespace scwx @@ -22,13 +19,6 @@ class ImGui::Impl public: explicit Impl() {} ~Impl() {} - - void Initialize(); - void UpdateMonospaceFont(); - - bool initialized_ {false}; - - ImFont* monospaceFont_ {nullptr}; }; ImGui::ImGui() : p(std::make_unique()) {} @@ -37,58 +27,13 @@ ImGui::~ImGui() = default; ImGui::ImGui(ImGui&&) noexcept = default; ImGui& ImGui::operator=(ImGui&&) noexcept = default; -void ImGui::Impl::Initialize() -{ - if (initialized_) - { - return; - } - - logger_->debug("Initialize"); - - // Configure monospace font - UpdateMonospaceFont(); - settings::GeneralSettings::Instance() - .font_sizes() - .RegisterValueChangedCallback([this](const std::vector&) - { UpdateMonospaceFont(); }); - - initialized_ = true; -} - -void ImGui::Impl::UpdateMonospaceFont() -{ - // Get monospace font size - std::size_t fontSize = 16; - auto fontSizes = - settings::GeneralSettings::Instance().font_sizes().GetValue(); - if (fontSizes.size() > 1) - { - fontSize = fontSizes[1]; - } - else if (fontSizes.size() > 0) - { - fontSize = fontSizes[0]; - } - - // Get monospace font pointer - auto monospace = - manager::ResourceManager::Font(types::Font::Inconsolata_Regular); - auto monospaceFont = monospace->ImGuiFont(fontSize); - - // Store monospace font pointer if not null - if (monospaceFont != nullptr) - { - monospaceFont_ = monospace->ImGuiFont(fontSize); - } -} - void ImGui::DrawTooltip(const std::string& hoverText) { - p->Initialize(); + auto tooltipFont = manager::FontManager::Instance().GetImGuiFont( + types::FontCategory::Tooltip); ::ImGui::BeginTooltip(); - ::ImGui::PushFont(p->monospaceFont_); + ::ImGui::PushFont(tooltipFont->font()); ::ImGui::TextUnformatted(hoverText.c_str()); ::ImGui::PopFont(); ::ImGui::EndTooltip(); diff --git a/scwx-qt/source/scwx/qt/util/tooltip.cpp b/scwx-qt/source/scwx/qt/util/tooltip.cpp index 3e9432df..509a0495 100644 --- a/scwx-qt/source/scwx/qt/util/tooltip.cpp +++ b/scwx-qt/source/scwx/qt/util/tooltip.cpp @@ -1,8 +1,6 @@ #include -#include +#include #include -#include -#include #include #include @@ -84,12 +82,22 @@ void Show(const std::string& text, const QPointF& mouseGlobalPos) } else if (tooltipMethod == types::TooltipMethod::QToolTip) { + QString fontFamily = QString::fromStdString( + textSettings.font_family(types::FontCategory::Tooltip).GetValue()); + QString fontStyle = QString::fromStdString( + textSettings.font_style(types::FontCategory::Tooltip).GetValue()); + double fontPointSize = + textSettings.font_point_size(types::FontCategory::Tooltip).GetValue(); + static std::size_t id = 0; QToolTip::showText( mouseGlobalPos.toPoint(), - QString("%3") + QString("%5") .arg(++id) - .arg("Inconsolata") + .arg(fontFamily) + .arg(fontStyle) + .arg(fontPointSize) .arg(QString::fromStdString(displayText).replace("\n", "
")), tooltipParent_.get(), {}, @@ -97,22 +105,9 @@ void Show(const std::string& text, const QPointF& mouseGlobalPos) } else if (tooltipMethod == types::TooltipMethod::QLabel) { - // Get monospace font size - units::font_size::pixels fontSize {16}; - auto fontSizes = - settings::GeneralSettings::Instance().font_sizes().GetValue(); - if (fontSizes.size() > 1) - { - fontSize = units::font_size::pixels {fontSizes[1]}; - } - else if (fontSizes.size() > 0) - { - fontSize = units::font_size::pixels {fontSizes[0]}; - } - // Configure the label - QFont font("Inconsolata"); - font.setPointSizeF(units::font_size::points(fontSize).value()); + QFont font = manager::FontManager::Instance().GetQFont( + types::FontCategory::Tooltip); tooltipLabel_->setFont(font); tooltipLabel_->setText(QString::fromStdString(displayText)); From 810b61f8f93f66d4e28234a6c7108d9f4addbce2 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 22:05:17 -0500 Subject: [PATCH 22/27] Use fonts defined in placefiles --- .../source/scwx/qt/gl/draw/placefile_text.cpp | 34 ++++++++++++++ .../source/scwx/qt/gl/draw/placefile_text.hpp | 13 ++++++ .../scwx/qt/manager/placefile_manager.cpp | 44 ++++++++++++++++--- .../scwx/qt/manager/placefile_manager.hpp | 4 ++ .../source/scwx/qt/map/placefile_layer.cpp | 2 + 5 files changed, 92 insertions(+), 5 deletions(-) diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_text.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_text.cpp index 18f2efee..b318e19f 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_text.cpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_text.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -37,6 +39,7 @@ public: const std::string& text, const std::string& hoverText, boost::gil::rgba8_pixel_t color, + std::size_t fontNumber, float x, float y); @@ -62,6 +65,9 @@ public: std::mutex listMutex_ {}; std::vector> textList_ {}; std::vector> newList_ {}; + + std::vector> fonts_ {}; + std::vector> newFonts_ {}; }; PlacefileText::PlacefileText(const std::shared_ptr& context, @@ -155,6 +161,7 @@ void PlacefileText::Impl::RenderTextDrawItem( di->text_, di->hoverText_, di->color_, + std::clamp(di->fontNumber_, 1, 8), rotatedX + di->x_ + halfWidth_, rotatedY + di->y_ + halfHeight_); } @@ -165,6 +172,7 @@ void PlacefileText::Impl::RenderText( const std::string& text, const std::string& hoverText, boost::gil::rgba8_pixel_t color, + std::size_t fontNumber, float x, float y) { @@ -184,10 +192,12 @@ void PlacefileText::Impl::RenderText( ImGuiWindowFlags_NoBackground); // Render text + ImGui::PushFont(fonts_[fontNumber - 1]->font()); ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(color[0], color[1], color[2], color[3])); ImGui::TextUnformatted(text.c_str()); ImGui::PopStyleColor(); + ImGui::PopFont(); // Store hover text for mouse picking pass if (!hoverText.empty() && ImGui::IsItemHovered()) @@ -231,6 +241,28 @@ void PlacefileText::StartText() p->newList_.clear(); } +void PlacefileText::SetFonts( + const boost::unordered_flat_map>& fonts) +{ + auto defaultFont = manager::FontManager::Instance().GetImGuiFont( + types::FontCategory::Default); + + // Valid font numbers are from 1 to 8, place in 0-based font vector + for (std::size_t i = 1; i <= 8; ++i) + { + auto it = fonts.find(i); + if (it != fonts.cend()) + { + p->newFonts_.push_back(it->second); + } + else + { + p->newFonts_.push_back(defaultFont); + } + } +} + void PlacefileText::AddText( const std::shared_ptr& di) { @@ -246,9 +278,11 @@ void PlacefileText::FinishText() // Swap text lists p->textList_.swap(p->newList_); + p->fonts_.swap(p->newFonts_); // Clear the new list p->newList_.clear(); + p->newFonts_.clear(); } } // namespace draw diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_text.hpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_text.hpp index 979a802a..a8d23bc3 100644 --- a/scwx-qt/source/scwx/qt/gl/draw/placefile_text.hpp +++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_text.hpp @@ -2,8 +2,11 @@ #include #include +#include #include +#include + namespace scwx { namespace qt @@ -44,6 +47,16 @@ public: */ void StartText(); + /** + * Configures the fonts for drawing the placefile text. + * + * @param [in] fonts A map of ImGui fonts + */ + void + SetFonts(const boost::unordered_flat_map>& + fonts); + /** * Adds placefile text to the internal draw list. * diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp index 52414031..67bc3f1c 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.cpp @@ -52,7 +52,8 @@ public: void ReadPlacefileSettings(); void WritePlacefileSettings(); - static void + static boost::unordered_flat_map> LoadFontResources(const std::shared_ptr& placefile); static std::vector> LoadImageResources(const std::shared_ptr& placefile); @@ -66,7 +67,7 @@ public: std::shared_ptr radarSite_ {}; std::vector> placefileRecords_ {}; - std::unordered_map> + boost::unordered_flat_map> placefileRecordMap_ {}; std::shared_mutex placefileRecordLock_ {}; }; @@ -137,6 +138,10 @@ public: std::mutex refreshMutex_ {}; std::mutex timerMutex_ {}; + boost::unordered_flat_map> + fonts_ {}; + std::mutex fontsMutex_ {}; + std::vector> images_ {}; std::string lastRadarSite_ {}; @@ -211,6 +216,20 @@ PlacefileManager::placefile(const std::string& name) return nullptr; } +boost::unordered_flat_map> +PlacefileManager::placefile_fonts(const std::string& name) +{ + std::shared_lock lock(p->placefileRecordLock_); + + auto it = p->placefileRecordMap_.find(name); + if (it != p->placefileRecordMap_.cend()) + { + std::unique_lock fontsLock {it->second->fontsMutex_}; + return it->second->fonts_; + } + return {}; +} + void PlacefileManager::set_placefile_enabled(const std::string& name, bool enabled) { @@ -281,6 +300,7 @@ void PlacefileManager::set_placefile_url(const std::string& name, auto placefileRecord = it->second; placefileRecord->name_ = normalizedUrl; placefileRecord->placefile_ = nullptr; + placefileRecord->fonts_.clear(); placefileRecord->images_.clear(); p->placefileRecordMap_.erase(it); p->placefileRecordMap_.insert_or_assign(normalizedUrl, placefileRecord); @@ -590,7 +610,7 @@ void PlacefileManager::Impl::PlacefileRecord::Update() if (updatedPlacefile != nullptr) { // Load placefile resources - Impl::LoadFontResources(updatedPlacefile); + auto newFonts = Impl::LoadFontResources(updatedPlacefile); auto newImages = Impl::LoadImageResources(updatedPlacefile); // Check the name matches, in case the name updated @@ -601,6 +621,13 @@ void PlacefileManager::Impl::PlacefileRecord::Update() title_ = placefile_->title(); lastUpdateTime_ = std::chrono::system_clock::now(); + // Update font resources + { + std::unique_lock fontsLock {fontsMutex_}; + fonts_.swap(newFonts); + newFonts.clear(); + } + // Update image resources images_.swap(newImages); newImages.clear(); @@ -688,9 +715,12 @@ std::shared_ptr PlacefileManager::Instance() return placefileManager; } -void PlacefileManager::Impl::LoadFontResources( +boost::unordered_flat_map> +PlacefileManager::Impl::LoadFontResources( const std::shared_ptr& placefile) { + boost::unordered_flat_map> + imGuiFonts {}; auto fonts = placefile->fonts(); for (auto& font : fonts) @@ -707,8 +737,12 @@ void PlacefileManager::Impl::LoadFontResources( styles.push_back("italic"); } - FontManager::Instance().LoadImGuiFont(font.second->face_, styles, size); + auto imGuiFont = FontManager::Instance().LoadImGuiFont( + font.second->face_, styles, size); + imGuiFonts.emplace(font.first, std::move(imGuiFont)); } + + return imGuiFonts; } std::vector> diff --git a/scwx-qt/source/scwx/qt/manager/placefile_manager.hpp b/scwx-qt/source/scwx/qt/manager/placefile_manager.hpp index 0567265f..ee9bf3a4 100644 --- a/scwx-qt/source/scwx/qt/manager/placefile_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/placefile_manager.hpp @@ -2,8 +2,10 @@ #include #include +#include #include +#include namespace scwx { @@ -24,6 +26,8 @@ public: bool placefile_thresholded(const std::string& name); std::string placefile_title(const std::string& name); std::shared_ptr placefile(const std::string& name); + boost::unordered_flat_map> + placefile_fonts(const std::string& name); void set_placefile_enabled(const std::string& name, bool enabled); void set_placefile_thresholded(const std::string& name, bool thresholded); diff --git a/scwx-qt/source/scwx/qt/map/placefile_layer.cpp b/scwx-qt/source/scwx/qt/map/placefile_layer.cpp index f29d63e4..3f6e5961 100644 --- a/scwx-qt/source/scwx/qt/map/placefile_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/placefile_layer.cpp @@ -197,6 +197,8 @@ void PlacefileLayer::ReloadData() p->placefileIcons_->SetIconFiles(placefile->icon_files(), placefile->name()); + p->placefileText_->SetFonts( + placefileManager->placefile_fonts(p->placefileName_)); for (auto& drawItem : placefile->GetDrawItems()) { From f4596a7964e1bd68b081a899729e4fbb1745642a Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 22:13:07 -0500 Subject: [PATCH 23/27] Removing some unused ImGui font code --- .../scwx/qt/manager/resource_manager.cpp | 3 - scwx-qt/source/scwx/qt/ui/settings_dialog.cpp | 24 +-- scwx-qt/source/scwx/qt/ui/settings_dialog.ui | 203 ++++++++---------- scwx-qt/source/scwx/qt/util/font.cpp | 41 ---- 4 files changed, 100 insertions(+), 171 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index e01e21ff..0302d1dc 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -117,9 +117,6 @@ static void LoadFonts() fonts_.emplace(fontName.first, font); } - ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); - fontAtlas->AddFontDefault(); - fontManager.InitializeFonts(); } diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index bfc17967..9e7310a2 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -93,7 +93,6 @@ public: fontCategoryModel_ {new QStandardItemModel(self)}, settings_ {std::initializer_list { &defaultRadarSite_, - &fontSizes_, &gridWidth_, &gridHeight_, &mapProvider_, @@ -169,16 +168,15 @@ public: types::FontCategory selectedFontCategory_ {types::FontCategory::Unknown}; - settings::SettingsInterface defaultRadarSite_ {}; - settings::SettingsInterface> fontSizes_ {}; - settings::SettingsInterface gridWidth_ {}; - settings::SettingsInterface gridHeight_ {}; - settings::SettingsInterface mapProvider_ {}; - settings::SettingsInterface mapboxApiKey_ {}; - settings::SettingsInterface mapTilerApiKey_ {}; - settings::SettingsInterface defaultAlertAction_ {}; - settings::SettingsInterface updateNotificationsEnabled_ {}; - settings::SettingsInterface debugEnabled_ {}; + settings::SettingsInterface defaultRadarSite_ {}; + settings::SettingsInterface gridWidth_ {}; + settings::SettingsInterface gridHeight_ {}; + settings::SettingsInterface mapProvider_ {}; + settings::SettingsInterface mapboxApiKey_ {}; + settings::SettingsInterface mapTilerApiKey_ {}; + settings::SettingsInterface defaultAlertAction_ {}; + settings::SettingsInterface updateNotificationsEnabled_ {}; + settings::SettingsInterface debugEnabled_ {}; std::unordered_map> colorTables_ {}; @@ -424,10 +422,6 @@ void SettingsDialogImpl::SetupGeneralTab() defaultRadarSite_.SetResetButton(self_->ui->resetRadarSiteButton); UpdateRadarDialogLocation(generalSettings.default_radar_site().GetValue()); - fontSizes_.SetSettingsVariable(generalSettings.font_sizes()); - fontSizes_.SetEditWidget(self_->ui->fontSizesLineEdit); - fontSizes_.SetResetButton(self_->ui->resetFontSizesButton); - gridWidth_.SetSettingsVariable(generalSettings.grid_width()); gridWidth_.SetEditWidget(self_->ui->gridWidthSpinBox); gridWidth_.SetResetButton(self_->ui->resetGridWidthButton); diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui index 8ed4a76f..787c124e 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.ui +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.ui @@ -102,7 +102,7 @@
- 2 + 0 @@ -127,27 +127,7 @@ 0 - - - - - - - Font Sizes - - - - - - - - - - Default Radar Site - - - - + ... @@ -158,45 +138,14 @@ - - - - - + + - Mapbox API Key + Grid Width - - - - MapTiler API Key - - - - - - - - - - ... - - - - :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg - - - - - - - ... - - - - + ... @@ -207,16 +156,6 @@ - - - - QLineEdit::Password - - - - - - @@ -228,32 +167,14 @@ - - + + - Grid Height + MapTiler API Key - - - - ... - - - - :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg - - - - - - - QLineEdit::Password - - - - + ... @@ -264,21 +185,97 @@ + + + + + + - + - Grid Width + Grid Height + + + + + + + Default Radar Site + + + + + + + QLineEdit::Password + + + + + + + QLineEdit::Password + + + + + + + Default Alert Action + + + Mapbox API Key + + + + + + + ... + + + + :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg + + + + + + + + + + ... + + + + Map Provider - + + + + ... + + + + :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg + + + + + + + ... @@ -289,26 +286,8 @@ - - - - Default Alert Action - - - - - - - - - - ... - - - - :/res/icons/font-awesome-6/rotate-left-solid.svg:/res/icons/font-awesome-6/rotate-left-solid.svg - - + + diff --git a/scwx-qt/source/scwx/qt/util/font.cpp b/scwx-qt/source/scwx/qt/util/font.cpp index 74374163..f2397bd2 100644 --- a/scwx-qt/source/scwx/qt/util/font.cpp +++ b/scwx-qt/source/scwx/qt/util/font.cpp @@ -126,9 +126,6 @@ public: } } - void CreateImGuiFont(QFile& fontFile, - QByteArray& fontData, - const std::vector& fontSizes); void ParseNames(FT_Face face); const std::string resource_; @@ -266,39 +263,6 @@ GLuint Font::GenerateTexture(gl::OpenGLFunctions& gl) return p->atlas_->id; } -void FontImpl::CreateImGuiFont(QFile& fontFile, - QByteArray& fontData, - const std::vector& fontSizes) -{ - QFileInfo fileInfo(fontFile); - ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); - ImFontConfig fontConfig {}; - - // Do not transfer ownership of font data to ImGui, makes const_cast safe - fontConfig.FontDataOwnedByAtlas = false; - - for (int64_t fontSize : fontSizes) - { - const float sizePixels = static_cast(fontSize); - - // Assign name to font - strncpy(fontConfig.Name, - fmt::format("{}:{}", fileInfo.fileName().toStdString(), fontSize) - .c_str(), - sizeof(fontConfig.Name) - 1); - fontConfig.Name[sizeof(fontConfig.Name) - 1] = 0; - - // Add font to atlas - imGuiFonts_.emplace( - fontSize, - fontAtlas->AddFontFromMemoryTTF( - const_cast(static_cast(fontData.constData())), - fontData.size(), - sizePixels, - &fontConfig)); - } -} - ImFont* Font::ImGuiFont(std::size_t fontPixelSize) { auto it = p->imGuiFonts_.find(fontPixelSize); @@ -334,11 +298,6 @@ std::shared_ptr Font::Create(const std::string& resource) font = std::make_shared(resource); QByteArray fontData = fontFile.readAll(); - font->p->CreateImGuiFont( - fontFile, - fontData, - settings::GeneralSettings::Instance().font_sizes().GetValue()); - font->p->atlas_ = ftgl::texture_atlas_new(512, 512, 1); ftgl::texture_font_t* textureFont = ftgl::texture_font_new_from_memory( font->p->atlas_, BASE_POINT_SIZE, fontData.constData(), fontData.size()); From 0d7b9ae9a7778d6458a4d8cc64272aab96b56a37 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 22:16:24 -0500 Subject: [PATCH 24/27] Disabling unused font initialization --- .../source/scwx/qt/manager/resource_manager.cpp | 17 +---------------- .../source/scwx/qt/manager/resource_manager.hpp | 4 +--- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 0302d1dc..36665aab 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -32,8 +31,7 @@ static const std::vector> fontNames_ { {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; -static std::unordered_map fontIds_ {}; -static std::unordered_map> fonts_ {}; +static std::unordered_map fontIds_ {}; void Initialize() { @@ -55,16 +53,6 @@ int FontId(types::Font font) return -1; } -std::shared_ptr Font(types::Font font) -{ - auto it = fonts_.find(font); - if (it != fonts_.cend()) - { - return it->second; - } - return nullptr; -} - std::shared_ptr LoadImageResource(const std::string& urlString) { @@ -112,9 +100,6 @@ static void LoadFonts() fontIds_.emplace(fontName.first, fontId); fontManager.LoadApplicationFont(fontName.second); - - auto font = util::Font::Create(fontName.second); - fonts_.emplace(fontName.first, font); } fontManager.InitializeFonts(); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp index 7b8003c9..12e95cab 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -19,8 +18,7 @@ namespace ResourceManager void Initialize(); void Shutdown(); -int FontId(types::Font font); -std::shared_ptr Font(types::Font font); +int FontId(types::Font font); std::shared_ptr LoadImageResource(const std::string& urlString); From 7af2edd3ec079217cfe3be2b84c2c012b6f18f62 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 22:25:17 -0500 Subject: [PATCH 25/27] Use a more friendly name for ImGui font registration --- .../source/scwx/qt/manager/font_manager.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 05d9e79e..8472b69f 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -261,10 +261,24 @@ FontManager::LoadImGuiFont(const std::string& family, return it->second; } + // Define a name for the ImGui font + std::string fontName; + try + { + fontName = fmt::format( + "{}:{}", + std::filesystem::path(fontRecord.filename_).filename().string(), + imFontSize.value()); + } + catch (const std::exception& ex) + { + logger_->warn(ex.what()); + fontName = fmt::format("{}:{}", fontRecord.filename_, imFontSize.value()); + } + // Create an ImGui font std::shared_ptr imguiFont = - std::make_shared( - fontRecord.filename_, rawFontData, imFontSize); + std::make_shared(fontName, rawFontData, imFontSize); // Store the ImGui font p->imguiFonts_.insert_or_assign(imguiFontKey, imguiFont); From fe1acb32cf1cf625796d632266c2ec9c4df5298c Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 23:33:27 -0500 Subject: [PATCH 26/27] Initialize Qt application fonts from cached versions on filesystem --- .../source/scwx/qt/manager/font_manager.cpp | 21 ++++++++++++++++++- .../source/scwx/qt/manager/font_manager.hpp | 4 +++- .../scwx/qt/manager/resource_manager.cpp | 19 +---------------- .../scwx/qt/manager/resource_manager.hpp | 2 -- scwx-qt/source/scwx/qt/ui/about_dialog.cpp | 4 ++-- scwx-qt/source/scwx/qt/ui/update_dialog.cpp | 4 ++-- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 8472b69f..84f37b03 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,8 @@ public: boost::unordered_flat_set dirtyFonts_ {}; std::mutex dirtyFontsMutex_ {}; + + boost::unordered_flat_map fontIds_ {}; }; FontManager::FontManager() : p(std::make_unique(this)) {} @@ -175,6 +178,16 @@ std::uint64_t FontManager::imgui_fonts_build_count() const return p->imguiFontsBuildCount_; } +int FontManager::GetFontId(types::Font font) const +{ + auto it = p->fontIds_.find(font); + if (it != p->fontIds_.cend()) + { + return it->second; + } + return -1; +} + std::shared_ptr FontManager::GetImGuiFont(types::FontCategory fontCategory) { @@ -322,7 +335,8 @@ FontManager::Impl::GetRawFontData(const std::string& filename) return result.first->second; } -void FontManager::LoadApplicationFont(const std::string& filename) +void FontManager::LoadApplicationFont(types::Font font, + const std::string& filename) { // If the font cache failed to create, don't attempt to cache any fonts if (p->fontCachePath_.empty()) @@ -358,6 +372,11 @@ void FontManager::LoadApplicationFont(const std::string& filename) return; } + // Load the file into the Qt Font Database + int fontId = + QFontDatabase::addApplicationFont(QString::fromStdString(cacheFilename)); + p->fontIds_.emplace(font, fontId); + // Load the file into fontconfig FcBool result = FcConfigAppFontAddFile( nullptr, reinterpret_cast(cacheFilename.c_str())); diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.hpp b/scwx-qt/source/scwx/qt/manager/font_manager.hpp index 7b824861..4cc21083 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -27,6 +28,7 @@ public: std::shared_mutex& imgui_font_atlas_mutex(); std::uint64_t imgui_fonts_build_count() const; + int GetFontId(types::Font font) const; std::shared_ptr GetImGuiFont(types::FontCategory fontCategory); std::shared_ptr @@ -35,7 +37,7 @@ public: units::font_size::points size, bool loadIfNotFound = true); - void LoadApplicationFont(const std::string& filename); + void LoadApplicationFont(types::Font font, const std::string& filename); void InitializeFonts(); static QFont GetQFont(types::FontCategory fontCategory); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp index 36665aab..3048fc6c 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.cpp @@ -8,7 +8,6 @@ #include #include -#include #include namespace scwx @@ -31,8 +30,6 @@ static const std::vector> fontNames_ { {types::Font::din1451alt_g, ":/res/fonts/din1451alt_g.ttf"}, {types::Font::Inconsolata_Regular, ":/res/fonts/Inconsolata-Regular.ttf"}}; -static std::unordered_map fontIds_ {}; - void Initialize() { config::CountyDatabase::Initialize(); @@ -43,16 +40,6 @@ void Initialize() void Shutdown() {} -int FontId(types::Font font) -{ - auto it = fontIds_.find(font); - if (it != fontIds_.cend()) - { - return it->second; - } - return -1; -} - std::shared_ptr LoadImageResource(const std::string& urlString) { @@ -95,11 +82,7 @@ static void LoadFonts() for (auto& fontName : fontNames_) { - int fontId = QFontDatabase::addApplicationFont( - QString::fromStdString(fontName.second)); - fontIds_.emplace(fontName.first, fontId); - - fontManager.LoadApplicationFont(fontName.second); + fontManager.LoadApplicationFont(fontName.first, fontName.second); } fontManager.InitializeFonts(); diff --git a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp index 12e95cab..00658891 100644 --- a/scwx-qt/source/scwx/qt/manager/resource_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/resource_manager.hpp @@ -18,8 +18,6 @@ namespace ResourceManager void Initialize(); void Shutdown(); -int FontId(types::Font font); - std::shared_ptr LoadImageResource(const std::string& urlString); std::vector> diff --git a/scwx-qt/source/scwx/qt/ui/about_dialog.cpp b/scwx-qt/source/scwx/qt/ui/about_dialog.cpp index a42be06f..42ec4e32 100644 --- a/scwx-qt/source/scwx/qt/ui/about_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/about_dialog.cpp @@ -1,7 +1,7 @@ #include "about_dialog.hpp" #include "ui_about_dialog.h" #include -#include +#include #include @@ -27,7 +27,7 @@ AboutDialog::AboutDialog(QWidget* parent) : ui->setupUi(this); int titleFontId = - manager::ResourceManager::FontId(types::Font::din1451alt_g); + manager::FontManager::Instance().GetFontId(types::Font::din1451alt_g); QString titleFontFamily = QFontDatabase::applicationFontFamilies(titleFontId).at(0); QFont titleFont(titleFontFamily, 14); diff --git a/scwx-qt/source/scwx/qt/ui/update_dialog.cpp b/scwx-qt/source/scwx/qt/ui/update_dialog.cpp index 93b3ac0a..4029fa9a 100644 --- a/scwx-qt/source/scwx/qt/ui/update_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/update_dialog.cpp @@ -1,7 +1,7 @@ #include "update_dialog.hpp" #include "ui_update_dialog.h" #include -#include +#include #include #include @@ -30,7 +30,7 @@ UpdateDialog::UpdateDialog(QWidget* parent) : ui->setupUi(this); int titleFontId = - manager::ResourceManager::FontId(types::Font::din1451alt_g); + manager::FontManager::Instance().GetFontId(types::Font::din1451alt_g); QString titleFontFamily = QFontDatabase::applicationFontFamilies(titleFontId).at(0); QFont titleFont(titleFontFamily, 12); From 104227aa42123fbdd63ed21cce2fea12fbf6b0b7 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 8 Oct 2023 23:35:55 -0500 Subject: [PATCH 27/27] Represent raw font data as char instead of std::uint8_t --- scwx-qt/source/scwx/qt/manager/font_manager.cpp | 15 +++++++-------- scwx-qt/source/scwx/qt/types/imgui_font.cpp | 16 ++++++++-------- scwx-qt/source/scwx/qt/types/imgui_font.hpp | 6 +++--- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/font_manager.cpp b/scwx-qt/source/scwx/qt/manager/font_manager.cpp index 84f37b03..2747fd4d 100644 --- a/scwx-qt/source/scwx/qt/manager/font_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/font_manager.cpp @@ -62,7 +62,7 @@ public: void InitializeFontconfig(); void UpdateImGuiFont(types::FontCategory fontCategory); - const std::vector& GetRawFontData(const std::string& filename); + const std::vector& GetRawFontData(const std::string& filename); static FontRecord MatchFontFile(const std::string& family, const std::vector& styles); @@ -81,8 +81,7 @@ public: imguiFonts_ {}; std::shared_mutex imguiFontsMutex_ {}; - boost::unordered_flat_map> - rawFontData_ {}; + boost::unordered_flat_map> rawFontData_ {}; std::mutex rawFontDataMutex_ {}; std::shared_ptr defaultFont_ {}; @@ -303,7 +302,7 @@ FontManager::LoadImGuiFont(const std::string& family, return imguiFont; } -const std::vector& +const std::vector& FontManager::Impl::GetRawFontData(const std::string& filename) { std::unique_lock rawFontDataLock {rawFontDataMutex_}; @@ -316,16 +315,16 @@ FontManager::Impl::GetRawFontData(const std::string& filename) } // Raw font data needs to be loaded - std::basic_ifstream ifs {filename, std::ios::binary}; + std::basic_ifstream ifs {filename, std::ios::binary}; ifs.seekg(0, std::ios_base::end); std::size_t dataSize = ifs.tellg(); ifs.seekg(0, std::ios_base::beg); // Store the font data in a buffer - std::vector buffer {}; + std::vector buffer {}; buffer.reserve(dataSize); - std::copy(std::istreambuf_iterator(ifs), - std::istreambuf_iterator(), + std::copy(std::istreambuf_iterator(ifs), + std::istreambuf_iterator(), std::back_inserter(buffer)); // Place the buffer in the cache diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.cpp b/scwx-qt/source/scwx/qt/types/imgui_font.cpp index f7ba126c..e6f22ad1 100644 --- a/scwx-qt/source/scwx/qt/types/imgui_font.cpp +++ b/scwx-qt/source/scwx/qt/types/imgui_font.cpp @@ -23,9 +23,9 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class ImGuiFont::Impl { public: - explicit Impl(const std::string& fontName, - const std::vector& fontData, - units::font_size::pixels size) : + explicit Impl(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size) : fontName_ {fontName}, size_ {size} { CreateImGuiFont(fontData); @@ -33,7 +33,7 @@ public: ~Impl() {} - void CreateImGuiFont(const std::vector& fontData); + void CreateImGuiFont(const std::vector& fontData); const std::string fontName_; const units::font_size::pixels size_; @@ -41,15 +41,15 @@ public: ImFont* imFont_ {nullptr}; }; -ImGuiFont::ImGuiFont(const std::string& fontName, - const std::vector& fontData, - units::font_size::pixels size) : +ImGuiFont::ImGuiFont(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size) : p(std::make_unique(fontName, fontData, size)) { } ImGuiFont::~ImGuiFont() = default; -void ImGuiFont::Impl::CreateImGuiFont(const std::vector& fontData) +void ImGuiFont::Impl::CreateImGuiFont(const std::vector& fontData) { logger_->debug("Creating Font: {}", fontName_); diff --git a/scwx-qt/source/scwx/qt/types/imgui_font.hpp b/scwx-qt/source/scwx/qt/types/imgui_font.hpp index 9a69ac6e..ace8ba09 100644 --- a/scwx-qt/source/scwx/qt/types/imgui_font.hpp +++ b/scwx-qt/source/scwx/qt/types/imgui_font.hpp @@ -18,9 +18,9 @@ namespace types class ImGuiFont { public: - explicit ImGuiFont(const std::string& fontName, - const std::vector& fontData, - units::font_size::pixels size); + explicit ImGuiFont(const std::string& fontName, + const std::vector& fontData, + units::font_size::pixels size); ~ImGuiFont(); ImGuiFont(const ImGuiFont&) = delete;