mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Implement preferred emote quality setting.
This doesn't work super well for Twitch emotes because they don't conform to a proper emote scaling standard Fixes #150
This commit is contained in:
parent
5baba39cdc
commit
9fa9d7f0e3
8 changed files with 194 additions and 72 deletions
|
@ -16,13 +16,65 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0"
|
||||
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}"
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
||||
namespace {
|
||||
|
||||
static QString GetTwitchEmoteLink(long id, const QString &emoteScale)
|
||||
{
|
||||
QString value = TWITCH_EMOTE_TEMPLATE;
|
||||
|
||||
value.detach();
|
||||
|
||||
return value.replace("{id}", QString::number(id)).replace("{scale}", emoteScale);
|
||||
}
|
||||
|
||||
static QString GetBTTVEmoteLink(QString urlTemplate, const QString &id, const QString &emoteScale)
|
||||
{
|
||||
urlTemplate.detach();
|
||||
|
||||
return urlTemplate.replace("{{id}}", id).replace("{{image}}", emoteScale);
|
||||
}
|
||||
|
||||
static QString GetFFZEmoteLink(const QJsonObject &urls, const QString &emoteScale)
|
||||
{
|
||||
auto emote = urls.value(emoteScale);
|
||||
if (emote.isUndefined()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
assert(emote.isString());
|
||||
|
||||
return "http:" + emote.toString();
|
||||
}
|
||||
|
||||
static void FillInFFZEmoteData(const QJsonObject &urls, const QString &code,
|
||||
util::EmoteData &emoteData)
|
||||
{
|
||||
QString url1x = GetFFZEmoteLink(urls, "1");
|
||||
QString url2x = GetFFZEmoteLink(urls, "2");
|
||||
QString url3x = GetFFZEmoteLink(urls, "4");
|
||||
|
||||
assert(!url1x.isEmpty());
|
||||
|
||||
emoteData.image1x = new LazyLoadedImage(url1x, 1, code, code + "<br />Global FFZ Emote");
|
||||
|
||||
if (!url2x.isEmpty()) {
|
||||
emoteData.image2x = new LazyLoadedImage(url2x, 0.5, code, code + "<br />Global FFZ Emote");
|
||||
}
|
||||
|
||||
if (!url3x.isEmpty()) {
|
||||
emoteData.image3x = new LazyLoadedImage(url3x, 0.25, code, code + "<br />Global FFZ Emote");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EmoteManager::EmoteManager(SettingManager &_settingsManager, WindowManager &_windowManager)
|
||||
: settingsManager(_settingsManager)
|
||||
, windowManager(_windowManager)
|
||||
|
@ -136,12 +188,13 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName,
|
|||
QString code = emoteObject.value("name").toString();
|
||||
|
||||
QJsonObject urls = emoteObject.value("urls").toObject();
|
||||
QString url1 = "http:" + urls.value("1").toString();
|
||||
|
||||
auto emote =
|
||||
this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code, &url1] {
|
||||
return util::EmoteData(
|
||||
new LazyLoadedImage(url1, 1, code, code + "<br/>Channel FFZ Emote"));
|
||||
this->getFFZChannelEmoteFromCaches().getOrAdd(id, [this, &code, &urls] {
|
||||
util::EmoteData emoteData;
|
||||
FillInFFZEmoteData(urls, code, emoteData);
|
||||
|
||||
return emoteData;
|
||||
});
|
||||
|
||||
this->ffzChannelEmotes.insert(code, emote);
|
||||
|
@ -310,8 +363,9 @@ void EmoteManager::parseEmojis(std::vector<std::tuple<util::EmoteData, QString>>
|
|||
|
||||
if (charactersFromLastParsedEmoji > 0) {
|
||||
// Add characters inbetween emojis
|
||||
parsedWords.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
nullptr, text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)));
|
||||
parsedWords.push_back(std::tuple<util::EmoteData, QString>(
|
||||
util::EmoteData(),
|
||||
text.mid(lastParsedEmojiEndIndex, charactersFromLastParsedEmoji)));
|
||||
}
|
||||
|
||||
QString url = "https://cdnjs.cloudflare.com/ajax/libs/"
|
||||
|
@ -334,8 +388,8 @@ void EmoteManager::parseEmojis(std::vector<std::tuple<util::EmoteData, QString>>
|
|||
|
||||
if (lastParsedEmojiEndIndex < text.length()) {
|
||||
// Add remaining characters
|
||||
parsedWords.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
nullptr, text.mid(lastParsedEmojiEndIndex)));
|
||||
parsedWords.push_back(std::tuple<util::EmoteData, QString>(
|
||||
util::EmoteData(), text.mid(lastParsedEmojiEndIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +467,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<twitch::TwitchUser>
|
|||
emoteData.filled = true;
|
||||
}
|
||||
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
void EmoteManager::loadBTTVEmotes()
|
||||
|
@ -427,20 +481,22 @@ void EmoteManager::loadBTTVEmotes()
|
|||
debug::Log("Got global bttv emotes");
|
||||
auto emotes = root.value("emotes").toArray();
|
||||
|
||||
QString linkTemplate = "https:" + root.value("urlTemplate").toString();
|
||||
QString urlTemplate = "https:" + root.value("urlTemplate").toString();
|
||||
|
||||
std::vector<std::string> codes;
|
||||
for (const QJsonValue &emote : emotes) {
|
||||
QString id = emote.toObject().value("id").toString();
|
||||
QString code = emote.toObject().value("code").toString();
|
||||
// emote.value("imageType").toString();
|
||||
|
||||
QString tmp = linkTemplate;
|
||||
tmp.detach();
|
||||
QString url = tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||
util::EmoteData emoteData;
|
||||
emoteData.image1x = new LazyLoadedImage(GetBTTVEmoteLink(urlTemplate, id, "1x"), 1,
|
||||
code, code + "<br />Global BTTV Emote");
|
||||
emoteData.image2x = new LazyLoadedImage(GetBTTVEmoteLink(urlTemplate, id, "2x"), 0.5,
|
||||
code, code + "<br />Global BTTV Emote");
|
||||
emoteData.image3x = new LazyLoadedImage(GetBTTVEmoteLink(urlTemplate, id, "3x"), 0.25,
|
||||
code, code + "<br />Global BTTV Emote");
|
||||
|
||||
this->bttvGlobalEmotes.insert(
|
||||
code, new LazyLoadedImage(url, 1, code, code + "<br/>Global BTTV Emote"));
|
||||
this->bttvGlobalEmotes.insert(code, emoteData);
|
||||
codes.push_back(code.toStdString());
|
||||
}
|
||||
|
||||
|
@ -467,46 +523,38 @@ void EmoteManager::loadFFZEmotes()
|
|||
for (const QJsonValue &emote : emoticons) {
|
||||
QJsonObject object = emote.toObject();
|
||||
|
||||
// margins
|
||||
|
||||
// int id = object.value("id").toInt();
|
||||
QString code = object.value("name").toString();
|
||||
|
||||
QJsonObject urls = object.value("urls").toObject();
|
||||
QString url1 = "http:" + urls.value("1").toString();
|
||||
|
||||
this->ffzGlobalEmotes.insert(
|
||||
code, new LazyLoadedImage(url1, 1, code, code + "<br/>Global FFZ Emote"));
|
||||
util::EmoteData emoteData;
|
||||
FillInFFZEmoteData(urls, code, emoteData);
|
||||
|
||||
this->ffzGlobalEmotes.insert(code, emoteData);
|
||||
codes.push_back(code.toStdString());
|
||||
}
|
||||
|
||||
this->ffzGlobalEmoteCodes = codes;
|
||||
}
|
||||
});
|
||||
} // namespace chatterino
|
||||
}
|
||||
|
||||
// id is used for lookup
|
||||
// emoteName is used for giving a name to the emote in case it doesn't exist
|
||||
util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteName)
|
||||
{
|
||||
return _twitchEmoteFromCache.getOrAdd(id, [this, &emoteName, &id] {
|
||||
qreal scale;
|
||||
QString url = getTwitchEmoteLink(id, scale);
|
||||
return new LazyLoadedImage(url, scale, emoteName, emoteName + "<br/>Twitch Emote");
|
||||
util::EmoteData newEmoteData;
|
||||
newEmoteData.image1x = new LazyLoadedImage(GetTwitchEmoteLink(id, "1.0"), 1, emoteName,
|
||||
emoteName + "<br/>Twitch Emote 1x");
|
||||
newEmoteData.image2x = new LazyLoadedImage(GetTwitchEmoteLink(id, "2.0"), .5, emoteName,
|
||||
emoteName + "<br/>Twitch Emote 2x");
|
||||
newEmoteData.image3x = new LazyLoadedImage(GetTwitchEmoteLink(id, "3.0"), .25, emoteName,
|
||||
emoteName + "<br/>Twitch Emote 3x");
|
||||
|
||||
return newEmoteData;
|
||||
});
|
||||
}
|
||||
|
||||
QString EmoteManager::getTwitchEmoteLink(long id, qreal &scale)
|
||||
{
|
||||
scale = .5;
|
||||
|
||||
QString value = TWITCH_EMOTE_TEMPLATE;
|
||||
|
||||
value.detach();
|
||||
|
||||
return value.replace("{id}", QString::number(id)).replace("{scale}", "2");
|
||||
}
|
||||
|
||||
util::EmoteData EmoteManager::getCheerImage(long long amount, bool animated)
|
||||
{
|
||||
// TODO: fix this xD
|
||||
|
@ -539,5 +587,5 @@ boost::signals2::signal<void()> &EmoteManager::getGifUpdateSignal()
|
|||
return this->gifUpdateTimerSignal;
|
||||
}
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
}
|
||||
|
|
|
@ -153,10 +153,7 @@ private:
|
|||
bool gifUpdateTimerInitiated = false;
|
||||
|
||||
int _generation = 0;
|
||||
|
||||
// methods
|
||||
static QString getTwitchEmoteLink(long id, qreal &scale);
|
||||
};
|
||||
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ class SettingManager : public QObject
|
|||
|
||||
using BoolSetting = ChatterinoSetting<bool>;
|
||||
using FloatSetting = ChatterinoSetting<float>;
|
||||
using IntSetting = ChatterinoSetting<int>;
|
||||
using StringSetting = ChatterinoSetting<std::string>;
|
||||
using QStringSetting = ChatterinoSetting<QString>;
|
||||
|
||||
|
@ -64,6 +65,12 @@ public:
|
|||
BoolSetting enableGifAnimations = {"/emotes/enableGifAnimations", true};
|
||||
FloatSetting emoteScale = {"/emotes/scale", 1.f};
|
||||
|
||||
// 0 = Smallest size
|
||||
// 1 = One size above 0 (usually size of 0 * 2)
|
||||
// 2 = One size above 1 (usually size of 1 * 2)
|
||||
// etc...
|
||||
IntSetting preferredEmoteQuality = {"/emotes/preferredEmoteQuality", 0};
|
||||
|
||||
/// Links
|
||||
BoolSetting linksDoubleClickOnly = {"/links/doubleClickToOpen", false};
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ SharedMessage TwitchMessageBuilder::parse()
|
|||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
singletons::EmoteManager &emoteManager = singletons::EmoteManager::getInstance();
|
||||
|
||||
auto preferredEmoteQuality = settings.preferredEmoteQuality.getValue();
|
||||
|
||||
this->originalMessage = this->ircMessage->content();
|
||||
|
||||
this->parseUsername();
|
||||
|
@ -121,14 +123,9 @@ SharedMessage TwitchMessageBuilder::parse()
|
|||
|
||||
// twitch emote
|
||||
if (currentTwitchEmote != twitchEmotes.end() && currentTwitchEmote->first == i) {
|
||||
this->appendWord(
|
||||
Word(currentTwitchEmote->second.image, Word::TwitchEmoteImage,
|
||||
currentTwitchEmote->second.image->getName(),
|
||||
currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote")));
|
||||
this->appendWord(
|
||||
Word(currentTwitchEmote->second.image->getName(), Word::TwitchEmoteText, textColor,
|
||||
singletons::FontManager::Medium, currentTwitchEmote->second.image->getName(),
|
||||
currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote")));
|
||||
auto emoteImage = currentTwitchEmote->second.getImageForSize(preferredEmoteQuality);
|
||||
this->appendWord(Word(emoteImage, Word::TwitchEmoteImage, emoteImage->getName(),
|
||||
emoteImage->getTooltip(), Link(Link::Url, emoteImage->getUrl())));
|
||||
|
||||
i += split.length() + 1;
|
||||
currentTwitchEmote = std::next(currentTwitchEmote);
|
||||
|
@ -145,7 +142,7 @@ SharedMessage TwitchMessageBuilder::parse()
|
|||
for (const auto &tuple : parsed) {
|
||||
const util::EmoteData &emoteData = std::get<0>(tuple);
|
||||
|
||||
if (emoteData.image == nullptr) { // is text
|
||||
if (!emoteData.isValid()) { // is text
|
||||
QString string = std::get<1>(tuple);
|
||||
|
||||
static QRegularExpression cheerRegex("cheer[1-9][0-9]*");
|
||||
|
@ -234,11 +231,12 @@ SharedMessage TwitchMessageBuilder::parse()
|
|||
this->appendWord(Word(string, Word::Text, textColor,
|
||||
singletons::FontManager::Medium, string, QString(), link));
|
||||
} else { // is emoji
|
||||
this->appendWord(Word(emoteData.image, Word::EmojiImage, emoteData.image->getName(),
|
||||
emoteData.image->getTooltip()));
|
||||
Word(emoteData.image->getName(), Word::EmojiText, textColor,
|
||||
singletons::FontManager::Medium, emoteData.image->getName(),
|
||||
emoteData.image->getTooltip());
|
||||
auto emoteImage = emoteData.getImageForSize(preferredEmoteQuality);
|
||||
this->appendWord(Word(emoteImage, Word::EmojiImage, emoteImage->getName(),
|
||||
emoteImage->getTooltip()));
|
||||
Word(emoteImage->getName(), Word::EmojiText, textColor,
|
||||
singletons::FontManager::Medium, emoteImage->getName(),
|
||||
emoteImage->getTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,11 +545,13 @@ bool TwitchMessageBuilder::tryAppendEmote(QString &emoteString)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool TwitchMessageBuilder::appendEmote(util::EmoteData &emoteData)
|
||||
bool TwitchMessageBuilder::appendEmote(const util::EmoteData &emoteData)
|
||||
{
|
||||
this->appendWord(Word(emoteData.image, Word::BttvEmoteImage, emoteData.image->getName(),
|
||||
emoteData.image->getTooltip(),
|
||||
Link(Link::Url, emoteData.image->getUrl())));
|
||||
auto emoteImage = emoteData.getImageForSize(
|
||||
singletons::SettingManager::getInstance().preferredEmoteQuality.getValue());
|
||||
|
||||
this->appendWord(Word(emoteImage, Word::BttvEmoteImage, emoteImage->getName(),
|
||||
emoteImage->getTooltip(), Link(Link::Url, emoteImage->getUrl())));
|
||||
|
||||
// Perhaps check for ignored emotes here?
|
||||
return true;
|
||||
|
|
|
@ -60,7 +60,7 @@ private:
|
|||
void appendTwitchEmote(const Communi::IrcPrivateMessage *ircMessage, const QString &emote,
|
||||
std::vector<std::pair<long, util::EmoteData>> &vec);
|
||||
bool tryAppendEmote(QString &emoteString);
|
||||
bool appendEmote(util::EmoteData &emoteData);
|
||||
bool appendEmote(const util::EmoteData &emoteData);
|
||||
|
||||
void parseTwitchBadges();
|
||||
void parseChatterinoBadges();
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "messages/lazyloadedimage.hpp"
|
||||
#include "util/concurrentmap.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace chatterino {
|
||||
namespace util {
|
||||
|
||||
|
@ -12,13 +14,51 @@ struct EmoteData {
|
|||
}
|
||||
|
||||
EmoteData(messages::LazyLoadedImage *_image)
|
||||
: image(_image)
|
||||
: image1x(_image)
|
||||
{
|
||||
}
|
||||
|
||||
messages::LazyLoadedImage *image = nullptr;
|
||||
messages::LazyLoadedImage *getImageForSize(unsigned emoteSize) const
|
||||
{
|
||||
messages::LazyLoadedImage *ret = nullptr;
|
||||
|
||||
switch (emoteSize) {
|
||||
case 0:
|
||||
ret = this->image1x;
|
||||
break;
|
||||
case 1:
|
||||
ret = this->image2x;
|
||||
break;
|
||||
case 2:
|
||||
ret = this->image3x;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = this->image1x;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == nullptr) {
|
||||
ret = this->image1x;
|
||||
}
|
||||
|
||||
assert(ret != nullptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Emotes must have a 1x image to be valid
|
||||
bool isValid() const
|
||||
{
|
||||
return this->image1x != nullptr;
|
||||
}
|
||||
|
||||
messages::LazyLoadedImage *image1x = nullptr;
|
||||
messages::LazyLoadedImage *image2x = nullptr;
|
||||
messages::LazyLoadedImage *image3x = nullptr;
|
||||
};
|
||||
|
||||
typedef ConcurrentMap<QString, EmoteData> EmoteMap;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -59,8 +59,11 @@ void EmotePopup::loadChannel(std::shared_ptr<Channel> _channel)
|
|||
builder2.getMessage()->centered = true;
|
||||
builder2.getMessage()->setDisableCompactEmotes(true);
|
||||
|
||||
int preferredEmoteSize = singletons::SettingManager::getInstance().preferredEmoteQuality;
|
||||
|
||||
map.each([&](const QString &key, const util::EmoteData &value) {
|
||||
builder2.appendWord(Word(value.image, Word::Flags::AlwaysShow, key, emoteDesc,
|
||||
builder2.appendWord(Word(value.getImageForSize(preferredEmoteSize),
|
||||
Word::Flags::AlwaysShow, key, emoteDesc,
|
||||
Link(Link::Type::InsertText, key)));
|
||||
});
|
||||
|
||||
|
@ -82,6 +85,7 @@ void EmotePopup::loadChannel(std::shared_ptr<Channel> _channel)
|
|||
|
||||
void EmotePopup::loadEmojis()
|
||||
{
|
||||
int preferredEmoteSize = singletons::SettingManager::getInstance().preferredEmoteQuality;
|
||||
util::EmoteMap &emojis = singletons::EmoteManager::getInstance().getEmojis();
|
||||
|
||||
std::shared_ptr<Channel> emojiChannel(new Channel(""));
|
||||
|
@ -99,10 +103,13 @@ void EmotePopup::loadEmojis()
|
|||
messages::MessageBuilder builder;
|
||||
builder.getMessage()->centered = true;
|
||||
builder.getMessage()->setDisableCompactEmotes(true);
|
||||
emojis.each([this, &builder](const QString &key, const util::EmoteData &value) {
|
||||
builder.appendWord(Word(value.image, Word::Flags::AlwaysShow, key, "emoji",
|
||||
Link(Link::Type::InsertText, key)));
|
||||
});
|
||||
|
||||
emojis.each(
|
||||
[this, &builder, preferredEmoteSize](const QString &key, const util::EmoteData &value) {
|
||||
auto emoteImage = value.getImageForSize(preferredEmoteSize);
|
||||
builder.appendWord(Word(emoteImage, Word::Flags::AlwaysShow, key, "emoji",
|
||||
Link(Link::Type::InsertText, key)));
|
||||
});
|
||||
emojiChannel->addMessage(builder.getMessage());
|
||||
|
||||
this->viewEmojis->setChannel(emojiChannel);
|
||||
|
|
|
@ -426,6 +426,29 @@ QVBoxLayout *SettingsDialog::createEmotesTab()
|
|||
|
||||
layout->addWidget(createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes));
|
||||
|
||||
// Preferred emote quality
|
||||
{
|
||||
auto box = new QHBoxLayout();
|
||||
auto label = new QLabel("Preferred emote quality");
|
||||
label->setToolTip("Select which emote quality you prefer to download");
|
||||
auto widget = new QComboBox();
|
||||
widget->addItems({"1x", "2x", "4x"});
|
||||
box->addWidget(label);
|
||||
box->addWidget(widget);
|
||||
|
||||
settings.preferredEmoteQuality.connect([widget](int newIndex, auto) {
|
||||
widget->setCurrentIndex(newIndex); //
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
widget, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [](int index) {
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
settings.preferredEmoteQuality = index; //
|
||||
});
|
||||
|
||||
layout->addLayout(box);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue