refactored message drawing and layouting

This commit is contained in:
fourtf 2017-10-11 10:34:04 +02:00
parent 8b25d37a37
commit f0c21f5b49
16 changed files with 225 additions and 249 deletions

View file

@ -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 =

View file

@ -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);

View file

@ -39,6 +39,8 @@ class EmoteManager
public:
explicit EmoteManager(WindowManager &_windowManager);
static EmoteManager *instance;
void loadGlobalEmotes();
void reloadBTTVChannelEmotes(const QString &channelName,

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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;

View file

@ -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";

View file

@ -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) {

View file

@ -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);

View file

@ -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()

View file

@ -56,7 +56,7 @@ public:
void giveFocus(Qt::FocusReason reason);
bool hasFocus() const;
void layoutMessages(bool forceUpdate = false);
void layoutMessages();
void updateGifEmotes();
protected: