diff --git a/CHANGELOG.md b/CHANGELOG.md index 7183dd47..90c9d449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,7 @@ - Dev: Move `clang-tidy` checker to its own CI job. (#4996) - Dev: Refactored the Image Uploader feature. (#4971) - Dev: Refactored the SplitOverlay code. (#5082) +- Dev: Refactored the Fonts code, making it less of a singleton. (#5228) - Dev: Refactored the TwitchBadges structure, making it less of a singleton. (#5096, #5144) - Dev: Refactored emotes out of TwitchIrcServer. (#5120, #5146) - Dev: Refactored the ChatterinoBadges structure, making it less of a singleton. (#5103) diff --git a/mocks/include/mocks/EmptyApplication.hpp b/mocks/include/mocks/EmptyApplication.hpp index e70be268..5d5ea106 100644 --- a/mocks/include/mocks/EmptyApplication.hpp +++ b/mocks/include/mocks/EmptyApplication.hpp @@ -5,6 +5,8 @@ #include "singletons/Paths.hpp" #include "singletons/Updates.hpp" +#include + namespace chatterino::mock { class EmptyApplication : public IApplication @@ -235,7 +237,8 @@ public: return nullptr; } -private: +protected: + QTemporaryDir settingsDir; Paths paths_; Args args_; Updates updates_; diff --git a/src/Application.cpp b/src/Application.cpp index bede3f43..e412fe8f 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -118,7 +118,7 @@ Application::Application(Settings &_settings, const Paths &paths, : paths_(paths) , args_(_args) , themes(&this->emplace()) - , fonts(&this->emplace()) + , fonts(new Fonts(_settings)) , emotes(&this->emplace()) , accounts(&this->emplace()) , hotkeys(&this->emplace()) @@ -170,6 +170,7 @@ void Application::fakeDtor() this->bttvEmotes.reset(); this->ffzEmotes.reset(); this->seventvEmotes.reset(); + this->fonts.reset(); } void Application::initialize(Settings &settings, const Paths &paths) @@ -335,8 +336,9 @@ Theme *Application::getThemes() Fonts *Application::getFonts() { assertInGuiThread(); + assert(this->fonts); - return this->fonts; + return this->fonts.get(); } IEmotes *Application::getEmotes() diff --git a/src/Application.hpp b/src/Application.hpp index eb79ffc5..f846ff00 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -135,7 +135,7 @@ public: private: Theme *const themes{}; - Fonts *const fonts{}; + std::unique_ptr fonts{}; Emotes *const emotes{}; AccountController *const accounts{}; HotkeyController *const hotkeys{}; diff --git a/src/singletons/Fonts.cpp b/src/singletons/Fonts.cpp index b9f54451..7afe081e 100644 --- a/src/singletons/Fonts.cpp +++ b/src/singletons/Fonts.cpp @@ -8,119 +8,73 @@ #include #include -#ifdef Q_OS_WIN32 -# define DEFAULT_FONT_FAMILY "Segoe UI" -# define DEFAULT_FONT_SIZE 10 -#else -# ifdef Q_OS_MACOS -# define DEFAULT_FONT_FAMILY "Helvetica Neue" -# define DEFAULT_FONT_SIZE 12 -# else -# define DEFAULT_FONT_FAMILY "Arial" -# define DEFAULT_FONT_SIZE 11 -# endif -#endif - -namespace chatterino { namespace { - int getBoldness() - { + +using namespace chatterino; + +int getBoldness() +{ #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}, - }}; + // 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(); + const int target = getSettings()->boldScale.getValue(); - int result = QFont::Medium; - int closestDist = INT_MAX; + int result = QFont::Medium; + int closestDist = INT_MAX; - // Go through and find the closest mapped value - for (const auto [weightOld, weightNew] : legacyToOpenTypeMap) + // Go through and find the closest mapped value + for (const auto [weightOld, weightNew] : legacyToOpenTypeMap) + { + const int dist = qAbs(weightOld - target); + if (dist < closestDist) { - const int dist = qAbs(weightOld - target); - if (dist < closestDist) - { - result = weightNew; - closestDist = dist; - } - else - { - // Break early since following values will be further away - break; - } + result = weightNew; + closestDist = dist; + } + else + { + // Break early since following values will be further away + break; } - - return result; -#else - return getSettings()->boldScale.getValue(); -#endif } + + return result; +#else + return getSettings()->boldScale.getValue(); +#endif +} } // namespace -Fonts *Fonts::instance = nullptr; +namespace chatterino { -Fonts::Fonts() - : chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY) - , chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE) +Fonts::Fonts(Settings &settings) { - Fonts::instance = this; - this->fontsByType_.resize(size_t(FontStyle::EndType)); -} -void Fonts::initialize(Settings &, const Paths &) -{ - this->chatFontFamily.connect( - [this]() { - assertInGuiThread(); + this->fontChangedListener.setCB([this] { + assertInGuiThread(); - for (auto &map : this->fontsByType_) - { - map.clear(); - } - this->fontChanged.invoke(); - }, - false); - - this->chatFontSize.connect( - [this]() { - assertInGuiThread(); - - for (auto &map : this->fontsByType_) - { - map.clear(); - } - this->fontChanged.invoke(); - }, - false); - -#ifdef CHATTERINO - getSettings()->boldScale.connect( - [this]() { - assertInGuiThread(); - - // REMOVED - getIApp()->getWindows()->incGeneration(); - - for (auto &map : this->fontsByType_) - { - map.clear(); - } - this->fontChanged.invoke(); - }, - false); -#endif + for (auto &map : this->fontsByType_) + { + map.clear(); + } + this->fontChanged.invoke(); + }); + this->fontChangedListener.addSetting(settings.chatFontFamily); + this->fontChangedListener.addSetting(settings.chatFontSize); + this->fontChangedListener.addSetting(settings.boldScale); } QFont Fonts::getFont(FontStyle type, float scale) @@ -159,6 +113,8 @@ Fonts::FontData &Fonts::getOrCreateFontData(FontStyle type, float scale) Fonts::FontData Fonts::createFontData(FontStyle type, float scale) { + auto *settings = getSettings(); + // check if it's a chat (scale the setting) if (type >= FontStyle::ChatStart && type <= FontStyle::ChatEnd) { @@ -176,8 +132,8 @@ Fonts::FontData Fonts::createFontData(FontStyle type, float scale) QFont::Weight(getBoldness())}; auto data = sizeScale[type]; return FontData( - QFont(this->chatFontFamily.getValue(), - int(this->chatFontSize.getValue() * data.scale * scale), + QFont(settings->chatFontFamily.getValue(), + int(settings->chatFontSize.getValue() * data.scale * scale), data.weight, data.italic)); } @@ -205,9 +161,4 @@ Fonts::FontData Fonts::createFontData(FontStyle type, float scale) } } -Fonts *getFonts() -{ - return Fonts::instance; -} - } // namespace chatterino diff --git a/src/singletons/Fonts.hpp b/src/singletons/Fonts.hpp index 1f0704d9..e6ea324a 100644 --- a/src/singletons/Fonts.hpp +++ b/src/singletons/Fonts.hpp @@ -1,15 +1,13 @@ #pragma once -#include "common/ChatterinoSetting.hpp" -#include "common/Singleton.hpp" +#include "pajlada/settings/settinglistener.hpp" #include #include -#include #include -#include #include +#include namespace chatterino { @@ -38,23 +36,17 @@ enum class FontStyle : uint8_t { ChatEnd = ChatVeryLarge, }; -class Fonts final : public Singleton +class Fonts final { public: - Fonts(); - - void initialize(Settings &settings, const Paths &paths) override; + explicit Fonts(Settings &settings); // font data gets set in createFontData(...) QFont getFont(FontStyle type, float scale); QFontMetrics getFontMetrics(FontStyle type, float scale); - QStringSetting chatFontFamily; - IntSetting chatFontSize; - pajlada::Signals::NoArgSignal fontChanged; - static Fonts *instance; private: struct FontData { @@ -85,8 +77,8 @@ private: FontData createFontData(FontStyle type, float scale); std::vector> fontsByType_; + + pajlada::SettingListener fontChangedListener; }; -Fonts *getFonts(); - } // namespace chatterino diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 3e0c693f..660b3c14 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -25,6 +25,19 @@ using TimeoutButton = std::pair; namespace chatterino { +#ifdef Q_OS_WIN32 +# define DEFAULT_FONT_FAMILY "Segoe UI" +# define DEFAULT_FONT_SIZE 10 +#else +# ifdef Q_OS_MACOS +# define DEFAULT_FONT_FAMILY "Helvetica Neue" +# define DEFAULT_FONT_SIZE 12 +# else +# define DEFAULT_FONT_FAMILY "Arial" +# define DEFAULT_FONT_SIZE 11 +# endif +#endif + void _actuallyRegisterSetting( std::weak_ptr setting); @@ -134,6 +147,14 @@ public: // BoolSetting collapseLongMessages = // {"/appearance/messages/collapseLongMessages", false}; + QStringSetting chatFontFamily{ + "/appearance/currentFontFamily", + DEFAULT_FONT_FAMILY, + }; + IntSetting chatFontSize{ + "/appearance/currentFontSize", + DEFAULT_FONT_SIZE, + }; BoolSetting hideReplyContext = {"/appearance/hideReplyContext", false}; BoolSetting showReplyButton = {"/appearance/showReplyButton", false}; BoolSetting stripReplyMention = {"/appearance/stripReplyMention", true}; diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index 71154706..56bea50a 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -9,7 +9,6 @@ #include "providers/irc/IrcChannel2.hpp" #include "providers/irc/IrcServer.hpp" #include "providers/twitch/TwitchIrcServer.hpp" -#include "singletons/Fonts.hpp" #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "singletons/Theme.hpp" diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index c34edf96..c819c8f4 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -775,7 +775,8 @@ void BaseWindow::scaleChangedEvent(float scale) this->calcButtonsSizes(); #endif - this->setFont(getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale())); + this->setFont( + getIApp()->getFonts()->getFont(FontStyle::UiTabs, this->qtFontScale())); } void BaseWindow::paintEvent(QPaintEvent *) diff --git a/src/widgets/Label.cpp b/src/widgets/Label.cpp index bcec1d28..37bd9df3 100644 --- a/src/widgets/Label.cpp +++ b/src/widgets/Label.cpp @@ -1,4 +1,6 @@ -#include "Label.hpp" +#include "widgets/Label.hpp" + +#include "Application.hpp" #include @@ -14,9 +16,10 @@ Label::Label(BaseWidget *parent, QString text, FontStyle style) , text_(std::move(text)) , fontStyle_(style) { - this->connections_.managedConnect(getFonts()->fontChanged, [this] { - this->updateSize(); - }); + this->connections_.managedConnect(getIApp()->getFonts()->fontChanged, + [this] { + this->updateSize(); + }); } const QString &Label::getText() const @@ -92,12 +95,12 @@ void Label::paintEvent(QPaintEvent *) 1.0; #endif - QFontMetrics metrics = getFonts()->getFontMetrics( + QFontMetrics metrics = getIApp()->getFonts()->getFontMetrics( this->getFontStyle(), this->scale() * 96.f / std::max( 0.01F, static_cast(this->logicalDpiX() * deviceDpi))); - painter.setFont(getFonts()->getFont( + painter.setFont(getIApp()->getFonts()->getFont( this->getFontStyle(), this->scale() * 96.f / std::max( @@ -128,7 +131,7 @@ void Label::paintEvent(QPaintEvent *) void Label::updateSize() { QFontMetrics metrics = - getFonts()->getFontMetrics(this->fontStyle_, this->scale()); + getIApp()->getFonts()->getFontMetrics(this->fontStyle_, this->scale()); int width = metrics.horizontalAdvance(this->text_) + (2 * this->getOffset()); diff --git a/src/widgets/TooltipWidget.cpp b/src/widgets/TooltipWidget.cpp index 2e1ddf89..00f5a2be 100644 --- a/src/widgets/TooltipWidget.cpp +++ b/src/widgets/TooltipWidget.cpp @@ -47,9 +47,10 @@ TooltipWidget::TooltipWidget(BaseWidget *parent) this->setLayout(this->vLayout_); this->currentStyle_ = TooltipStyle::Vertical; - this->connections_.managedConnect(getFonts()->fontChanged, [this] { - this->updateFont(); - }); + this->connections_.managedConnect(getIApp()->getFonts()->fontChanged, + [this] { + this->updateFont(); + }); this->updateFont(); auto *windows = getIApp()->getWindows(); @@ -299,8 +300,8 @@ void TooltipWidget::scaleChangedEvent(float) void TooltipWidget::updateFont() { - this->setFont( - getFonts()->getFont(FontStyle::ChatMediumSmall, this->scale())); + this->setFont(getIApp()->getFonts()->getFont(FontStyle::ChatMediumSmall, + this->scale())); } void TooltipWidget::setWordWrap(bool wrap) diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 20d42459..953be229 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -572,7 +572,8 @@ void UserInfoPopup::themeChangedEvent() for (auto &&child : this->findChildren()) { - child->setFont(getFonts()->getFont(FontStyle::UiMedium, this->scale())); + child->setFont( + getIApp()->getFonts()->getFont(FontStyle::UiMedium, this->scale())); } } diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index e7920d87..0cac3913 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -620,7 +620,7 @@ void ChannelView::scaleChangedEvent(float scale) 0.01, this->logicalDpiX() * this->devicePixelRatioF()); #endif this->goToBottom_->getLabel().setFont( - getFonts()->getFont(FontStyle::UiMedium, factor)); + getIApp()->getFonts()->getFont(FontStyle::UiMedium, factor)); } } diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 546bc70f..50136dce 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -166,8 +166,7 @@ void GeneralPage::initLayout(GeneralPageView &layout) } layout.addDropdown( - "Font", {"Segoe UI", "Arial", "Choose..."}, - getIApp()->getFonts()->chatFontFamily, + "Font", {"Segoe UI", "Arial", "Choose..."}, s.chatFontFamily, [](auto val) { return val; }, @@ -177,7 +176,7 @@ void GeneralPage::initLayout(GeneralPageView &layout) true, "", true); layout.addDropdown( "Font size", {"9pt", "10pt", "12pt", "14pt", "16pt", "20pt"}, - getIApp()->getFonts()->chatFontSize, + s.chatFontSize, [](auto val) { return QString::number(val) + "pt"; }, diff --git a/tests/src/NotebookTab.cpp b/tests/src/NotebookTab.cpp index 56a14989..2ac4903f 100644 --- a/tests/src/NotebookTab.cpp +++ b/tests/src/NotebookTab.cpp @@ -5,6 +5,7 @@ #include "gmock/gmock.h" #include "mocks/EmptyApplication.hpp" #include "singletons/Fonts.hpp" +#include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "widgets/Notebook.hpp" @@ -21,6 +22,11 @@ namespace { class MockApplication : mock::EmptyApplication { public: + MockApplication() + : settings(this->settingsDir.filePath("settings.json")) + , fonts(this->settings) + { + } Theme *getThemes() override { return &this->theme; @@ -36,6 +42,7 @@ public: return &this->fonts; } + Settings settings; Theme theme; HotkeyController hotkeys; Fonts fonts; diff --git a/tests/src/SplitInput.cpp b/tests/src/SplitInput.cpp index 5d542f73..ed092f94 100644 --- a/tests/src/SplitInput.cpp +++ b/tests/src/SplitInput.cpp @@ -9,6 +9,7 @@ #include "singletons/Emotes.hpp" #include "singletons/Fonts.hpp" #include "singletons/Paths.hpp" +#include "singletons/Settings.hpp" #include "singletons/Theme.hpp" #include "singletons/WindowManager.hpp" #include "widgets/Notebook.hpp" @@ -28,7 +29,9 @@ class MockApplication : mock::EmptyApplication { public: MockApplication() - : windowManager(this->paths) + : settings(this->settingsDir.filePath("settings.json")) + , fonts(this->settings) + , windowManager(this->paths) { } Theme *getThemes() override @@ -66,6 +69,7 @@ public: return &this->emotes; } + Settings settings; Theme theme; HotkeyController hotkeys; Fonts fonts;