Merge branch 'master' of github.com:fourtf/chatterino2
This commit is contained in:
+10
@@ -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 -qt=qt5 -v
|
||||||
|
- qmake -qt=qt5
|
||||||
|
- make
|
||||||
@@ -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`
|
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/)
|
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.
|
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) contains the style file for clang format.
|
||||||
|
|||||||
@@ -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'
|
||||||
|
}
|
||||||
+5
-1
@@ -26,6 +26,10 @@ ConcurrentMap<int, messages::LazyLoadedImage *>
|
|||||||
Emotes::ffzChannelEmoteFromCaches;
|
Emotes::ffzChannelEmoteFromCaches;
|
||||||
ConcurrentMap<long, messages::LazyLoadedImage *> Emotes::twitchEmoteFromCache;
|
ConcurrentMap<long, messages::LazyLoadedImage *> Emotes::twitchEmoteFromCache;
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::miscImageFromCache;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::miscImageFromCache;
|
||||||
|
boost::signals2::signal<void()> Emotes::gifUpdateTimerSignal;
|
||||||
|
|
||||||
|
QTimer Emotes::gifUpdateTimer;
|
||||||
|
bool Emotes::gifUpdateTimerInitiated(false);
|
||||||
|
|
||||||
int Emotes::generation = 0;
|
int Emotes::generation = 0;
|
||||||
|
|
||||||
@@ -85,7 +89,7 @@ Emotes::loadBttvEmotes()
|
|||||||
void
|
void
|
||||||
Emotes::loadFfzEmotes()
|
Emotes::loadFfzEmotes()
|
||||||
{
|
{
|
||||||
// bttv
|
// ffz
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
|
||||||
QUrl url("https://api.frankerfacez.com/v1/set/global");
|
QUrl url("https://api.frankerfacez.com/v1/set/global");
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
#ifndef EMOTES_H
|
#ifndef EMOTES_H
|
||||||
#define EMOTES_H
|
#define EMOTES_H
|
||||||
|
|
||||||
|
#define GIF_FRAME_LENGTH 33
|
||||||
|
|
||||||
#include "concurrentmap.h"
|
#include "concurrentmap.h"
|
||||||
#include "messages/lazyloadedimage.h"
|
#include "messages/lazyloadedimage.h"
|
||||||
#include "twitchemotevalue.h"
|
#include "twitchemotevalue.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -82,6 +87,24 @@ public:
|
|||||||
generation++;
|
generation++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boost::signals2::signal<void()> &
|
||||||
|
getGifUpdateSignal()
|
||||||
|
{
|
||||||
|
if (!gifUpdateTimerInitiated) {
|
||||||
|
gifUpdateTimerInitiated = true;
|
||||||
|
|
||||||
|
gifUpdateTimer.setInterval(30);
|
||||||
|
gifUpdateTimer.start();
|
||||||
|
|
||||||
|
QObject::connect(&gifUpdateTimer, &QTimer::timeout, [] {
|
||||||
|
gifUpdateTimerSignal();
|
||||||
|
Windows::repaintGifEmotes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return gifUpdateTimerSignal;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Emotes();
|
Emotes();
|
||||||
|
|
||||||
@@ -102,10 +125,15 @@ private:
|
|||||||
|
|
||||||
static QString getTwitchEmoteLink(long id, qreal &scale);
|
static QString getTwitchEmoteLink(long id, qreal &scale);
|
||||||
|
|
||||||
|
static QTimer gifUpdateTimer;
|
||||||
|
static bool gifUpdateTimerInitiated;
|
||||||
|
|
||||||
static void loadFfzEmotes();
|
static void loadFfzEmotes();
|
||||||
static void loadBttvEmotes();
|
static void loadBttvEmotes();
|
||||||
|
|
||||||
static int generation;
|
static int generation;
|
||||||
|
|
||||||
|
static boost::signals2::signal<void()> gifUpdateTimerSignal;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,12 @@
|
|||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QImageReader>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
#include <QTimer>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
@@ -16,7 +19,10 @@ namespace messages {
|
|||||||
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
||||||
const QString &name, const QString &tooltip,
|
const QString &name, const QString &tooltip,
|
||||||
const QMargins &margin, bool isHat)
|
const QMargins &margin, bool isHat)
|
||||||
: pixmap(NULL)
|
: currentPixmap(NULL)
|
||||||
|
, allFrames()
|
||||||
|
, currentFrame(0)
|
||||||
|
, currentFrameOffset(0)
|
||||||
, url(url)
|
, url(url)
|
||||||
, name(name)
|
, name(name)
|
||||||
, tooltip(tooltip)
|
, tooltip(tooltip)
|
||||||
@@ -31,7 +37,10 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
|||||||
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
||||||
const QString &name, const QString &tooltip,
|
const QString &name, const QString &tooltip,
|
||||||
const QMargins &margin, bool isHat)
|
const QMargins &margin, bool isHat)
|
||||||
: pixmap(image)
|
: currentPixmap(image)
|
||||||
|
, allFrames()
|
||||||
|
, currentFrame(0)
|
||||||
|
, currentFrameOffset(0)
|
||||||
, url()
|
, url()
|
||||||
, name(name)
|
, name(name)
|
||||||
, tooltip(tooltip)
|
, tooltip(tooltip)
|
||||||
@@ -46,7 +55,6 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
|||||||
void
|
void
|
||||||
LazyLoadedImage::loadImage()
|
LazyLoadedImage::loadImage()
|
||||||
{
|
{
|
||||||
// QThreadPool::globalInstance()->start(new LambdaQRunnable([=] {
|
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
|
||||||
QUrl url(this->url);
|
QUrl url(this->url);
|
||||||
@@ -55,21 +63,64 @@ LazyLoadedImage::loadImage()
|
|||||||
QNetworkReply *reply = manager->get(request);
|
QNetworkReply *reply = manager->get(request);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
QPixmap *pixmap = new QPixmap();
|
QByteArray array = reply->readAll();
|
||||||
pixmap->loadFromData(reply->readAll());
|
QBuffer buffer(&array);
|
||||||
|
buffer.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
if (pixmap->isNull()) {
|
QImage image;
|
||||||
return;
|
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) {
|
||||||
|
this->animated = true;
|
||||||
|
|
||||||
|
Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
this->pixmap = pixmap;
|
|
||||||
Emotes::incGeneration();
|
Emotes::incGeneration();
|
||||||
Windows::layoutVisibleChatWidgets();
|
Windows::layoutVisibleChatWidgets();
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
});
|
});
|
||||||
// }));
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LazyLoadedImage::gifUpdateTimout()
|
||||||
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
class LazyLoadedImage
|
class LazyLoadedImage : QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit LazyLoadedImage(const QString &url, qreal scale = 1,
|
explicit LazyLoadedImage(const QString &url, qreal scale = 1,
|
||||||
@@ -15,7 +15,7 @@ public:
|
|||||||
const QString &tooltip = "",
|
const QString &tooltip = "",
|
||||||
const QMargins &margin = QMargins(),
|
const QMargins &margin = QMargins(),
|
||||||
bool isHat = false);
|
bool isHat = false);
|
||||||
explicit LazyLoadedImage(QPixmap *pixmap, qreal scale = 1,
|
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1,
|
||||||
const QString &name = "",
|
const QString &name = "",
|
||||||
const QString &tooltip = "",
|
const QString &tooltip = "",
|
||||||
const QMargins &margin = QMargins(),
|
const QMargins &margin = QMargins(),
|
||||||
@@ -29,7 +29,7 @@ public:
|
|||||||
|
|
||||||
loadImage();
|
loadImage();
|
||||||
}
|
}
|
||||||
return pixmap;
|
return currentPixmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal
|
qreal
|
||||||
@@ -77,23 +77,31 @@ public:
|
|||||||
int
|
int
|
||||||
getWidth() const
|
getWidth() const
|
||||||
{
|
{
|
||||||
if (pixmap == NULL) {
|
if (currentPixmap == NULL) {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
return pixmap->width();
|
return currentPixmap->width();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
getHeight() const
|
getHeight() const
|
||||||
{
|
{
|
||||||
if (pixmap == NULL) {
|
if (currentPixmap == NULL) {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
return pixmap->height();
|
return currentPixmap->height();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPixmap *pixmap;
|
struct FrameData {
|
||||||
|
QPixmap *image;
|
||||||
|
int duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
QPixmap *currentPixmap;
|
||||||
|
std::vector<FrameData> allFrames;
|
||||||
|
int currentFrame;
|
||||||
|
int currentFrameOffset;
|
||||||
|
|
||||||
QString url;
|
QString url;
|
||||||
QString name;
|
QString name;
|
||||||
@@ -106,6 +114,8 @@ private:
|
|||||||
bool isLoading;
|
bool isLoading;
|
||||||
|
|
||||||
void loadImage();
|
void loadImage();
|
||||||
|
|
||||||
|
void gifUpdateTimout();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-30
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include "messages/limitedqueuesnapshot.h"
|
#include "messages/limitedqueuesnapshot.h"
|
||||||
|
|
||||||
#include <boost/signals2.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -17,14 +15,13 @@ class LimitedQueue
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LimitedQueue(int limit = 100, int buffer = 25)
|
LimitedQueue(int limit = 100, int buffer = 25)
|
||||||
: vector(new std::vector<T>(limit + buffer))
|
: mutex()
|
||||||
, vectorPtr(this->vector)
|
|
||||||
, mutex()
|
|
||||||
, offset(0)
|
, offset(0)
|
||||||
, length(0)
|
|
||||||
, limit(limit)
|
, limit(limit)
|
||||||
, buffer(buffer)
|
, buffer(buffer)
|
||||||
{
|
{
|
||||||
|
vector = std::make_shared<std::vector<T>>();
|
||||||
|
vector->reserve(this->limit + this->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -32,11 +29,10 @@ public:
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
this->vector = new std::vector<T>(this->limit + this->buffer);
|
this->vector = std::make_shared<std::vector<T>>();
|
||||||
this->vectorPtr = std::shared_ptr<std::vector<T>>(this->vector);
|
this->vector->reserve(this->limit + this->buffer);
|
||||||
|
|
||||||
this->offset = 0;
|
this->offset = 0;
|
||||||
this->length = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return true if an item was deleted
|
// return true if an item was deleted
|
||||||
@@ -46,39 +42,36 @@ public:
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
if (this->length == this->limit) {
|
if (this->vector->size() >= this->limit) {
|
||||||
// vector is full
|
// vector is full
|
||||||
if (this->offset == this->buffer) {
|
if (this->offset == this->buffer) {
|
||||||
deleted = this->vector->at(this->offset);
|
deleted = this->vector->at(this->offset);
|
||||||
|
|
||||||
// create new vector
|
// create new vector
|
||||||
auto *vector = new std::vector<T>(this->limit + this->buffer);
|
auto newVector = std::make_shared<std::vector<T>>();
|
||||||
|
newVector->reserve(this->limit + this->buffer);
|
||||||
|
|
||||||
for (int i = 0; i < this->limit; i++) {
|
for (unsigned int i = 0; i < this->limit - 1; i++) {
|
||||||
vector->at(i) = this->vector->at(i + this->offset);
|
newVector->push_back(this->vector->at(i + this->offset));
|
||||||
}
|
}
|
||||||
|
newVector->push_back(item);
|
||||||
vector->at(limit - 1) = item;
|
|
||||||
|
|
||||||
this->offset = 0;
|
this->offset = 0;
|
||||||
|
this->vector = newVector;
|
||||||
this->vector = vector;
|
|
||||||
this->vectorPtr = std::shared_ptr<std::vector<T>>(vector);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// append item and remove first
|
|
||||||
deleted = this->vector->at(this->offset);
|
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++;
|
this->offset++;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// append item
|
// append item
|
||||||
this->vector->at(this->length) = item;
|
this->vector->push_back(item);
|
||||||
this->length++;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -87,21 +80,23 @@ public:
|
|||||||
messages::LimitedQueueSnapshot<T>
|
messages::LimitedQueueSnapshot<T>
|
||||||
getSnapshot()
|
getSnapshot()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
return LimitedQueueSnapshot<T>(vectorPtr, offset, length);
|
if(vector->size() < limit) {
|
||||||
|
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size());
|
||||||
|
} else {
|
||||||
|
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<T> *vector;
|
std::shared_ptr<std::vector<T>> vector;
|
||||||
std::shared_ptr<std::vector<T>> vectorPtr;
|
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
|
|
||||||
int offset;
|
unsigned int offset;
|
||||||
int length;
|
unsigned int limit;
|
||||||
int limit;
|
unsigned int buffer;
|
||||||
int buffer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#ifndef LIMITEDQUEUESNAPSHOT_H
|
#ifndef LIMITEDQUEUESNAPSHOT_H
|
||||||
#define LIMITEDQUEUESNAPSHOT_H
|
#define LIMITEDQUEUESNAPSHOT_H
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -14,8 +13,7 @@ class LimitedQueueSnapshot
|
|||||||
public:
|
public:
|
||||||
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset,
|
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset,
|
||||||
int length)
|
int length)
|
||||||
: vectorPtr(ptr)
|
: vector(ptr)
|
||||||
, vector(ptr.get())
|
|
||||||
, offset(offset)
|
, offset(offset)
|
||||||
, length(length)
|
, length(length)
|
||||||
{
|
{
|
||||||
@@ -24,20 +22,16 @@ public:
|
|||||||
int
|
int
|
||||||
getLength()
|
getLength()
|
||||||
{
|
{
|
||||||
return length;
|
return this->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
T const &operator[](int index) const
|
T const &operator[](int index) const
|
||||||
{
|
{
|
||||||
// assert(index >= 0);
|
return this->vector->at(index + this->offset);
|
||||||
// assert(index < length);
|
|
||||||
|
|
||||||
return vector->at(index + offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<std::vector<T>> vectorPtr;
|
std::shared_ptr<std::vector<T>> vector;
|
||||||
std::vector<T> *vector;
|
|
||||||
|
|
||||||
int offset;
|
int offset;
|
||||||
int length;
|
int length;
|
||||||
|
|||||||
+3
-3
@@ -24,19 +24,19 @@ public:
|
|||||||
Link(Type getType, const QString &getValue);
|
Link(Type getType, const QString &getValue);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
getIsValid()
|
getIsValid() const
|
||||||
{
|
{
|
||||||
return type == None;
|
return type == None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type
|
Type
|
||||||
getType()
|
getType() const
|
||||||
{
|
{
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &
|
const QString &
|
||||||
getValue()
|
getValue() const
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QObjectUserData>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -228,7 +229,7 @@ Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel,
|
|||||||
end > ircMessage.content().length())
|
end > ircMessage.content().length())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QString name = ircMessage.content().mid(start, end - start);
|
QString name = ircMessage.content().mid(start, end - start + 1);
|
||||||
|
|
||||||
twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
|
twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
|
||||||
start, Emotes::getTwitchEmoteById(name, id)));
|
start, Emotes::getTwitchEmoteById(name, id)));
|
||||||
|
|||||||
+127
-9
@@ -36,11 +36,13 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
bool recalculateImages =
|
bool recalculateImages =
|
||||||
this->emoteGeneration != Emotes::getGeneration();
|
this->emoteGeneration != Emotes::getGeneration();
|
||||||
bool recalculateText = this->fontGeneration != Fonts::getGeneration();
|
bool recalculateText = this->fontGeneration != Fonts::getGeneration();
|
||||||
|
bool newWordTypes =
|
||||||
|
this->currentWordTypes != Settings::getInstance().getWordTypeMask();
|
||||||
|
|
||||||
qreal emoteScale = settings.emoteScale.get();
|
qreal emoteScale = settings.emoteScale.get();
|
||||||
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
||||||
|
|
||||||
if (recalculateImages || recalculateText) {
|
if (recalculateImages || recalculateText || newWordTypes) {
|
||||||
this->emoteGeneration = Emotes::getGeneration();
|
this->emoteGeneration = Emotes::getGeneration();
|
||||||
this->fontGeneration = Fonts::getGeneration();
|
this->fontGeneration = Fonts::getGeneration();
|
||||||
|
|
||||||
@@ -72,6 +74,10 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newWordTypes) {
|
||||||
|
this->currentWordTypes = Settings::getInstance().getWordTypeMask();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!redraw) {
|
if (!redraw) {
|
||||||
@@ -85,6 +91,7 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
|
|
||||||
int right = width - MARGIN_RIGHT - MARGIN_LEFT;
|
int right = width - MARGIN_RIGHT - MARGIN_LEFT;
|
||||||
|
|
||||||
|
int lineNumber = 0;
|
||||||
int lineStart = 0;
|
int lineStart = 0;
|
||||||
int lineHeight = 0;
|
int lineHeight = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
@@ -139,22 +146,23 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
|
|
||||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y,
|
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y,
|
||||||
width, word.getHeight(),
|
width, word.getHeight(),
|
||||||
mid, mid));
|
lineNumber, mid, mid));
|
||||||
|
|
||||||
y += metrics.height();
|
y += metrics.height();
|
||||||
|
|
||||||
start = i - 1;
|
start = i - 1;
|
||||||
|
|
||||||
width = 0;
|
width = 0;
|
||||||
|
lineNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString mid(text.mid(start));
|
QString mid(text.mid(start));
|
||||||
width = metrics.width(mid);
|
width = metrics.width(mid);
|
||||||
|
|
||||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT,
|
this->wordParts.push_back(
|
||||||
y - word.getHeight(), width,
|
WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||||
word.getHeight(), mid, mid));
|
word.getHeight(), lineNumber, mid, mid));
|
||||||
x = width + MARGIN_LEFT + spaceWidth;
|
x = width + MARGIN_LEFT + spaceWidth;
|
||||||
|
|
||||||
lineHeight = word.getHeight();
|
lineHeight = word.getHeight();
|
||||||
@@ -164,8 +172,8 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
first = false;
|
first = false;
|
||||||
} else if (first || x + word.getWidth() + xOffset <= right) {
|
} else if (first || x + word.getWidth() + xOffset <= right) {
|
||||||
// fits in the line
|
// fits in the line
|
||||||
this->wordParts.push_back(
|
this->wordParts.push_back(WordPart(word, x, y - word.getHeight(),
|
||||||
WordPart(word, x, y - word.getHeight(), word.getCopyText()));
|
lineNumber, word.getCopyText()));
|
||||||
|
|
||||||
x += word.getWidth() + xOffset;
|
x += word.getWidth() + xOffset;
|
||||||
x += spaceWidth;
|
x += spaceWidth;
|
||||||
@@ -179,8 +187,9 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
|
|
||||||
y += lineHeight;
|
y += lineHeight;
|
||||||
|
|
||||||
this->wordParts.push_back(WordPart(
|
this->wordParts.push_back(WordPart(word, MARGIN_LEFT,
|
||||||
word, MARGIN_LEFT, y - word.getHeight(), word.getCopyText()));
|
y - word.getHeight(), lineNumber,
|
||||||
|
word.getCopyText()));
|
||||||
|
|
||||||
lineStart = this->wordParts.size() - 1;
|
lineStart = this->wordParts.size() - 1;
|
||||||
|
|
||||||
@@ -188,6 +197,8 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||||||
|
|
||||||
x = word.getWidth() + MARGIN_LEFT;
|
x = word.getWidth() + MARGIN_LEFT;
|
||||||
x += spaceWidth;
|
x += spaceWidth;
|
||||||
|
|
||||||
|
lineNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,5 +227,112 @@ MessageRef::alignWordParts(int lineStart, int lineHeight)
|
|||||||
wordPart2.setY(wordPart2.getY() + 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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ public:
|
|||||||
std::shared_ptr<QPixmap> buffer = nullptr;
|
std::shared_ptr<QPixmap> buffer = nullptr;
|
||||||
bool updateBuffer = false;
|
bool updateBuffer = false;
|
||||||
|
|
||||||
|
bool tryGetWordPart(QPoint point, messages::Word &word);
|
||||||
|
|
||||||
|
int getSelectionIndex(QPoint position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Message *message;
|
Message *message;
|
||||||
std::shared_ptr<Message> messagePtr;
|
std::shared_ptr<Message> messagePtr;
|
||||||
@@ -48,6 +52,7 @@ private:
|
|||||||
int currentLayoutWidth = -1;
|
int currentLayoutWidth = -1;
|
||||||
int fontGeneration = -1;
|
int fontGeneration = -1;
|
||||||
int emoteGeneration = -1;
|
int emoteGeneration = -1;
|
||||||
|
Word::Type currentWordTypes = Word::None;
|
||||||
|
|
||||||
void alignWordParts(int lineStart, int lineHeight);
|
void alignWordParts(int lineStart, int lineHeight);
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-2
@@ -65,6 +65,9 @@ public:
|
|||||||
ButtonTimeout
|
ButtonTimeout
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Word()
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Word(LazyLoadedImage *image, Type getType, const QString ©text,
|
explicit Word(LazyLoadedImage *image, Type getType, const QString ©text,
|
||||||
const QString &getTooltip, const Link &getLink = Link());
|
const QString &getTooltip, const Link &getLink = Link());
|
||||||
explicit Word(const QString &text, Type getType, const QColor &getColor,
|
explicit Word(const QString &text, Type getType, const QColor &getColor,
|
||||||
@@ -182,7 +185,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<short> &
|
std::vector<short> &
|
||||||
getCharacterWidthCache()
|
getCharacterWidthCache() const
|
||||||
{
|
{
|
||||||
return this->characterWidthCache;
|
return this->characterWidthCache;
|
||||||
}
|
}
|
||||||
@@ -206,7 +209,7 @@ private:
|
|||||||
Fonts::Type font = Fonts::Medium;
|
Fonts::Type font = Fonts::Medium;
|
||||||
Link link;
|
Link link;
|
||||||
|
|
||||||
std::vector<short> characterWidthCache;
|
mutable std::vector<short> characterWidthCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
WordPart::WordPart(Word &word, int x, int y, const QString ©Text,
|
WordPart::WordPart(Word &word, int x, int y, int lineNumber,
|
||||||
bool allowTrailingSpace)
|
const QString ©Text, bool allowTrailingSpace)
|
||||||
: m_word(word)
|
: m_word(word)
|
||||||
, copyText(copyText)
|
, copyText(copyText)
|
||||||
, text(word.isText() ? m_word.getText() : QString())
|
, text(word.isText() ? m_word.getText() : QString())
|
||||||
@@ -13,13 +13,14 @@ WordPart::WordPart(Word &word, int x, int y, const QString ©Text,
|
|||||||
, y(y)
|
, y(y)
|
||||||
, width(word.getWidth())
|
, width(word.getWidth())
|
||||||
, height(word.getHeight())
|
, height(word.getHeight())
|
||||||
|
, lineNumber(lineNumber)
|
||||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
WordPart::WordPart(Word &word, int x, int y, int width, int height,
|
WordPart::WordPart(Word &word, int x, int y, int width, int height,
|
||||||
const QString ©Text, const QString &customText,
|
int lineNumber, const QString ©Text,
|
||||||
bool allowTrailingSpace)
|
const QString &customText, bool allowTrailingSpace)
|
||||||
: m_word(word)
|
: m_word(word)
|
||||||
, copyText(copyText)
|
, copyText(copyText)
|
||||||
, text(customText)
|
, text(customText)
|
||||||
@@ -27,6 +28,7 @@ WordPart::WordPart(Word &word, int x, int y, int width, int height,
|
|||||||
, y(y)
|
, y(y)
|
||||||
, width(width)
|
, width(width)
|
||||||
, height(height)
|
, height(height)
|
||||||
|
, lineNumber(lineNumber)
|
||||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -12,12 +12,12 @@ class Word;
|
|||||||
class WordPart
|
class WordPart
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WordPart(Word &getWord, int getX, int getY, const QString &getCopyText,
|
WordPart(Word &getWord, int getX, int getY, int lineNumber,
|
||||||
bool allowTrailingSpace = true);
|
const QString &getCopyText, bool allowTrailingSpace = true);
|
||||||
|
|
||||||
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight,
|
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight,
|
||||||
const QString &getCopyText, const QString &customText,
|
int lineNumber, const QString &getCopyText,
|
||||||
bool allowTrailingSpace = true);
|
const QString &customText, bool allowTrailingSpace = true);
|
||||||
|
|
||||||
const Word &
|
const Word &
|
||||||
getWord() const
|
getWord() const
|
||||||
@@ -98,6 +98,12 @@ public:
|
|||||||
return this->text;
|
return this->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getLineNumber()
|
||||||
|
{
|
||||||
|
return this->lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Word &m_word;
|
Word &m_word;
|
||||||
|
|
||||||
@@ -109,6 +115,8 @@ private:
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
|
int lineNumber;
|
||||||
|
|
||||||
bool _trailingSpace;
|
bool _trailingSpace;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+140
-30
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QGraphicsBlurEffect>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
@@ -18,8 +20,11 @@ ChatWidgetView::ChatWidgetView(ChatWidget *parent)
|
|||||||
: QWidget()
|
: QWidget()
|
||||||
, chatWidget(parent)
|
, chatWidget(parent)
|
||||||
, scrollbar(this)
|
, scrollbar(this)
|
||||||
|
, onlyUpdateEmotes(false)
|
||||||
{
|
{
|
||||||
|
this->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
this->scrollbar.setSmallChange(5);
|
this->scrollbar.setSmallChange(5);
|
||||||
|
this->setMouseTracking(true);
|
||||||
|
|
||||||
QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged,
|
QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged,
|
||||||
this, &ChatWidgetView::wordTypeMaskChanged);
|
this, &ChatWidgetView::wordTypeMaskChanged);
|
||||||
@@ -49,33 +54,26 @@ ChatWidgetView::layoutMessages()
|
|||||||
|
|
||||||
int start = this->scrollbar.getCurrentValue();
|
int start = this->scrollbar.getCurrentValue();
|
||||||
|
|
||||||
if (messages.getLength() <= start) {
|
// layout the visible messages in the view
|
||||||
// The scrollbar wants to show more values than we can offer
|
if (messages.getLength() > start) {
|
||||||
|
int y = -(messages[start].get()->getHeight() *
|
||||||
|
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
||||||
|
|
||||||
// just return for now
|
for (int i = start; i < messages.getLength(); ++i) {
|
||||||
return false;
|
auto messagePtr = messages[i];
|
||||||
|
auto message = messagePtr.get();
|
||||||
|
|
||||||
|
redraw |= message->layout(this->width(), true);
|
||||||
|
|
||||||
// Lower start value to the last message
|
y += message->getHeight();
|
||||||
// start = messages.getLength() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int y = -(messages[start].get()->getHeight() *
|
if (y >= height()) {
|
||||||
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
break;
|
||||||
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// layout the messages at the bottom to determine the scrollbar thumb size
|
||||||
int h = this->height() - 8;
|
int h = this->height() - 8;
|
||||||
|
|
||||||
for (int i = messages.getLength() - 1; i >= 0; i--) {
|
for (int i = messages.getLength() - 1; i >= 0; i--) {
|
||||||
@@ -97,6 +95,10 @@ ChatWidgetView::layoutMessages()
|
|||||||
|
|
||||||
this->scrollbar.setVisible(showScrollbar);
|
this->scrollbar.setVisible(showScrollbar);
|
||||||
|
|
||||||
|
if (!showScrollbar) {
|
||||||
|
this->scrollbar.setDesiredValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
this->scrollbar.setMaximum(messages.getLength());
|
this->scrollbar.setMaximum(messages.getLength());
|
||||||
|
|
||||||
return redraw;
|
return redraw;
|
||||||
@@ -109,10 +111,12 @@ ChatWidgetView::resizeEvent(QResizeEvent *)
|
|||||||
this->scrollbar.move(width() - this->scrollbar.width(), 0);
|
this->scrollbar.move(width() - this->scrollbar.width(), 0);
|
||||||
|
|
||||||
layoutMessages();
|
layoutMessages();
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatWidgetView::paintEvent(QPaintEvent *)
|
ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
{
|
{
|
||||||
QPainter _painter(this);
|
QPainter _painter(this);
|
||||||
|
|
||||||
@@ -120,6 +124,24 @@ ChatWidgetView::paintEvent(QPaintEvent *)
|
|||||||
|
|
||||||
ColorScheme &scheme = ColorScheme::getInstance();
|
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
|
// code for tesing colors
|
||||||
/*
|
/*
|
||||||
QColor color;
|
QColor color;
|
||||||
@@ -178,16 +200,13 @@ ChatWidgetView::paintEvent(QPaintEvent *)
|
|||||||
updateBuffer = true;
|
updateBuffer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update messages that have been changed
|
||||||
if (updateBuffer) {
|
if (updateBuffer) {
|
||||||
QPainter painter(buffer);
|
QPainter painter(buffer);
|
||||||
painter.fillRect(buffer->rect(), scheme.ChatBackground);
|
painter.fillRect(buffer->rect(), scheme.ChatBackground);
|
||||||
|
|
||||||
for (messages::WordPart const &wordPart :
|
for (messages::WordPart const &wordPart :
|
||||||
messageRef->getWordParts()) {
|
messageRef->getWordParts()) {
|
||||||
painter.setPen(QColor(255, 0, 0));
|
|
||||||
painter.drawRect(wordPart.getX(), wordPart.getY(),
|
|
||||||
wordPart.getWidth(), wordPart.getHeight());
|
|
||||||
|
|
||||||
// image
|
// image
|
||||||
if (wordPart.getWord().isImage()) {
|
if (wordPart.getWord().isImage()) {
|
||||||
messages::LazyLoadedImage &lli =
|
messages::LazyLoadedImage &lli =
|
||||||
@@ -221,6 +240,24 @@ ChatWidgetView::paintEvent(QPaintEvent *)
|
|||||||
messageRef->updateBuffer = false;
|
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;
|
messageRef->buffer = bufferPtr;
|
||||||
|
|
||||||
_painter.drawPixmap(0, y, *buffer);
|
_painter.drawPixmap(0, y, *buffer);
|
||||||
@@ -231,16 +268,89 @@ ChatWidgetView::paintEvent(QPaintEvent *)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (GifEmoteData &item : this->gifEmotes) {
|
||||||
|
_painter.fillRect(item.rect, scheme.ChatBackground);
|
||||||
|
|
||||||
|
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatWidgetView::wheelEvent(QWheelEvent *event)
|
ChatWidgetView::wheelEvent(QWheelEvent *event)
|
||||||
{
|
{
|
||||||
this->scrollbar.setDesiredValue(
|
if (this->scrollbar.isVisible()) {
|
||||||
this->scrollbar.getDesiredValue() -
|
this->scrollbar.setDesiredValue(
|
||||||
event->delta() / 10.0 *
|
this->scrollbar.getDesiredValue() -
|
||||||
Settings::getInstance().mouseScrollMultiplier.get(),
|
event->delta() / 10.0 *
|
||||||
true);
|
Settings::getInstance().mouseScrollMultiplier.get(),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
std::shared_ptr<messages::MessageRef> 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<messages::MessageRef> &_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define CHATVIEW_H
|
#define CHATVIEW_H
|
||||||
|
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
#include "messages/lazyloadedimage.h"
|
||||||
#include "messages/messageref.h"
|
#include "messages/messageref.h"
|
||||||
#include "messages/word.h"
|
#include "messages/word.h"
|
||||||
#include "widgets/scrollbar.h"
|
#include "widgets/scrollbar.h"
|
||||||
@@ -25,24 +26,44 @@ public:
|
|||||||
|
|
||||||
bool layoutMessages();
|
bool layoutMessages();
|
||||||
|
|
||||||
|
void
|
||||||
|
updateGifEmotes()
|
||||||
|
{
|
||||||
|
this->onlyUpdateEmotes = true;
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *);
|
void resizeEvent(QResizeEvent *);
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *);
|
void paintEvent(QPaintEvent *);
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent *event);
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
|
||||||
|
bool tryGetMessageAt(QPoint p,
|
||||||
|
std::shared_ptr<messages::MessageRef> &message,
|
||||||
|
QPoint &relativePos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct GifEmoteData {
|
||||||
|
messages::LazyLoadedImage *image;
|
||||||
|
QRect rect;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<GifEmoteData> gifEmotes;
|
||||||
|
|
||||||
ChatWidget *chatWidget;
|
ChatWidget *chatWidget;
|
||||||
|
|
||||||
ScrollBar scrollbar;
|
ScrollBar scrollbar;
|
||||||
|
bool onlyUpdateEmotes;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void
|
void
|
||||||
wordTypeMaskChanged()
|
wordTypeMaskChanged()
|
||||||
{
|
{
|
||||||
if (layoutMessages()) {
|
layoutMessages();
|
||||||
update();
|
update();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-2
@@ -44,7 +44,7 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
|||||||
|
|
||||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||||
if (widget->getView().layoutMessages()) {
|
if (widget->getView().layoutMessages()) {
|
||||||
widget->update();
|
widget->getView().update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,11 +66,29 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
|||||||
|
|
||||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||||
widget->getView().layoutMessages();
|
widget->getView().layoutMessages();
|
||||||
widget->update();
|
widget->getView().update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MainWindow::repaintGifEmotes()
|
||||||
|
{
|
||||||
|
auto *page = notebook.getSelectedPage();
|
||||||
|
|
||||||
|
if (page == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<ChatWidget *> &widgets = page->getChatWidgets();
|
||||||
|
|
||||||
|
for (auto it = widgets.begin(); it != widgets.end(); ++it) {
|
||||||
|
ChatWidget *widget = *it;
|
||||||
|
|
||||||
|
widget->getView().updateGifEmotes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::load(const boost::property_tree::ptree &tree)
|
MainWindow::load(const boost::property_tree::ptree &tree)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public:
|
|||||||
|
|
||||||
void layoutVisibleChatWidgets(Channel *channel = NULL);
|
void layoutVisibleChatWidgets(Channel *channel = NULL);
|
||||||
void repaintVisibleChatWidgets(Channel *channel = NULL);
|
void repaintVisibleChatWidgets(Channel *channel = NULL);
|
||||||
|
void repaintGifEmotes();
|
||||||
|
|
||||||
void load(const boost::property_tree::ptree &tree);
|
void load(const boost::property_tree::ptree &tree);
|
||||||
boost::property_tree::ptree save();
|
boost::property_tree::ptree save();
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ Notebook::performLayout(bool animated)
|
|||||||
if (Settings::getInstance().hideUserButton.get()) {
|
if (Settings::getInstance().hideUserButton.get()) {
|
||||||
userButton.hide();
|
userButton.hide();
|
||||||
} else {
|
} else {
|
||||||
|
userButton.move(x, 0);
|
||||||
userButton.show();
|
userButton.show();
|
||||||
x += 24;
|
x += 24;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ Windows::repaintVisibleChatWidgets(Channel *channel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Windows::repaintGifEmotes()
|
||||||
|
{
|
||||||
|
if (Windows::mainWindow != nullptr) {
|
||||||
|
Windows::mainWindow->repaintGifEmotes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Windows::updateAll()
|
Windows::updateAll()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class Windows
|
|||||||
public:
|
public:
|
||||||
static void layoutVisibleChatWidgets(Channel *channel = NULL);
|
static void layoutVisibleChatWidgets(Channel *channel = NULL);
|
||||||
static void repaintVisibleChatWidgets(Channel *channel = NULL);
|
static void repaintVisibleChatWidgets(Channel *channel = NULL);
|
||||||
|
static void repaintGifEmotes();
|
||||||
static void updateAll();
|
static void updateAll();
|
||||||
|
|
||||||
static widgets::MainWindow &
|
static widgets::MainWindow &
|
||||||
|
|||||||
Reference in New Issue
Block a user