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