Merge branch 'feature/placefiles-font' into feature/placefiles

This commit is contained in:
Dan Paulat 2023-10-08 23:37:25 -05:00
commit cfa62d5fbc
52 changed files with 1988 additions and 542 deletions

View file

@ -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) |

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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.
*

View file

@ -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);

View file

@ -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;
}

View 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

View 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

View file

@ -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();

View file

@ -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);

View file

@ -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()

View file

@ -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>>

View file

@ -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

View file

@ -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

View file

@ -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());

View file

@ -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);

View file

@ -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();

View file

@ -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())

View file

@ -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())
{

View file

@ -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_ &&

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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_;

View file

@ -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

View file

@ -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,7 +121,7 @@ bool SettingsCategory::ReadJson(const boost::json::object& json)
{
if (value == nullptr)
{
logger_->warn("Key {} is not present, resetting to defaults",
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)
{

View file

@ -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);

View file

@ -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,6 +324,8 @@ void SettingsInterface<T>::SetResetButton(QAbstractButton* button)
p->resetButton_ = button;
if (p->resetButton_ != nullptr)
{
QObject::connect(p->resetButton_,
&QAbstractButton::clicked,
p->context_.get(),
@ -283,8 +335,8 @@ void SettingsInterface<T>::SetResetButton(QAbstractButton* button)
if (p->variable_->GetValue() == defaultValue)
{
// If the current value is default, reset the staged
// value
// If the current value is default, reset the
// staged value
p->variable_->Reset();
p->stagedValid_ = true;
p->UpdateEditWidget();
@ -301,6 +353,7 @@ void SettingsInterface<T>::SetResetButton(QAbstractButton* button)
});
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));
SetWidgetText(lineEdit, currentValue);
}
else if constexpr (std::is_same_v<T, std::string>)
else if (QLabel* label = dynamic_cast<QLabel*>(editWidget_))
{
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(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());
}
}

View file

@ -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>;

View file

@ -24,6 +24,14 @@ 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.

View file

@ -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
{

View file

@ -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.

View file

@ -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_);
}

View file

@ -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,6 +26,13 @@ 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;

View 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

View 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

View file

@ -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(

View file

@ -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);

View file

@ -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);

View file

@ -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,8 +175,8 @@ void AlertDockWidgetImpl::ConnectSignals()
// If an item is selected
if (selectedAlertKey_ != types::TextEventKey {})
{
types::AlertAction alertAction = types::GetAlertAction(
manager::SettingsManager::general_settings()
types::AlertAction alertAction =
types::GetAlertAction(settings::GeneralSettings::Instance()
.default_alert_action()
.GetValue());

View file

@ -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)));

View file

@ -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

View file

@ -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,9 +162,13 @@ public:
SettingsDialog* self_;
PlacefileSettingsWidget* placefileSettingsWidget_ {nullptr};
RadarSiteDialog* radarSiteDialog_;
QFontDialog* fontDialog_;
QStandardItemModel* fontCategoryModel_;
types::FontCategory selectedFontCategory_ {types::FontCategory::Unknown};
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_ {};
@ -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();
}
}

View file

@ -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">

View file

@ -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);

View file

@ -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());

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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