mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 14:50:05 +00:00 
			
		
		
		
	Merge branch 'feature/placefiles-font' into feature/placefiles
This commit is contained in:
		
						commit
						cfa62d5fbc
					
				
					 52 changed files with 1988 additions and 542 deletions
				
			
		|  | @ -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) | | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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) | ||||
|  | @ -73,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 | ||||
|  | @ -81,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 | ||||
|  | @ -157,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 | ||||
|  | @ -164,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 | ||||
|  | @ -476,6 +481,7 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets | |||
|                                      Boost::timer | ||||
|                                      qmaplibregl | ||||
|                                      $<$<CXX_COMPILER_ID:MSVC>:opengl32> | ||||
|                                      Fontconfig::Fontconfig | ||||
|                                      freetype-gl | ||||
|                                      GeographicLib::GeographicLib | ||||
|                                      glm::glm | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| #include <scwx/qt/gl/draw/placefile_text.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/manager/placefile_manager.hpp> | ||||
| #include <scwx/qt/util/maplibre.hpp> | ||||
| #include <scwx/qt/util/tooltip.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
|  | @ -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<std::shared_ptr<const gr::Placefile::TextDrawItem>> textList_ {}; | ||||
|    std::vector<std::shared_ptr<const gr::Placefile::TextDrawItem>> newList_ {}; | ||||
| 
 | ||||
|    std::vector<std::shared_ptr<types::ImGuiFont>> fonts_ {}; | ||||
|    std::vector<std::shared_ptr<types::ImGuiFont>> newFonts_ {}; | ||||
| }; | ||||
| 
 | ||||
| PlacefileText::PlacefileText(const std::shared_ptr<GlContext>& context, | ||||
|  | @ -155,6 +161,7 @@ void PlacefileText::Impl::RenderTextDrawItem( | |||
|                  di->text_, | ||||
|                  di->hoverText_, | ||||
|                  di->color_, | ||||
|                  std::clamp<std::size_t>(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<std::size_t, | ||||
|                                    std::shared_ptr<types::ImGuiFont>>& 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<gr::Placefile::TextDrawItem>& 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
 | ||||
|  |  | |||
|  | @ -2,8 +2,11 @@ | |||
| 
 | ||||
| #include <scwx/qt/gl/gl_context.hpp> | ||||
| #include <scwx/qt/gl/draw/draw_item.hpp> | ||||
| #include <scwx/qt/types/imgui_font.hpp> | ||||
| #include <scwx/gr/placefile.hpp> | ||||
| 
 | ||||
| #include <boost/unordered/unordered_flat_map.hpp> | ||||
| 
 | ||||
| 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<std::size_t, | ||||
|                                             std::shared_ptr<types::ImGuiFont>>& | ||||
|                fonts); | ||||
| 
 | ||||
|    /**
 | ||||
|     * Adds placefile text to the internal draw list. | ||||
|     * | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -7,13 +7,14 @@ | |||
| #include <scwx/qt/main/versions.hpp> | ||||
| #include <scwx/qt/manager/placefile_manager.hpp> | ||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/manager/text_event_manager.hpp> | ||||
| #include <scwx/qt/manager/timeline_manager.hpp> | ||||
| #include <scwx/qt/manager/update_manager.hpp> | ||||
| #include <scwx/qt/map/map_provider.hpp> | ||||
| #include <scwx/qt/map/map_widget.hpp> | ||||
| #include <scwx/qt/model/radar_product_model.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/map_settings.hpp> | ||||
| #include <scwx/qt/settings/ui_settings.hpp> | ||||
| #include <scwx/qt/ui/about_dialog.hpp> | ||||
| #include <scwx/qt/ui/alert_dock_widget.hpp> | ||||
|  | @ -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; | ||||
|          } | ||||
|  |  | |||
							
								
								
									
										512
									
								
								scwx-qt/source/scwx/qt/manager/font_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										512
									
								
								scwx-qt/source/scwx/qt/manager/font_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,512 @@ | |||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/settings/text_settings.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
| 
 | ||||
| #include <QFile> | ||||
| #include <QFileInfo> | ||||
| #include <QFontDatabase> | ||||
| #include <QStandardPaths> | ||||
| #include <boost/container_hash/hash.hpp> | ||||
| #include <boost/unordered/unordered_flat_map.hpp> | ||||
| #include <boost/unordered/unordered_flat_set.hpp> | ||||
| #include <fontconfig/fontconfig.h> | ||||
| 
 | ||||
| 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_); | ||||
| 
 | ||||
| static const std::string kFcTrueType_ {"TrueType"}; | ||||
| 
 | ||||
| struct FontRecord | ||||
| { | ||||
|    std::string family_ {}; | ||||
|    std::string style_ {}; | ||||
|    std::string filename_ {}; | ||||
| }; | ||||
| 
 | ||||
| typedef std::pair<FontRecord, units::font_size::pixels<int>> FontRecordPair; | ||||
| 
 | ||||
| template<class Key> | ||||
| struct FontRecordHash; | ||||
| 
 | ||||
| template<> | ||||
| struct FontRecordHash<FontRecordPair> | ||||
| { | ||||
|    size_t operator()(const FontRecordPair& x) const; | ||||
| }; | ||||
| 
 | ||||
| class FontManager::Impl | ||||
| { | ||||
| public: | ||||
|    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<char>& GetRawFontData(const std::string& filename); | ||||
| 
 | ||||
|    static FontRecord MatchFontFile(const std::string&              family, | ||||
|                                    const std::vector<std::string>& styles); | ||||
| 
 | ||||
|    FontManager* self_; | ||||
| 
 | ||||
|    std::string fontCachePath_ {}; | ||||
| 
 | ||||
|    std::shared_mutex imguiFontAtlasMutex_ {}; | ||||
| 
 | ||||
|    std::uint64_t imguiFontsBuildCount_ {}; | ||||
| 
 | ||||
|    boost::unordered_flat_map<FontRecordPair, | ||||
|                              std::shared_ptr<types::ImGuiFont>, | ||||
|                              FontRecordHash<FontRecordPair>> | ||||
|                      imguiFonts_ {}; | ||||
|    std::shared_mutex imguiFontsMutex_ {}; | ||||
| 
 | ||||
|    boost::unordered_flat_map<std::string, std::vector<char>> rawFontData_ {}; | ||||
|    std::mutex rawFontDataMutex_ {}; | ||||
| 
 | ||||
|    std::shared_ptr<types::ImGuiFont> defaultFont_ {}; | ||||
|    boost::unordered_flat_map<types::FontCategory, | ||||
|                              std::shared_ptr<types::ImGuiFont>> | ||||
|               fontCategoryMap_ {}; | ||||
|    std::mutex fontCategoryMutex_ {}; | ||||
| 
 | ||||
|    boost::unordered_flat_set<types::FontCategory> dirtyFonts_ {}; | ||||
|    std::mutex                                     dirtyFontsMutex_ {}; | ||||
| 
 | ||||
|    boost::unordered_flat_map<types::Font, int> fontIds_ {}; | ||||
| }; | ||||
| 
 | ||||
| FontManager::FontManager() : p(std::make_unique<Impl>(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<double> 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_; | ||||
| } | ||||
| 
 | ||||
| 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<types::ImGuiFont> | ||||
| 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_; | ||||
| } | ||||
| 
 | ||||
| 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<double> 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<types::ImGuiFont> | ||||
| FontManager::LoadImGuiFont(const std::string&               family, | ||||
|                            const std::vector<std::string>&  styles, | ||||
|                            units::font_size::points<double> size, | ||||
|                            bool                             loadIfNotFound) | ||||
| { | ||||
|    const std::string styleString = fmt::format("{}", fmt::join(styles, " ")); | ||||
|    const std::string fontString = | ||||
|       fmt::format("{}-{}:{}", family, size.value(), styleString); | ||||
| 
 | ||||
|    logger_->debug("LoadFontResource: {}", fontString); | ||||
| 
 | ||||
|    FontRecord fontRecord = Impl::MatchFontFile(family, styles); | ||||
| 
 | ||||
|    // Only allow whole pixels, and clamp to 6-72 pt
 | ||||
|    units::font_size::pixels<double> pixels {size}; | ||||
|    units::font_size::pixels<int>    imFontSize { | ||||
|       std::clamp(static_cast<int>(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(imguiFontKey); | ||||
|       if (it != p->imguiFonts_.end()) | ||||
|       { | ||||
|          return it->second; | ||||
|       } | ||||
| 
 | ||||
|       // 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_); | ||||
| 
 | ||||
|    // 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_}; | ||||
| 
 | ||||
|    // 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; | ||||
|    } | ||||
| 
 | ||||
|    // 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<types::ImGuiFont> imguiFont = | ||||
|       std::make_shared<types::ImGuiFont>(fontName, rawFontData, imFontSize); | ||||
| 
 | ||||
|    // Store the ImGui font
 | ||||
|    p->imguiFonts_.insert_or_assign(imguiFontKey, imguiFont); | ||||
| 
 | ||||
|    // Increment ImGui font build count
 | ||||
|    ++p->imguiFontsBuildCount_; | ||||
| 
 | ||||
|    // Return the ImGui font
 | ||||
|    return imguiFont; | ||||
| } | ||||
| 
 | ||||
| const std::vector<char>& | ||||
| 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<char> 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<char> buffer {}; | ||||
|    buffer.reserve(dataSize); | ||||
|    std::copy(std::istreambuf_iterator<char>(ifs), | ||||
|              std::istreambuf_iterator<char>(), | ||||
|              std::back_inserter(buffer)); | ||||
| 
 | ||||
|    // Place the buffer in the cache
 | ||||
|    auto result = rawFontData_.emplace(filename, std::move(buffer)); | ||||
| 
 | ||||
|    // Return the cached buffer
 | ||||
|    return result.first->second; | ||||
| } | ||||
| 
 | ||||
| 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()) | ||||
|    { | ||||
|       return; | ||||
|    } | ||||
| 
 | ||||
|    // 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 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<const FcChar8*>(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<std::string>& 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<const FcChar8*>(family.c_str())); | ||||
|    FcPatternAddString(pattern, | ||||
|                       FC_FONTFORMAT, | ||||
|                       reinterpret_cast<const FcChar8*>(kFcTrueType_.c_str())); | ||||
| 
 | ||||
|    if (!styles.empty()) | ||||
|    { | ||||
|       FcPatternAddString(pattern, | ||||
|                          FC_STYLE, | ||||
|                          reinterpret_cast<const FcChar8*>(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<char*>(fcFamily); | ||||
|          record.style_    = reinterpret_cast<char*>(fcStyle); | ||||
|          record.filename_ = reinterpret_cast<char*>(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_; | ||||
| } | ||||
| 
 | ||||
| size_t FontRecordHash<FontRecordPair>::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.value()); | ||||
|    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
 | ||||
							
								
								
									
										54
									
								
								scwx-qt/source/scwx/qt/manager/font_manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								scwx-qt/source/scwx/qt/manager/font_manager.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/types/imgui_font.hpp> | ||||
| #include <scwx/qt/types/font_types.hpp> | ||||
| #include <scwx/qt/types/text_types.hpp> | ||||
| 
 | ||||
| #include <shared_mutex> | ||||
| 
 | ||||
| #include <QFont> | ||||
| #include <QObject> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace manager | ||||
| { | ||||
| 
 | ||||
| class FontManager : public QObject | ||||
| { | ||||
|    Q_OBJECT | ||||
|    Q_DISABLE_COPY_MOVE(FontManager) | ||||
| 
 | ||||
| public: | ||||
|    explicit FontManager(); | ||||
|    ~FontManager(); | ||||
| 
 | ||||
|    std::shared_mutex& imgui_font_atlas_mutex(); | ||||
|    std::uint64_t      imgui_fonts_build_count() const; | ||||
| 
 | ||||
|    int GetFontId(types::Font font) const; | ||||
|    std::shared_ptr<types::ImGuiFont> | ||||
|    GetImGuiFont(types::FontCategory fontCategory); | ||||
|    std::shared_ptr<types::ImGuiFont> | ||||
|    LoadImGuiFont(const std::string&               family, | ||||
|                  const std::vector<std::string>&  styles, | ||||
|                  units::font_size::points<double> size, | ||||
|                  bool                             loadIfNotFound = true); | ||||
| 
 | ||||
|    void LoadApplicationFont(types::Font font, const std::string& filename); | ||||
|    void InitializeFonts(); | ||||
| 
 | ||||
|    static QFont GetQFont(types::FontCategory fontCategory); | ||||
| 
 | ||||
|    static FontManager& Instance(); | ||||
| 
 | ||||
| private: | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
|  | @ -1,4 +1,5 @@ | |||
| #include <scwx/qt/manager/placefile_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/main/application.hpp> | ||||
| #include <scwx/qt/util/json.hpp> | ||||
|  | @ -51,8 +52,11 @@ public: | |||
|    void ReadPlacefileSettings(); | ||||
|    void WritePlacefileSettings(); | ||||
| 
 | ||||
|    static boost::unordered_flat_map<std::size_t, | ||||
|                                     std::shared_ptr<types::ImGuiFont>> | ||||
|    LoadFontResources(const std::shared_ptr<gr::Placefile>& placefile); | ||||
|    static std::vector<std::shared_ptr<boost::gil::rgba8_image_t>> | ||||
|    LoadResources(const std::shared_ptr<gr::Placefile>& placefile); | ||||
|    LoadImageResources(const std::shared_ptr<gr::Placefile>& placefile); | ||||
| 
 | ||||
|    boost::asio::thread_pool threadPool_ {1u}; | ||||
| 
 | ||||
|  | @ -63,7 +67,7 @@ public: | |||
|    std::shared_ptr<config::RadarSite> radarSite_ {}; | ||||
| 
 | ||||
|    std::vector<std::shared_ptr<PlacefileRecord>> placefileRecords_ {}; | ||||
|    std::unordered_map<std::string, std::shared_ptr<PlacefileRecord>> | ||||
|    boost::unordered_flat_map<std::string, std::shared_ptr<PlacefileRecord>> | ||||
|                      placefileRecordMap_ {}; | ||||
|    std::shared_mutex placefileRecordLock_ {}; | ||||
| }; | ||||
|  | @ -134,6 +138,10 @@ public: | |||
|    std::mutex                     refreshMutex_ {}; | ||||
|    std::mutex                     timerMutex_ {}; | ||||
| 
 | ||||
|    boost::unordered_flat_map<std::size_t, std::shared_ptr<types::ImGuiFont>> | ||||
|               fonts_ {}; | ||||
|    std::mutex fontsMutex_ {}; | ||||
| 
 | ||||
|    std::vector<std::shared_ptr<boost::gil::rgba8_image_t>> images_ {}; | ||||
| 
 | ||||
|    std::string                           lastRadarSite_ {}; | ||||
|  | @ -208,6 +216,20 @@ PlacefileManager::placefile(const std::string& name) | |||
|    return nullptr; | ||||
| } | ||||
| 
 | ||||
| boost::unordered_flat_map<std::size_t, std::shared_ptr<types::ImGuiFont>> | ||||
| 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) | ||||
| { | ||||
|  | @ -278,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); | ||||
|  | @ -587,7 +610,8 @@ void PlacefileManager::Impl::PlacefileRecord::Update() | |||
|    if (updatedPlacefile != nullptr) | ||||
|    { | ||||
|       // Load placefile resources
 | ||||
|       auto newImages = Impl::LoadResources(updatedPlacefile); | ||||
|       auto newFonts  = Impl::LoadFontResources(updatedPlacefile); | ||||
|       auto newImages = Impl::LoadImageResources(updatedPlacefile); | ||||
| 
 | ||||
|       // Check the name matches, in case the name updated
 | ||||
|       if (name_ == name) | ||||
|  | @ -597,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(); | ||||
|  | @ -684,8 +715,38 @@ std::shared_ptr<PlacefileManager> PlacefileManager::Instance() | |||
|    return placefileManager; | ||||
| } | ||||
| 
 | ||||
| boost::unordered_flat_map<std::size_t, std::shared_ptr<types::ImGuiFont>> | ||||
| PlacefileManager::Impl::LoadFontResources( | ||||
|    const std::shared_ptr<gr::Placefile>& placefile) | ||||
| { | ||||
|    boost::unordered_flat_map<std::size_t, std::shared_ptr<types::ImGuiFont>> | ||||
|         imGuiFonts {}; | ||||
|    auto fonts = placefile->fonts(); | ||||
| 
 | ||||
|    for (auto& font : fonts) | ||||
|    { | ||||
|       units::font_size::pixels<double> size {font.second->pixels_}; | ||||
|       std::vector<std::string>         styles {}; | ||||
| 
 | ||||
|       if (font.second->IsBold()) | ||||
|       { | ||||
|          styles.push_back("bold"); | ||||
|       } | ||||
|       if (font.second->IsItalic()) | ||||
|       { | ||||
|          styles.push_back("italic"); | ||||
|       } | ||||
| 
 | ||||
|       auto imGuiFont = FontManager::Instance().LoadImGuiFont( | ||||
|          font.second->face_, styles, size); | ||||
|       imGuiFonts.emplace(font.first, std::move(imGuiFont)); | ||||
|    } | ||||
| 
 | ||||
|    return imGuiFonts; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::shared_ptr<boost::gil::rgba8_image_t>> | ||||
| PlacefileManager::Impl::LoadResources( | ||||
| PlacefileManager::Impl::LoadImageResources( | ||||
|    const std::shared_ptr<gr::Placefile>& placefile) | ||||
| { | ||||
|    const auto iconFiles = placefile->icon_files(); | ||||
|  |  | |||
|  | @ -2,8 +2,10 @@ | |||
| 
 | ||||
| #include <scwx/gr/placefile.hpp> | ||||
| #include <scwx/qt/config/radar_site.hpp> | ||||
| #include <scwx/qt/types/imgui_font.hpp> | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <boost/unordered/unordered_flat_map.hpp> | ||||
| 
 | ||||
| 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<gr::Placefile> placefile(const std::string& name); | ||||
|    boost::unordered_flat_map<std::size_t, std::shared_ptr<types::ImGuiFont>> | ||||
|    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); | ||||
|  |  | |||
|  | @ -1,14 +1,13 @@ | |||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/config/county_database.hpp> | ||||
| #include <scwx/qt/model/imgui_context_model.hpp> | ||||
| #include <scwx/qt/util/font.hpp> | ||||
| #include <scwx/qt/util/texture_atlas.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <execution> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <QFontDatabase> | ||||
| #include <imgui.h> | ||||
| 
 | ||||
| namespace scwx | ||||
|  | @ -26,14 +25,11 @@ static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | |||
| static void LoadFonts(); | ||||
| static void LoadTextures(); | ||||
| 
 | ||||
| static const std::unordered_map<types::Font, std::string> fontNames_ { | ||||
| static const std::vector<std::pair<types::Font, std::string>> 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::unordered_map<types::Font, int>                         fontIds_ {}; | ||||
| static std::unordered_map<types::Font, std::shared_ptr<util::Font>> fonts_ {}; | ||||
| 
 | ||||
| void Initialize() | ||||
| { | ||||
|    config::CountyDatabase::Initialize(); | ||||
|  | @ -44,26 +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<util::Font> Font(types::Font font) | ||||
| { | ||||
|    auto it = fonts_.find(font); | ||||
|    if (it != fonts_.cend()) | ||||
|    { | ||||
|       return it->second; | ||||
|    } | ||||
|    return nullptr; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<boost::gil::rgba8_image_t> | ||||
| LoadImageResource(const std::string& urlString) | ||||
| { | ||||
|  | @ -102,18 +78,14 @@ LoadImageResources(const std::vector<std::string>& urlStrings) | |||
| 
 | ||||
| static void LoadFonts() | ||||
| { | ||||
|    auto& fontManager = FontManager::Instance(); | ||||
| 
 | ||||
|    for (auto& fontName : fontNames_) | ||||
|    { | ||||
|       int fontId = QFontDatabase::addApplicationFont( | ||||
|          QString::fromStdString(fontName.second)); | ||||
|       fontIds_.emplace(fontName.first, fontId); | ||||
| 
 | ||||
|       auto font = util::Font::Create(fontName.second); | ||||
|       fonts_.emplace(fontName.first, font); | ||||
|       fontManager.LoadApplicationFont(fontName.first, fontName.second); | ||||
|    } | ||||
| 
 | ||||
|    ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); | ||||
|    fontAtlas->AddFontDefault(); | ||||
|    fontManager.InitializeFonts(); | ||||
| } | ||||
| 
 | ||||
| static void LoadTextures() | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/types/font_types.hpp> | ||||
| #include <scwx/qt/util/font.hpp> | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
|  | @ -19,9 +18,6 @@ namespace ResourceManager | |||
| void Initialize(); | ||||
| void Shutdown(); | ||||
| 
 | ||||
| int                         FontId(types::Font font); | ||||
| std::shared_ptr<util::Font> Font(types::Font font); | ||||
| 
 | ||||
| std::shared_ptr<boost::gil::rgba8_image_t> | ||||
| LoadImageResource(const std::string& urlString); | ||||
| std::vector<std::shared_ptr<boost::gil::rgba8_image_t>> | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/map/map_provider.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/map_settings.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <scwx/qt/settings/text_settings.hpp> | ||||
| #include <scwx/qt/settings/ui_settings.hpp> | ||||
| #include <scwx/qt/util/json.hpp> | ||||
|  | @ -18,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<Impl>(this)) {} | ||||
| 
 | ||||
| SettingsManager::~SettingsManager() {}; | ||||
| 
 | ||||
| void SettingsManager::Initialize() | ||||
| { | ||||
|    std::string appDataPath { | ||||
|       QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) | ||||
|  | @ -47,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; | ||||
| 
 | ||||
|  | @ -65,39 +80,41 @@ 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); | ||||
| 
 | ||||
|       Q_EMIT SettingsSaved(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void Shutdown() | ||||
| void SettingsManager::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,70 +123,53 @@ 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::value SettingsManager::Impl::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); | ||||
| 
 | ||||
|    return settingsJson; | ||||
| } | ||||
| 
 | ||||
| static void GenerateDefaultSettings() | ||||
| void SettingsManager::Impl::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(); | ||||
| } | ||||
| 
 | ||||
| static bool LoadSettings(const boost::json::object& settingsJson) | ||||
| bool SettingsManager::Impl::LoadSettings( | ||||
|    const boost::json::object& settingsJson) | ||||
| { | ||||
|    logger_->info("Loading settings"); | ||||
| 
 | ||||
|    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); | ||||
| 
 | ||||
|    return jsonDirty; | ||||
| } | ||||
| 
 | ||||
| static void ValidateSettings() | ||||
| void SettingsManager::Impl::ValidateSettings() | ||||
| { | ||||
|    logger_->debug("Validating settings"); | ||||
| 
 | ||||
|    bool settingsChanged = false; | ||||
| 
 | ||||
|    auto& generalSettings = general_settings(); | ||||
|    auto& generalSettings = settings::GeneralSettings::Instance(); | ||||
| 
 | ||||
|    // Validate map provider
 | ||||
|    std::string mapProviderName = generalSettings.map_provider().GetValue(); | ||||
|  | @ -200,11 +200,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
 | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/map_settings.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <string> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QObject> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
|  | @ -10,19 +11,31 @@ 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(); | ||||
| 
 | ||||
| settings::GeneralSettings& general_settings(); | ||||
| settings::MapSettings&     map_settings(); | ||||
| settings::PaletteSettings& palette_settings(); | ||||
|    void Initialize(); | ||||
|    void ReadSettings(const std::string& settingsPath); | ||||
|    void SaveSettings(); | ||||
|    void Shutdown(); | ||||
| 
 | ||||
|    static SettingsManager& Instance(); | ||||
| 
 | ||||
| signals: | ||||
|    void SettingsSaved(); | ||||
| 
 | ||||
| private: | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace SettingsManager
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| #include <scwx/qt/manager/timeline_manager.hpp> | ||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| #include <scwx/util/map.hpp> | ||||
| #include <scwx/util/time.hpp> | ||||
|  | @ -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()); | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| #include <scwx/qt/map/alert_layer.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/manager/text_event_manager.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <scwx/qt/util/color.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| #include <scwx/util/threads.hpp> | ||||
|  | @ -394,7 +394,7 @@ static void AddAlertLayer(std::shared_ptr<QMapLibreGL::Map> map, | |||
|                           const QString&                    beforeLayer) | ||||
| { | ||||
|    settings::PaletteSettings& paletteSettings = | ||||
|       manager::SettingsManager::palette_settings(); | ||||
|       settings::PaletteSettings::Instance(); | ||||
| 
 | ||||
|    QString sourceId     = GetSourceId(phenomenon, alertActive); | ||||
|    QString idSuffix     = GetSuffix(phenomenon, alertActive); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| #include <scwx/qt/map/map_provider.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| 
 | ||||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| #include <scwx/qt/map/map_widget.hpp> | ||||
| #include <scwx/qt/gl/gl.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/manager/placefile_manager.hpp> | ||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/map/alert_layer.hpp> | ||||
| #include <scwx/qt/map/color_table_layer.hpp> | ||||
| #include <scwx/qt/map/layer_wrapper.hpp> | ||||
|  | @ -12,6 +12,8 @@ | |||
| #include <scwx/qt/map/radar_product_layer.hpp> | ||||
| #include <scwx/qt/map/radar_range_layer.hpp> | ||||
| #include <scwx/qt/model/imgui_context_model.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <scwx/qt/util/file.hpp> | ||||
| #include <scwx/qt/util/maplibre.hpp> | ||||
| #include <scwx/qt/util/tooltip.hpp> | ||||
|  | @ -80,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()); | ||||
| 
 | ||||
|  | @ -123,6 +124,7 @@ public: | |||
|                  std::shared_ptr<GenericLayer> layer, | ||||
|                  const std::string&            before = {}); | ||||
|    void ConnectSignals(); | ||||
|    void ImGuiCheckFonts(); | ||||
|    void InitializeNewRadarProductView(const std::string& colorPalette); | ||||
|    void RadarProductManagerConnect(); | ||||
|    void RadarProductManagerDisconnect(); | ||||
|  | @ -154,6 +156,7 @@ public: | |||
|    ImGuiContext* imGuiContext_; | ||||
|    std::string   imGuiContextName_; | ||||
|    bool          imGuiRendererInitialized_; | ||||
|    std::uint64_t imGuiFontsBuildCount_ {}; | ||||
| 
 | ||||
|    std::shared_ptr<manager::PlacefileManager> placefileManager_ { | ||||
|       manager::PlacefileManager::Instance()}; | ||||
|  | @ -980,9 +983,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( | ||||
|  | @ -1023,16 +1032,27 @@ void MapWidget::initializeGL() | |||
| 
 | ||||
| void MapWidget::paintGL() | ||||
| { | ||||
|    auto defaultFont = manager::FontManager::Instance().GetImGuiFont( | ||||
|       types::FontCategory::Default); | ||||
| 
 | ||||
|    p->frameDraws_++; | ||||
| 
 | ||||
|    // 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(); | ||||
|    p->ImGuiCheckFonts(); | ||||
|    ImGui::NewFrame(); | ||||
| 
 | ||||
|    // Set default font
 | ||||
|    ImGui::PushFont(defaultFont->font()); | ||||
| 
 | ||||
|    // Update pixel ratio
 | ||||
|    p->context_->set_pixel_ratio(pixelRatio()); | ||||
| 
 | ||||
|  | @ -1055,14 +1075,36 @@ void MapWidget::paintGL() | |||
|       p->lastItemPicked_ = false; | ||||
|    } | ||||
| 
 | ||||
|    // Pop default font
 | ||||
|    ImGui::PopFont(); | ||||
| 
 | ||||
|    // Render ImGui Frame
 | ||||
|    ImGui::Render(); | ||||
|    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|    // Unlock ImGui font atlas after rendering
 | ||||
|    imguiFontAtlasLock.unlock(); | ||||
| 
 | ||||
|    // Paint complete
 | ||||
|    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 = | ||||
|  | @ -1203,7 +1245,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()) | ||||
|  |  | |||
|  | @ -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()) | ||||
|          { | ||||
|  |  | |||
|  | @ -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<bool>        debugEnabled_ {"debug_enabled"}; | ||||
|    SettingsVariable<std::string> defaultAlertAction_ {"default_alert_action"}; | ||||
|  | @ -120,7 +120,7 @@ public: | |||
| }; | ||||
| 
 | ||||
| GeneralSettings::GeneralSettings() : | ||||
|     SettingsCategory("general"), p(std::make_unique<GeneralSettingsImpl>()) | ||||
|     SettingsCategory("general"), p(std::make_unique<Impl>()) | ||||
| { | ||||
|    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_ && | ||||
|  |  | |||
|  | @ -13,8 +13,6 @@ namespace qt | |||
| namespace settings | ||||
| { | ||||
| 
 | ||||
| class GeneralSettingsImpl; | ||||
| 
 | ||||
| class GeneralSettings : public SettingsCategory | ||||
| { | ||||
| public: | ||||
|  | @ -41,13 +39,16 @@ public: | |||
|    SettingsVariable<std::string>&                maptiler_api_key() const; | ||||
|    SettingsVariable<bool>& update_notifications_enabled() const; | ||||
| 
 | ||||
|    static GeneralSettings& Instance(); | ||||
| 
 | ||||
|    friend bool operator==(const GeneralSettings& lhs, | ||||
|                           const GeneralSettings& rhs); | ||||
| 
 | ||||
|    bool Shutdown(); | ||||
| 
 | ||||
| private: | ||||
|    std::unique_ptr<GeneralSettingsImpl> p; | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace settings
 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ static const std::string kDefaultRadarProductGroupString_ = "L3"; | |||
| static const std::array<std::string, kCount_> kDefaultRadarProduct_ { | ||||
|    "N0B", "N0G", "N0C", "N0X"}; | ||||
| 
 | ||||
| class MapSettingsImpl | ||||
| class MapSettings::Impl | ||||
| { | ||||
| public: | ||||
|    struct MapData | ||||
|  | @ -47,7 +47,7 @@ public: | |||
|       SettingsVariable<std::string> 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<MapData, kCount_>       map_ {}; | ||||
|    std::vector<SettingsVariableBase*> variables_ {}; | ||||
| }; | ||||
| 
 | ||||
| MapSettings::MapSettings() : | ||||
|     SettingsCategory("maps"), p(std::make_unique<MapSettingsImpl>()) | ||||
|     SettingsCategory("maps"), p(std::make_unique<Impl>()) | ||||
| { | ||||
|    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
 | ||||
|  |  | |||
|  | @ -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<MapSettingsImpl> p; | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace settings
 | ||||
|  |  | |||
|  | @ -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<SettingsVariableBase*> 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<PaletteSettingsImpl>()) | ||||
|     SettingsCategory("palette"), p(std::make_unique<Impl>()) | ||||
| { | ||||
|    RegisterVariables(p->variables_); | ||||
|    SetDefaults(); | ||||
|  | @ -200,6 +200,12 @@ const std::vector<awips::Phenomenon>& 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_; | ||||
|  |  | |||
|  | @ -14,8 +14,6 @@ namespace qt | |||
| namespace settings | ||||
| { | ||||
| 
 | ||||
| class PaletteSettingsImpl; | ||||
| 
 | ||||
| class PaletteSettings : public SettingsCategory | ||||
| { | ||||
| public: | ||||
|  | @ -34,11 +32,14 @@ public: | |||
| 
 | ||||
|    static const std::vector<awips::Phenomenon>& alert_phenomena(); | ||||
| 
 | ||||
|    static PaletteSettings& Instance(); | ||||
| 
 | ||||
|    friend bool operator==(const PaletteSettings& lhs, | ||||
|                           const PaletteSettings& rhs); | ||||
| 
 | ||||
| private: | ||||
|    std::unique_ptr<PaletteSettingsImpl> p; | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace settings
 | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
| #include <scwx/qt/util/json.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
|  | @ -21,6 +23,8 @@ public: | |||
| 
 | ||||
|    const std::string name_; | ||||
| 
 | ||||
|    std::vector<std::pair<std::string, std::vector<SettingsCategory*>>> | ||||
|                                       subcategoryArrays_; | ||||
|    std::vector<SettingsVariableBase*> 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<SettingsCategory>& subcategories) | ||||
| { | ||||
|    auto& newSubcategories = p->subcategoryArrays_.emplace_back( | ||||
|       name, std::vector<SettingsCategory*> {}); | ||||
| 
 | ||||
|    std::transform(subcategories.begin(), | ||||
|                   subcategories.end(), | ||||
|                   std::back_inserter(newSubcategories.second), | ||||
|                   [](SettingsCategory& subcategory) { return &subcategory; }); | ||||
| } | ||||
| 
 | ||||
| void SettingsCategory::RegisterVariables( | ||||
|    std::initializer_list<SettingsVariableBase*> variables) | ||||
| { | ||||
|  |  | |||
|  | @ -50,7 +50,8 @@ public: | |||
|     */ | ||||
|    virtual void WriteJson(boost::json::object& json) const; | ||||
| 
 | ||||
| protected: | ||||
|    void RegisterSubcategoryArray(const std::string&             name, | ||||
|                                  std::vector<SettingsCategory>& subcategories); | ||||
|    void | ||||
|    RegisterVariables(std::initializer_list<SettingsVariableBase*> variables); | ||||
|    void RegisterVariables(std::vector<SettingsVariableBase*> variables); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
| #include <QCheckBox> | ||||
| #include <QComboBox> | ||||
| #include <QCoreApplication> | ||||
| #include <QLabel> | ||||
| #include <QLineEdit> | ||||
| #include <QSpinBox> | ||||
| #include <QWidget> | ||||
|  | @ -26,16 +27,21 @@ template<class T> | |||
| class SettingsInterface<T>::Impl | ||||
| { | ||||
| public: | ||||
|    explicit Impl() | ||||
|    explicit Impl(SettingsInterface* self) : self_ {self} | ||||
|    { | ||||
|       context_->moveToThread(QCoreApplication::instance()->thread()); | ||||
|    } | ||||
| 
 | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    template<class U> | ||||
|    void SetWidgetText(U* widget, const T& currentValue); | ||||
| 
 | ||||
|    void UpdateEditWidget(); | ||||
|    void UpdateResetButton(); | ||||
| 
 | ||||
|    SettingsInterface<T>* self_; | ||||
| 
 | ||||
|    SettingsVariable<T>* variable_ {nullptr}; | ||||
|    bool                 stagedValid_ {true}; | ||||
| 
 | ||||
|  | @ -49,17 +55,27 @@ public: | |||
| 
 | ||||
| template<class T> | ||||
| SettingsInterface<T>::SettingsInterface() : | ||||
|     SettingsInterfaceBase(), p(std::make_unique<Impl>()) | ||||
|     SettingsInterfaceBase(), p(std::make_unique<Impl>(this)) | ||||
| { | ||||
| } | ||||
| template<class T> | ||||
| SettingsInterface<T>::~SettingsInterface() = default; | ||||
| 
 | ||||
| template<class T> | ||||
| SettingsInterface<T>::SettingsInterface(SettingsInterface&&) noexcept = default; | ||||
| SettingsInterface<T>::SettingsInterface(SettingsInterface&& o) noexcept : | ||||
|     p {std::move(o.p)} | ||||
| { | ||||
|    p->self_ = this; | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| SettingsInterface<T>& | ||||
| SettingsInterface<T>::operator=(SettingsInterface&&) noexcept = default; | ||||
| SettingsInterface<T>::operator=(SettingsInterface&& o) noexcept | ||||
| { | ||||
|    p        = std::move(o.p); | ||||
|    p->self_ = this; | ||||
|    return *this; | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| void SettingsInterface<T>::SetSettingsVariable(SettingsVariable<T>& variable) | ||||
|  | @ -73,6 +89,27 @@ SettingsVariable<T>* SettingsInterface<T>::GetSettingsVariable() const | |||
|    return p->variable_; | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| bool SettingsInterface<T>::IsDefault() | ||||
| { | ||||
|    bool isDefault = false; | ||||
| 
 | ||||
|    const std::optional<T> 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<class T> | ||||
| bool SettingsInterface<T>::Commit() | ||||
| { | ||||
|  | @ -95,6 +132,14 @@ void SettingsInterface<T>::StageDefault() | |||
|    p->UpdateResetButton(); | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| void SettingsInterface<T>::StageValue(const T& value) | ||||
| { | ||||
|    p->variable_->StageValue(value); | ||||
|    p->UpdateEditWidget(); | ||||
|    p->UpdateResetButton(); | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| void SettingsInterface<T>::SetEditWidget(QWidget* widget) | ||||
| { | ||||
|  | @ -105,6 +150,11 @@ void SettingsInterface<T>::SetEditWidget(QWidget* widget) | |||
| 
 | ||||
|    p->editWidget_ = widget; | ||||
| 
 | ||||
|    if (widget == nullptr) | ||||
|    { | ||||
|       return; | ||||
|    } | ||||
| 
 | ||||
|    if (QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(widget)) | ||||
|    { | ||||
|       if constexpr (std::is_same_v<T, std::string>) | ||||
|  | @ -274,33 +324,36 @@ void SettingsInterface<T>::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<class T> | ||||
|  | @ -317,6 +370,39 @@ void SettingsInterface<T>::SetMapToValueFunction( | |||
|    p->mapToValue_ = function; | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| template<class U> | ||||
| void SettingsInterface<T>::Impl::SetWidgetText(U* widget, const T& currentValue) | ||||
| { | ||||
|    if constexpr (std::is_integral_v<T>) | ||||
|    { | ||||
|       widget->setText(QString::number(currentValue)); | ||||
|    } | ||||
|    else if constexpr (std::is_same_v<T, std::string>) | ||||
|    { | ||||
|       if (mapFromValue_ != nullptr) | ||||
|       { | ||||
|          widget->setText(QString::fromStdString(mapFromValue_(currentValue))); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|          widget->setText(QString::fromStdString(currentValue)); | ||||
|       } | ||||
|    } | ||||
|    else if constexpr (std::is_same_v<T, std::vector<std::int64_t>>) | ||||
|    { | ||||
|       if (mapFromValue_ != nullptr) | ||||
|       { | ||||
|          widget->setText(QString::fromStdString(mapFromValue_(currentValue))); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|          widget->setText(QString::fromStdString( | ||||
|             fmt::format("{}", fmt::join(currentValue, ", ")))); | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| void SettingsInterface<T>::Impl::UpdateEditWidget() | ||||
| { | ||||
|  | @ -327,35 +413,11 @@ void SettingsInterface<T>::Impl::UpdateEditWidget() | |||
| 
 | ||||
|    if (QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(editWidget_)) | ||||
|    { | ||||
|       if constexpr (std::is_integral_v<T>) | ||||
|       { | ||||
|          lineEdit->setText(QString::number(currentValue)); | ||||
|       } | ||||
|       else if constexpr (std::is_same_v<T, std::string>) | ||||
|       { | ||||
|          if (mapFromValue_ != nullptr) | ||||
|          { | ||||
|             lineEdit->setText( | ||||
|                QString::fromStdString(mapFromValue_(currentValue))); | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|             lineEdit->setText(QString::fromStdString(currentValue)); | ||||
|          } | ||||
|       } | ||||
|       else if constexpr (std::is_same_v<T, std::vector<std::int64_t>>) | ||||
|       { | ||||
|          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<QLabel*>(editWidget_)) | ||||
|    { | ||||
|       SetWidgetText(label, currentValue); | ||||
|    } | ||||
|    else if (QCheckBox* checkBox = dynamic_cast<QCheckBox*>(editWidget_)) | ||||
|    { | ||||
|  | @ -391,20 +453,9 @@ void SettingsInterface<T>::Impl::UpdateEditWidget() | |||
| template<class T> | ||||
| void SettingsInterface<T>::Impl::UpdateResetButton() | ||||
| { | ||||
|    const std::optional<T> 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()); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -45,6 +45,14 @@ public: | |||
|     */ | ||||
|    SettingsVariable<T>* 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. | ||||
|     * | ||||
|  | @ -103,6 +116,7 @@ private: | |||
| 
 | ||||
| #ifdef SETTINGS_INTERFACE_IMPLEMENTATION | ||||
| template class SettingsInterface<bool>; | ||||
| template class SettingsInterface<double>; | ||||
| template class SettingsInterface<std::int64_t>; | ||||
| template class SettingsInterface<std::string>; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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. | ||||
|     */ | ||||
|  |  | |||
|  | @ -239,6 +239,12 @@ std::optional<T> SettingsVariable<T>::GetStaged() const | |||
|    return p->staged_; | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| T SettingsVariable<T>::GetStagedOrValue() const | ||||
| { | ||||
|    return p->staged_.value_or(GetValue()); | ||||
| } | ||||
| 
 | ||||
| template<class T> | ||||
| T SettingsVariable<T>::GetDefault() const | ||||
| { | ||||
|  |  | |||
|  | @ -103,6 +103,14 @@ public: | |||
|     */ | ||||
|    std::optional<T> 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. | ||||
|  |  | |||
|  | @ -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<types::FontCategory, std::string> | ||||
|    kDefaultFontFamily_ { | ||||
|       {types::FontCategory::Default, kAlteDIN1451Mittelscrhift_}, | ||||
|       {types::FontCategory::Tooltip, kInconsolata_}}; | ||||
| static const std::unordered_map<types::FontCategory, std::string> | ||||
|    kDefaultFontStyle_ {{types::FontCategory::Default, kRegular_}, | ||||
|                        {types::FontCategory::Tooltip, kRegular_}}; | ||||
| static const std::unordered_map<types::FontCategory, double> | ||||
|    kDefaultFontPointSize_ {{types::FontCategory::Default, 12.0}, | ||||
|                            {types::FontCategory::Tooltip, 10.5}}; | ||||
| 
 | ||||
| class TextSettings::Impl | ||||
| { | ||||
| public: | ||||
|    explicit Impl() | ||||
|    struct FontData | ||||
|    { | ||||
|       SettingsVariable<std::string> fontFamily_ {"font_family"}; | ||||
|       SettingsVariable<std::string> fontStyle_ {"font_style"}; | ||||
|       SettingsVariable<double>      fontPointSize_ {"font_point_size"}; | ||||
|    }; | ||||
| 
 | ||||
|    explicit Impl(TextSettings* self) : self_ {self} | ||||
|    { | ||||
|       std::string defaultTooltipMethodValue = | ||||
|          types::GetTooltipMethodName(types::TooltipMethod::ImGui); | ||||
|  | @ -47,16 +71,32 @@ public: | |||
|             // No match found, invalid
 | ||||
|             return false; | ||||
|          }); | ||||
| 
 | ||||
|       InitializeFontVariables(); | ||||
|    } | ||||
| 
 | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    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<types::FontCategory, FontData> fontData_ {}; | ||||
|    std::vector<SettingsCategory>                     fontSettings_ {}; | ||||
| 
 | ||||
|    SettingsVariable<std::int64_t> hoverTextWrap_ {"hover_text_wrap"}; | ||||
|    SettingsVariable<std::string>  tooltipMethod_ {"tooltip_method"}; | ||||
| }; | ||||
| 
 | ||||
| TextSettings::TextSettings() : | ||||
|     SettingsCategory("text"), p(std::make_unique<Impl>()) | ||||
|     SettingsCategory("text"), p(std::make_unique<Impl>(this)) | ||||
| { | ||||
|    RegisterVariables({&p->hoverTextWrap_, &p->tooltipMethod_}); | ||||
|    SetDefaults(); | ||||
|  | @ -66,6 +106,57 @@ 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); | ||||
| 
 | ||||
|       // Variable registration
 | ||||
|       auto& settings = fontSettings_.emplace_back( | ||||
|          SettingsCategory {types::GetFontCategoryName(fontCategory)}); | ||||
| 
 | ||||
|       settings.RegisterVariables( | ||||
|          {&font.fontFamily_, &font.fontStyle_, &font.fontPointSize_}); | ||||
|    } | ||||
| 
 | ||||
|    self_->RegisterSubcategoryArray("fonts", fontSettings_); | ||||
| } | ||||
| 
 | ||||
| SettingsVariable<std::string>& | ||||
| TextSettings::font_family(types::FontCategory fontCategory) const | ||||
| { | ||||
|    return p->fontData_.at(fontCategory).fontFamily_; | ||||
| } | ||||
| 
 | ||||
| SettingsVariable<std::string>& | ||||
| TextSettings::font_style(types::FontCategory fontCategory) const | ||||
| { | ||||
|    return p->fontData_.at(fontCategory).fontStyle_; | ||||
| } | ||||
| 
 | ||||
| SettingsVariable<double>& | ||||
| TextSettings::font_point_size(types::FontCategory fontCategory) const | ||||
| { | ||||
|    return p->fontData_.at(fontCategory).fontPointSize_; | ||||
| } | ||||
| 
 | ||||
| SettingsVariable<std::int64_t>& TextSettings::hover_text_wrap() const | ||||
| { | ||||
|    return p->hoverTextWrap_; | ||||
|  | @ -78,13 +169,14 @@ SettingsVariable<std::string>& 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) | ||||
| { | ||||
|    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_); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| #include <scwx/qt/settings/settings_category.hpp> | ||||
| #include <scwx/qt/settings/settings_variable.hpp> | ||||
| #include <scwx/qt/types/text_types.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
|  | @ -25,8 +26,15 @@ public: | |||
|    TextSettings(TextSettings&&) noexcept; | ||||
|    TextSettings& operator=(TextSettings&&) noexcept; | ||||
| 
 | ||||
|    SettingsVariable<std::string>& | ||||
|    font_family(types::FontCategory fontCategory) const; | ||||
|    SettingsVariable<std::string>& | ||||
|    font_style(types::FontCategory fontCategory) const; | ||||
|    SettingsVariable<double>& | ||||
|    font_point_size(types::FontCategory fontCategory) const; | ||||
| 
 | ||||
|    SettingsVariable<std::int64_t>& hover_text_wrap() const; | ||||
|    SettingsVariable<std::string>& tooltip_method() const; | ||||
|    SettingsVariable<std::string>&  tooltip_method() const; | ||||
| 
 | ||||
|    static TextSettings& Instance(); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										83
									
								
								scwx-qt/source/scwx/qt/types/imgui_font.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								scwx-qt/source/scwx/qt/types/imgui_font.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| // Disable strncpy warning
 | ||||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| 
 | ||||
| #include <scwx/qt/types/imgui_font.hpp> | ||||
| #include <scwx/qt/model/imgui_context_model.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
| 
 | ||||
| #include <imgui.h> | ||||
| 
 | ||||
| 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::vector<char>&      fontData, | ||||
|                  units::font_size::pixels<int> size) : | ||||
|        fontName_ {fontName}, size_ {size} | ||||
|    { | ||||
|       CreateImGuiFont(fontData); | ||||
|    } | ||||
| 
 | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    void CreateImGuiFont(const std::vector<char>& fontData); | ||||
| 
 | ||||
|    const std::string                   fontName_; | ||||
|    const units::font_size::pixels<int> size_; | ||||
| 
 | ||||
|    ImFont* imFont_ {nullptr}; | ||||
| }; | ||||
| 
 | ||||
| ImGuiFont::ImGuiFont(const std::string&            fontName, | ||||
|                      const std::vector<char>&      fontData, | ||||
|                      units::font_size::pixels<int> size) : | ||||
|     p(std::make_unique<Impl>(fontName, fontData, size)) | ||||
| { | ||||
| } | ||||
| ImGuiFont::~ImGuiFont() = default; | ||||
| 
 | ||||
| void ImGuiFont::Impl::CreateImGuiFont(const std::vector<char>& fontData) | ||||
| { | ||||
|    logger_->debug("Creating Font: {}", fontName_); | ||||
| 
 | ||||
|    ImFontAtlas* fontAtlas = model::ImGuiContextModel::Instance().font_atlas(); | ||||
|    ImFontConfig fontConfig {}; | ||||
| 
 | ||||
|    const float sizePixels = static_cast<float>(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<void*>(static_cast<const void*>(fontData.data())), | ||||
|       static_cast<int>(std::clamp<std::size_t>( | ||||
|          fontData.size(), 0, std::numeric_limits<int>::max())), | ||||
|       sizePixels, | ||||
|       &fontConfig); | ||||
| } | ||||
| 
 | ||||
| ImFont* ImGuiFont::font() | ||||
| { | ||||
|    return p->imFont_; | ||||
| } | ||||
| 
 | ||||
| } // namespace types
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										41
									
								
								scwx-qt/source/scwx/qt/types/imgui_font.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								scwx-qt/source/scwx/qt/types/imgui_font.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <scwx/qt/types/font_types.hpp> | ||||
| 
 | ||||
| struct ImFont; | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace types | ||||
| { | ||||
| 
 | ||||
| class ImGuiFont | ||||
| { | ||||
| public: | ||||
|    explicit ImGuiFont(const std::string&            fontName, | ||||
|                       const std::vector<char>&      fontData, | ||||
|                       units::font_size::pixels<int> 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<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace types
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
|  | @ -11,12 +11,40 @@ namespace qt | |||
| namespace types | ||||
| { | ||||
| 
 | ||||
| static const std::unordered_map<FontCategory, std::string> fontCategoryName_ { | ||||
|    {FontCategory::Default, "Default"}, | ||||
|    {FontCategory::Tooltip, "Tooltip"}, | ||||
|    {FontCategory::Unknown, "?"}}; | ||||
| 
 | ||||
| static const std::unordered_map<TooltipMethod, std::string> 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<FontCategory, std::string>& 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( | ||||
|  |  | |||
|  | @ -11,6 +11,16 @@ namespace qt | |||
| namespace types | ||||
| { | ||||
| 
 | ||||
| enum class FontCategory | ||||
| { | ||||
|    Default, | ||||
|    Tooltip, | ||||
|    Unknown | ||||
| }; | ||||
| typedef scwx::util:: | ||||
|    Iterator<FontCategory, FontCategory::Default, FontCategory::Tooltip> | ||||
|       FontCategoryIterator; | ||||
| 
 | ||||
| enum class TooltipMethod | ||||
| { | ||||
|    ImGui, | ||||
|  | @ -22,6 +32,8 @@ typedef scwx::util:: | |||
|    Iterator<TooltipMethod, TooltipMethod::ImGui, TooltipMethod::QLabel> | ||||
|       TooltipMethodIterator; | ||||
| 
 | ||||
| FontCategory  GetFontCategory(const std::string& name); | ||||
| std::string   GetFontCategoryName(FontCategory fontCategory); | ||||
| TooltipMethod GetTooltipMethod(const std::string& name); | ||||
| std::string   GetTooltipMethodName(TooltipMethod tooltipMethod); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #include "about_dialog.hpp" | ||||
| #include "ui_about_dialog.h" | ||||
| #include <scwx/qt/main/versions.hpp> | ||||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| 
 | ||||
| #include <QFontDatabase> | ||||
| 
 | ||||
|  | @ -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); | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| #include "alert_dock_widget.hpp" | ||||
| #include "ui_alert_dock_widget.h" | ||||
| 
 | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/manager/text_event_manager.hpp> | ||||
| #include <scwx/qt/model/alert_model.hpp> | ||||
| #include <scwx/qt/model/alert_proxy_model.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/types/alert_types.hpp> | ||||
| #include <scwx/qt/types/qt_types.hpp> | ||||
| #include <scwx/qt/ui/alert_dialog.hpp> | ||||
|  | @ -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) | ||||
|                  { | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #include "animation_dock_widget.hpp" | ||||
| #include "ui_animation_dock_widget.h" | ||||
| 
 | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/util/time.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
|  | @ -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<std::int64_t>(d * 1000.0)); | ||||
|          Q_EMIT self_->LoopDelayChanged(std::chrono::milliseconds( | ||||
|             static_cast<typename std::chrono::milliseconds::rep>(d * 1000.0))); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include <scwx/qt/ui/imgui_debug_widget.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/model/imgui_context_model.hpp> | ||||
| 
 | ||||
| #include <set> | ||||
|  | @ -50,6 +51,8 @@ public: | |||
|       model::ImGuiContextModel::Instance().DestroyContext(contextName_); | ||||
|    } | ||||
| 
 | ||||
|    void ImGuiCheckFonts(); | ||||
| 
 | ||||
|    ImGuiDebugWidget* self_; | ||||
|    ImGuiContext*     context_; | ||||
|    std::string       contextName_; | ||||
|  | @ -58,6 +61,7 @@ public: | |||
| 
 | ||||
|    std::set<ImGuiContext*> renderedSet_ {}; | ||||
|    bool                    imGuiRendererInitialized_ {false}; | ||||
|    std::uint64_t           imGuiFontsBuildCount_ {}; | ||||
| }; | ||||
| 
 | ||||
| ImGuiDebugWidget::ImGuiDebugWidget(QWidget* parent) : | ||||
|  | @ -102,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; | ||||
| } | ||||
| 
 | ||||
|  | @ -109,9 +115,13 @@ 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(); | ||||
| 
 | ||||
|    p->ImGuiCheckFonts(); | ||||
|    ImGui::NewFrame(); | ||||
| 
 | ||||
|    if (!p->renderedSet_.contains(p->currentContext_)) | ||||
|  | @ -131,6 +141,29 @@ void ImGuiDebugWidget::paintGL() | |||
| 
 | ||||
|    ImGui::Render(); | ||||
|    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|    // Unlock ImGui font atlas after rendering
 | ||||
|    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
 | ||||
|  |  | |||
|  | @ -6,9 +6,12 @@ | |||
| #include <scwx/qt/config/radar_site.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/map/map_provider.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <scwx/qt/settings/settings_interface.hpp> | ||||
| #include <scwx/qt/settings/text_settings.hpp> | ||||
| #include <scwx/qt/types/alert_types.hpp> | ||||
| #include <scwx/qt/types/font_types.hpp> | ||||
| #include <scwx/qt/types/text_types.hpp> | ||||
| #include <scwx/qt/ui/placefile_settings_widget.hpp> | ||||
| #include <scwx/qt/ui/radar_site_dialog.hpp> | ||||
|  | @ -21,6 +24,8 @@ | |||
| #include <fmt/format.h> | ||||
| #include <QColorDialog> | ||||
| #include <QFileDialog> | ||||
| #include <QFontDialog> | ||||
| #include <QStandardItemModel> | ||||
| #include <QToolButton> | ||||
| 
 | ||||
| namespace scwx | ||||
|  | @ -84,9 +89,10 @@ public: | |||
|    explicit SettingsDialogImpl(SettingsDialog* self) : | ||||
|        self_ {self}, | ||||
|        radarSiteDialog_ {new RadarSiteDialog(self)}, | ||||
|        fontDialog_ {new QFontDialog(self)}, | ||||
|        fontCategoryModel_ {new QStandardItemModel(self)}, | ||||
|        settings_ {std::initializer_list<settings::SettingsInterfaceBase*> { | ||||
|           &defaultRadarSite_, | ||||
|           &fontSizes_, | ||||
|           &gridWidth_, | ||||
|           &gridHeight_, | ||||
|           &mapProvider_, | ||||
|  | @ -99,7 +105,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()) | ||||
|  | @ -113,6 +119,12 @@ public: | |||
|             QColor(QString::fromStdString( | ||||
|                paletteSettings.alert_color(phenomenon, false).GetDefault()))); | ||||
|       } | ||||
| 
 | ||||
|       // Configure font dialog
 | ||||
|       fontDialog_->setOptions( | ||||
|          QFontDialog::FontDialogOption::DontUseNativeDialog | | ||||
|          QFontDialog::FontDialogOption::ScalableFonts); | ||||
|       fontDialog_->setWindowModality(Qt::WindowModality::WindowModal); | ||||
|    } | ||||
|    ~SettingsDialogImpl() = default; | ||||
| 
 | ||||
|  | @ -126,6 +138,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(); | ||||
|  | @ -146,17 +162,21 @@ public: | |||
|    SettingsDialog*          self_; | ||||
|    PlacefileSettingsWidget* placefileSettingsWidget_ {nullptr}; | ||||
|    RadarSiteDialog*         radarSiteDialog_; | ||||
|    QFontDialog*             fontDialog_; | ||||
| 
 | ||||
|    settings::SettingsInterface<std::string>               defaultRadarSite_ {}; | ||||
|    settings::SettingsInterface<std::vector<std::int64_t>> fontSizes_ {}; | ||||
|    settings::SettingsInterface<std::int64_t>              gridWidth_ {}; | ||||
|    settings::SettingsInterface<std::int64_t>              gridHeight_ {}; | ||||
|    settings::SettingsInterface<std::string>               mapProvider_ {}; | ||||
|    settings::SettingsInterface<std::string>               mapboxApiKey_ {}; | ||||
|    settings::SettingsInterface<std::string>               mapTilerApiKey_ {}; | ||||
|    settings::SettingsInterface<std::string> defaultAlertAction_ {}; | ||||
|    settings::SettingsInterface<bool>        updateNotificationsEnabled_ {}; | ||||
|    settings::SettingsInterface<bool>        debugEnabled_ {}; | ||||
|    QStandardItemModel* fontCategoryModel_; | ||||
| 
 | ||||
|    types::FontCategory selectedFontCategory_ {types::FontCategory::Unknown}; | ||||
| 
 | ||||
|    settings::SettingsInterface<std::string>  defaultRadarSite_ {}; | ||||
|    settings::SettingsInterface<std::int64_t> gridWidth_ {}; | ||||
|    settings::SettingsInterface<std::int64_t> gridHeight_ {}; | ||||
|    settings::SettingsInterface<std::string>  mapProvider_ {}; | ||||
|    settings::SettingsInterface<std::string>  mapboxApiKey_ {}; | ||||
|    settings::SettingsInterface<std::string>  mapTilerApiKey_ {}; | ||||
|    settings::SettingsInterface<std::string>  defaultAlertAction_ {}; | ||||
|    settings::SettingsInterface<bool>         updateNotificationsEnabled_ {}; | ||||
|    settings::SettingsInterface<bool>         debugEnabled_ {}; | ||||
| 
 | ||||
|    std::unordered_map<std::string, settings::SettingsInterface<std::string>> | ||||
|       colorTables_ {}; | ||||
|  | @ -167,6 +187,15 @@ public: | |||
|                       settings::SettingsInterface<std::string>> | ||||
|       inactiveAlertColors_ {}; | ||||
| 
 | ||||
|    std::unordered_map<types::FontCategory, | ||||
|                       settings::SettingsInterface<std::string>> | ||||
|       fontFamilies_ {}; | ||||
|    std::unordered_map<types::FontCategory, | ||||
|                       settings::SettingsInterface<std::string>> | ||||
|       fontStyles_ {}; | ||||
|    std::unordered_map<types::FontCategory, settings::SettingsInterface<double>> | ||||
|       fontPointSizes_ {}; | ||||
| 
 | ||||
|    settings::SettingsInterface<std::int64_t> hoverTextWrap_ {}; | ||||
|    settings::SettingsInterface<std::string>  tooltipMethod_ {}; | ||||
| 
 | ||||
|  | @ -239,6 +268,72 @@ 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) | ||||
|                     { | ||||
|                        fontFamilies_.at(selectedFontCategory_) | ||||
|                           .StageValue(font.family().toStdString()); | ||||
|                        fontStyles_.at(selectedFontCategory_) | ||||
|                           .StageValue(font.styleName().toStdString()); | ||||
|                        fontPointSizes_.at(selectedFontCategory_) | ||||
|                           .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(); | ||||
|                     }); | ||||
| 
 | ||||
|    QObject::connect( | ||||
|       self_->ui->buttonBox, | ||||
|       &QDialogButtonBox::clicked, | ||||
|  | @ -289,7 +384,7 @@ void SettingsDialogImpl::SetupGeneralTab() | |||
|    } | ||||
| 
 | ||||
|    settings::GeneralSettings& generalSettings = | ||||
|       manager::SettingsManager::general_settings(); | ||||
|       settings::GeneralSettings::Instance(); | ||||
| 
 | ||||
|    defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site()); | ||||
|    defaultRadarSite_.SetMapFromValueFunction( | ||||
|  | @ -327,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); | ||||
|  | @ -430,7 +521,7 @@ void SettingsDialogImpl::SetupGeneralTab() | |||
| void SettingsDialogImpl::SetupPalettesColorTablesTab() | ||||
| { | ||||
|    settings::PaletteSettings& paletteSettings = | ||||
|       manager::SettingsManager::palette_settings(); | ||||
|       settings::PaletteSettings::Instance(); | ||||
| 
 | ||||
|    // Palettes > Color Tables
 | ||||
|    QGridLayout* colorTableLayout = | ||||
|  | @ -522,7 +613,7 @@ void SettingsDialogImpl::SetupPalettesColorTablesTab() | |||
| void SettingsDialogImpl::SetupPalettesAlertsTab() | ||||
| { | ||||
|    settings::PaletteSettings& paletteSettings = | ||||
|       manager::SettingsManager::palette_settings(); | ||||
|       settings::PaletteSettings::Instance(); | ||||
| 
 | ||||
|    // Palettes > Alerts
 | ||||
|    QGridLayout* alertsLayout = | ||||
|  | @ -645,6 +736,39 @@ 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<std::string> {}); | ||||
|       auto fontStyleResult = fontStyles_.emplace( | ||||
|          fontCategory, settings::SettingsInterface<std::string> {}); | ||||
|       auto fontSizeResult = fontPointSizes_.emplace( | ||||
|          fontCategory, settings::SettingsInterface<double> {}); | ||||
| 
 | ||||
|       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)); | ||||
|    SelectFontCategory(*types::FontCategoryIterator().begin()); | ||||
|    UpdateFontDisplayData(); | ||||
| 
 | ||||
|    hoverTextWrap_.SetSettingsVariable(textSettings.hover_text_wrap()); | ||||
|    hoverTextWrap_.SetEditWidget(self_->ui->hoverTextWrapSpinBox); | ||||
|    hoverTextWrap_.SetResetButton(self_->ui->resetHoverTextWrapButton); | ||||
|  | @ -799,6 +923,53 @@ 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<double> 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) | ||||
| { | ||||
|    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); | ||||
| 
 | ||||
|    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() | ||||
| { | ||||
|    logger_->info("Applying settings changes"); | ||||
|  | @ -812,7 +983,7 @@ void SettingsDialogImpl::ApplyChanges() | |||
| 
 | ||||
|    if (committed) | ||||
|    { | ||||
|       manager::SettingsManager::SaveSettings(); | ||||
|       manager::SettingsManager::Instance().SaveSettings(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -127,27 +127,7 @@ | |||
|              <property name="bottomMargin"> | ||||
|               <number>0</number> | ||||
|              </property> | ||||
|              <item row="5" column="2"> | ||||
|               <widget class="QComboBox" name="mapProviderComboBox"/> | ||||
|              </item> | ||||
|              <item row="1" column="0"> | ||||
|               <widget class="QLabel" name="label_5"> | ||||
|                <property name="text"> | ||||
|                 <string>Font Sizes</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="0" column="2"> | ||||
|               <widget class="QComboBox" name="radarSiteComboBox"/> | ||||
|              </item> | ||||
|              <item row="0" column="0"> | ||||
|               <widget class="QLabel" name="label"> | ||||
|                <property name="text"> | ||||
|                 <string>Default Radar Site</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="5" column="4"> | ||||
|              <item row="4" column="4"> | ||||
|               <widget class="QToolButton" name="resetMapProviderButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|  | @ -158,45 +138,14 @@ | |||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="1" column="2"> | ||||
|               <widget class="QLineEdit" name="fontSizesLineEdit"/> | ||||
|              </item> | ||||
|              <item row="6" column="0"> | ||||
|               <widget class="QLabel" name="label_4"> | ||||
|              <item row="1" column="0"> | ||||
|               <widget class="QLabel" name="label_2"> | ||||
|                <property name="text"> | ||||
|                 <string>Mapbox API Key</string> | ||||
|                 <string>Grid Width</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="7" column="0"> | ||||
|               <widget class="QLabel" name="label_6"> | ||||
|                <property name="text"> | ||||
|                 <string>MapTiler API Key</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="2" column="2"> | ||||
|               <widget class="QSpinBox" name="gridWidthSpinBox"/> | ||||
|              </item> | ||||
|              <item row="7" column="4"> | ||||
|               <widget class="QToolButton" name="resetMapTilerApiKeyButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|                <property name="icon"> | ||||
|                 <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                  <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="0" column="3"> | ||||
|               <widget class="QToolButton" name="radarSiteSelectButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="3" column="4"> | ||||
|              <item row="2" column="4"> | ||||
|               <widget class="QToolButton" name="resetGridHeightButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|  | @ -207,16 +156,6 @@ | |||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="7" column="2"> | ||||
|               <widget class="QLineEdit" name="mapTilerApiKeyLineEdit"> | ||||
|                <property name="echoMode"> | ||||
|                 <enum>QLineEdit::Password</enum> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="3" column="2"> | ||||
|               <widget class="QSpinBox" name="gridHeightSpinBox"/> | ||||
|              </item> | ||||
|              <item row="0" column="4"> | ||||
|               <widget class="QToolButton" name="resetRadarSiteButton"> | ||||
|                <property name="text"> | ||||
|  | @ -228,32 +167,14 @@ | |||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="3" column="0"> | ||||
|               <widget class="QLabel" name="label_3"> | ||||
|              <item row="6" column="0"> | ||||
|               <widget class="QLabel" name="label_6"> | ||||
|                <property name="text"> | ||||
|                 <string>Grid Height</string> | ||||
|                 <string>MapTiler API Key</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="1" column="4"> | ||||
|               <widget class="QToolButton" name="resetFontSizesButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|                <property name="icon"> | ||||
|                 <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                  <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="6" column="2"> | ||||
|               <widget class="QLineEdit" name="mapboxApiKeyLineEdit"> | ||||
|                <property name="echoMode"> | ||||
|                 <enum>QLineEdit::Password</enum> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="6" column="4"> | ||||
|              <item row="5" column="4"> | ||||
|               <widget class="QToolButton" name="resetMapboxApiKeyButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|  | @ -264,21 +185,97 @@ | |||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="2" column="2"> | ||||
|               <widget class="QSpinBox" name="gridHeightSpinBox"/> | ||||
|              </item> | ||||
|              <item row="1" column="2"> | ||||
|               <widget class="QSpinBox" name="gridWidthSpinBox"/> | ||||
|              </item> | ||||
|              <item row="2" column="0"> | ||||
|               <widget class="QLabel" name="label_2"> | ||||
|               <widget class="QLabel" name="label_3"> | ||||
|                <property name="text"> | ||||
|                 <string>Grid Width</string> | ||||
|                 <string>Grid Height</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="0" column="0"> | ||||
|               <widget class="QLabel" name="label"> | ||||
|                <property name="text"> | ||||
|                 <string>Default Radar Site</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="6" column="2"> | ||||
|               <widget class="QLineEdit" name="mapTilerApiKeyLineEdit"> | ||||
|                <property name="echoMode"> | ||||
|                 <enum>QLineEdit::Password</enum> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="5" column="2"> | ||||
|               <widget class="QLineEdit" name="mapboxApiKeyLineEdit"> | ||||
|                <property name="echoMode"> | ||||
|                 <enum>QLineEdit::Password</enum> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="7" column="0"> | ||||
|               <widget class="QLabel" name="defaultAlertActionLabel"> | ||||
|                <property name="text"> | ||||
|                 <string>Default Alert Action</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="5" column="0"> | ||||
|               <widget class="QLabel" name="label_4"> | ||||
|                <property name="text"> | ||||
|                 <string>Mapbox API Key</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="7" column="4"> | ||||
|               <widget class="QToolButton" name="resetDefaultAlertActionButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|                <property name="icon"> | ||||
|                 <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                  <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="4" column="2"> | ||||
|               <widget class="QComboBox" name="mapProviderComboBox"/> | ||||
|              </item> | ||||
|              <item row="0" column="3"> | ||||
|               <widget class="QToolButton" name="radarSiteSelectButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="4" column="0"> | ||||
|               <widget class="QLabel" name="label_7"> | ||||
|                <property name="text"> | ||||
|                 <string>Map Provider</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="2" column="4"> | ||||
|              <item row="6" column="4"> | ||||
|               <widget class="QToolButton" name="resetMapTilerApiKeyButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|                <property name="icon"> | ||||
|                 <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                  <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="7" column="2"> | ||||
|               <widget class="QComboBox" name="defaultAlertActionComboBox"/> | ||||
|              </item> | ||||
|              <item row="1" column="4"> | ||||
|               <widget class="QToolButton" name="resetGridWidthButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|  | @ -289,26 +286,8 @@ | |||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="8" column="0"> | ||||
|               <widget class="QLabel" name="defaultAlertActionLabel"> | ||||
|                <property name="text"> | ||||
|                 <string>Default Alert Action</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="8" column="2"> | ||||
|               <widget class="QComboBox" name="defaultAlertActionComboBox"/> | ||||
|              </item> | ||||
|              <item row="8" column="4"> | ||||
|               <widget class="QToolButton" name="resetDefaultAlertActionButton"> | ||||
|                <property name="text"> | ||||
|                 <string>...</string> | ||||
|                </property> | ||||
|                <property name="icon"> | ||||
|                 <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                  <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                </property> | ||||
|               </widget> | ||||
|              <item row="0" column="2"> | ||||
|               <widget class="QComboBox" name="radarSiteComboBox"/> | ||||
|              </item> | ||||
|             </layout> | ||||
|            </widget> | ||||
|  | @ -438,6 +417,194 @@ | |||
|         </widget> | ||||
|         <widget class="QWidget" name="text"> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|           <item> | ||||
|            <widget class="QFrame" name="frame_4"> | ||||
|             <property name="frameShape"> | ||||
|              <enum>QFrame::StyledPanel</enum> | ||||
|             </property> | ||||
|             <property name="frameShadow"> | ||||
|              <enum>QFrame::Plain</enum> | ||||
|             </property> | ||||
|             <layout class="QGridLayout" name="gridLayout_8" columnstretch="2,3"> | ||||
|              <item row="0" column="0"> | ||||
|               <widget class="QFrame" name="frame_5"> | ||||
|                <property name="frameShape"> | ||||
|                 <enum>QFrame::StyledPanel</enum> | ||||
|                </property> | ||||
|                <property name="frameShadow"> | ||||
|                 <enum>QFrame::Raised</enum> | ||||
|                </property> | ||||
|                <layout class="QGridLayout" name="gridLayout_9"> | ||||
|                 <property name="leftMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="topMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="rightMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="bottomMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <item row="0" column="0" colspan="2"> | ||||
|                  <widget class="QLabel" name="label_10"> | ||||
|                   <property name="text"> | ||||
|                    <string>Display Item:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="1" column="0" colspan="2"> | ||||
|                  <widget class="QListView" name="fontListView"/> | ||||
|                 </item> | ||||
|                </layout> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item row="0" column="1"> | ||||
|               <widget class="QFrame" name="frame_6"> | ||||
|                <property name="frameShape"> | ||||
|                 <enum>QFrame::StyledPanel</enum> | ||||
|                </property> | ||||
|                <property name="frameShadow"> | ||||
|                 <enum>QFrame::Raised</enum> | ||||
|                </property> | ||||
|                <layout class="QGridLayout" name="gridLayout_6"> | ||||
|                 <property name="leftMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="topMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="rightMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <property name="bottomMargin"> | ||||
|                  <number>0</number> | ||||
|                 </property> | ||||
|                 <item row="8" column="0"> | ||||
|                  <spacer name="verticalSpacer_5"> | ||||
|                   <property name="orientation"> | ||||
|                    <enum>Qt::Vertical</enum> | ||||
|                   </property> | ||||
|                   <property name="sizeHint" stdset="0"> | ||||
|                    <size> | ||||
|                     <width>20</width> | ||||
|                     <height>40</height> | ||||
|                    </size> | ||||
|                   </property> | ||||
|                  </spacer> | ||||
|                 </item> | ||||
|                 <item row="7" column="0" colspan="5"> | ||||
|                  <widget class="QFrame" name="frame_7"> | ||||
|                   <property name="frameShape"> | ||||
|                    <enum>QFrame::Panel</enum> | ||||
|                   </property> | ||||
|                   <property name="frameShadow"> | ||||
|                    <enum>QFrame::Plain</enum> | ||||
|                   </property> | ||||
|                   <layout class="QVBoxLayout" name="verticalLayout_7"> | ||||
|                    <item> | ||||
|                     <widget class="QLabel" name="fontPreviewLabel"> | ||||
|                      <property name="text"> | ||||
|                       <string>Tornado Warning expires in 15 minutes</string> | ||||
|                      </property> | ||||
|                      <property name="alignment"> | ||||
|                       <set>Qt::AlignCenter</set> | ||||
|                      </property> | ||||
|                      <property name="wordWrap"> | ||||
|                       <bool>true</bool> | ||||
|                      </property> | ||||
|                     </widget> | ||||
|                    </item> | ||||
|                   </layout> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="3"> | ||||
|                  <widget class="QToolButton" name="fontSelectButton"> | ||||
|                   <property name="text"> | ||||
|                    <string>...</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="3" column="2"> | ||||
|                  <widget class="QLabel" name="fontStyleLabel"> | ||||
|                   <property name="text"> | ||||
|                    <string>[Style]</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="2"> | ||||
|                  <widget class="QLabel" name="fontNameLabel"> | ||||
|                   <property name="text"> | ||||
|                    <string>[Font Name]</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="5" column="2"> | ||||
|                  <widget class="QLabel" name="fontSizeLabel"> | ||||
|                   <property name="text"> | ||||
|                    <string>[Size]</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="0" column="0"> | ||||
|                  <widget class="QLabel" name="label_11"> | ||||
|                   <property name="text"> | ||||
|                    <string>Font:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="3" column="0"> | ||||
|                  <widget class="QLabel" name="label_15"> | ||||
|                   <property name="text"> | ||||
|                    <string>Style:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="5" column="0"> | ||||
|                  <widget class="QLabel" name="label_18"> | ||||
|                   <property name="text"> | ||||
|                    <string>Size:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="6" column="0" colspan="2"> | ||||
|                  <widget class="QLabel" name="label_13"> | ||||
|                   <property name="text"> | ||||
|                    <string>Preview:</string> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                 <item row="6" column="2"> | ||||
|                  <spacer name="horizontalSpacer"> | ||||
|                   <property name="orientation"> | ||||
|                    <enum>Qt::Horizontal</enum> | ||||
|                   </property> | ||||
|                   <property name="sizeHint" stdset="0"> | ||||
|                    <size> | ||||
|                     <width>40</width> | ||||
|                     <height>20</height> | ||||
|                    </size> | ||||
|                   </property> | ||||
|                  </spacer> | ||||
|                 </item> | ||||
|                 <item row="0" column="4"> | ||||
|                  <widget class="QToolButton" name="resetFontButton"> | ||||
|                   <property name="text"> | ||||
|                    <string>...</string> | ||||
|                   </property> | ||||
|                   <property name="icon"> | ||||
|                    <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                     <normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</normaloff>:/res/icons/font-awesome-6/rotate-left-solid.svg</iconset> | ||||
|                   </property> | ||||
|                  </widget> | ||||
|                 </item> | ||||
|                </layout> | ||||
|               </widget> | ||||
|              </item> | ||||
|             </layout> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QFrame" name="frame_3"> | ||||
|             <property name="frameShape"> | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #include "update_dialog.hpp" | ||||
| #include "ui_update_dialog.h" | ||||
| #include <scwx/qt/main/versions.hpp> | ||||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| 
 | ||||
| #include <QDesktopServices> | ||||
| #include <QFontDatabase> | ||||
|  | @ -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); | ||||
|  |  | |||
|  | @ -5,8 +5,8 @@ | |||
| #define _CRT_SECURE_NO_WARNINGS | ||||
| 
 | ||||
| #include <scwx/qt/util/font.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/model/imgui_context_model.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <codecvt> | ||||
|  | @ -126,9 +126,6 @@ public: | |||
|       } | ||||
|    } | ||||
| 
 | ||||
|    void CreateImGuiFont(QFile&                      fontFile, | ||||
|                         QByteArray&                 fontData, | ||||
|                         const std::vector<int64_t>& 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<int64_t>& 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<float>(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<void*>(static_cast<const void*>(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> Font::Create(const std::string& resource) | |||
|    font                = std::make_shared<Font>(resource); | ||||
|    QByteArray fontData = fontFile.readAll(); | ||||
| 
 | ||||
|    font->p->CreateImGuiFont( | ||||
|       fontFile, | ||||
|       fontData, | ||||
|       manager::SettingsManager::general_settings().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()); | ||||
|  |  | |||
|  | @ -1,10 +1,7 @@ | |||
| #include <scwx/qt/util/imgui.hpp> | ||||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <imgui.h> | ||||
| 
 | ||||
| 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<Impl>()) {} | ||||
|  | @ -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(); | ||||
|    manager::SettingsManager::general_settings() | ||||
|       .font_sizes() | ||||
|       .RegisterValueChangedCallback([this](const std::vector<std::int64_t>&) | ||||
|                                     { UpdateMonospaceFont(); }); | ||||
| 
 | ||||
|    initialized_ = true; | ||||
| } | ||||
| 
 | ||||
| void ImGui::Impl::UpdateMonospaceFont() | ||||
| { | ||||
|    // Get monospace font size
 | ||||
|    std::size_t fontSize = 16; | ||||
|    auto        fontSizes = | ||||
|       manager::SettingsManager::general_settings().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(); | ||||
|  |  | |||
|  | @ -1,8 +1,6 @@ | |||
| #include <scwx/qt/util/tooltip.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/manager/font_manager.hpp> | ||||
| #include <scwx/qt/settings/text_settings.hpp> | ||||
| #include <scwx/qt/types/font_types.hpp> | ||||
| #include <scwx/qt/types/text_types.hpp> | ||||
| #include <scwx/qt/util/imgui.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
|  | @ -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("<span id='%1' style='font-family:\"%2\"'>%3</span>") | ||||
|          QString("<span id='%1' style='font-family:\"%2\"; font-style:\"%3\"; " | ||||
|                  "font-size:\"%4pt\";'>%5</span>") | ||||
|             .arg(++id) | ||||
|             .arg("Inconsolata") | ||||
|             .arg(fontFamily) | ||||
|             .arg(fontStyle) | ||||
|             .arg(fontPointSize) | ||||
|             .arg(QString::fromStdString(displayText).replace("\n", "<br/>")), | ||||
|          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<double> fontSize {16}; | ||||
|       auto                             fontSizes = | ||||
|          manager::SettingsManager::general_settings().font_sizes().GetValue(); | ||||
|       if (fontSizes.size() > 1) | ||||
|       { | ||||
|          fontSize = units::font_size::pixels<double> {fontSizes[1]}; | ||||
|       } | ||||
|       else if (fontSizes.size() > 0) | ||||
|       { | ||||
|          fontSize = units::font_size::pixels<double> {fontSizes[0]}; | ||||
|       } | ||||
| 
 | ||||
|       // Configure the label
 | ||||
|       QFont font("Inconsolata"); | ||||
|       font.setPointSizeF(units::font_size::points<double>(fontSize).value()); | ||||
|       QFont font = manager::FontManager::Instance().GetQFont( | ||||
|          types::FontCategory::Tooltip); | ||||
|       tooltipLabel_->setFont(font); | ||||
|       tooltipLabel_->setText(QString::fromStdString(displayText)); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| Subproject commit 33caca188b1007c643db75afa560fdfe348c0ee5 | ||||
| Subproject commit 1685e4048ef4a9f34bc11ecbb8db4905dd0a2e19 | ||||
|  | @ -1,5 +1,10 @@ | |||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/config/radar_site.hpp> | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/settings/map_settings.hpp> | ||||
| #include <scwx/qt/settings/palette_settings.hpp> | ||||
| #include <scwx/qt/settings/text_settings.hpp> | ||||
| #include <scwx/qt/settings/ui_settings.hpp> | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
|  | @ -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) | ||||
|  | @ -67,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); | ||||
| 
 | ||||
|  | @ -83,14 +92,14 @@ 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( | ||||
|       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"); | ||||
|    } | ||||
| } | ||||
|  | @ -103,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); | ||||
|  | @ -131,7 +140,7 @@ TEST_P(BadSettingsTest, BadSettings) | |||
| 
 | ||||
|    std::filesystem::copy_file(sourceFile, filename); | ||||
| 
 | ||||
|    SettingsManager::ReadSettings(filename); | ||||
|    SettingsManager::Instance().ReadSettings(filename); | ||||
| 
 | ||||
|    CompareFiles(filename, goodFile); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat