added spicy new segfaults

This commit is contained in:
fourtf 2017-01-11 01:08:20 +01:00
parent a8c2b1151f
commit 580a411e9d
22 changed files with 472 additions and 111 deletions

BIN
.vs/chatterino/v14/.suo Normal file

Binary file not shown.

View file

@ -11,6 +11,17 @@ public:
} }
static bool isIgnoredEmote(const QString& emote); static bool isIgnoredEmote(const QString& emote);
static qreal emoteScale() {
return 1;
}
static qreal badgeScale() {
return 1;
}
static bool scaleEmotesByLineHeight() {
return false;
}
private: private:
AppSettings(); AppSettings();

View file

@ -1,8 +1,10 @@
#include "channel.h" #include "channel.h"
#include "message.h" #include "message.h"
Channel Channel::whispers = Channel(QString("/whispers")); #include <memory>
Channel Channel::mentions = Channel(QString("/mentions"));
Channel Channel::whispers(QString("/whispers"));
Channel Channel::mentions(QString("/mentions"));
QMap<QString, Channel*> Channel::channels = QMap<QString, Channel*>(); QMap<QString, Channel*> Channel::channels = QMap<QString, Channel*>();
@ -30,7 +32,7 @@ Channel* Channel::addChannel(const QString &channel)
return c; return c;
} }
c->referenceCount++; c->m_referenceCount++;
return c; return c;
} }
@ -60,25 +62,26 @@ void Channel::removeChannel(const QString &channel)
if (c == NULL) return; if (c == NULL) return;
c->referenceCount--; c->m_referenceCount--;
if (c->referenceCount == 0) { if (c->m_referenceCount == 0) {
channels.remove(channel); channels.remove(channel);
delete c; delete c;
} }
} }
QVector<Message*> Channel::getMessagesClone() QVector<std::shared_ptr<Message>> Channel::getMessagesClone()
{ {
m_messageMutex.lock(); m_messageMutex.lock();
QVector M = QVector<Message*>(*m_messages); QVector<std::shared_ptr<Message>> M(m_messages);
M.detach();
m_messageMutex.unlock(); m_messageMutex.unlock();
return M; return M;
} }
void Channel::addMessage(Message *message) void Channel::addMessage(std::shared_ptr<Message> message)
{ {
m_messageMutex.lock(); m_messageMutex.lock();
// messages m_messages.append(message);
m_messageMutex.unlock(); m_messageMutex.unlock();
} }

View file

@ -8,6 +8,7 @@
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
#include <QVector> #include <QVector>
#include <memory>
class Message; class Message;
@ -48,9 +49,9 @@ public:
const QString& streamGame() const { return m_streamGame; } const QString& streamGame() const { return m_streamGame; }
// methods // methods
void addMessage(Message* message); void addMessage(std::shared_ptr<Message> message);
QVector<Message*> getMessagesClone(); QVector<std::shared_ptr<Message>> getMessagesClone();
private: private:
Channel(QString channel); Channel(QString channel);
@ -61,7 +62,7 @@ private:
int m_referenceCount = 0; int m_referenceCount = 0;
QVector<Message*> m_messages; QVector<std::shared_ptr<Message>> m_messages;
QString m_name; QString m_name;
int m_roomID; int m_roomID;

View file

@ -56,7 +56,8 @@ SOURCES += main.cpp\
link.cpp \ link.cpp \
fonts.cpp \ fonts.cpp \
appsettings.cpp \ appsettings.cpp \
emojis.cpp emojis.cpp \
wordpart.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
chatwidget.h \ chatwidget.h \
@ -87,11 +88,12 @@ HEADERS += mainwindow.h \
word.h \ word.h \
link.h \ link.h \
fonts.h \ fonts.h \
common.h \
appsettings.h \ appsettings.h \
emojis.h emojis.h \
wordpart.h \
common.h
PRECOMPILED_HEADER = common.h PRECOMPILED_HEADER =
FORMS += \ FORMS += \
dialog.ui dialog.ui

View file

@ -1,6 +1,10 @@
#include "chatwidgetview.h" #include "chatwidgetview.h"
#include "QScroller" #include "word.h"
#include "QPainter" #include "wordpart.h"
#include "message.h"
#include <QScroller>
#include <QPainter>
ChatWidgetView::ChatWidgetView() ChatWidgetView::ChatWidgetView()
: QWidget(), : QWidget(),
@ -18,6 +22,17 @@ void ChatWidgetView::resizeEvent(QResizeEvent *)
{ {
scrollbar.resize(scrollbar.width(), height()); scrollbar.resize(scrollbar.width(), height());
scrollbar.move(width() - scrollbar.width(), 0); scrollbar.move(width() - scrollbar.width(), 0);
auto c = channel();
if (c == NULL) return;
auto messages = c->getMessagesClone();
for (std::shared_ptr<Message>& message : messages)
{
message.get()->layout(width(), true);
}
} }
void ChatWidgetView::paintEvent(QPaintEvent *) void ChatWidgetView::paintEvent(QPaintEvent *)
@ -28,7 +43,34 @@ void ChatWidgetView::paintEvent(QPaintEvent *)
if (c == NULL) return; if (c == NULL) return;
auto M = c->getMessagesClone(); auto messages = c->getMessagesClone();
int y = 0;
for (std::shared_ptr<Message> const& message : messages)
{
for (WordPart const& wordPart : message.get()->wordParts())
{
// image
if (wordPart.word().isImage())
{
LazyLoadedImage& lli = wordPart.word().getImage();
const QImage* image = lli.image();
if (image != NULL)
{
painter.drawImage(QRect(wordPart.x(), wordPart.y() + y, wordPart.width(), wordPart.height()), *image);
}
}
// text
else
{
painter.drawText(wordPart.x(), wordPart.y() + y, wordPart.getText());
}
}
y += message.get()->height();
}
} }

16
common.h.cpp Normal file
View file

@ -0,0 +1,16 @@
/*--------------------------------------------------------------------
* Precompiled header source file used by Visual Studio.NET to generate
* the .pch file.
*
* Due to issues with the dependencies checker within the IDE, it
* sometimes fails to recompile the PCH file, if we force the IDE to
* create the PCH file directly from the header file.
*
* This file is auto-generated by qmake since no PRECOMPILED_SOURCE was
* specified, and is used as the common stdafx.cpp. The file is only
* generated when creating .vcxproj project files, and is not used for
* command line compilations by nmake.
*
* WARNING: All changes made in this file will be lost.
--------------------------------------------------------------------*/
#include "common.h"

View file

@ -14,7 +14,7 @@ public:
map = new QMap<TKey, TValue>(); map = new QMap<TKey, TValue>();
} }
bool tryGet(const TKey &name, TValue& value) { bool tryGet(const TKey &name, TValue& value) const {
mutex->lock(); mutex->lock();
auto a = map->find(name); auto a = map->find(name);
if (a == map->end()) { if (a == map->end()) {

View file

@ -15,7 +15,7 @@ ConcurrentMap<QString, LazyLoadedImage*>* Emojis::imageCache = new ConcurrentMap
QString Emojis::replaceShortCodes(const QString &text) QString Emojis::replaceShortCodes(const QString &text)
{ {
#warning "xD" #pragma message WARN("xD")
return text; return text;
} }

View file

@ -23,14 +23,14 @@ Emotes::Emotes()
LazyLoadedImage* Emotes::getTwitchEmoteById(const QString &name, long id) LazyLoadedImage* Emotes::getTwitchEmoteById(const QString &name, long id)
{ {
#warning "xD" #pragma message WARN("xD")
return new LazyLoadedImage(NULL); return new LazyLoadedImage(NULL);
// return m_twitchEmoteFromCache->getOrAdd() // return m_twitchEmoteFromCache->getOrAdd()
} }
LazyLoadedImage* Emotes::getCheerImage(long long amount, bool animated) LazyLoadedImage* Emotes::getCheerImage(long long amount, bool animated)
{ {
#warning "xD" #pragma message WARN("xD")
return getCheerBadge(amount); return getCheerBadge(amount);
} }

View file

@ -9,6 +9,13 @@ QFont* Fonts::small = new QFont(DEFAULT_FONT);
QFont* Fonts::large = new QFont(DEFAULT_FONT); QFont* Fonts::large = new QFont(DEFAULT_FONT);
QFont* Fonts::veryLarge = new QFont(DEFAULT_FONT); QFont* Fonts::veryLarge = new QFont(DEFAULT_FONT);
QFontMetrics* Fonts::metricsMedium = new QFontMetrics(*medium );
QFontMetrics* Fonts::metricsMediumBold = new QFontMetrics(*mediumBold );
QFontMetrics* Fonts::metricsMediumItalic = new QFontMetrics(*mediumItalic);
QFontMetrics* Fonts::metricsSmall = new QFontMetrics(*small );
QFontMetrics* Fonts::metricsLarge = new QFontMetrics(*large );
QFontMetrics* Fonts::metricsVeryLarge = new QFontMetrics(*veryLarge );
Fonts::Fonts() Fonts::Fonts()
{ {
@ -25,3 +32,15 @@ QFont& Fonts::getFont(Type type)
return *medium; return *medium;
} }
QFontMetrics& Fonts::getFontMetrics(Type type)
{
if (type == Medium ) return *metricsMedium ;
if (type == MediumBold ) return *metricsMediumBold ;
if (type == MediumItalic) return *metricsMediumItalic;
if (type == Small ) return *metricsSmall ;
if (type == Large ) return *metricsLarge ;
if (type == VeryLarge ) return *metricsVeryLarge ;
return *metricsMedium;
}

11
fonts.h
View file

@ -1,7 +1,8 @@
#ifndef FONTS_H #ifndef FONTS_H
#define FONTS_H #define FONTS_H
#include "QFont" #include <QFont>
#include <QFontMetrics>
class Fonts class Fonts
{ {
@ -16,6 +17,7 @@ public:
}; };
static QFont& getFont(Type type); static QFont& getFont(Type type);
static QFontMetrics& getFontMetrics(Type type);
private: private:
Fonts(); Fonts();
@ -26,6 +28,13 @@ private:
static QFont* small; static QFont* small;
static QFont* large; static QFont* large;
static QFont* veryLarge; static QFont* veryLarge;
static QFontMetrics* metricsMedium;
static QFontMetrics* metricsMediumBold;
static QFontMetrics* metricsMediumItalic;
static QFontMetrics* metricsSmall;
static QFontMetrics* metricsLarge;
static QFontMetrics* metricsVeryLarge;
}; };
#endif // FONTS_H #endif // FONTS_H

View file

@ -22,7 +22,7 @@ QMutex* IrcManager::twitchBlockedUsersMutex = new QMutex();
IrcManager::IrcManager() IrcManager::IrcManager()
{ {
// account = Account::anon();
} }
void IrcManager::connect() void IrcManager::connect()
@ -160,7 +160,7 @@ void IrcManager::privateMessageReceived(IrcPrivateMessage *message)
auto c = Channel::getChannel(message->target().mid(1)); auto c = Channel::getChannel(message->target().mid(1));
if (c != NULL) { if (c != NULL) {
c->addMessage(new Message(*message, *c)); c->addMessage(std::shared_ptr<Message>(new Message(*message, *c)));
} }
} }

View file

@ -1,7 +1,8 @@
#include "lazyloadedimage.h" #include "lazyloadedimage.h"
LazyLoadedImage::LazyLoadedImage(const QString& url, qreal scale, const QString& name, const QString& tooltip, const QMargins& margin, bool isHat) LazyLoadedImage::LazyLoadedImage(const QString& url, qreal scale, const QString& name, const QString& tooltip, const QMargins& margin, bool isHat)
: m_url(url) : m_image(NULL)
, m_url(url)
, m_name(name) , m_name(name)
, m_tooltip(tooltip) , m_tooltip(tooltip)
, m_animated(false) , m_animated(false)

View file

@ -25,8 +25,22 @@ public:
bool animated() const { return m_animated; } bool animated() const { return m_animated; }
bool isHat() const { return m_ishat; } bool isHat() const { return m_ishat; }
const long width() const {
if (m_image == NULL) {
return 16;
}
return m_image->width();
}
const long height() const {
if (m_image == NULL) {
return 16;
}
return m_image->height();
}
private: private:
QImage* m_image = NULL; QImage* m_image;
qreal m_scale; qreal m_scale;
QString m_url; QString m_url;

View file

@ -1,8 +1,9 @@
#include <QApplication>
#include "mainwindow.h" #include "mainwindow.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "emojis.h" #include "emojis.h"
#include <QApplication>
#include <QClipboard> #include <QClipboard>
int main(int argc, char *argv[]) int main(int argc, char *argv[])

View file

@ -6,11 +6,18 @@
#include "link.h" #include "link.h"
#include "appsettings.h" #include "appsettings.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "fonts.h"
#include <ctime> #include <ctime>
#include <tuple> #include <tuple>
#include <list>
#include <QStringList> #include <QStringList>
#define MARGIN_LEFT 8
#define MARGIN_RIGHT 8
#define MARGIN_TOP 8
#define MARGIN_BOTTOM 8
LazyLoadedImage* Message::badgeStaff = new LazyLoadedImage(new QImage(":/images/staff_bg.png")); LazyLoadedImage* Message::badgeStaff = new LazyLoadedImage(new QImage(":/images/staff_bg.png"));
LazyLoadedImage* Message::badgeAdmin = new LazyLoadedImage(new QImage(":/images/admin_bg.png")); LazyLoadedImage* Message::badgeAdmin = new LazyLoadedImage(new QImage(":/images/admin_bg.png"));
LazyLoadedImage* Message::badgeModerator = new LazyLoadedImage(new QImage(":/images/moderator_bg.png")); LazyLoadedImage* Message::badgeModerator = new LazyLoadedImage(new QImage(":/images/moderator_bg.png"));
@ -22,29 +29,33 @@ LazyLoadedImage* Message::badgePremium = new LazyLoadedImage(new QImage(":/i
QRegularExpression* Message::cheerRegex = new QRegularExpression("cheer[1-9][0-9]*"); QRegularExpression* Message::cheerRegex = new QRegularExpression("cheer[1-9][0-9]*");
Message::Message(const QString &text) Message::Message(const QString &text)
: m_wordParts(new std::list<WordPart>())
{ {
} }
Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bool enablePingSound, Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bool enablePingSound,
bool isReceivedWhisper, bool isSentWhisper, bool includeChannel ) bool isReceivedWhisper, bool isSentWhisper, bool includeChannel )
: m_wordParts(new std::list<WordPart>())
{ {
m_parseTime = std::chrono::system_clock::now(); m_parseTime = std::chrono::system_clock::now();
auto words = new QList<Word>(); auto words = std::vector<Word>();
auto iterator = ircMessage.tags().find("id"); auto tags = ircMessage.tags();
if (iterator != ircMessage.tags().end()) auto iterator = tags.find("id");
if (iterator != tags.end())
{ {
m_id = iterator.value().toString(); m_id = iterator.value().toString();
} }
// timestamps // timestamps
iterator = ircMessage.tags().find("tmi-sent-ts"); iterator = tags.find("tmi-sent-ts");
std::time_t time = std::time(NULL); std::time_t time = std::time(NULL);
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
time = strtoll(iterator.value().toString().toStdString().c_str(), NULL, 10); time = strtoll(iterator.value().toString().toStdString().c_str(), NULL, 10);
} }
@ -57,13 +68,13 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time)); strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
QString timestampWithSeconds = QString(timeStampBuffer); QString timestampWithSeconds = QString(timeStampBuffer);
words->append(Word(timestamp, Word::TimestampNoSeconds, ColorScheme::instance().SystemMessageColor, QString(), QString())); words.push_back(Word(timestamp, Word::TimestampNoSeconds, ColorScheme::instance().SystemMessageColor, QString(), QString()));
words->append(Word(timestampWithSeconds, Word::TimestampWithSeconds, ColorScheme::instance().SystemMessageColor, QString(), QString())); words.push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds, ColorScheme::instance().SystemMessageColor, QString(), QString()));
// badges // badges
iterator = ircMessage.tags().find("badges"); iterator = tags.find("badges");
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
auto badges = iterator.value().toString().split(','); auto badges = iterator.value().toString().split(',');
@ -72,36 +83,36 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
if (badge.startsWith("bits/")) if (badge.startsWith("bits/"))
{ {
long long int cheer = strtoll(badge.mid(5).toStdString().c_str(), NULL, 10); long long int cheer = strtoll(badge.mid(5).toStdString().c_str(), NULL, 10);
words->append(Word(Emotes::getCheerBadge(cheer), Word::BadgeCheer, QString(), QString("Twitch Cheer" + QString::number(cheer)))); words.push_back(Word(Emotes::getCheerBadge(cheer), Word::BadgeCheer, QString(), QString("Twitch Cheer" + QString::number(cheer))));
} }
else if (badge == "staff/1") else if (badge == "staff/1")
{ {
words->append(Word(badgeStaff, Word::BadgeStaff, QString(), QString("Twitch Staff"))); words.push_back(Word(badgeStaff, Word::BadgeStaff, QString(), QString("Twitch Staff")));
} }
else if (badge == "admin/1") else if (badge == "admin/1")
{ {
words->append(Word(badgeAdmin, Word::BadgeAdmin, QString(), QString("Twitch Admin"))); words.push_back(Word(badgeAdmin, Word::BadgeAdmin, QString(), QString("Twitch Admin")));
} }
else if (badge == "global_mod/1") else if (badge == "global_mod/1")
{ {
words->append(Word(badgeGlobalmod, Word::BadgeGlobalMod, QString(), QString("Global Moderator"))); words.push_back(Word(badgeGlobalmod, Word::BadgeGlobalMod, QString(), QString("Global Moderator")));
} }
else if (badge == "moderator/1") else if (badge == "moderator/1")
{ {
#warning "xD" #pragma message WARN("xD")
words->append(Word(badgeTurbo, Word::BadgeModerator, QString(), QString("Channel Moderator"))); // custom badge words.push_back(Word(badgeTurbo, Word::BadgeModerator, QString(), QString("Channel Moderator"))); // custom badge
} }
else if (badge == "turbo/1") else if (badge == "turbo/1")
{ {
words->append(Word(badgeStaff, Word::BadgeTurbo, QString(), QString("Turbo Subscriber"))); words.push_back(Word(badgeStaff, Word::BadgeTurbo, QString(), QString("Turbo Subscriber")));
} }
else if (badge == "broadcaster/1") else if (badge == "broadcaster/1")
{ {
words->append(Word(badgeBroadcaster, Word::BadgeBroadcaster, QString(), QString("Channel Broadcaster"))); words.push_back(Word(badgeBroadcaster, Word::BadgeBroadcaster, QString(), QString("Channel Broadcaster")));
} }
else if (badge == "premium/1") else if (badge == "premium/1")
{ {
words->append(Word(badgePremium, Word::BadgePremium, QString(), QString("Twitch Prime"))); words.push_back(Word(badgePremium, Word::BadgePremium, QString(), QString("Twitch Prime")));
} }
} }
} }
@ -109,8 +120,8 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
// color // color
QColor usernameColor = ColorScheme::instance().SystemMessageColor; QColor usernameColor = ColorScheme::instance().SystemMessageColor;
iterator = ircMessage.tags().find("color"); iterator = tags.find("color");
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
usernameColor = QColor(iterator.value().toString()); usernameColor = QColor(iterator.value().toString());
} }
@ -119,7 +130,7 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
if (includeChannel) if (includeChannel)
{ {
QString channelName("#" + channel.name()); QString channelName("#" + channel.name());
words->append(Word(channelName, Word::Misc, ColorScheme::instance().SystemMessageColor, QString(channelName), QString(), Link(Link::Url, channel.name() + "\n" + m_id))); words.push_back(Word(channelName, Word::Misc, ColorScheme::instance().SystemMessageColor, QString(channelName), QString(), Link(Link::Url, channel.name() + "\n" + m_id)));
} }
// username // username
@ -127,9 +138,9 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
if (m_userName.isEmpty()) if (m_userName.isEmpty())
{ {
auto iterator = ircMessage.tags().find("login"); auto iterator = tags.find("login");
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
m_userName = iterator.value().toString(); m_userName = iterator.value().toString();
} }
@ -137,8 +148,8 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
QString displayName; QString displayName;
iterator = ircMessage.tags().find("display-name"); iterator = tags.find("display-name");
if (iterator == ircMessage.tags().end()) { if (iterator == tags.end()) {
displayName = ircMessage.account(); displayName = ircMessage.account();
} }
else { else {
@ -163,7 +174,7 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
userDisplayString += ": "; userDisplayString += ": ";
} }
words->append(Word(userDisplayString, Word::Username, usernameColor, userDisplayString, QString())); words.push_back(Word(userDisplayString, Word::Username, usernameColor, userDisplayString, QString()));
// highlights // highlights
#pragma message WARN("xD") #pragma message WARN("xD")
@ -171,18 +182,18 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
// bits // bits
QString bits = ""; QString bits = "";
iterator = ircMessage.tags().find("bits"); iterator = tags.find("bits");
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
bits = iterator.value().toString(); bits = iterator.value().toString();
} }
// twitch emotes // twitch emotes
QVector<std::pair<long int, LazyLoadedImage*>> twitchEmotes; std::vector<std::pair<long int, LazyLoadedImage*>> twitchEmotes;
iterator = ircMessage.tags().find("emotes"); iterator = tags.find("emotes");
if (iterator != ircMessage.tags().end()) if (iterator != tags.end())
{ {
auto emotes = iterator.value().toString().split('/'); auto emotes = iterator.value().toString().split('/');
@ -211,7 +222,7 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
QString name = ircMessage.content().mid(start, end - start); QString name = ircMessage.content().mid(start, end - start);
twitchEmotes.append(std::pair<long int, LazyLoadedImage*>(start, Emotes::getTwitchEmoteById(name, id))); twitchEmotes.push_back(std::pair<long int, LazyLoadedImage*>(start, Emotes::getTwitchEmoteById(name, id)));
} }
} }
@ -234,8 +245,8 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
if (currentTwitchEmote->first == i) if (currentTwitchEmote->first == i)
{ {
words->append(Word(currentTwitchEmote->second, Word::TwitchEmoteImage, currentTwitchEmote->second->name(), currentTwitchEmote->second->name() + QString("\nTwitch Emote"))); words.push_back(Word(currentTwitchEmote->second, Word::TwitchEmoteImage, currentTwitchEmote->second->name(), currentTwitchEmote->second->name() + QString("\nTwitch Emote")));
words->append(Word(currentTwitchEmote->second->name(), Word::TwitchEmoteText, textColor, currentTwitchEmote->second->name(), currentTwitchEmote->second->name() + QString("\nTwitch Emote"))); words.push_back(Word(currentTwitchEmote->second->name(), Word::TwitchEmoteText, textColor, currentTwitchEmote->second->name(), currentTwitchEmote->second->name() + QString("\nTwitch Emote")));
i += split.length() + 1; i += split.length() + 1;
currentTwitchEmote = std::next(currentTwitchEmote); currentTwitchEmote = std::next(currentTwitchEmote);
@ -296,26 +307,26 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
LazyLoadedImage* imageAnimated = Emotes::miscImageFromCache().getOrAdd(bitsLinkAnimated, [&bitsLinkAnimated]{ return new LazyLoadedImage(bitsLinkAnimated); }); LazyLoadedImage* imageAnimated = Emotes::miscImageFromCache().getOrAdd(bitsLinkAnimated, [&bitsLinkAnimated]{ return new LazyLoadedImage(bitsLinkAnimated); });
LazyLoadedImage* image = Emotes::miscImageFromCache().getOrAdd(bitsLink, [&bitsLink]{ return new LazyLoadedImage(bitsLink); }); LazyLoadedImage* image = Emotes::miscImageFromCache().getOrAdd(bitsLink, [&bitsLink]{ return new LazyLoadedImage(bitsLink); });
words->append(Word(imageAnimated, Word::BitsAnimated, QString("cheer"), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6")))); words.push_back(Word(imageAnimated, Word::BitsAnimated, QString("cheer"), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6"))));
words->append(Word(image, Word::Bits, QString("cheer"), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6")))); words.push_back(Word(image, Word::Bits, QString("cheer"), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6"))));
words->append(Word(QString("x" + string.mid(5)), Word::BitsAmount, bitsColor, QString(string.mid(5)), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6")))); words.push_back(Word(QString("x" + string.mid(5)), Word::BitsAmount, bitsColor, QString(string.mid(5)), QString("Twitch Cheer"), Link(Link::Url, QString("https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6"))));
continue;
} }
continue;
// bttv / ffz emotes // bttv / ffz emotes
LazyLoadedImage* bttvEmote; LazyLoadedImage* bttvEmote;
#pragma message WARN( "xD ignored emotes")
if ( if (
#warning "xD ignored emotes"
Emotes::bttvEmotes().tryGet(string, bttvEmote) || Emotes::bttvEmotes().tryGet(string, bttvEmote) ||
channel.bttvChannelEmotes().tryGet(string, bttvEmote) || channel.bttvChannelEmotes().tryGet(string, bttvEmote) ||
Emotes::ffzEmotes().tryGet(string, bttvEmote) || Emotes::ffzEmotes().tryGet(string, bttvEmote) ||
channel.ffzChannelEmotes().tryGet(string, bttvEmote) || channel.ffzChannelEmotes().tryGet(string, bttvEmote) ||
Emotes::chatterinoEmotes().tryGet(string, bttvEmote)) Emotes::chatterinoEmotes().tryGet(string, bttvEmote))
{ {
words->append(Word(bttvEmote, Word::BttvEmoteImage, bttvEmote->name(), bttvEmote->tooltip(), Link(Link::Url, bttvEmote->url()))); words.push_back(Word(bttvEmote, Word::BttvEmoteImage, bttvEmote->name(), bttvEmote->tooltip(), Link(Link::Url, bttvEmote->url())));
continue; continue;
} }
@ -323,22 +334,131 @@ Message::Message(const IrcPrivateMessage& ircMessage, const Channel& channel, bo
// actually just a word // actually just a word
QString link = matchLink(string); QString link = matchLink(string);
words->append(Word(string, Word::Text, textColor, string, QString(), link.isEmpty() ? Link() : Link(Link::Url, link))); words.push_back(Word(string, Word::Text, textColor, string, QString(), link.isEmpty() ? Link() : Link(Link::Url, link)));
} }
} }
i += split.length() + 1; i += split.length() + 1;
} }
this->words() = words; this->m_words = words;
#warning "xD" #pragma message WARN("xD")
// if (!isReceivedWhisper && AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) // if (!isReceivedWhisper && AppSettings.HighlightIgnoredUsers.ContainsKey(Username))
// { // {
// HighlightTab = false; // HighlightTab = false;
// } // }
} }
//static void normalize
bool Message::layout(int width, bool enableEmoteMargins)
{
width = width - (width % 2);
int mediumTextLineHeight = Fonts::getFontMetrics(Fonts::Medium).height();
bool redraw = width != m_currentLayoutWidth || m_relayoutRequested;
if (m_recalculateImages || m_recalculateText)
{
redraw = true;
for (auto& word : m_words)
{
if (word.isImage())
{
if (m_recalculateImages)
{
auto& image = word.getImage();
qreal w = image.width();
qreal h = image.height();
if (AppSettings::scaleEmotesByLineHeight())
{
word.setSize(w * mediumTextLineHeight / h * AppSettings::emoteScale(), mediumTextLineHeight * AppSettings::emoteScale());
}
else
{
word.setSize(w * image.scale() * AppSettings::emoteScale(), h * image.scale() * AppSettings::emoteScale());
}
}
}
else
{
if (m_recalculateText)
{
QFontMetrics& metrics = word.getFontMetrics();
word.setSize(metrics.width(word.getText()), metrics.height());
}
}
}
m_recalculateImages = false;
m_recalculateText = false;
}
if (redraw)
{
int x = MARGIN_LEFT;
int y = MARGIN_TOP;
int right = width - MARGIN_RIGHT;
std::list<WordPart>* parts;
auto lineStart = m_wordParts->begin();
int lineHeight = 0;
for (auto it = m_words.begin(); it != m_words.end(); ++it)
{
Word& word = *it;
int xOffset = 0, yOffset = 0;
if (enableEmoteMargins)
{
if (word.isImage() && word.getImage().isHat())
{
xOffset = -word.width() + 2;
}
else
{
xOffset = word.xOffset();
yOffset = word.yOffset();
}
lineHeight = std::max(word.height(), lineHeight);
}
if (x + word.width() + xOffset <= right)
{
parts->push_back(WordPart(word, x, y, QStringRef(&word.copyText())));
x += word.width() + xOffset;
}
// else if (word.isText() && word.getText().length() > 2)
// {
// }
else
{
parts->push_back(WordPart(word, x, y, QStringRef(&word.copyText())));
lineHeight = std::max(word.height(), lineHeight);
}
}
auto tmp = m_wordParts;
m_wordParts = parts;
delete tmp;
m_height = y + lineHeight;
}
return redraw;
}
bool Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage*>& a, const std::pair<long int, LazyLoadedImage*>& b) bool Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage*>& a, const std::pair<long int, LazyLoadedImage*>& b)
{ {
return a.first < b.first; return a.first < b.first;
@ -346,6 +466,6 @@ bool Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage*>& a, c
QString Message::matchLink(const QString &string) QString Message::matchLink(const QString &string)
{ {
#warning "xD" #pragma message WARN("xD")
return QString(); return QString();
} }

View file

@ -1,29 +1,27 @@
#ifndef MESSAGE_H #ifndef MESSAGE_H
#define MESSAGE_H #define MESSAGE_H
#include "IrcMessage"
#include "word.h" #include "word.h"
#include "chrono" #include "wordpart.h"
#include "channel.h" #include "channel.h"
#include <IrcMessage>
#include <QVector>
#include <chrono>
class Message class Message
{ {
public: public:
// enum Badges : char {
// None = 0,
// Mod = 1,
// Turbo = 2,
// Sub = 4,
// Staff = 8,
// GlobalMod = 16,
// Admin = 32,
// Broadcaster = 64,
// };
Message(const QString& text); Message(const QString& text);
Message(const IrcPrivateMessage& ircMessage, const Channel& Channel, bool enablePingSound = true, Message(const IrcPrivateMessage& ircMessage, const Channel& Channel, bool enablePingSound = true,
bool isReceivedWhisper = false, bool isSentWhisper = false, bool includeChannel = false); bool isReceivedWhisper = false, bool isSentWhisper = false, bool includeChannel = false);
~Message() {
if (m_wordParts != NULL) {
delete m_wordParts;
}
}
bool canHighlightTab() const { bool canHighlightTab() const {
return m_highlightTab; return m_highlightTab;
} }
@ -44,10 +42,14 @@ public:
return m_displayName; return m_displayName;
} }
QList<Word> words() const { const std::vector<Word> words() const {
return m_words; return m_words;
} }
const std::list<WordPart> wordParts() const {
return *m_wordParts;
}
bool disabled() const { bool disabled() const {
return m_disabled; return m_disabled;
} }
@ -56,6 +58,16 @@ public:
return m_id; return m_id;
} }
int height() const {
return m_height;
}
bool layout(int width, bool enableEmoteMargins = true);
void requestRelayout() { m_relayoutRequested = true; }
void requestTextRecalculation() { m_recalculateText = true; }
void requestImageRecalculation() { m_recalculateImages = true; }
private: private:
static LazyLoadedImage* badgeStaff; static LazyLoadedImage* badgeStaff;
static LazyLoadedImage* badgeAdmin; static LazyLoadedImage* badgeAdmin;
@ -77,7 +89,15 @@ private:
QString m_displayName = ""; QString m_displayName = "";
QString m_id = ""; QString m_id = "";
QList<Word> m_words; int m_height = 0;
std::vector<Word> m_words;
std::list<WordPart>* m_wordParts;
long m_currentLayoutWidth = -1;
bool m_relayoutRequested = true;
bool m_recalculateText = true;
bool m_recalculateImages = true;
static QString matchLink(const QString& string); static QString matchLink(const QString& string);

View file

@ -11,13 +11,14 @@ Word::Word(LazyLoadedImage* image, Type type, const QString& copytext, const QSt
, m_color() , m_color()
, m_link(link) , m_link(link)
{ {
image->width(); // professional segfault test
} }
// Text word // Text word
Word::Word(const QString& text, Type type, const QColor& color, const QString& copytext, const QString& tooltip, const Link& link) Word::Word(const QString& text, Type type, const QColor& color, const QString& copytext, const QString& tooltip, const Link& link)
: m_image(nullptr) : m_image(NULL)
, m_text(text) , m_text(text)
, m_isImage(true) , m_isImage(false)
, m_type(type) , m_type(type)
, m_copyText(copytext) , m_copyText(copytext)
, m_tooltip(tooltip) , m_tooltip(tooltip)

49
word.h
View file

@ -27,6 +27,7 @@ public:
BttvGifEmoteText = 0x200, BttvGifEmoteText = 0x200,
FfzEmoteImage = 0x400, FfzEmoteImage = 0x400,
FfzEmoteText = 0x800, FfzEmoteText = 0x800,
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage,
Bits = 0x1000, Bits = 0x1000,
BitsAnimated = 0x2000, BitsAnimated = 0x2000,
@ -75,30 +76,19 @@ public:
return m_height; return m_height;
} }
int x() const { void setSize(int width, int height) {
return m_x; m_width = width;
} m_height = height;
int y() const {
return m_y;
}
int right() const {
return m_x + m_width;
}
int bottom() const {
return m_y + m_height;
}
QRect rect() const {
return QRect(m_x, m_y, m_width, m_height);
} }
bool isImage() const { bool isImage() const {
return m_isImage; return m_isImage;
} }
bool isText() const {
return !m_isImage;
}
const QString& copyText() const { const QString& copyText() const {
return m_copyText; return m_copyText;
} }
@ -111,6 +101,10 @@ public:
return Fonts::getFont(m_font); return Fonts::getFont(m_font);
} }
QFontMetrics& getFontMetrics() const {
return Fonts::getFontMetrics(m_font);
}
Type type() const { Type type() const {
return m_type; return m_type;
} }
@ -127,6 +121,19 @@ public:
return m_link; return m_link;
} }
int xOffset() const {
return m_xOffset;
}
int yOffset() const {
return m_yOffset;
}
void setOffset(int xOffset, int yOffset) {
m_xOffset = std::max(0, xOffset);
m_yOffset = std::max(0, yOffset);
}
private: private:
LazyLoadedImage* m_image; LazyLoadedImage* m_image;
QString m_text; QString m_text;
@ -136,10 +143,12 @@ private:
Type m_type; Type m_type;
QString m_copyText; QString m_copyText;
QString m_tooltip; QString m_tooltip;
int m_x;
int m_y;
int m_width; int m_width;
int m_height; int m_height;
int m_xOffset;
int m_yOffset;
bool m_hasTrailingSpace; bool m_hasTrailingSpace;
Fonts::Type m_font = Fonts::Medium; Fonts::Type m_font = Fonts::Medium;
Link m_link; Link m_link;

18
wordpart.cpp Normal file
View file

@ -0,0 +1,18 @@
#include "wordpart.h"
#include "word.h"
WordPart::WordPart(Word& word, int x, int y, const QStringRef& copyText, bool allowTrailingSpace)
: m_word(word)
, m_copyText(copyText)
, m_x(x)
, m_y(y)
, m_width(word.width())
, m_height(word.height())
, m_trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
{
}
const QString& WordPart::getText() const
{
return m_word.getText();
}

74
wordpart.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef WORDPART_H
#define WORDPART_H
#include <QRect>
#include <QStringRef>
class Word;
class WordPart
{
public:
WordPart(Word& word, int x, int y, const QStringRef& copyText, bool allowTrailingSpace = true);
const Word& word() const {
return m_word;
}
int width() const {
return m_width;
}
int height() const {
return m_height;
}
int x() const {
return m_x;
}
int y() const {
return m_y;
}
void setPosition(int x, int y) {
m_x = x;
m_y = y;
}
int right() const {
return m_x + m_width;
}
int bottom() const {
return m_y + m_height;
}
QRect rect() const {
return QRect(m_x, m_y, m_width, m_height);
}
const QStringRef copyText() const {
return m_copyText;
}
int hasTrailingSpace() const {
return m_trailingSpace;
}
const QString& getText() const;
private:
Word& m_word;
QStringRef m_copyText;
int m_x;
int m_y;
int m_width;
int m_height;
bool m_trailingSpace;
};
#endif // WORDPART_H