mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
refactored message drawing and layouting
This commit is contained in:
parent
8b25d37a37
commit
f0c21f5b49
|
@ -164,7 +164,8 @@ HEADERS += \
|
|||
src/messages/messagecolor.hpp \
|
||||
src/util/nativeeventhelper.hpp \
|
||||
src/debug/log.hpp \
|
||||
src/messages/imageloadermanager.hpp
|
||||
src/messages/imageloadermanager.hpp \
|
||||
src/util/benchmark.hpp
|
||||
|
||||
PRECOMPILED_HEADER =
|
||||
|
||||
|
|
|
@ -20,10 +20,14 @@ using namespace chatterino::messages;
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
EmoteManager *EmoteManager::instance = nullptr;
|
||||
|
||||
EmoteManager::EmoteManager(WindowManager &_windowManager)
|
||||
: windowManager(_windowManager)
|
||||
, findShortCodesRegex(":([-+\\w]+):")
|
||||
{
|
||||
this->instance = this;
|
||||
|
||||
pajlada::Settings::Setting<std::string> roomID(
|
||||
"/accounts/current/roomID", "", pajlada::Settings::SettingOption::DoNotWriteToJSON);
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ class EmoteManager
|
|||
public:
|
||||
explicit EmoteManager(WindowManager &_windowManager);
|
||||
|
||||
static EmoteManager *instance;
|
||||
|
||||
void loadGlobalEmotes();
|
||||
|
||||
void reloadBTTVChannelEmotes(const QString &channelName,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "messages/lazyloadedimage.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkAccessManager>
|
||||
|
@ -81,7 +82,7 @@ void ImageLoaderWorker::handleLoad(chatterino::messages::LazyLoadedImage *lli, Q
|
|||
lli->emoteManager.incGeneration();
|
||||
lli->windowManager.layoutVisibleChatWidgets();
|
||||
|
||||
delete reply;
|
||||
reply->deleteLater();
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "messages/limitedqueuesnapshot.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
template <typename T>
|
||||
class LimitedQueue
|
||||
{
|
||||
public:
|
||||
LimitedQueue(int _limit = 1000, int _buffer = 250)
|
||||
: offset(0)
|
||||
, limit(_limit)
|
||||
, buffer(_buffer)
|
||||
{
|
||||
this->vector = std::make_shared<std::vector<T>>();
|
||||
this->vector->reserve(this->limit + this->buffer);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::lockthis->guard<std::mutex> lock(this->mutex);
|
||||
|
||||
this->vector = std::make_shared<std::vector<T>>();
|
||||
this->vector->reserve(this->limit + this->buffer);
|
||||
|
||||
this->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(this->mutex);
|
||||
|
||||
if (this->vector->size() >= this->limit) {
|
||||
// vector is full
|
||||
if (this->offset == this->buffer) {
|
||||
deleted = this->vector->at(this->offset);
|
||||
|
||||
// create new vector
|
||||
auto newVector = std::make_shared<std::vector<T>>();
|
||||
newVector->reserve(this->limit + this->buffer);
|
||||
|
||||
for (unsigned int i = 0; i < this->limit; ++i) {
|
||||
newVector->pushthis->back(this->vector->at(i + this->offset));
|
||||
}
|
||||
newVector->push_back(item);
|
||||
|
||||
this->offset = 0;
|
||||
this->vector = newVector;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
deleted = this->vector->at(this->offset);
|
||||
|
||||
// append item and increment offset("deleting" first element)
|
||||
this->vector->push_back(item);
|
||||
this->offset++;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// append item
|
||||
this->vector->pushthis->back(item);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
messages::LimitedQueueSnapshot<T> getSnapshot()
|
||||
{
|
||||
std::lockthis->guard<std::mutex> lock(this->mutex);
|
||||
|
||||
if (this->vector->size() < this->limit) {
|
||||
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size());
|
||||
} else {
|
||||
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->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
|
|
@ -30,84 +30,72 @@ int MessageRef::getHeight() const
|
|||
return this->height;
|
||||
}
|
||||
|
||||
bool MessageRef::layout(int width, bool enableEmoteMargins)
|
||||
// return true if redraw is required
|
||||
bool MessageRef::layout(int width)
|
||||
{
|
||||
auto &settings = SettingsManager::getInstance();
|
||||
bool layoutRequired = false;
|
||||
|
||||
bool sizeChanged = width != this->currentLayoutWidth;
|
||||
bool redraw = width != this->currentLayoutWidth;
|
||||
int spaceWidth = 4;
|
||||
// check if width changed
|
||||
const bool widthChanged = width != this->currentLayoutWidth;
|
||||
layoutRequired |= widthChanged;
|
||||
this->currentLayoutWidth = width;
|
||||
// check if emotes changed
|
||||
const bool imagesChanged = this->emoteGeneration != EmoteManager::instance->getGeneration();
|
||||
layoutRequired |= imagesChanged;
|
||||
this->emoteGeneration = EmoteManager::instance->getGeneration();
|
||||
// check if text changed
|
||||
const bool textChanged = this->fontGeneration != FontManager::getInstance().getGeneration();
|
||||
layoutRequired |= textChanged;
|
||||
this->fontGeneration = FontManager::getInstance().getGeneration();
|
||||
// check if work mask changed
|
||||
const bool wordMaskChanged =
|
||||
this->currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
||||
layoutRequired |= wordMaskChanged;
|
||||
this->currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
|
||||
|
||||
int mediumTextLineHeight =
|
||||
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
||||
// update word sizes if needed
|
||||
if (imagesChanged) {
|
||||
this->updateImageSizes();
|
||||
}
|
||||
if (textChanged) {
|
||||
this->updateTextSizes();
|
||||
}
|
||||
if (widthChanged) {
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
/* TODO(pajlada): Re-implement
|
||||
bool recalculateImages = this->emoteGeneration != EmoteManager::getInstance().getGeneration();
|
||||
*/
|
||||
bool recalculateImages = true;
|
||||
|
||||
bool recalculateText = this->fontGeneration != FontManager::getInstance().getGeneration();
|
||||
bool newWordTypes = this->currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
||||
|
||||
qreal emoteScale = settings.emoteScale.get();
|
||||
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
||||
|
||||
// calculate word sizes
|
||||
if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
|
||||
// return if no layout is required
|
||||
if (!layoutRequired) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// this->emoteGeneration = EmoteManager::getInstance().getGeneration();
|
||||
this->fontGeneration = FontManager::getInstance().getGeneration();
|
||||
this->actuallyLayout(width);
|
||||
|
||||
for (auto &word : this->message->getWords()) {
|
||||
if (word.isImage()) {
|
||||
if (!recalculateImages) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto &image = word.getImage();
|
||||
void MessageRef::actuallyLayout(int width)
|
||||
{
|
||||
auto &settings = SettingsManager::getInstance();
|
||||
|
||||
qreal w = image.getWidth();
|
||||
qreal h = image.getHeight();
|
||||
const int spaceWidth = 4;
|
||||
const int right = width - MARGIN_RIGHT;
|
||||
|
||||
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) {
|
||||
this->currentWordTypes = settings.getWordTypeMask();
|
||||
}
|
||||
// clear word parts
|
||||
this->wordParts.clear();
|
||||
|
||||
// layout
|
||||
this->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;
|
||||
|
||||
this->wordParts.clear();
|
||||
|
||||
uint32_t flags = settings.getWordTypeMask();
|
||||
|
||||
// loop throught all the words and add them when a line is full
|
||||
for (auto it = this->message->getWords().begin(); it != this->message->getWords().end(); ++it) {
|
||||
Word &word = *it;
|
||||
|
||||
|
@ -118,64 +106,60 @@ bool MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
|
||||
int xOffset = 0, yOffset = 0;
|
||||
|
||||
if (enableEmoteMargins) {
|
||||
if (word.isImage() && word.getImage().isHat()) {
|
||||
xOffset = -word.getWidth() + 2;
|
||||
} else {
|
||||
xOffset = word.getXOffset();
|
||||
yOffset = word.getYOffset();
|
||||
}
|
||||
}
|
||||
/// 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) {
|
||||
// align and end the current line
|
||||
alignWordParts(lineStart, lineHeight, width);
|
||||
|
||||
y += lineHeight;
|
||||
|
||||
const QString &text = word.getText();
|
||||
int currentPartStart = 0;
|
||||
int currentLineWidth = 0;
|
||||
|
||||
int start = 0;
|
||||
QFontMetrics &metrics = word.getFontMetrics();
|
||||
// go through the text, break text when it doesn't fit in the line anymore
|
||||
for (int i = 1; i <= word.getText().length(); i++) {
|
||||
currentLineWidth += word.getCharWidth(i - 1);
|
||||
|
||||
int width = 0;
|
||||
if (currentLineWidth + MARGIN_LEFT > right) {
|
||||
// add the current line
|
||||
QString mid = word.getText().mid(currentPartStart, i - currentPartStart - 1);
|
||||
|
||||
std::vector<short> &charWidths = word.getCharacterWidthCache();
|
||||
int charOffset = 0;
|
||||
|
||||
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);
|
||||
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width,
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, currentLineWidth,
|
||||
word.getHeight(), lineNumber, mid, mid,
|
||||
false, charOffset));
|
||||
false, currentPartStart));
|
||||
|
||||
charOffset = i;
|
||||
y += word.getFontMetrics().height();
|
||||
|
||||
y += metrics.height();
|
||||
currentPartStart = i - 1;
|
||||
|
||||
start = i - 1;
|
||||
|
||||
width = 0;
|
||||
currentLineWidth = 0;
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
QString mid(text.mid(start));
|
||||
width = metrics.width(mid);
|
||||
QString mid(word.getText().mid(currentPartStart));
|
||||
currentLineWidth = word.getFontMetrics().width(mid);
|
||||
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||
word.getHeight(), lineNumber, mid, mid, charOffset));
|
||||
x = width + MARGIN_LEFT + spaceWidth;
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(),
|
||||
currentLineWidth, word.getHeight(), lineNumber, mid,
|
||||
mid, true, currentPartStart));
|
||||
|
||||
x = currentLineWidth + MARGIN_LEFT + spaceWidth;
|
||||
lineHeight = word.getHeight();
|
||||
|
||||
lineStart = this->wordParts.size() - 1;
|
||||
|
||||
first = false;
|
||||
} else if (first || x + word.getWidth() + xOffset <= right) {
|
||||
// fits in the line
|
||||
}
|
||||
// fits in the current line
|
||||
else if (first || x + word.getWidth() + xOffset <= right) {
|
||||
this->wordParts.push_back(
|
||||
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||
|
||||
|
@ -185,8 +169,10 @@ bool MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
lineHeight = std::max(word.getHeight(), lineHeight);
|
||||
|
||||
first = false;
|
||||
} else {
|
||||
// doesn't fit in the line
|
||||
}
|
||||
// doesn't fit in the line
|
||||
else {
|
||||
// align and end the current line
|
||||
alignWordParts(lineStart, lineHeight, width);
|
||||
|
||||
y += lineHeight;
|
||||
|
@ -205,22 +191,56 @@ bool MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
}
|
||||
}
|
||||
|
||||
// align and end the current line
|
||||
alignWordParts(lineStart, lineHeight, width);
|
||||
|
||||
if (this->height != y + lineHeight) {
|
||||
sizeChanged = true;
|
||||
this->height = y + lineHeight;
|
||||
}
|
||||
// update height
|
||||
int oldHeight = this->height;
|
||||
this->height = y + lineHeight + MARGIN_BOTTOM;
|
||||
|
||||
this->height += MARGIN_BOTTOM;
|
||||
|
||||
if (sizeChanged) {
|
||||
buffer = nullptr;
|
||||
// invalidate buffer if height changed
|
||||
if (oldHeight != this->height) {
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
|
||||
updateBuffer = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
void MessageRef::updateTextSizes()
|
||||
{
|
||||
for (auto &word : this->message->getWords()) {
|
||||
if (!word.isText())
|
||||
continue;
|
||||
|
||||
QFontMetrics &metrics = word.getFontMetrics();
|
||||
word.setSize(metrics.width(word.getText()), metrics.height());
|
||||
}
|
||||
}
|
||||
|
||||
void MessageRef::updateImageSizes()
|
||||
{
|
||||
const int mediumTextLineHeight =
|
||||
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
||||
const qreal emoteScale = SettingsManager::getInstance().emoteScale.get();
|
||||
const bool scaleEmotesByLineHeight =
|
||||
SettingsManager::getInstance().scaleEmotesByLineHeight.get();
|
||||
|
||||
for (auto &word : this->message->getWords()) {
|
||||
if (!word.isImage())
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<WordPart> &MessageRef::getWordParts() const
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
Message *getMessage();
|
||||
int getHeight() const;
|
||||
|
||||
bool layout(int width, bool enableEmoteMargins = true);
|
||||
bool layout(int width);
|
||||
|
||||
const std::vector<WordPart> &getWordParts() const;
|
||||
|
||||
|
@ -30,7 +30,6 @@ public:
|
|||
bool updateBuffer = false;
|
||||
|
||||
const Word *tryGetWordPart(QPoint point);
|
||||
|
||||
int getSelectionIndex(QPoint position);
|
||||
|
||||
private:
|
||||
|
@ -42,13 +41,15 @@ private:
|
|||
|
||||
int currentLayoutWidth = -1;
|
||||
int fontGeneration = -1;
|
||||
/* TODO(pajlada): Re-implement
|
||||
int emoteGeneration = -1;
|
||||
*/
|
||||
|
||||
Word::Type currentWordTypes = Word::None;
|
||||
|
||||
// methods
|
||||
void actuallyLayout(int width);
|
||||
void alignWordParts(int lineStart, int lineHeight, int width);
|
||||
void updateTextSizes();
|
||||
void updateImageSizes();
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "messages/word.hpp"
|
||||
#include "util/benchmark.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
@ -127,6 +128,11 @@ int Word::getCharacterLength() const
|
|||
return this->isImage() ? 2 : this->getText().length() + 1;
|
||||
}
|
||||
|
||||
short Word::getCharWidth(int index) const
|
||||
{
|
||||
return this->getCharacterWidthCache().at(index);
|
||||
}
|
||||
|
||||
std::vector<short> &Word::getCharacterWidthCache() const
|
||||
{
|
||||
// lock not required because there is only one gui thread
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
void setOffset(int _xOffset, int _yOffset);
|
||||
int getCharacterLength() const;
|
||||
|
||||
std::vector<short> &getCharacterWidthCache() const;
|
||||
short getCharWidth(int index) const;
|
||||
|
||||
private:
|
||||
LazyLoadedImage *image;
|
||||
|
@ -136,6 +136,7 @@ private:
|
|||
FontManager::Type font = FontManager::Medium;
|
||||
Link link;
|
||||
|
||||
std::vector<short> &getCharacterWidthCache() const;
|
||||
mutable std::vector<short> charWidthCache;
|
||||
};
|
||||
|
||||
|
|
|
@ -115,9 +115,9 @@ int WordPart::getCharacterLength() const
|
|||
return this->getWord().isImage() ? 2 : this->getText().length() + 1;
|
||||
}
|
||||
|
||||
short WordPart::getCharacterWidth(int index) const
|
||||
short WordPart::getCharWidth(int index) const
|
||||
{
|
||||
return this->getWord().getCharacterWidthCache().at(index + this->wordCharOffset);
|
||||
return this->getWord().getCharWidth(index + this->wordCharOffset);
|
||||
}
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
const QString &getText() const;
|
||||
int getLineNumber() const;
|
||||
int getCharacterLength() const;
|
||||
short getCharacterWidth(int index) const;
|
||||
short getCharWidth(int index) const;
|
||||
|
||||
private:
|
||||
Word &word;
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
#include <boost/current_function.hpp>
|
||||
|
||||
#define BENCH(x) \
|
||||
QElapsedTimer x; \
|
||||
x.start();
|
||||
|
||||
#define MARK(x) \
|
||||
qDebug() << __FILE__ << __LINE__ << static_cast<float>(x.nsecsElapsed()) / 100000.0 << "ms";
|
||||
#define MARK(x) \
|
||||
qDebug() << BOOST_CURRENT_FUNCTION << __LINE__ \
|
||||
<< static_cast<float>(x.nsecsElapsed()) / 100000.0 << "ms";
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "messages/messageref.hpp"
|
||||
#include "settingsmanager.hpp"
|
||||
#include "ui_accountpopupform.h"
|
||||
#include "util/benchmark.hpp"
|
||||
#include "util/distancebetweenpoints.hpp"
|
||||
#include "widgets/chatwidget.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
|
@ -46,12 +47,12 @@ ChannelView::ChannelView(WindowManager &windowManager, BaseWidget *parent)
|
|||
|
||||
this->goToBottom->setVisible(this->scrollBar.isVisible() && !this->scrollBar.isAtBottom());
|
||||
|
||||
this->update();
|
||||
this->queueUpdate();
|
||||
});
|
||||
|
||||
this->repaintGifsConnection =
|
||||
windowManager.repaintGifs.connect([&] { this->updateGifEmotes(); });
|
||||
this->layoutConnection = windowManager.repaintGifs.connect([&] { this->layout(); });
|
||||
this->layoutConnection = windowManager.layout.connect([&] { this->layoutMessages(); });
|
||||
|
||||
this->goToBottom = new RippleEffectLabel(this, 0);
|
||||
this->goToBottom->setStyleSheet("background-color: rgba(0,0,0,0.5); color: #FFF;");
|
||||
|
@ -60,6 +61,16 @@ ChannelView::ChannelView(WindowManager &windowManager, BaseWidget *parent)
|
|||
|
||||
connect(goToBottom, &RippleEffectLabel::clicked, this,
|
||||
[this] { QTimer::singleShot(180, [this] { this->scrollBar.scrollToBottom(); }); });
|
||||
|
||||
this->updateTimer.setInterval(1000 / 60);
|
||||
this->updateTimer.setSingleShot(true);
|
||||
connect(&this->updateTimer, &QTimer::timeout, this, [this] {
|
||||
if (this->updateQueued) {
|
||||
this->update();
|
||||
}
|
||||
|
||||
this->updateTimer.start();
|
||||
});
|
||||
}
|
||||
|
||||
ChannelView::~ChannelView()
|
||||
|
@ -68,17 +79,35 @@ ChannelView::~ChannelView()
|
|||
this, &ChannelView::wordTypeMaskChanged);
|
||||
}
|
||||
|
||||
bool ChannelView::layoutMessages()
|
||||
void ChannelView::queueUpdate()
|
||||
{
|
||||
if (this->updateTimer.isActive()) {
|
||||
this->updateQueued = true;
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
this->updateTimer.start();
|
||||
}
|
||||
|
||||
void ChannelView::layoutMessages()
|
||||
{
|
||||
this->actuallyLayoutMessages();
|
||||
}
|
||||
|
||||
void ChannelView::actuallyLayoutMessages()
|
||||
{
|
||||
BENCH(timer)
|
||||
auto messages = this->getMessagesSnapshot();
|
||||
|
||||
if (messages.getLength() == 0) {
|
||||
this->scrollBar.setVisible(false);
|
||||
return false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool redrawRequired = false;
|
||||
bool showScrollbar = false;
|
||||
bool redraw = false;
|
||||
|
||||
// Bool indicating whether or not we were showing all messages
|
||||
// True if one of the following statements are true:
|
||||
|
@ -97,7 +126,7 @@ bool ChannelView::layoutMessages()
|
|||
for (size_t i = start; i < messages.getLength(); ++i) {
|
||||
auto message = messages[i];
|
||||
|
||||
redraw |= message->layout(layoutWidth, true);
|
||||
redrawRequired |= message->layout(layoutWidth);
|
||||
|
||||
y += message->getHeight();
|
||||
|
||||
|
@ -113,7 +142,7 @@ bool ChannelView::layoutMessages()
|
|||
for (std::size_t i = messages.getLength() - 1; i > 0; i--) {
|
||||
auto *message = messages[i].get();
|
||||
|
||||
message->layout(layoutWidth, true);
|
||||
message->layout(layoutWidth);
|
||||
|
||||
h -= message->getHeight();
|
||||
|
||||
|
@ -144,7 +173,11 @@ bool ChannelView::layoutMessages()
|
|||
this->scrollBar.scrollToBottom();
|
||||
}
|
||||
|
||||
return redraw;
|
||||
MARK(timer);
|
||||
|
||||
if (redrawRequired) {
|
||||
this->queueUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelView::clearMessages()
|
||||
|
@ -154,13 +187,15 @@ void ChannelView::clearMessages()
|
|||
|
||||
// Layout chat widget messages, and force an update regardless if there are no messages
|
||||
this->layoutMessages();
|
||||
this->update();
|
||||
this->queueUpdate();
|
||||
}
|
||||
|
||||
void ChannelView::updateGifEmotes()
|
||||
{
|
||||
this->onlyUpdateEmotes = true;
|
||||
this->update();
|
||||
if (!this->gifEmotes.empty()) {
|
||||
this->onlyUpdateEmotes = true;
|
||||
this->queueUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar &ChannelView::getScrollBar()
|
||||
|
@ -278,7 +313,6 @@ void ChannelView::clearSelection()
|
|||
{
|
||||
this->selection = Selection();
|
||||
layoutMessages();
|
||||
update();
|
||||
}
|
||||
|
||||
messages::LimitedQueueSnapshot<SharedMessageRef> ChannelView::getMessagesSnapshot()
|
||||
|
@ -318,8 +352,7 @@ void ChannelView::setChannel(std::shared_ptr<Channel> channel)
|
|||
this->selection.start.messageIndex--;
|
||||
this->selection.end.messageIndex--;
|
||||
|
||||
layoutMessages();
|
||||
update();
|
||||
this->layoutMessages();
|
||||
});
|
||||
|
||||
auto snapshot = channel->getMessageSnapshot();
|
||||
|
@ -373,9 +406,10 @@ void ChannelView::setSelection(const SelectionItem &start, const SelectionItem &
|
|||
|
||||
void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
||||
{
|
||||
// BENCH(timer);
|
||||
QPainter painter(this);
|
||||
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
// painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
|
||||
// only update gif emotes
|
||||
#ifndef Q_OS_MAC
|
||||
|
@ -402,10 +436,11 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/)
|
|||
|
||||
// draw gif emotes
|
||||
for (GifEmoteData &item : this->gifEmotes) {
|
||||
painter.fillRect(item.rect, this->colorScheme.ChatBackground);
|
||||
// painter.fillRect(item.rect, this->colorScheme.ChatBackground);
|
||||
|
||||
painter.drawPixmap(item.rect, *item.image->getPixmap());
|
||||
}
|
||||
// MARK(timer);
|
||||
}
|
||||
|
||||
void ChannelView::drawMessages(QPainter &painter)
|
||||
|
@ -423,14 +458,13 @@ void ChannelView::drawMessages(QPainter &painter)
|
|||
for (size_t i = start; i < messages.getLength(); ++i) {
|
||||
messages::MessageRef *messageRef = messages[i].get();
|
||||
|
||||
std::shared_ptr<QPixmap> bufferPtr = messageRef->buffer;
|
||||
QPixmap *buffer = bufferPtr.get();
|
||||
std::shared_ptr<QPixmap> buffer = messageRef->buffer;
|
||||
|
||||
bool updateBuffer = messageRef->updateBuffer;
|
||||
// bool updateBuffer = messageRef->updateBuffer;
|
||||
bool updateBuffer = false;
|
||||
|
||||
if (buffer == nullptr) {
|
||||
buffer = new QPixmap(width(), messageRef->getHeight());
|
||||
bufferPtr = std::shared_ptr<QPixmap>(buffer);
|
||||
if (!buffer) {
|
||||
buffer = std::shared_ptr<QPixmap>(new QPixmap(width(), messageRef->getHeight()));
|
||||
updateBuffer = true;
|
||||
}
|
||||
|
||||
|
@ -438,7 +472,8 @@ void ChannelView::drawMessages(QPainter &painter)
|
|||
|
||||
// update messages that have been changed
|
||||
if (updateBuffer) {
|
||||
this->updateMessageBuffer(messageRef, buffer, i);
|
||||
this->updateMessageBuffer(messageRef, buffer.get(), i);
|
||||
qDebug() << "updating buffer xD";
|
||||
}
|
||||
|
||||
// get gif emotes
|
||||
|
@ -459,11 +494,11 @@ void ChannelView::drawMessages(QPainter &painter)
|
|||
}
|
||||
}
|
||||
|
||||
messageRef->buffer = bufferPtr;
|
||||
messageRef->buffer = buffer;
|
||||
|
||||
// if (buffer != nullptr) {
|
||||
painter.drawPixmap(0, y, *buffer);
|
||||
// }
|
||||
if (buffer) {
|
||||
painter.drawPixmap(0, y, *buffer.get());
|
||||
}
|
||||
|
||||
y += messageRef->getHeight();
|
||||
|
||||
|
@ -502,7 +537,7 @@ void ChannelView::updateMessageBuffer(messages::MessageRef *messageRef, QPixmap
|
|||
|
||||
const QPixmap *image = lli.getPixmap();
|
||||
|
||||
if (image != nullptr) {
|
||||
if (image != nullptr && !lli.getAnimated()) {
|
||||
painter.drawPixmap(QRect(wordPart.getX(), wordPart.getY(), wordPart.getWidth(),
|
||||
wordPart.getHeight()),
|
||||
*image);
|
||||
|
@ -582,7 +617,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
|
|||
int offset = this->selection.min.charIndex - charIndex;
|
||||
|
||||
for (int j = 0; j < offset; j++) {
|
||||
rect.setLeft(rect.left() + part.getCharacterWidth(j));
|
||||
rect.setLeft(rect.left() + part.getCharWidth(j));
|
||||
}
|
||||
|
||||
if (isSingleWord) {
|
||||
|
@ -591,7 +626,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
|
|||
rect.setRight(part.getX());
|
||||
|
||||
for (int j = 0; j < offset + length; j++) {
|
||||
rect.setRight(rect.right() + part.getCharacterWidth(j));
|
||||
rect.setRight(rect.right() + part.getCharWidth(j));
|
||||
}
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
|
@ -648,7 +683,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
|
|||
rect.setRight(part.getX());
|
||||
|
||||
for (int j = 0; j < offset + length; j++) {
|
||||
rect.setRight(rect.right() + part.getCharacterWidth(j));
|
||||
rect.setRight(rect.right() + part.getCharWidth(j));
|
||||
}
|
||||
} else {
|
||||
if (this->selection.max.charIndex == charIndex) {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <QPaintEvent>
|
||||
#include <QScroller>
|
||||
#include <QTimer>
|
||||
#include <QWheelEvent>
|
||||
#include <QWidget>
|
||||
|
||||
|
@ -88,6 +89,7 @@ public:
|
|||
~ChannelView();
|
||||
|
||||
void updateGifEmotes();
|
||||
void queueUpdate();
|
||||
ScrollBar &getScrollBar();
|
||||
QString getSelectedText();
|
||||
bool hasSelection();
|
||||
|
@ -95,7 +97,7 @@ public:
|
|||
|
||||
void setChannel(std::shared_ptr<Channel> channel);
|
||||
messages::LimitedQueueSnapshot<messages::SharedMessageRef> getMessagesSnapshot();
|
||||
bool layoutMessages();
|
||||
void layoutMessages();
|
||||
|
||||
void clearMessages();
|
||||
|
||||
|
@ -122,8 +124,11 @@ private:
|
|||
};
|
||||
|
||||
WindowManager &windowManager;
|
||||
QTimer updateTimer;
|
||||
bool updateQueued = false;
|
||||
|
||||
void detachChannel();
|
||||
void actuallyLayoutMessages();
|
||||
|
||||
void drawMessages(QPainter &painter);
|
||||
void updateMessageBuffer(messages::MessageRef *messageRef, QPixmap *buffer, int messageIndex);
|
||||
|
|
|
@ -167,14 +167,9 @@ bool ChatWidget::showChangeChannelPopup(const char *dialogTitle, bool empty)
|
|||
return false;
|
||||
}
|
||||
|
||||
void ChatWidget::layoutMessages(bool forceUpdate)
|
||||
void ChatWidget::layoutMessages()
|
||||
{
|
||||
this->view.layoutMessages();
|
||||
this->view.update();
|
||||
|
||||
// if (this->view.layoutMessages() || forceUpdate) {
|
||||
// this->view.update();
|
||||
// }
|
||||
}
|
||||
|
||||
void ChatWidget::updateGifEmotes()
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
void giveFocus(Qt::FocusReason reason);
|
||||
bool hasFocus() const;
|
||||
|
||||
void layoutMessages(bool forceUpdate = false);
|
||||
void layoutMessages();
|
||||
void updateGifEmotes();
|
||||
|
||||
protected:
|
||||
|
|
Loading…
Reference in a new issue