diff --git a/CHANGELOG.md b/CHANGELOG.md index dd61597e..77f4e0eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Bugfix: Handle CMD + BACKSPACE behavior explicitly in main chat dialog input for macOS. (#6111) - Bugfix: Fixed a small typo in the settings page. (#6134) - Bugfix: Fixed some Twitch commands not getting tab-completed correctly. (#6143) +- Bugfix: Fixed shared chat badges displaying pixelated when Chatterino is scaled too much. (#6146) - Dev: Mini refactor of Split. (#6148) - Dev: Conan will no longer generate a `CMakeUserPresets.json` file. (#6117) - Dev: Pass `--force-openssl` when installing from CMake in Qt 6.8+. (#6129) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 920d8a43..a13c888b 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -391,8 +391,10 @@ EmotePtr makeSharedChatBadge(const QString &sourceName, { if (!sourceProfileURL.isEmpty()) { - QString modifiedUrl = sourceProfileURL; - modifiedUrl.replace("300x300", "28x28"); + auto [urlBegin, urlEnd] = splitOnce(sourceProfileURL, u"300x300"); + QString url28px = urlBegin % u"28x28" % urlEnd; + QString url70px = urlBegin % u"70x70" % urlEnd; + QString url150px = urlBegin % u"150x150" % urlEnd; auto badgeLink = [&] { if (sourceLogin.isEmpty()) @@ -405,9 +407,13 @@ EmotePtr makeSharedChatBadge(const QString &sourceName, return std::make_shared(Emote{ .name = EmoteName{}, - .images = ImageSet{Image::fromUrl( - Url{modifiedUrl}, - 18.F / 28.F)}, // get as close to 18x18 as possible + .images = + ImageSet{ + // The images should be displayed like an 18x18 image + Image::fromUrl({url28px}, 18.F / 28.F), + Image::fromUrl({url70px}, 18.F / 70.F), + Image::fromUrl({url150px}, 18.F / 150.F), + }, .tooltip = Tooltip{"Shared Message" + (sourceName.isEmpty() ? "" : " from " + sourceName)}, diff --git a/src/util/Helpers.cpp b/src/util/Helpers.cpp index 25ad9f82..8cbfb53f 100644 --- a/src/util/Helpers.cpp +++ b/src/util/Helpers.cpp @@ -438,4 +438,32 @@ bool readProviderEmotesCache(const QString &id, const QString &provider, return false; } +std::pair splitOnce(QStringView haystack, + QStringView needle) noexcept +{ + auto idx = haystack.indexOf(needle); + if (idx < 0) + { + return {haystack, {}}; + } + return { + haystack.sliced(0, idx), + haystack.sliced(idx + needle.size()), + }; +} + +std::pair splitOnce(QStringView haystack, + QChar needle) noexcept +{ + auto idx = haystack.indexOf(needle); + if (idx < 0) + { + return {haystack, {}}; + } + return { + haystack.sliced(0, idx), + haystack.sliced(idx + 1), + }; +} + } // namespace chatterino diff --git a/src/util/Helpers.hpp b/src/util/Helpers.hpp index 0ae95035..b405d844 100644 --- a/src/util/Helpers.hpp +++ b/src/util/Helpers.hpp @@ -226,4 +226,14 @@ bool readProviderEmotesCache( const QString &id, const QString &provider, const std::function &callback); +/// Splits `haystack` by `needle`. If `needle` doesn't occur in `haystack`, +/// `{haystack, {}}` is returned. +std::pair splitOnce(QStringView haystack, + QStringView needle) noexcept; + +/// Splits `haystack` by `needle`. If `needle` doesn't occur in `haystack`, +/// `{haystack, {}}` is returned. +std::pair splitOnce(QStringView haystack, + QChar needle) noexcept; + } // namespace chatterino diff --git a/tests/src/Helpers.cpp b/tests/src/Helpers.cpp index 1a127041..2a183fe7 100644 --- a/tests/src/Helpers.cpp +++ b/tests/src/Helpers.cpp @@ -613,3 +613,26 @@ TEST(Helpers, codepointSlice) ASSERT_EQ(codepointSlice(u"🍩🍟\xD83E", 0, 3), u"🍩🍟\xD83E"); ASSERT_EQ(codepointSlice(u"🍩🍟\xD83E🥚", 0, 4), u"🍩🍟\xD83E🥚"); } + +TEST(Helpers, splitOnce) +{ + auto pair = [](auto first, auto second) { + return std::pair{QStringView(first), QStringView(second)}; + }; + + ASSERT_EQ(splitOnce(u"foo bar", u" "), pair(u"foo", u"bar")); + ASSERT_EQ(splitOnce(u"foo bar", u"oo"), pair(u"f", u" bar")); + ASSERT_EQ(splitOnce(u"foo bar", u"foo"), pair(u"", u" bar")); + ASSERT_EQ(splitOnce(u"foo bar", u"bar"), pair(u"foo ", u"")); + ASSERT_EQ(splitOnce(u"foo bar", u"baz"), pair(u"foo bar", u"")); + ASSERT_EQ(splitOnce(u"foo bar", u"bars"), pair(u"foo bar", u"")); + ASSERT_EQ(splitOnce(u"foo bar", u""), pair(u"", u"foo bar")); + ASSERT_EQ(splitOnce(u"", u"foo"), pair(u"", u"")); + ASSERT_EQ(splitOnce(u"", u""), pair(u"", u"")); + + ASSERT_EQ(splitOnce(u"foo bar", u' '), pair(u"foo", u"bar")); + ASSERT_EQ(splitOnce(u"foo bar", u'f'), pair(u"", u"oo bar")); + ASSERT_EQ(splitOnce(u"foo bar", u'r'), pair(u"foo ba", u"")); + ASSERT_EQ(splitOnce(u"foo bar", u'z'), pair(u"foo bar", u"")); + ASSERT_EQ(splitOnce(u"", u'z'), pair(u"", u"")); +}