#include "singletons/Fonts.hpp" #include "Application.hpp" #include "debug/AssertInGuiThread.hpp" #include "singletons/Settings.hpp" #include "singletons/WindowManager.hpp" #include #include namespace { using namespace chatterino; int getUsernameBoldness() { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // From qfont.cpp // https://github.com/qt/qtbase/blob/589c6d066f84833a7c3dda1638037f4b2e91b7aa/src/gui/text/qfont.cpp#L143-L169 static constexpr std::array, 9> legacyToOpenTypeMap{{ {0, QFont::Thin}, {12, QFont::ExtraLight}, {25, QFont::Light}, {50, QFont::Normal}, {57, QFont::Medium}, {63, QFont::DemiBold}, {75, QFont::Bold}, {81, QFont::ExtraBold}, {87, QFont::Black}, }}; const int target = getSettings()->boldScale.getValue(); int result = QFont::Medium; int closestDist = INT_MAX; // Go through and find the closest mapped value for (const auto [weightOld, weightNew] : legacyToOpenTypeMap) { const int dist = qAbs(weightOld - target); if (dist < closestDist) { result = weightNew; closestDist = dist; } else { // Break early since following values will be further away break; } } return result; #else return getSettings()->boldScale.getValue(); #endif } float fontSize(FontStyle style) { auto chatSize = [] { return static_cast(getSettings()->chatFontSize); }; switch (style) { case FontStyle::ChatSmall: return 0.6F * chatSize(); case FontStyle::ChatMediumSmall: return 0.8F * chatSize(); case FontStyle::ChatMedium: case FontStyle::ChatMediumBold: case FontStyle::ChatMediumItalic: case FontStyle::TimestampMedium: return chatSize(); case FontStyle::ChatLarge: return 1.2F * chatSize(); case FontStyle::ChatVeryLarge: return 1.4F * chatSize(); case FontStyle::Tiny: return 8; case FontStyle::UiMedium: case FontStyle::UiMediumBold: case FontStyle::UiTabs: case FontStyle::EndType: return 9; } assert(false); return 9; } int fontWeight(FontStyle style) { switch (style) { case FontStyle::ChatSmall: case FontStyle::ChatMediumSmall: case FontStyle::ChatMedium: case FontStyle::ChatMediumItalic: case FontStyle::ChatLarge: case FontStyle::ChatVeryLarge: case FontStyle::TimestampMedium: return getSettings()->chatFontWeight.getValue(); case FontStyle::ChatMediumBold: return getUsernameBoldness(); case FontStyle::Tiny: case FontStyle::UiMedium: case FontStyle::UiTabs: case FontStyle::EndType: return QFont::Normal; case FontStyle::UiMediumBold: return QFont::Bold; } assert(false); return QFont::Normal; } bool isItalic(FontStyle style) { switch (style) { case FontStyle::Tiny: case FontStyle::ChatSmall: case FontStyle::ChatMediumSmall: case FontStyle::ChatMedium: case FontStyle::ChatMediumBold: case FontStyle::ChatLarge: case FontStyle::ChatVeryLarge: case FontStyle::TimestampMedium: case FontStyle::UiMedium: case FontStyle::UiMediumBold: case FontStyle::UiTabs: case FontStyle::EndType: return false; case FontStyle::ChatMediumItalic: return true; } assert(false); return false; } QString fontFamily(FontStyle style) { switch (style) { case FontStyle::Tiny: return QStringLiteral("Monospace"); case FontStyle::ChatSmall: case FontStyle::ChatMediumSmall: case FontStyle::ChatMedium: case FontStyle::ChatMediumBold: case FontStyle::ChatMediumItalic: case FontStyle::ChatLarge: case FontStyle::ChatVeryLarge: case FontStyle::TimestampMedium: return getSettings()->chatFontFamily.getValue(); case FontStyle::UiMedium: case FontStyle::UiMediumBold: case FontStyle::UiTabs: case FontStyle::EndType: return QStringLiteral(DEFAULT_FONT_FAMILY); } assert(false); return QStringLiteral(DEFAULT_FONT_FAMILY); } } // namespace namespace chatterino { Fonts::Fonts(Settings &settings) { this->fontsByType_.resize(size_t(FontStyle::EndType)); this->fontChangedListener.setCB([this] { assertInGuiThread(); for (auto &map : this->fontsByType_) { map.clear(); } this->fontChanged.invoke(); }); this->fontChangedListener.addSetting(settings.chatFontFamily); this->fontChangedListener.addSetting(settings.chatFontSize); this->fontChangedListener.addSetting(settings.chatFontWeight); this->fontChangedListener.addSetting(settings.boldScale); } QFont Fonts::getFont(FontStyle type, float scale) { return this->getOrCreateFontData(type, scale).font; } QFontMetricsF Fonts::getFontMetrics(FontStyle type, float scale) { return this->getOrCreateFontData(type, scale).metrics; } Fonts::FontData &Fonts::getOrCreateFontData(FontStyle type, float scale) { assertInGuiThread(); assert(type < FontStyle::EndType); auto &map = this->fontsByType_[size_t(type)]; // find element auto it = map.find(scale); if (it != map.end()) { // return if found return it->second; } // emplace new element auto result = map.emplace(scale, Fonts::createFontData(type, scale)); assert(result.second); return result.first->second; } Fonts::FontData Fonts::createFontData(FontStyle type, float scale) { QFont font{ fontFamily(type), static_cast(fontSize(type) * scale), fontWeight(type), isItalic(type), }; switch (type) { case FontStyle::TimestampMedium: { #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) // Apply the OpenType feature `tnum` to timestamp fonts // https://sparanoid.com/lab/opentype-features/#tnum auto tag = QFont::Tag("tnum"); font.setFeature(tag, 1); #endif } break; } return font; } } // namespace chatterino