move around files
This commit is contained in:
Symlink
+1
@@ -0,0 +1 @@
|
||||
../.clang-format
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "messages/lazyloadedimage.h"
|
||||
|
||||
#include "asyncexec.h"
|
||||
#include "emotemanager.h"
|
||||
#include "ircmanager.h"
|
||||
#include "util/urlfetch.h"
|
||||
#include "windowmanager.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name,
|
||||
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||
: _currentPixmap(NULL)
|
||||
, _currentFrame(0)
|
||||
, _currentFrameOffset(0)
|
||||
, _url(url)
|
||||
, _name(name)
|
||||
, _tooltip(tooltip)
|
||||
, _animated(false)
|
||||
, _margin(margin)
|
||||
, _ishat(isHat)
|
||||
, _scale(scale)
|
||||
, _isLoading(false)
|
||||
{
|
||||
}
|
||||
|
||||
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name,
|
||||
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||
: _currentPixmap(image)
|
||||
, _currentFrame(0)
|
||||
, _currentFrameOffset(0)
|
||||
, _name(name)
|
||||
, _tooltip(tooltip)
|
||||
, _animated(false)
|
||||
, _margin(margin)
|
||||
, _ishat(isHat)
|
||||
, _scale(scale)
|
||||
, _isLoading(true)
|
||||
{
|
||||
}
|
||||
|
||||
void LazyLoadedImage::loadImage()
|
||||
{
|
||||
util::urlFetch(_url, [=](QNetworkReply &reply) {
|
||||
QByteArray array = reply.readAll();
|
||||
QBuffer buffer(&array);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QImage image;
|
||||
QImageReader reader(&buffer);
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||
if (reader.read(&image)) {
|
||||
auto pixmap = new QPixmap(QPixmap::fromImage(image));
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
_currentPixmap = pixmap;
|
||||
}
|
||||
|
||||
FrameData data;
|
||||
data.duration = std::max(20, reader.nextImageDelay());
|
||||
data.image = pixmap;
|
||||
|
||||
_allFrames.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (_allFrames.size() > 1) {
|
||||
_animated = true;
|
||||
|
||||
EmoteManager::getInstance().getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
||||
}
|
||||
|
||||
EmoteManager::getInstance().incGeneration();
|
||||
WindowManager::getInstance().layoutVisibleChatWidgets();
|
||||
});
|
||||
}
|
||||
|
||||
void LazyLoadedImage::gifUpdateTimout()
|
||||
{
|
||||
_currentFrameOffset += GIF_FRAME_LENGTH;
|
||||
|
||||
while (true) {
|
||||
if (_currentFrameOffset > _allFrames.at(_currentFrame).duration) {
|
||||
_currentFrameOffset -= _allFrames.at(_currentFrame).duration;
|
||||
_currentFrame = (_currentFrame + 1) % _allFrames.size();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_currentPixmap = _allFrames[_currentFrame].image;
|
||||
}
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,109 @@
|
||||
#ifndef LAZYLOADEDIMAGE_H
|
||||
#define LAZYLOADEDIMAGE_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class LazyLoadedImage : QObject
|
||||
{
|
||||
public:
|
||||
explicit LazyLoadedImage(const QString &_url, qreal _scale = 1, const QString &_name = "",
|
||||
const QString &_tooltip = "", const QMargins &_margin = QMargins(),
|
||||
bool isHat = false);
|
||||
explicit LazyLoadedImage(QPixmap *_currentPixmap, qreal _scale = 1, const QString &_name = "",
|
||||
const QString &_tooltip = "", const QMargins &_margin = QMargins(),
|
||||
bool isHat = false);
|
||||
|
||||
const QPixmap *getPixmap()
|
||||
{
|
||||
if (!_isLoading) {
|
||||
_isLoading = true;
|
||||
|
||||
loadImage();
|
||||
}
|
||||
return _currentPixmap;
|
||||
}
|
||||
|
||||
qreal getScale() const
|
||||
{
|
||||
return _scale;
|
||||
}
|
||||
|
||||
const QString &getUrl() const
|
||||
{
|
||||
return _url;
|
||||
}
|
||||
|
||||
const QString &getName() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
const QString &getTooltip() const
|
||||
{
|
||||
return _tooltip;
|
||||
}
|
||||
|
||||
const QMargins &getMargin() const
|
||||
{
|
||||
return _margin;
|
||||
}
|
||||
|
||||
bool getAnimated() const
|
||||
{
|
||||
return _animated;
|
||||
}
|
||||
|
||||
bool isHat() const
|
||||
{
|
||||
return _ishat;
|
||||
}
|
||||
|
||||
int getWidth() const
|
||||
{
|
||||
if (_currentPixmap == NULL) {
|
||||
return 16;
|
||||
}
|
||||
return _currentPixmap->width();
|
||||
}
|
||||
|
||||
int getHeight() const
|
||||
{
|
||||
if (_currentPixmap == NULL) {
|
||||
return 16;
|
||||
}
|
||||
return _currentPixmap->height();
|
||||
}
|
||||
|
||||
private:
|
||||
struct FrameData {
|
||||
QPixmap *image;
|
||||
int duration;
|
||||
};
|
||||
|
||||
QPixmap *_currentPixmap;
|
||||
std::vector<FrameData> _allFrames;
|
||||
int _currentFrame;
|
||||
int _currentFrameOffset;
|
||||
|
||||
QString _url;
|
||||
QString _name;
|
||||
QString _tooltip;
|
||||
bool _animated;
|
||||
QMargins _margin;
|
||||
bool _ishat;
|
||||
qreal _scale;
|
||||
|
||||
bool _isLoading;
|
||||
|
||||
void loadImage();
|
||||
|
||||
void gifUpdateTimout();
|
||||
};
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // LAZYLOADEDIMAGE_H
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef LIMITEDQUEUE_H
|
||||
#define LIMITEDQUEUE_H
|
||||
|
||||
#include "messages/limitedqueuesnapshot.h"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
template <typename T>
|
||||
class LimitedQueue
|
||||
{
|
||||
public:
|
||||
LimitedQueue(int limit = 100, int buffer = 25)
|
||||
: _offset(0)
|
||||
, _limit(limit)
|
||||
, _buffer(buffer)
|
||||
{
|
||||
_vector = std::make_shared<std::vector<T>>();
|
||||
_vector->reserve(_limit + _buffer);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
_vector = std::make_shared<std::vector<T>>();
|
||||
_vector->reserve(_limit + _buffer);
|
||||
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
// return true if an item was deleted
|
||||
// deleted will be set if the item was deleted
|
||||
bool appendItem(const T &item, T &deleted)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_vector->size() >= _limit) {
|
||||
// vector is full
|
||||
if (_offset == _buffer) {
|
||||
deleted = _vector->at(_offset);
|
||||
|
||||
// create new vector
|
||||
auto newVector = std::make_shared<std::vector<T>>();
|
||||
newVector->reserve(_limit + _buffer);
|
||||
|
||||
for (unsigned int i = 0; i < _limit - 1; i++) {
|
||||
newVector->push_back(_vector->at(i + _offset));
|
||||
}
|
||||
newVector->push_back(item);
|
||||
|
||||
_offset = 0;
|
||||
_vector = newVector;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
deleted = _vector->at(_offset);
|
||||
|
||||
// append item and increment offset("deleting" first element)
|
||||
_vector->push_back(item);
|
||||
_offset++;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// append item
|
||||
_vector->push_back(item);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
messages::LimitedQueueSnapshot<T> getSnapshot()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
||||
if (_vector->size() < _limit) {
|
||||
return LimitedQueueSnapshot<T>(_vector, _offset, _vector->size());
|
||||
} else {
|
||||
return LimitedQueueSnapshot<T>(_vector, _offset, _limit);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<T>> _vector;
|
||||
std::mutex _mutex;
|
||||
|
||||
unsigned int _offset;
|
||||
unsigned int _limit;
|
||||
unsigned int _buffer;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // LIMITEDQUEUE_H
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef LIMITEDQUEUESNAPSHOT_H
|
||||
#define LIMITEDQUEUESNAPSHOT_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
template <typename T>
|
||||
class LimitedQueueSnapshot
|
||||
{
|
||||
public:
|
||||
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> vector, int offset, int size)
|
||||
: _vector(vector)
|
||||
, _offset(offset)
|
||||
, _length(size)
|
||||
{
|
||||
}
|
||||
|
||||
int getSize()
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
T const &operator[](int index) const
|
||||
{
|
||||
return _vector->at(index + _offset);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<T>> _vector;
|
||||
|
||||
int _offset;
|
||||
int _length;
|
||||
};
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // LIMITEDQUEUESNAPSHOT_H
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "messages/link.h"
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
Link::Link()
|
||||
: type(None)
|
||||
, value(QString())
|
||||
{
|
||||
}
|
||||
|
||||
Link::Link(Type type, const QString &value)
|
||||
: type(type)
|
||||
, value(value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef LINK_H
|
||||
#define LINK_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class Link
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
None,
|
||||
Url,
|
||||
CloseCurrentSplit,
|
||||
UserInfo,
|
||||
UserTimeout,
|
||||
UserBan,
|
||||
InsertText,
|
||||
ShowMessage,
|
||||
};
|
||||
|
||||
Link();
|
||||
Link(Type getType, const QString &getValue);
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return type != None;
|
||||
}
|
||||
|
||||
Type getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
const QString &getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type;
|
||||
QString value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // LINK_H
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "messages/message.h"
|
||||
#include "channel.h"
|
||||
#include "colorscheme.h"
|
||||
#include "emojis.h"
|
||||
#include "emotemanager.h"
|
||||
#include "fontmanager.h"
|
||||
#include "ircmanager.h"
|
||||
#include "messages/link.h"
|
||||
#include "qcolor.h"
|
||||
#include "resources.h"
|
||||
#include "settingsmanager.h"
|
||||
|
||||
#include <QObjectUserData>
|
||||
#include <QStringList>
|
||||
#include <ctime>
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
Message::Message(const QString &text)
|
||||
: _words()
|
||||
{
|
||||
_words.push_back(
|
||||
Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString()));
|
||||
}
|
||||
|
||||
Message::Message(const std::vector<Word> &words)
|
||||
: _words(words)
|
||||
{
|
||||
}
|
||||
|
||||
bool Message::getCanHighlightTab() const
|
||||
{
|
||||
return _highlightTab;
|
||||
}
|
||||
|
||||
const QString &Message::getTimeoutUser() const
|
||||
{
|
||||
return _timeoutUser;
|
||||
}
|
||||
|
||||
int Message::getTimeoutCount() const
|
||||
{
|
||||
return _timeoutCount;
|
||||
}
|
||||
|
||||
const QString &Message::getUserName() const
|
||||
{
|
||||
return _userName;
|
||||
}
|
||||
|
||||
const QString &Message::getDisplayName() const
|
||||
{
|
||||
return _displayName;
|
||||
}
|
||||
|
||||
const QString &Message::getContent() const
|
||||
{
|
||||
return _content;
|
||||
}
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock> &Message::getParseTime() const
|
||||
{
|
||||
return _parseTime;
|
||||
}
|
||||
|
||||
std::vector<Word> &Message::getWords()
|
||||
{
|
||||
return _words;
|
||||
}
|
||||
|
||||
bool Message::isDisabled() const
|
||||
{
|
||||
return _isDisabled;
|
||||
}
|
||||
|
||||
const QString &Message::getId() const
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef MESSAGE_H
|
||||
#define MESSAGE_H
|
||||
|
||||
#include "messages/message.h"
|
||||
#include "messages/messageparseargs.h"
|
||||
#include "messages/word.h"
|
||||
#include "messages/wordpart.h"
|
||||
|
||||
#include <IrcMessage>
|
||||
#include <QVector>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Channel;
|
||||
|
||||
namespace messages {
|
||||
class Message;
|
||||
|
||||
typedef std::shared_ptr<Message> SharedMessage;
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
Message(const QString &text);
|
||||
Message(const std::vector<messages::Word> &words);
|
||||
|
||||
bool getCanHighlightTab() const;
|
||||
const QString &getTimeoutUser() const;
|
||||
int getTimeoutCount() const;
|
||||
const QString &getUserName() const;
|
||||
const QString &getDisplayName() const;
|
||||
const QString &getContent() const;
|
||||
const std::chrono::time_point<std::chrono::system_clock> &getParseTime() const;
|
||||
std::vector<Word> &getWords();
|
||||
bool isDisabled() const;
|
||||
const QString &getId() const;
|
||||
|
||||
private:
|
||||
static LazyLoadedImage *badgeStaff;
|
||||
static LazyLoadedImage *badgeAdmin;
|
||||
static LazyLoadedImage *badgeGlobalmod;
|
||||
static LazyLoadedImage *badgeModerator;
|
||||
static LazyLoadedImage *badgeTurbo;
|
||||
static LazyLoadedImage *badgeBroadcaster;
|
||||
static LazyLoadedImage *badgePremium;
|
||||
|
||||
static QRegularExpression *cheerRegex;
|
||||
|
||||
bool _highlightTab = false;
|
||||
QString _timeoutUser = "";
|
||||
int _timeoutCount = 0;
|
||||
bool _isDisabled = false;
|
||||
std::chrono::time_point<std::chrono::system_clock> _parseTime;
|
||||
|
||||
QString _userName = "";
|
||||
QString _displayName = "";
|
||||
QString _content;
|
||||
QString _id = "";
|
||||
|
||||
std::vector<Word> _words;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // MESSAGE_H
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "messagebuilder.h"
|
||||
#include "colorscheme.h"
|
||||
#include "emotemanager.h"
|
||||
#include "resources.h"
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
MessageBuilder::MessageBuilder()
|
||||
: _words()
|
||||
{
|
||||
_parseTime = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
SharedMessage MessageBuilder::build()
|
||||
{
|
||||
return SharedMessage(new Message(_words));
|
||||
}
|
||||
|
||||
void MessageBuilder::appendWord(const Word &word)
|
||||
{
|
||||
_words.push_back(word);
|
||||
}
|
||||
|
||||
void MessageBuilder::appendTimestamp()
|
||||
{
|
||||
time_t t;
|
||||
time(&t);
|
||||
appendTimestamp(t);
|
||||
}
|
||||
|
||||
void MessageBuilder::appendTimestamp(time_t time)
|
||||
{
|
||||
char timeStampBuffer[69];
|
||||
|
||||
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
|
||||
QString timestamp = QString(timeStampBuffer);
|
||||
|
||||
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
|
||||
QString timestampWithSeconds = QString(timeStampBuffer);
|
||||
|
||||
appendWord(Word(timestamp, Word::TimestampNoSeconds,
|
||||
ColorScheme::getInstance().SystemMessageColor, QString(), QString()));
|
||||
appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds,
|
||||
ColorScheme::getInstance().SystemMessageColor, QString(), QString()));
|
||||
}
|
||||
|
||||
QString MessageBuilder::matchLink(const QString &string)
|
||||
{
|
||||
// TODO: Implement this xD
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef MESSAGEBUILDER_H
|
||||
#define MESSAGEBUILDER_H
|
||||
|
||||
#include "messages/message.h"
|
||||
#include <ctime>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class MessageBuilder
|
||||
{
|
||||
public:
|
||||
MessageBuilder();
|
||||
|
||||
SharedMessage build();
|
||||
|
||||
void appendWord(const Word &word);
|
||||
void appendTimestamp();
|
||||
void appendTimestamp(std::time_t time);
|
||||
|
||||
QString matchLink(const QString &string);
|
||||
|
||||
private:
|
||||
std::vector<Word> _words;
|
||||
std::chrono::time_point<std::chrono::system_clock> _parseTime;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MESSAGEBUILDER_H
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef MESSAGEPARSEARGS_H
|
||||
#define MESSAGEPARSEARGS_H
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
struct MessageParseArgs {
|
||||
public:
|
||||
bool disablePingSoungs = false;
|
||||
bool isReceivedWhisper = false;
|
||||
bool isSentWhisper = false;
|
||||
bool includeChannelName = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MESSAGEPARSEARGS_H
|
||||
@@ -0,0 +1,320 @@
|
||||
#include "messageref.h"
|
||||
#include "emotemanager.h"
|
||||
#include "settingsmanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#define MARGIN_LEFT 8
|
||||
#define MARGIN_RIGHT 8
|
||||
#define MARGIN_TOP 8
|
||||
#define MARGIN_BOTTOM 8
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
MessageRef::MessageRef(SharedMessage message)
|
||||
: _message(message)
|
||||
, _wordParts()
|
||||
{
|
||||
}
|
||||
|
||||
Message *MessageRef::getMessage()
|
||||
{
|
||||
return _message.get();
|
||||
}
|
||||
|
||||
int MessageRef::getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
bool MessageRef::layout(int width, bool enableEmoteMargins)
|
||||
{
|
||||
auto &settings = SettingsManager::getInstance();
|
||||
|
||||
bool sizeChanged = width != _currentLayoutWidth;
|
||||
bool redraw = width != _currentLayoutWidth;
|
||||
int spaceWidth = 4;
|
||||
|
||||
int mediumTextLineHeight =
|
||||
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
||||
|
||||
bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration();
|
||||
bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration();
|
||||
bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
||||
|
||||
qreal emoteScale = settings.emoteScale.get();
|
||||
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
||||
|
||||
// calculate word sizes
|
||||
if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_emoteGeneration = EmoteManager::getInstance().getGeneration();
|
||||
_fontGeneration = FontManager::getInstance().getGeneration();
|
||||
|
||||
for (auto &word : _message->getWords()) {
|
||||
if (word.isImage()) {
|
||||
if (!recalculateImages) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &image = word.getImage();
|
||||
|
||||
qreal w = image.getWidth();
|
||||
qreal h = image.getHeight();
|
||||
|
||||
if (scaleEmotesByLineHeight) {
|
||||
word.setSize(w * mediumTextLineHeight / h * emoteScale,
|
||||
mediumTextLineHeight * emoteScale);
|
||||
} else {
|
||||
word.setSize(w * image.getScale() * emoteScale, h * image.getScale() * emoteScale);
|
||||
}
|
||||
} else {
|
||||
if (!recalculateText) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QFontMetrics &metrics = word.getFontMetrics();
|
||||
word.setSize(metrics.width(word.getText()), metrics.height());
|
||||
}
|
||||
}
|
||||
|
||||
if (newWordTypes) {
|
||||
_currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
|
||||
}
|
||||
|
||||
// layout
|
||||
_currentLayoutWidth = width;
|
||||
|
||||
int x = MARGIN_LEFT;
|
||||
int y = MARGIN_TOP;
|
||||
|
||||
int right = width - MARGIN_RIGHT;
|
||||
|
||||
int lineNumber = 0;
|
||||
int lineStart = 0;
|
||||
int lineHeight = 0;
|
||||
bool first = true;
|
||||
|
||||
_wordParts.clear();
|
||||
|
||||
uint32_t flags = SettingsManager::getInstance().getWordTypeMask();
|
||||
|
||||
for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) {
|
||||
Word &word = *it;
|
||||
|
||||
if ((word.getType() & flags) == Word::None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int xOffset = 0, yOffset = 0;
|
||||
|
||||
if (enableEmoteMargins) {
|
||||
if (word.isImage() && word.getImage().isHat()) {
|
||||
xOffset = -word.getWidth() + 2;
|
||||
} else {
|
||||
xOffset = word.getXOffset();
|
||||
yOffset = word.getYOffset();
|
||||
}
|
||||
}
|
||||
|
||||
// word wrapping
|
||||
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
|
||||
alignWordParts(lineStart, lineHeight);
|
||||
|
||||
y += lineHeight;
|
||||
|
||||
const QString &text = word.getText();
|
||||
|
||||
int start = 0;
|
||||
QFontMetrics &metrics = word.getFontMetrics();
|
||||
|
||||
int width = 0;
|
||||
|
||||
std::vector<short> &charWidths = word.getCharacterWidthCache();
|
||||
|
||||
if (charWidths.size() == 0) {
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
charWidths.push_back(metrics.charWidth(text, i));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 2; i <= text.length(); i++) {
|
||||
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
|
||||
QString mid = text.mid(start, i - start - 1);
|
||||
|
||||
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(),
|
||||
lineNumber, mid, mid));
|
||||
|
||||
y += metrics.height();
|
||||
|
||||
start = i - 1;
|
||||
|
||||
width = 0;
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
QString mid(text.mid(start));
|
||||
width = metrics.width(mid);
|
||||
|
||||
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||
word.getHeight(), lineNumber, mid, mid));
|
||||
x = width + MARGIN_LEFT + spaceWidth;
|
||||
|
||||
lineHeight = word.getHeight();
|
||||
|
||||
lineStart = _wordParts.size() - 1;
|
||||
|
||||
first = false;
|
||||
} else if (first || x + word.getWidth() + xOffset <= right) {
|
||||
// fits in the line
|
||||
_wordParts.push_back(
|
||||
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||
|
||||
x += word.getWidth() + xOffset;
|
||||
x += spaceWidth;
|
||||
|
||||
lineHeight = std::max(word.getHeight(), lineHeight);
|
||||
|
||||
first = false;
|
||||
} else {
|
||||
// doesn't fit in the line
|
||||
alignWordParts(lineStart, lineHeight);
|
||||
|
||||
y += lineHeight;
|
||||
|
||||
_wordParts.push_back(
|
||||
WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||
|
||||
lineStart = _wordParts.size() - 1;
|
||||
|
||||
lineHeight = word.getHeight();
|
||||
|
||||
x = word.getWidth() + MARGIN_LEFT;
|
||||
x += spaceWidth;
|
||||
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
alignWordParts(lineStart, lineHeight);
|
||||
|
||||
if (_height != y + lineHeight) {
|
||||
sizeChanged = true;
|
||||
_height = y + lineHeight;
|
||||
}
|
||||
|
||||
if (sizeChanged) {
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
updateBuffer = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<WordPart> &MessageRef::getWordParts() const
|
||||
{
|
||||
return _wordParts;
|
||||
}
|
||||
|
||||
void MessageRef::alignWordParts(int lineStart, int lineHeight)
|
||||
{
|
||||
for (size_t i = lineStart; i < _wordParts.size(); i++) {
|
||||
WordPart &wordPart2 = _wordParts.at(i);
|
||||
|
||||
wordPart2.setY(wordPart2.getY() + lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageRef::tryGetWordPart(QPoint point, Word &word)
|
||||
{
|
||||
// go through all words and return the first one that contains the point.
|
||||
for (WordPart &wordPart : _wordParts) {
|
||||
if (wordPart.getRect().contains(point)) {
|
||||
word = wordPart.getWord();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int MessageRef::getSelectionIndex(QPoint position)
|
||||
{
|
||||
if (_wordParts.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find out in which line the cursor is
|
||||
int lineNumber = 0, lineStart = 0, lineEnd = 0;
|
||||
|
||||
for (int i = 0; i < _wordParts.size(); i++) {
|
||||
WordPart &part = _wordParts[i];
|
||||
|
||||
if (part.getLineNumber() != 0 && position.y() < part.getY()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (part.getLineNumber() != lineNumber) {
|
||||
lineStart = i - 1;
|
||||
lineNumber = part.getLineNumber();
|
||||
}
|
||||
|
||||
lineEnd = part.getLineNumber() == 0 ? i : i + 1;
|
||||
}
|
||||
|
||||
// count up to the cursor
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; i < lineStart; i++) {
|
||||
WordPart &part = _wordParts[i];
|
||||
|
||||
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
||||
}
|
||||
|
||||
for (int i = lineStart; i < lineEnd; i++) {
|
||||
WordPart &part = _wordParts[i];
|
||||
|
||||
// curser is left of the word part
|
||||
if (position.x() < part.getX()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// cursor is right of the word part
|
||||
if (position.x() > part.getX() + part.getWidth()) {
|
||||
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// cursor is over the word part
|
||||
if (part.getWord().isImage()) {
|
||||
index++;
|
||||
} else {
|
||||
auto text = part.getWord().getText();
|
||||
|
||||
int x = part.getX();
|
||||
|
||||
for (int j = 0; j < text.length(); j++) {
|
||||
if (x > position.x()) {
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef MESSAGEREF_H
|
||||
#define MESSAGEREF_H
|
||||
|
||||
#include "messages/message.h"
|
||||
|
||||
#include <QPixmap>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class MessageRef;
|
||||
|
||||
typedef std::shared_ptr<MessageRef> SharedMessageRef;
|
||||
|
||||
class MessageRef
|
||||
{
|
||||
public:
|
||||
MessageRef(SharedMessage message);
|
||||
|
||||
Message *getMessage();
|
||||
int getHeight() const;
|
||||
|
||||
bool layout(int width, bool enableEmoteMargins = true);
|
||||
|
||||
const std::vector<WordPart> &getWordParts() const;
|
||||
|
||||
std::shared_ptr<QPixmap> buffer = nullptr;
|
||||
bool updateBuffer = false;
|
||||
|
||||
bool tryGetWordPart(QPoint point, messages::Word &word);
|
||||
|
||||
int getSelectionIndex(QPoint position);
|
||||
|
||||
private:
|
||||
// variables
|
||||
SharedMessage _message;
|
||||
std::vector<messages::WordPart> _wordParts;
|
||||
|
||||
int _height = 0;
|
||||
|
||||
int _currentLayoutWidth = -1;
|
||||
int _fontGeneration = -1;
|
||||
int _emoteGeneration = -1;
|
||||
Word::Type _currentWordTypes = Word::None;
|
||||
|
||||
// methods
|
||||
void alignWordParts(int lineStart, int lineHeight);
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // MESSAGEREF_H
|
||||
@@ -0,0 +1,134 @@
|
||||
#include "messages/word.h"
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
// Image word
|
||||
Word::Word(LazyLoadedImage *image, Type type, const QString ©text, const QString &tooltip,
|
||||
const Link &link)
|
||||
: _image(image)
|
||||
, _text()
|
||||
, _color()
|
||||
, _isImage(true)
|
||||
, _type(type)
|
||||
, _copyText(copytext)
|
||||
, _tooltip(tooltip)
|
||||
, _link(link)
|
||||
, _characterWidthCache()
|
||||
{
|
||||
image->getWidth(); // professional segfault test
|
||||
}
|
||||
|
||||
// Text word
|
||||
Word::Word(const QString &text, Type type, const QColor &color, const QString ©text,
|
||||
const QString &tooltip, const Link &link)
|
||||
: _image(NULL)
|
||||
, _text(text)
|
||||
, _color(color)
|
||||
, _isImage(false)
|
||||
, _type(type)
|
||||
, _copyText(copytext)
|
||||
, _tooltip(tooltip)
|
||||
, _link(link)
|
||||
, _characterWidthCache()
|
||||
{
|
||||
}
|
||||
|
||||
LazyLoadedImage &Word::getImage() const
|
||||
{
|
||||
return *_image;
|
||||
}
|
||||
|
||||
const QString &Word::getText() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
int Word::getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int Word::getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
void Word::setSize(int width, int height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
bool Word::isImage() const
|
||||
{
|
||||
return _isImage;
|
||||
}
|
||||
|
||||
bool Word::isText() const
|
||||
{
|
||||
return !_isImage;
|
||||
}
|
||||
|
||||
const QString &Word::getCopyText() const
|
||||
{
|
||||
return _copyText;
|
||||
}
|
||||
|
||||
bool Word::hasTrailingSpace() const
|
||||
{
|
||||
return _hasTrailingSpace;
|
||||
}
|
||||
|
||||
QFont &Word::getFont() const
|
||||
{
|
||||
return FontManager::getInstance().getFont(_font);
|
||||
}
|
||||
|
||||
QFontMetrics &Word::getFontMetrics() const
|
||||
{
|
||||
return FontManager::getInstance().getFontMetrics(_font);
|
||||
}
|
||||
|
||||
Word::Type Word::getType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
const QString &Word::getTooltip() const
|
||||
{
|
||||
return _tooltip;
|
||||
}
|
||||
|
||||
const QColor &Word::getColor() const
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
|
||||
const Link &Word::getLink() const
|
||||
{
|
||||
return _link;
|
||||
}
|
||||
|
||||
int Word::getXOffset() const
|
||||
{
|
||||
return _xOffset;
|
||||
}
|
||||
|
||||
int Word::getYOffset() const
|
||||
{
|
||||
return _yOffset;
|
||||
}
|
||||
|
||||
void Word::setOffset(int xOffset, int yOffset)
|
||||
{
|
||||
_xOffset = std::max(0, xOffset);
|
||||
_yOffset = std::max(0, yOffset);
|
||||
}
|
||||
|
||||
std::vector<short> &Word::getCharacterWidthCache() const
|
||||
{
|
||||
return _characterWidthCache;
|
||||
}
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,120 @@
|
||||
#ifndef WORD_H
|
||||
#define WORD_H
|
||||
|
||||
#include "fontmanager.h"
|
||||
#include "messages/lazyloadedimage.h"
|
||||
#include "messages/link.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class Word
|
||||
{
|
||||
public:
|
||||
enum Type : uint32_t {
|
||||
None = 0,
|
||||
Misc = (1 << 0),
|
||||
Text = (1 << 1),
|
||||
|
||||
TimestampNoSeconds = (1 << 2),
|
||||
TimestampWithSeconds = (1 << 3),
|
||||
|
||||
TwitchEmoteImage = (1 << 4),
|
||||
TwitchEmoteText = (1 << 5),
|
||||
BttvEmoteImage = (1 << 6),
|
||||
BttvEmoteText = (1 << 7),
|
||||
BttvGifEmoteImage = (1 << 8),
|
||||
BttvGifEmoteText = (1 << 9),
|
||||
FfzEmoteImage = (1 << 10),
|
||||
FfzEmoteText = (1 << 11),
|
||||
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage,
|
||||
|
||||
BitsStatic = (1 << 12),
|
||||
BitsAnimated = (1 << 13),
|
||||
|
||||
BadgeStaff = (1 << 14),
|
||||
BadgeAdmin = (1 << 15),
|
||||
BadgeGlobalMod = (1 << 16),
|
||||
BadgeModerator = (1 << 17),
|
||||
BadgeTurbo = (1 << 18),
|
||||
BadgeBroadcaster = (1 << 19),
|
||||
BadgePremium = (1 << 20),
|
||||
BadgeChatterino = (1 << 21),
|
||||
BadgeCheer = (1 << 22),
|
||||
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | BadgeTurbo |
|
||||
BadgeBroadcaster | BadgePremium | BadgeChatterino | BadgeCheer,
|
||||
|
||||
Username = (1 << 23),
|
||||
BitsAmount = (1 << 24),
|
||||
|
||||
ButtonBan = (1 << 25),
|
||||
ButtonTimeout = (1 << 26),
|
||||
|
||||
EmojiImage = (1 << 27),
|
||||
EmojiText = (1 << 28),
|
||||
|
||||
Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
|
||||
BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
|
||||
ButtonBan | ButtonTimeout
|
||||
};
|
||||
|
||||
Word()
|
||||
{
|
||||
}
|
||||
explicit Word(LazyLoadedImage *_image, Type getType, const QString ©text,
|
||||
const QString &getTooltip, const Link &getLink = Link());
|
||||
explicit Word(const QString &_text, Type getType, const QColor &getColor,
|
||||
const QString ©text, const QString &getTooltip, const Link &getLink = Link());
|
||||
|
||||
LazyLoadedImage &getImage() const;
|
||||
const QString &getText() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
void setSize(int _width, int _height);
|
||||
|
||||
bool isImage() const;
|
||||
bool isText() const;
|
||||
const QString &getCopyText() const;
|
||||
bool hasTrailingSpace() const;
|
||||
QFont &getFont() const;
|
||||
QFontMetrics &getFontMetrics() const;
|
||||
Type getType() const;
|
||||
const QString &getTooltip() const;
|
||||
const QColor &getColor() const;
|
||||
const Link &getLink() const;
|
||||
int getXOffset() const;
|
||||
int getYOffset() const;
|
||||
void setOffset(int _xOffset, int _yOffset);
|
||||
|
||||
std::vector<short> &getCharacterWidthCache() const;
|
||||
|
||||
private:
|
||||
LazyLoadedImage *_image;
|
||||
QString _text;
|
||||
QColor _color;
|
||||
bool _isImage;
|
||||
|
||||
Type _type;
|
||||
QString _copyText;
|
||||
QString _tooltip;
|
||||
|
||||
int _width = 16;
|
||||
int _height = 16;
|
||||
int _xOffset = 0;
|
||||
int _yOffset = 0;
|
||||
|
||||
bool _hasTrailingSpace;
|
||||
FontManager::Type _font = FontManager::Medium;
|
||||
Link _link;
|
||||
|
||||
mutable std::vector<short> _characterWidthCache;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
||||
#endif // WORD_H
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "messages/wordpart.h"
|
||||
#include "messages/word.h"
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
WordPart::WordPart(Word &word, int x, int y, int lineNumber, const QString ©Text,
|
||||
bool allowTrailingSpace)
|
||||
: _word(word)
|
||||
, _copyText(copyText)
|
||||
, _text(word.isText() ? _word.getText() : QString())
|
||||
, _x(x)
|
||||
, _y(y)
|
||||
, _width(word.getWidth())
|
||||
, _height(word.getHeight())
|
||||
, _lineNumber(lineNumber)
|
||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||
{
|
||||
}
|
||||
|
||||
WordPart::WordPart(Word &word, int x, int y, int width, int height, int lineNumber,
|
||||
const QString ©Text, const QString &customText, bool allowTrailingSpace)
|
||||
: _word(word)
|
||||
, _copyText(copyText)
|
||||
, _text(customText)
|
||||
, _x(x)
|
||||
, _y(y)
|
||||
, _width(width)
|
||||
, _height(height)
|
||||
, _lineNumber(lineNumber)
|
||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||
{
|
||||
}
|
||||
|
||||
const Word &WordPart::getWord() const
|
||||
{
|
||||
return _word;
|
||||
}
|
||||
|
||||
int WordPart::getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
int WordPart::getHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
int WordPart::getX() const
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
int WordPart::getY() const
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
void WordPart::setPosition(int x, int y)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
}
|
||||
|
||||
void WordPart::setY(int y)
|
||||
{
|
||||
_y = y;
|
||||
}
|
||||
|
||||
int WordPart::getRight() const
|
||||
{
|
||||
return _x + _width;
|
||||
}
|
||||
|
||||
int WordPart::getBottom() const
|
||||
{
|
||||
return _y + _height;
|
||||
}
|
||||
|
||||
QRect WordPart::getRect() const
|
||||
{
|
||||
return QRect(_x, _y, _width, _height);
|
||||
}
|
||||
|
||||
const QString WordPart::getCopyText() const
|
||||
{
|
||||
return _copyText;
|
||||
}
|
||||
|
||||
int WordPart::hasTrailingSpace() const
|
||||
{
|
||||
return _trailingSpace;
|
||||
}
|
||||
|
||||
const QString &WordPart::getText() const
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
|
||||
int WordPart::getLineNumber()
|
||||
{
|
||||
return _lineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef WORDPART_H
|
||||
#define WORDPART_H
|
||||
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class Word;
|
||||
|
||||
class WordPart
|
||||
{
|
||||
public:
|
||||
WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText,
|
||||
bool allowTrailingSpace = true);
|
||||
|
||||
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber,
|
||||
const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true);
|
||||
|
||||
const Word &getWord() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getX() const;
|
||||
int getY() const;
|
||||
void setPosition(int _x, int _y);
|
||||
void setY(int _y);
|
||||
int getRight() const;
|
||||
int getBottom() const;
|
||||
QRect getRect() const;
|
||||
const QString getCopyText() const;
|
||||
int hasTrailingSpace() const;
|
||||
const QString &getText() const;
|
||||
int getLineNumber();
|
||||
|
||||
private:
|
||||
Word &_word;
|
||||
|
||||
QString _copyText;
|
||||
QString _text;
|
||||
|
||||
int _x;
|
||||
int _y;
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
int _lineNumber;
|
||||
|
||||
bool _trailingSpace;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WORDPART_H
|
||||
Reference in New Issue
Block a user