From 29e9d00f36013d07aaff1fa4b9b6389d165ea83b Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Feb 2017 14:47:47 +0100 Subject: [PATCH 01/10] added clangformat.txt and updated readme --- README.md | 3 +++ clangformat.txt | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 clangformat.txt diff --git a/README.md b/README.md index 8c997b216..d62b156ea 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,6 @@ Chatterino 2 is the second installment of my twitch chat client series "Chatteri 1. download the [boost library](https://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.zip/download) and extract it to `C:\local\boost` 2. download binaries for OpenSSL >= 1.0.2 or compile it from source. [example download](https://indy.fulgan.com/SSL/) 3. Place libeay32.dll and ssleay32.dll from OpenSSL in a directory in PATH. + +## code style +The code is normally formated using clang format in qt creator. [clangformat.txt](https://github.com/fourtf/chatterino2/blob/master/clangformat.txt) conatins the style file for clang format. diff --git a/clangformat.txt b/clangformat.txt new file mode 100644 index 000000000..b40edcf56 --- /dev/null +++ b/clangformat.txt @@ -0,0 +1,28 @@ +IndentCaseLabels: true +BasedOnStyle: Google +IndentWidth: 4 +Standard: Auto +PointerBindsToType: false +Language: Cpp +SpacesBeforeTrailingComments: 2 +AccessModifierOffset: -1 +AlignEscapedNewlinesLeft: true +AlwaysBreakAfterDefinitionReturnType: true +AlwaysBreakBeforeMultilineStrings: false +BreakConstructorInitializersBeforeComma: true +# BreakBeforeBraces: Linux +BreakBeforeBraces: Custom +AccessModifierOffset: -4 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +DerivePointerBinding: false +BraceWrapping: { + AfterNamespace: 'false' + AfterClass: 'true' + BeforeElse: 'false' + AfterControlStatement: 'false' + AfterFunction: 'true' + BeforeCatch: 'false' +} \ No newline at end of file From 33ba35471f5beb6f233108042fa9e6afe7251156 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Feb 2017 17:42:28 +0100 Subject: [PATCH 02/10] added gif frame updating --- emotes.cpp | 3 ++ emotes.h | 17 +++++++++++ messages/lazyloadedimage.cpp | 57 ++++++++++++++++++++++++++++++------ messages/lazyloadedimage.h | 25 +++++++++++----- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/emotes.cpp b/emotes.cpp index 95d8e528b..cade3b075 100644 --- a/emotes.cpp +++ b/emotes.cpp @@ -27,6 +27,9 @@ ConcurrentMap ConcurrentMap Emotes::twitchEmoteFromCache; ConcurrentMap Emotes::miscImageFromCache; +QTimer Emotes::gifUpdateTimer; +bool Emotes::gifUpdateTimerInitiated(false); + int Emotes::generation = 0; Emotes::Emotes() diff --git a/emotes.h b/emotes.h index 50df0aa76..62d1f7a33 100644 --- a/emotes.h +++ b/emotes.h @@ -7,6 +7,7 @@ #include #include +#include namespace chatterino { @@ -82,6 +83,19 @@ public: generation++; } + static QTimer & + getGifUpdateTimer() + { + if (!gifUpdateTimerInitiated) { + gifUpdateTimerInitiated = true; + + gifUpdateTimer.setInterval(33); + gifUpdateTimer.start(); + } + + return gifUpdateTimer; + } + private: Emotes(); @@ -102,6 +116,9 @@ private: static QString getTwitchEmoteLink(long id, qreal &scale); + static QTimer gifUpdateTimer; + static bool gifUpdateTimerInitiated; + static void loadFfzEmotes(); static void loadBttvEmotes(); diff --git a/messages/lazyloadedimage.cpp b/messages/lazyloadedimage.cpp index 09a9ad37c..3854d1e7a 100644 --- a/messages/lazyloadedimage.cpp +++ b/messages/lazyloadedimage.cpp @@ -5,9 +5,12 @@ #include "ircmanager.h" #include "windows.h" +#include +#include #include #include #include +#include #include namespace chatterino { @@ -16,7 +19,9 @@ namespace messages { LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name, const QString &tooltip, const QMargins &margin, bool isHat) - : pixmap(NULL) + : currentPixmap(NULL) + , allFrames() + , currentFrame(0) , url(url) , name(name) , tooltip(tooltip) @@ -31,7 +36,9 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name, const QString &tooltip, const QMargins &margin, bool isHat) - : pixmap(image) + : currentPixmap(image) + , allFrames() + , currentFrame(0) , url() , name(name) , tooltip(tooltip) @@ -46,7 +53,6 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, void LazyLoadedImage::loadImage() { - // QThreadPool::globalInstance()->start(new LambdaQRunnable([=] { QNetworkAccessManager *manager = new QNetworkAccessManager(); QUrl url(this->url); @@ -55,21 +61,54 @@ LazyLoadedImage::loadImage() QNetworkReply *reply = manager->get(request); QObject::connect(reply, &QNetworkReply::finished, [=] { - QPixmap *pixmap = new QPixmap(); - pixmap->loadFromData(reply->readAll()); + QByteArray array = reply->readAll(); + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); - if (pixmap->isNull()) { - return; + 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; + this->currentPixmap = pixmap; + } + + FrameData data; + data.duration = std::max(20, reader.nextImageDelay()); + data.image = pixmap; + + allFrames.push_back(data); + } + } + + if (allFrames.size() > 1) { + QObject::connect(&Emotes::getGifUpdateTimer(), &QTimer::timeout, + [this] { gifUpdateTimout(); }); } - this->pixmap = pixmap; Emotes::incGeneration(); Windows::layoutVisibleChatWidgets(); reply->deleteLater(); manager->deleteLater(); }); - // })); +} + +void +LazyLoadedImage::gifUpdateTimout() +{ + if (this->currentFrame >= this->allFrames.size() - 1) { + this->currentFrame = 0; + this->currentPixmap = this->allFrames.at(0).image; + } else { + this->currentPixmap = this->allFrames.at(++this->currentFrame).image; + } } } } diff --git a/messages/lazyloadedimage.h b/messages/lazyloadedimage.h index 482af09cf..138f731bb 100644 --- a/messages/lazyloadedimage.h +++ b/messages/lazyloadedimage.h @@ -7,7 +7,7 @@ namespace chatterino { namespace messages { -class LazyLoadedImage +class LazyLoadedImage : QObject { public: explicit LazyLoadedImage(const QString &url, qreal scale = 1, @@ -15,7 +15,7 @@ public: const QString &tooltip = "", const QMargins &margin = QMargins(), bool isHat = false); - explicit LazyLoadedImage(QPixmap *pixmap, qreal scale = 1, + explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, const QString &name = "", const QString &tooltip = "", const QMargins &margin = QMargins(), @@ -29,7 +29,7 @@ public: loadImage(); } - return pixmap; + return currentPixmap; } qreal @@ -77,23 +77,30 @@ public: int getWidth() const { - if (pixmap == NULL) { + if (currentPixmap == NULL) { return 16; } - return pixmap->width(); + return currentPixmap->width(); } int getHeight() const { - if (pixmap == NULL) { + if (currentPixmap == NULL) { return 16; } - return pixmap->height(); + return currentPixmap->height(); } private: - QPixmap *pixmap; + struct FrameData { + QPixmap *image; + float duration; + }; + + QPixmap *currentPixmap; + std::vector allFrames; + int currentFrame; QString url; QString name; @@ -106,6 +113,8 @@ private: bool isLoading; void loadImage(); + + void gifUpdateTimout(); }; } } From 5673efa0db0929fabcc9a4d577aabe01dffe648a Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 6 Feb 2017 18:31:25 +0100 Subject: [PATCH 03/10] disabled scrolling when scrollbar is invisible --- emotes.h | 4 +++- messages/lazyloadedimage.cpp | 21 +++++++++++++---- messages/lazyloadedimage.h | 3 ++- widgets/chatwidgetview.cpp | 45 ++++++++++++++++-------------------- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/emotes.h b/emotes.h index 62d1f7a33..c733ed7d4 100644 --- a/emotes.h +++ b/emotes.h @@ -1,6 +1,8 @@ #ifndef EMOTES_H #define EMOTES_H +#define GIF_FRAME_LENGTH 33 + #include "concurrentmap.h" #include "messages/lazyloadedimage.h" #include "twitchemotevalue.h" @@ -89,7 +91,7 @@ public: if (!gifUpdateTimerInitiated) { gifUpdateTimerInitiated = true; - gifUpdateTimer.setInterval(33); + gifUpdateTimer.setInterval(GIF_FRAME_LENGTH); gifUpdateTimer.start(); } diff --git a/messages/lazyloadedimage.cpp b/messages/lazyloadedimage.cpp index 3854d1e7a..1fbbf5725 100644 --- a/messages/lazyloadedimage.cpp +++ b/messages/lazyloadedimage.cpp @@ -22,6 +22,7 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, : currentPixmap(NULL) , allFrames() , currentFrame(0) + , currentFrameOffset(0) , url(url) , name(name) , tooltip(tooltip) @@ -39,6 +40,7 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, : currentPixmap(image) , allFrames() , currentFrame(0) + , currentFrameOffset(0) , url() , name(name) , tooltip(tooltip) @@ -103,12 +105,21 @@ LazyLoadedImage::loadImage() void LazyLoadedImage::gifUpdateTimout() { - if (this->currentFrame >= this->allFrames.size() - 1) { - this->currentFrame = 0; - this->currentPixmap = this->allFrames.at(0).image; - } else { - this->currentPixmap = this->allFrames.at(++this->currentFrame).image; + this->currentFrameOffset += GIF_FRAME_LENGTH; + + while (true) { + if (this->currentFrameOffset > + this->allFrames.at(this->currentFrame).duration) { + this->currentFrameOffset -= + this->allFrames.at(this->currentFrame).duration; + this->currentFrame = + (this->currentFrame + 1) % this->allFrames.size(); + } else { + break; + } } + + this->currentPixmap = this->allFrames[this->currentFrame].image; } } } diff --git a/messages/lazyloadedimage.h b/messages/lazyloadedimage.h index 138f731bb..564d3c21c 100644 --- a/messages/lazyloadedimage.h +++ b/messages/lazyloadedimage.h @@ -95,12 +95,13 @@ public: private: struct FrameData { QPixmap *image; - float duration; + int duration; }; QPixmap *currentPixmap; std::vector allFrames; int currentFrame; + int currentFrameOffset; QString url; QString name; diff --git a/widgets/chatwidgetview.cpp b/widgets/chatwidgetview.cpp index b5b9045f9..290595685 100644 --- a/widgets/chatwidgetview.cpp +++ b/widgets/chatwidgetview.cpp @@ -49,33 +49,26 @@ ChatWidgetView::layoutMessages() int start = this->scrollbar.getCurrentValue(); - if (messages.getLength() <= start) { - // The scrollbar wants to show more values than we can offer + // layout the visible messages in the view + if (messages.getLength() > start) { + int y = -(messages[start].get()->getHeight() * + (fmod(this->scrollbar.getCurrentValue(), 1))); - // just return for now - return false; + for (int i = start; i < messages.getLength(); ++i) { + auto messagePtr = messages[i]; + auto message = messagePtr.get(); + redraw |= message->layout(this->width(), true); - // Lower start value to the last message - // start = messages.getLength() - 1; - } + y += message->getHeight(); - int y = -(messages[start].get()->getHeight() * - (fmod(this->scrollbar.getCurrentValue(), 1))); - - for (int i = start; i < messages.getLength(); ++i) { - auto messagePtr = messages[i]; - auto message = messagePtr.get(); - - redraw |= message->layout(this->width(), true); - - y += message->getHeight(); - - if (y >= height()) { - break; + if (y >= height()) { + break; + } } } + // layout the messages at the bottom to determine the scrollbar thumb size int h = this->height() - 8; for (int i = messages.getLength() - 1; i >= 0; i--) { @@ -236,11 +229,13 @@ ChatWidgetView::paintEvent(QPaintEvent *) void ChatWidgetView::wheelEvent(QWheelEvent *event) { - this->scrollbar.setDesiredValue( - this->scrollbar.getDesiredValue() - - event->delta() / 10.0 * - Settings::getInstance().mouseScrollMultiplier.get(), - true); + if (this->scrollbar.isVisible()) { + this->scrollbar.setDesiredValue( + this->scrollbar.getDesiredValue() - + event->delta() / 10.0 * + Settings::getInstance().mouseScrollMultiplier.get(), + true); + } } } } From 5b03712a8243fb84953a3813fbccd6c578985dea Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 7 Feb 2017 00:03:15 +0100 Subject: [PATCH 04/10] added gif emotes --- emotes.cpp | 1 + emotes.h | 17 ++++++++--- messages/lazyloadedimage.cpp | 5 +-- widgets/chatwidgetview.cpp | 59 +++++++++++++++++++++++++++++++++--- widgets/chatwidgetview.h | 20 ++++++++++-- widgets/mainwindow.cpp | 22 ++++++++++++-- widgets/mainwindow.h | 1 + windows.cpp | 8 +++++ windows.h | 1 + 9 files changed, 118 insertions(+), 16 deletions(-) diff --git a/emotes.cpp b/emotes.cpp index cade3b075..dd8ab5b1d 100644 --- a/emotes.cpp +++ b/emotes.cpp @@ -26,6 +26,7 @@ ConcurrentMap Emotes::ffzChannelEmoteFromCaches; ConcurrentMap Emotes::twitchEmoteFromCache; ConcurrentMap Emotes::miscImageFromCache; +boost::signals2::signal Emotes::gifUpdateTimerSignal; QTimer Emotes::gifUpdateTimer; bool Emotes::gifUpdateTimerInitiated(false); diff --git a/emotes.h b/emotes.h index c733ed7d4..1ae669b4b 100644 --- a/emotes.h +++ b/emotes.h @@ -6,10 +6,12 @@ #include "concurrentmap.h" #include "messages/lazyloadedimage.h" #include "twitchemotevalue.h" +#include "windows.h" #include #include #include +#include namespace chatterino { @@ -85,17 +87,22 @@ public: generation++; } - static QTimer & - getGifUpdateTimer() + static boost::signals2::signal & + getGifUpdateSignal() { if (!gifUpdateTimerInitiated) { gifUpdateTimerInitiated = true; - gifUpdateTimer.setInterval(GIF_FRAME_LENGTH); + gifUpdateTimer.setInterval(30); gifUpdateTimer.start(); + + QObject::connect(&gifUpdateTimer, &QTimer::timeout, [] { + gifUpdateTimerSignal(); + Windows::repaintGifEmotes(); + }); } - return gifUpdateTimer; + return gifUpdateTimerSignal; } private: @@ -125,6 +132,8 @@ private: static void loadBttvEmotes(); static int generation; + + static boost::signals2::signal gifUpdateTimerSignal; }; } diff --git a/messages/lazyloadedimage.cpp b/messages/lazyloadedimage.cpp index 1fbbf5725..4886d17ea 100644 --- a/messages/lazyloadedimage.cpp +++ b/messages/lazyloadedimage.cpp @@ -90,8 +90,9 @@ LazyLoadedImage::loadImage() } if (allFrames.size() > 1) { - QObject::connect(&Emotes::getGifUpdateTimer(), &QTimer::timeout, - [this] { gifUpdateTimout(); }); + this->animated = true; + + Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); }); } Emotes::incGeneration(); diff --git a/widgets/chatwidgetview.cpp b/widgets/chatwidgetview.cpp index 290595685..cd37f1ac9 100644 --- a/widgets/chatwidgetview.cpp +++ b/widgets/chatwidgetview.cpp @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include namespace chatterino { @@ -18,7 +20,9 @@ ChatWidgetView::ChatWidgetView(ChatWidget *parent) : QWidget() , chatWidget(parent) , scrollbar(this) + , onlyUpdateEmotes(false) { + this->setAttribute(Qt::WA_OpaquePaintEvent); this->scrollbar.setSmallChange(5); QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged, @@ -90,6 +94,10 @@ ChatWidgetView::layoutMessages() this->scrollbar.setVisible(showScrollbar); + if (!showScrollbar) { + this->scrollbar.setDesiredValue(0); + } + this->scrollbar.setMaximum(messages.getLength()); return redraw; @@ -102,10 +110,12 @@ ChatWidgetView::resizeEvent(QResizeEvent *) this->scrollbar.move(width() - this->scrollbar.width(), 0); layoutMessages(); + + update(); } void -ChatWidgetView::paintEvent(QPaintEvent *) +ChatWidgetView::paintEvent(QPaintEvent *event) { QPainter _painter(this); @@ -113,6 +123,24 @@ ChatWidgetView::paintEvent(QPaintEvent *) ColorScheme &scheme = ColorScheme::getInstance(); + // only update gif emotes + if (onlyUpdateEmotes) { + onlyUpdateEmotes = false; + + for (GifEmoteData &item : this->gifEmotes) { + _painter.fillRect(item.rect, scheme.ChatBackground); + + _painter.drawPixmap(item.rect, *item.image->getPixmap()); + } + + return; + } + + // update all messages + gifEmotes.clear(); + + _painter.fillRect(rect(), scheme.ChatBackground); + // code for tesing colors /* QColor color; @@ -171,16 +199,13 @@ ChatWidgetView::paintEvent(QPaintEvent *) updateBuffer = true; } + // update messages that have been changed if (updateBuffer) { QPainter painter(buffer); painter.fillRect(buffer->rect(), scheme.ChatBackground); for (messages::WordPart const &wordPart : messageRef->getWordParts()) { - painter.setPen(QColor(255, 0, 0)); - painter.drawRect(wordPart.getX(), wordPart.getY(), - wordPart.getWidth(), wordPart.getHeight()); - // image if (wordPart.getWord().isImage()) { messages::LazyLoadedImage &lli = @@ -214,6 +239,24 @@ ChatWidgetView::paintEvent(QPaintEvent *) messageRef->updateBuffer = false; } + // get gif emotes + for (messages::WordPart const &wordPart : messageRef->getWordParts()) { + if (wordPart.getWord().isImage()) { + messages::LazyLoadedImage &lli = wordPart.getWord().getImage(); + + if (lli.getAnimated()) { + GifEmoteData data; + data.image = &lli; + QRect rect(wordPart.getX(), wordPart.getY() + y, + wordPart.getWidth(), wordPart.getHeight()); + + data.rect = rect; + + gifEmotes.push_back(data); + } + } + } + messageRef->buffer = bufferPtr; _painter.drawPixmap(0, y, *buffer); @@ -224,6 +267,12 @@ ChatWidgetView::paintEvent(QPaintEvent *) break; } } + + for (GifEmoteData &item : this->gifEmotes) { + _painter.fillRect(item.rect, scheme.ChatBackground); + + _painter.drawPixmap(item.rect, *item.image->getPixmap()); + } } void diff --git a/widgets/chatwidgetview.h b/widgets/chatwidgetview.h index 086a12f80..dc195d642 100644 --- a/widgets/chatwidgetview.h +++ b/widgets/chatwidgetview.h @@ -2,6 +2,7 @@ #define CHATVIEW_H #include "channel.h" +#include "messages/lazyloadedimage.h" #include "messages/messageref.h" #include "messages/word.h" #include "widgets/scrollbar.h" @@ -25,6 +26,13 @@ public: bool layoutMessages(); + void + updateGifEmotes() + { + this->onlyUpdateEmotes = true; + this->update(); + } + protected: void resizeEvent(QResizeEvent *); @@ -32,17 +40,23 @@ protected: void wheelEvent(QWheelEvent *event); private: + struct GifEmoteData { + messages::LazyLoadedImage *image; + QRect rect; + }; + + std::vector gifEmotes; + ChatWidget *chatWidget; ScrollBar scrollbar; + bool onlyUpdateEmotes; private slots: void wordTypeMaskChanged() { - if (layoutMessages()) { - update(); - } + update(); } }; } diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index a910938a3..f684ffa1e 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -44,7 +44,7 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel) if (channel == NULL || channel == widget->getChannel().get()) { if (widget->getView().layoutMessages()) { - widget->update(); + widget->getView().update(); } } } @@ -66,11 +66,29 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel) if (channel == NULL || channel == widget->getChannel().get()) { widget->getView().layoutMessages(); - widget->update(); + widget->getView().update(); } } } +void +MainWindow::repaintGifEmotes() +{ + auto *page = notebook.getSelectedPage(); + + if (page == NULL) { + return; + } + + const std::vector &widgets = page->getChatWidgets(); + + for (auto it = widgets.begin(); it != widgets.end(); ++it) { + ChatWidget *widget = *it; + + widget->getView().updateGifEmotes(); + } +} + void MainWindow::load(const boost::property_tree::ptree &tree) { diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 560dd4a71..e77e587df 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -20,6 +20,7 @@ public: void layoutVisibleChatWidgets(Channel *channel = NULL); void repaintVisibleChatWidgets(Channel *channel = NULL); + void repaintGifEmotes(); void load(const boost::property_tree::ptree &tree); boost::property_tree::ptree save(); diff --git a/windows.cpp b/windows.cpp index 317deedb5..0d298dbe4 100644 --- a/windows.cpp +++ b/windows.cpp @@ -38,6 +38,14 @@ Windows::repaintVisibleChatWidgets(Channel *channel) } } +void +Windows::repaintGifEmotes() +{ + if (Windows::mainWindow != nullptr) { + Windows::mainWindow->repaintGifEmotes(); + } +} + void Windows::updateAll() { diff --git a/windows.h b/windows.h index 0265815dc..49aa73c04 100644 --- a/windows.h +++ b/windows.h @@ -12,6 +12,7 @@ class Windows public: static void layoutVisibleChatWidgets(Channel *channel = NULL); static void repaintVisibleChatWidgets(Channel *channel = NULL); + static void repaintGifEmotes(); static void updateAll(); static widgets::MainWindow & From 8e99cc81bff7c1510c488395d9d5c0d593ec7d77 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 7 Feb 2017 13:41:38 +0100 Subject: [PATCH 05/10] nuuls ping NaM --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d62b156ea..acd7b4e64 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ Chatterino 2 is the second installment of my twitch chat client series "Chatteri 3. Place libeay32.dll and ssleay32.dll from OpenSSL in a directory in PATH. ## code style -The code is normally formated using clang format in qt creator. [clangformat.txt](https://github.com/fourtf/chatterino2/blob/master/clangformat.txt) conatins the style file for clang format. +The code is normally formated using clang format in qt creator. [clangformat.txt](https://github.com/fourtf/chatterino2/blob/master/clangformat.txt) contains the style file for clang format. From 081bce351146dd6b849774d11e6071fad6d80c49 Mon Sep 17 00:00:00 2001 From: Burak Ok Date: Tue, 7 Feb 2017 20:40:43 +0100 Subject: [PATCH 06/10] Cleaned up LimitedQueue and its Snapshot class --- messages/limitedqueue.h | 55 +++++++++++++++------------------ messages/limitedqueuesnapshot.h | 14 +++------ 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/messages/limitedqueue.h b/messages/limitedqueue.h index 9bb3d53fb..64e3e6c2e 100644 --- a/messages/limitedqueue.h +++ b/messages/limitedqueue.h @@ -3,8 +3,6 @@ #include "messages/limitedqueuesnapshot.h" -#include - #include #include #include @@ -17,14 +15,13 @@ class LimitedQueue { public: LimitedQueue(int limit = 100, int buffer = 25) - : vector(new std::vector(limit + buffer)) - , vectorPtr(this->vector) - , mutex() + : mutex() , offset(0) - , length(0) , limit(limit) , buffer(buffer) { + vector = std::make_shared>(); + vector->reserve(this->limit + this->buffer); } void @@ -32,11 +29,10 @@ public: { std::lock_guard lock(this->mutex); - this->vector = new std::vector(this->limit + this->buffer); - this->vectorPtr = std::shared_ptr>(this->vector); + this->vector = std::make_shared>(); + this->vector->reserve(this->limit + this->buffer); this->offset = 0; - this->length = 0; } // return true if an item was deleted @@ -46,39 +42,36 @@ public: { std::lock_guard lock(this->mutex); - if (this->length == this->limit) { + if (this->vector->size() >= this->limit) { // vector is full if (this->offset == this->buffer) { deleted = this->vector->at(this->offset); // create new vector - auto *vector = new std::vector(this->limit + this->buffer); + auto newVector = std::make_shared>(); + newVector->reserve(this->limit + this->buffer); - for (int i = 0; i < this->limit; i++) { - vector->at(i) = this->vector->at(i + this->offset); + for (unsigned int i = 0; i < this->limit - 1; i++) { + newVector->push_back(this->vector->at(i + this->offset)); } - - vector->at(limit - 1) = item; + newVector->push_back(item); this->offset = 0; - - this->vector = vector; - this->vectorPtr = std::shared_ptr>(vector); + this->vector = newVector; return true; } else { - // append item and remove first deleted = this->vector->at(this->offset); - this->vector->at(this->length + this->offset) = item; + //append item and increment offset("deleting" first element) + this->vector->push_back(item); this->offset++; return true; } } else { // append item - this->vector->at(this->length) = item; - this->length++; + this->vector->push_back(item); return false; } @@ -87,21 +80,23 @@ public: messages::LimitedQueueSnapshot getSnapshot() { - std::lock_guard lock(mutex); + std::lock_guard lock(this->mutex); - return LimitedQueueSnapshot(vectorPtr, offset, length); + if(vector->size() < limit) { + return LimitedQueueSnapshot(this->vector, this->offset, this->vector->size()); + } else { + return LimitedQueueSnapshot(this->vector, this->offset, this->limit); + } } private: - std::vector *vector; - std::shared_ptr> vectorPtr; + std::shared_ptr> vector; std::mutex mutex; - int offset; - int length; - int limit; - int buffer; + unsigned int offset; + unsigned int limit; + unsigned int buffer; }; } // namespace messages diff --git a/messages/limitedqueuesnapshot.h b/messages/limitedqueuesnapshot.h index c1816814e..fd76b4489 100644 --- a/messages/limitedqueuesnapshot.h +++ b/messages/limitedqueuesnapshot.h @@ -1,7 +1,6 @@ #ifndef LIMITEDQUEUESNAPSHOT_H #define LIMITEDQUEUESNAPSHOT_H -#include #include #include @@ -14,8 +13,7 @@ class LimitedQueueSnapshot public: LimitedQueueSnapshot(std::shared_ptr> ptr, int offset, int length) - : vectorPtr(ptr) - , vector(ptr.get()) + : vector(ptr) , offset(offset) , length(length) { @@ -24,20 +22,16 @@ public: int getLength() { - return length; + return this->length; } T const &operator[](int index) const { - // assert(index >= 0); - // assert(index < length); - - return vector->at(index + offset); + return this->vector->at(index + this->offset); } private: - std::shared_ptr> vectorPtr; - std::vector *vector; + std::shared_ptr> vector; int offset; int length; From c03705d263ed5c3b2c3cb20e00717a5cab1e1fa6 Mon Sep 17 00:00:00 2001 From: hemirt Date: Thu, 9 Feb 2017 00:03:46 +0100 Subject: [PATCH 07/10] Fix typo (#30) --- emotes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emotes.cpp b/emotes.cpp index dd8ab5b1d..7fa16e9e3 100644 --- a/emotes.cpp +++ b/emotes.cpp @@ -89,7 +89,7 @@ Emotes::loadBttvEmotes() void Emotes::loadFfzEmotes() { - // bttv + // ffz QNetworkAccessManager *manager = new QNetworkAccessManager(); QUrl url("https://api.frankerfacez.com/v1/set/global"); From c9a4c44c350180377e5f0e40b1dfd7340ba440da Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 9 Feb 2017 21:18:51 +0100 Subject: [PATCH 08/10] added travis file --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..1e59eaacb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +before_install: + - sudo add-apt-repository --yes ppa:ubuntu-sdk-team/ppa + - sudo apt-get update -qq + - sudo apt-get install qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev + - sudo apt-get install qt5-default qttools5-dev-tools + +script: + - qmake -project + - qmake Ultron.pro + - make \ No newline at end of file From 8a128695578bf29b9986da37ba61d7fe0217e1d8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Thu, 9 Feb 2017 21:21:14 +0100 Subject: [PATCH 09/10] triggering travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1e59eaacb..9c9adbdfd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,4 @@ before_install: script: - qmake -project - qmake Ultron.pro - - make \ No newline at end of file + - make \ No newline at end of file From 181fb69a44041a7fa42e79876ddb39220dab3f11 Mon Sep 17 00:00:00 2001 From: fourtf Date: Fri, 17 Feb 2017 23:51:35 +0100 Subject: [PATCH 10/10] started adding text selection --- .travis.yml | 6 +- messages/link.h | 6 +- messages/message.cpp | 5 +- messages/messageref.cpp | 136 ++++++++++++++++++++++++++++++++++--- messages/messageref.h | 5 ++ messages/word.h | 7 +- messages/wordpart.cpp | 10 +-- messages/wordpart.h | 16 +++-- widgets/chatwidget.cpp | 2 - widgets/chatwidgetview.cpp | 66 ++++++++++++++++++ widgets/chatwidgetview.h | 7 ++ widgets/notebook.cpp | 1 + 12 files changed, 238 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c9adbdfd..1267bf14c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,6 @@ before_install: - sudo apt-get install qt5-default qttools5-dev-tools script: - - qmake -project - - qmake Ultron.pro - - make \ No newline at end of file + - qmake -qt=qt5 -v + - qmake -qt=qt5 + - make diff --git a/messages/link.h b/messages/link.h index 022e0e3ae..1fe5f545b 100644 --- a/messages/link.h +++ b/messages/link.h @@ -24,19 +24,19 @@ public: Link(Type getType, const QString &getValue); bool - getIsValid() + getIsValid() const { return type == None; } Type - getType() + getType() const { return type; } const QString & - getValue() + getValue() const { return value; } diff --git a/messages/message.cpp b/messages/message.cpp index 80700f003..76f7d03ab 100644 --- a/messages/message.cpp +++ b/messages/message.cpp @@ -9,6 +9,7 @@ #include "resources.h" #include "settings.h" +#include #include #include #include @@ -144,7 +145,7 @@ Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, } // username - this->userName = ircMessage.account(); + this->userName = ircMessage.nick(); if (this->userName.isEmpty()) { auto iterator = tags.find("login"); @@ -230,7 +231,7 @@ Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, end > ircMessage.content().length()) continue; - QString name = ircMessage.content().mid(start, end - start); + QString name = ircMessage.content().mid(start, end - start + 1); twitchEmotes.push_back(std::pair( start, Emotes::getTwitchEmoteById(name, id))); diff --git a/messages/messageref.cpp b/messages/messageref.cpp index f67b0a9fb..7f4edce1e 100644 --- a/messages/messageref.cpp +++ b/messages/messageref.cpp @@ -36,11 +36,13 @@ MessageRef::layout(int width, bool enableEmoteMargins) bool recalculateImages = this->emoteGeneration != Emotes::getGeneration(); bool recalculateText = this->fontGeneration != Fonts::getGeneration(); + bool newWordTypes = + this->currentWordTypes != Settings::getInstance().getWordTypeMask(); qreal emoteScale = settings.emoteScale.get(); bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get(); - if (recalculateImages || recalculateText) { + if (recalculateImages || recalculateText || newWordTypes) { this->emoteGeneration = Emotes::getGeneration(); this->fontGeneration = Fonts::getGeneration(); @@ -72,6 +74,10 @@ MessageRef::layout(int width, bool enableEmoteMargins) } } } + + if (newWordTypes) { + this->currentWordTypes = Settings::getInstance().getWordTypeMask(); + } } if (!redraw) { @@ -85,6 +91,7 @@ MessageRef::layout(int width, bool enableEmoteMargins) int right = width - MARGIN_RIGHT - MARGIN_LEFT; + int lineNumber = 0; int lineStart = 0; int lineHeight = 0; bool first = true; @@ -139,22 +146,23 @@ MessageRef::layout(int width, bool enableEmoteMargins) this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(), - mid, mid)); + lineNumber, mid, mid)); y += metrics.height(); start = i - 1; width = 0; + lineNumber++; } } QString mid(text.mid(start)); width = metrics.width(mid); - this->wordParts.push_back(WordPart(word, MARGIN_LEFT, - y - word.getHeight(), width, - word.getHeight(), mid, mid)); + this->wordParts.push_back( + WordPart(word, MARGIN_LEFT, y - word.getHeight(), width, + word.getHeight(), lineNumber, mid, mid)); x = width + MARGIN_LEFT + spaceWidth; lineHeight = word.getHeight(); @@ -164,8 +172,8 @@ MessageRef::layout(int width, bool enableEmoteMargins) first = false; } else if (first || x + word.getWidth() + xOffset <= right) { // fits in the line - this->wordParts.push_back( - WordPart(word, x, y - word.getHeight(), word.getCopyText())); + this->wordParts.push_back(WordPart(word, x, y - word.getHeight(), + lineNumber, word.getCopyText())); x += word.getWidth() + xOffset; x += spaceWidth; @@ -179,8 +187,9 @@ MessageRef::layout(int width, bool enableEmoteMargins) y += lineHeight; - this->wordParts.push_back(WordPart( - word, MARGIN_LEFT, y - word.getHeight(), word.getCopyText())); + this->wordParts.push_back(WordPart(word, MARGIN_LEFT, + y - word.getHeight(), lineNumber, + word.getCopyText())); lineStart = this->wordParts.size() - 1; @@ -188,6 +197,8 @@ MessageRef::layout(int width, bool enableEmoteMargins) x = word.getWidth() + MARGIN_LEFT; x += spaceWidth; + + lineNumber++; } } @@ -216,5 +227,112 @@ MessageRef::alignWordParts(int lineStart, int lineHeight) wordPart2.setY(wordPart2.getY() + lineHeight); } } + +bool +MessageRef::tryGetWordPart(QPoint point, messages::Word &word) +{ + for (messages::WordPart &wordPart : this->wordParts) { + if (wordPart.getRect().contains(point)) { + word = wordPart.getWord(); + return true; + } + } + + return false; +} + +int +MessageRef::getSelectionIndex(QPoint position) +{ + if (this->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 < this->wordParts.size(); i++) { + WordPart &part = this->wordParts[i]; + + // return if curser under the word + if (position.y() >= part.getBottom()) { + break; + } + + if (part.getLineNumber() != lineNumber) { + lineStart = i; + lineNumber = part.getLineNumber(); + } + + lineEnd = i; + } + + // count up to the cursor + int index = 0; + + for (int i = 0; i < lineStart; i++) { + WordPart &part = this->wordParts[i]; + + index += part.getWord().isImage() ? 2 : part.getText().length() + 1; + } + + for (int i = lineStart; i < lineEnd; i++) { + WordPart &part = this->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()) { + 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; + + // go through all the wordparts + // for (int i = 0; i < this->wordParts; i < this->wordParts.size()) { + + // WordPart &part = this->wordParts[i]; + + // // return if curser under the word + // if (position.y() >= part.getBottom()) { + // break; + // } + + // // increment index and continue if the curser x is bigger than the + // words + // // right edge + // if (position.x() > part.getRight()) { + // index += part.getWord().isImage() ? 2 + + // part.getText().length() + 1; + // continue; + // } + // } +} } } diff --git a/messages/messageref.h b/messages/messageref.h index fba83fea1..95339779d 100644 --- a/messages/messageref.h +++ b/messages/messageref.h @@ -37,6 +37,10 @@ public: std::shared_ptr buffer = nullptr; bool updateBuffer = false; + bool tryGetWordPart(QPoint point, messages::Word &word); + + int getSelectionIndex(QPoint position); + private: Message *message; std::shared_ptr messagePtr; @@ -48,6 +52,7 @@ private: int currentLayoutWidth = -1; int fontGeneration = -1; int emoteGeneration = -1; + Word::Type currentWordTypes = Word::None; void alignWordParts(int lineStart, int lineHeight); }; diff --git a/messages/word.h b/messages/word.h index 1e3d3808f..7e13d6015 100644 --- a/messages/word.h +++ b/messages/word.h @@ -65,6 +65,9 @@ public: 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, @@ -182,7 +185,7 @@ public: } std::vector & - getCharacterWidthCache() + getCharacterWidthCache() const { return this->characterWidthCache; } @@ -206,7 +209,7 @@ private: Fonts::Type font = Fonts::Medium; Link link; - std::vector characterWidthCache; + mutable std::vector characterWidthCache; }; } } diff --git a/messages/wordpart.cpp b/messages/wordpart.cpp index da8f41745..0080b223c 100644 --- a/messages/wordpart.cpp +++ b/messages/wordpart.cpp @@ -4,8 +4,8 @@ namespace chatterino { namespace messages { -WordPart::WordPart(Word &word, int x, int y, const QString ©Text, - bool allowTrailingSpace) +WordPart::WordPart(Word &word, int x, int y, int lineNumber, + const QString ©Text, bool allowTrailingSpace) : m_word(word) , copyText(copyText) , text(word.isText() ? m_word.getText() : QString()) @@ -13,13 +13,14 @@ WordPart::WordPart(Word &word, int x, int y, const QString ©Text, , 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, - const QString ©Text, const QString &customText, - bool allowTrailingSpace) + int lineNumber, const QString ©Text, + const QString &customText, bool allowTrailingSpace) : m_word(word) , copyText(copyText) , text(customText) @@ -27,6 +28,7 @@ WordPart::WordPart(Word &word, int x, int y, int width, int height, , y(y) , width(width) , height(height) + , lineNumber(lineNumber) , _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) { } diff --git a/messages/wordpart.h b/messages/wordpart.h index a0bda61d5..0a19026fe 100644 --- a/messages/wordpart.h +++ b/messages/wordpart.h @@ -12,12 +12,12 @@ class Word; class WordPart { public: - WordPart(Word &getWord, int getX, int getY, const QString &getCopyText, - bool allowTrailingSpace = true); + 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, - const QString &getCopyText, const QString &customText, - bool allowTrailingSpace = true); + int lineNumber, const QString &getCopyText, + const QString &customText, bool allowTrailingSpace = true); const Word & getWord() const @@ -98,6 +98,12 @@ public: return this->text; } + int + getLineNumber() + { + return this->lineNumber; + } + private: Word &m_word; @@ -109,6 +115,8 @@ private: int width; int height; + int lineNumber; + bool _trailingSpace; }; } diff --git a/widgets/chatwidget.cpp b/widgets/chatwidget.cpp index 1c05fc20a..fb2f5abf4 100644 --- a/widgets/chatwidget.cpp +++ b/widgets/chatwidget.cpp @@ -76,8 +76,6 @@ ChatWidget::setChannelName(const QString &name) auto messageRef = new messages::MessageRef(message); - qDebug() << "xD"; - this->messages.appendItem( std::shared_ptr(messageRef), deleted); }); diff --git a/widgets/chatwidgetview.cpp b/widgets/chatwidgetview.cpp index cd37f1ac9..8fd4fbefb 100644 --- a/widgets/chatwidgetview.cpp +++ b/widgets/chatwidgetview.cpp @@ -24,6 +24,7 @@ ChatWidgetView::ChatWidgetView(ChatWidget *parent) { this->setAttribute(Qt::WA_OpaquePaintEvent); this->scrollbar.setSmallChange(5); + this->setMouseTracking(true); QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged, this, &ChatWidgetView::wordTypeMaskChanged); @@ -286,5 +287,70 @@ ChatWidgetView::wheelEvent(QWheelEvent *event) true); } } + +void +ChatWidgetView::mouseMoveEvent(QMouseEvent *event) +{ + std::shared_ptr message; + QPoint relativePos; + + if (!tryGetMessageAt(event->pos(), message, relativePos)) { + this->setCursor(Qt::ArrowCursor); + return; + } + + auto _message = message->getMessage(); + auto user = _message->getUserName(); + + messages::Word hoverWord; + + if (!message->tryGetWordPart(relativePos, hoverWord)) { + this->setCursor(Qt::ArrowCursor); + return; + } + + int index = message->getSelectionIndex(relativePos); + + qDebug() << index; + + if (hoverWord.getLink().getIsValid()) { + this->setCursor(Qt::PointingHandCursor); + qDebug() << hoverWord.getLink().getValue(); + } else { + this->setCursor(Qt::ArrowCursor); + } +} + +bool +ChatWidgetView::tryGetMessageAt(QPoint p, + std::shared_ptr &_message, + QPoint &relativePos) +{ + auto messages = chatWidget->getMessagesSnapshot(); + + int start = this->scrollbar.getCurrentValue(); + + if (start >= messages.getLength()) { + return false; + } + + int y = -(messages[start].get()->getHeight() * + (fmod(this->scrollbar.getCurrentValue(), 1))) + + 12; + + for (int i = start; i < messages.getLength(); ++i) { + auto message = messages[i]; + + y += message->getHeight(); + + if (p.y() < y) { + relativePos = QPoint(p.x(), y - p.y()); + _message = message; + return true; + } + } + + return false; +} } } diff --git a/widgets/chatwidgetview.h b/widgets/chatwidgetview.h index dc195d642..fdbf834b8 100644 --- a/widgets/chatwidgetview.h +++ b/widgets/chatwidgetview.h @@ -39,6 +39,12 @@ protected: void paintEvent(QPaintEvent *); void wheelEvent(QWheelEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + bool tryGetMessageAt(QPoint p, + std::shared_ptr &message, + QPoint &relativePos); + private: struct GifEmoteData { messages::LazyLoadedImage *image; @@ -56,6 +62,7 @@ private slots: void wordTypeMaskChanged() { + layoutMessages(); update(); } }; diff --git a/widgets/notebook.cpp b/widgets/notebook.cpp index 9030e886d..5c110db88 100644 --- a/widgets/notebook.cpp +++ b/widgets/notebook.cpp @@ -152,6 +152,7 @@ Notebook::performLayout(bool animated) if (Settings::getInstance().hideUserButton.get()) { userButton.hide(); } else { + userButton.move(x, 0); userButton.show(); x += 24; }