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""));
+}