Merge branch 'master' into apa-notification-on-live
This commit is contained in:
+24
-54
@@ -15,61 +15,31 @@ bool operator!=(const Emote &a, const Emote &b)
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
// EmotePtr Emote::create(const EmoteData2 &data)
|
||||
//{
|
||||
//}
|
||||
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache)
|
||||
{
|
||||
// reuse old shared_ptr if nothing changed
|
||||
auto it = cache.find(emote.name);
|
||||
if (it != cache.end() && *it->second == emote) return it->second;
|
||||
|
||||
// EmotePtr Emote::create(EmoteData2 &&data)
|
||||
//{
|
||||
//}
|
||||
return std::make_shared<Emote>(std::move(emote));
|
||||
}
|
||||
|
||||
// Emote::Emote(EmoteData2 &&data)
|
||||
// : data_(data)
|
||||
//{
|
||||
//}
|
||||
//
|
||||
// Emote::Emote(const EmoteData2 &data)
|
||||
// : data_(data)
|
||||
//{
|
||||
//}
|
||||
//
|
||||
// const Url &Emote::getHomePage() const
|
||||
//{
|
||||
// return this->data_.homePage;
|
||||
//}
|
||||
//
|
||||
// const EmoteName &Emote::getName() const
|
||||
//{
|
||||
// return this->data_.name;
|
||||
//}
|
||||
//
|
||||
// const Tooltip &Emote::getTooltip() const
|
||||
//{
|
||||
// return this->data_.tooltip;
|
||||
//}
|
||||
//
|
||||
// const ImageSet &Emote::getImages() const
|
||||
//{
|
||||
// return this->data_.images;
|
||||
//}
|
||||
//
|
||||
// const QString &Emote::getCopyString() const
|
||||
//{
|
||||
// return this->data_.name.string;
|
||||
//}
|
||||
//
|
||||
// bool Emote::operator==(const Emote &other) const
|
||||
//{
|
||||
// auto &a = this->data_;
|
||||
// auto &b = other.data_;
|
||||
//
|
||||
// return std::tie(a.homePage, a.name, a.tooltip, a.images) ==
|
||||
// std::tie(b.homePage, b.name, b.tooltip, b.images);
|
||||
//}
|
||||
//
|
||||
// bool Emote::operator!=(const Emote &other) const
|
||||
//{
|
||||
// return !this->operator==(other);
|
||||
//}
|
||||
EmotePtr cachedOrMakeEmotePtr(
|
||||
Emote &&emote,
|
||||
std::unordered_map<EmoteId, std::weak_ptr<const Emote>> &cache,
|
||||
std::mutex &mutex, const EmoteId &id)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
|
||||
auto shared = cache[id].lock();
|
||||
if (shared && *shared == emote) {
|
||||
// reuse old shared_ptr if nothing changed
|
||||
return shared;
|
||||
} else {
|
||||
shared = std::make_shared<Emote>(std::move(emote));
|
||||
cache[id] = shared;
|
||||
return shared;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
+5
-27
@@ -7,9 +7,6 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
QStringAlias(EmoteId);
|
||||
QStringAlias(EmoteName);
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Emote {
|
||||
@@ -37,29 +34,10 @@ using EmoteIdMap = std::unordered_map<EmoteId, EmotePtr>;
|
||||
using WeakEmoteMap = std::unordered_map<EmoteName, std::weak_ptr<const Emote>>;
|
||||
using WeakEmoteIdMap = std::unordered_map<EmoteId, std::weak_ptr<const Emote>>;
|
||||
|
||||
// struct EmoteData2 {
|
||||
// EmoteName name;
|
||||
// ImageSet images;
|
||||
// Tooltip tooltip;
|
||||
// Url homePage;
|
||||
//};
|
||||
//
|
||||
// class Emote
|
||||
//{
|
||||
// public:
|
||||
// Emote(EmoteData2 &&data);
|
||||
// Emote(const EmoteData2 &data);
|
||||
//
|
||||
// const Url &getHomePage() const;
|
||||
// const EmoteName &getName() const;
|
||||
// const Tooltip &getTooltip() const;
|
||||
// const ImageSet &getImages() const;
|
||||
// const QString &getCopyString() const;
|
||||
// bool operator==(const Emote &other) const;
|
||||
// bool operator!=(const Emote &other) const;
|
||||
//
|
||||
// private:
|
||||
// EmoteData2 data_;
|
||||
//};
|
||||
EmotePtr cachedOrMakeEmotePtr(Emote &&emote, const EmoteMap &cache);
|
||||
EmotePtr cachedOrMakeEmotePtr(
|
||||
Emote &&emote,
|
||||
std::unordered_map<EmoteId, std::weak_ptr<const Emote>> &cache,
|
||||
std::mutex &mutex, const EmoteId &id);
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <boost/optional.hpp>
|
||||
#include <unordered_map>
|
||||
#include <util/QStringHash.hpp>
|
||||
|
||||
#include "common/UniqueAccess.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
template <typename TKey>
|
||||
class MapReplacement
|
||||
{
|
||||
public:
|
||||
MapReplacement(std::unordered_map<TKey, EmotePtr> &items)
|
||||
: oldItems_(items)
|
||||
{
|
||||
}
|
||||
|
||||
void add(const TKey &key, const Emote &data)
|
||||
{
|
||||
this->add(key, Emote(data));
|
||||
}
|
||||
|
||||
void add(const TKey &key, Emote &&data)
|
||||
{
|
||||
auto it = this->oldItems_.find(key);
|
||||
if (it != this->oldItems_.end() && *it->second == data) {
|
||||
this->newItems_[key] = it->second;
|
||||
} else {
|
||||
this->newItems_[key] = std::make_shared<Emote>(std::move(data));
|
||||
}
|
||||
}
|
||||
|
||||
void apply()
|
||||
{
|
||||
this->oldItems_ = std::move(this->newItems_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<TKey, EmotePtr> &oldItems_;
|
||||
std::unordered_map<TKey, EmotePtr> newItems_;
|
||||
};
|
||||
|
||||
template <typename TKey>
|
||||
class EmoteCache
|
||||
{
|
||||
public:
|
||||
using Iterator = typename std::unordered_map<TKey, EmotePtr>::iterator;
|
||||
using ConstIterator = typename std::unordered_map<TKey, EmotePtr>::iterator;
|
||||
|
||||
Iterator begin()
|
||||
{
|
||||
return this->items_.begin();
|
||||
}
|
||||
|
||||
ConstIterator begin() const
|
||||
{
|
||||
return this->items_.begin();
|
||||
}
|
||||
|
||||
Iterator end()
|
||||
{
|
||||
return this->items_.end();
|
||||
}
|
||||
|
||||
ConstIterator end() const
|
||||
{
|
||||
return this->items_.end();
|
||||
}
|
||||
|
||||
boost::optional<EmotePtr> get(const TKey &key) const
|
||||
{
|
||||
auto it = this->items_.find(key);
|
||||
|
||||
if (it == this->items_.end())
|
||||
return boost::none;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
MapReplacement<TKey> makeReplacment()
|
||||
{
|
||||
return MapReplacement<TKey>(this->items_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<TKey, EmotePtr> items_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,44 +0,0 @@
|
||||
#include "EmoteMap.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
// EmoteData::EmoteData(Image *image)
|
||||
// : image1x(image)
|
||||
//{
|
||||
//}
|
||||
|
||||
//// Emotes must have a 1x image to be valid
|
||||
// bool EmoteData::isValid() const
|
||||
//{
|
||||
// return this->image1x != nullptr;
|
||||
//}
|
||||
|
||||
// Image *EmoteData::getImage(float scale) const
|
||||
//{
|
||||
// int quality = getApp()->settings->preferredEmoteQuality;
|
||||
|
||||
// if (quality == 0) {
|
||||
// scale *= getApp()->settings->emoteScale.getValue();
|
||||
// quality = [&] {
|
||||
// if (scale <= 1) return 1;
|
||||
// if (scale <= 2) return 2;
|
||||
// return 3;
|
||||
// }();
|
||||
// }
|
||||
|
||||
// Image *_image;
|
||||
// if (quality == 3 && this->image3x != nullptr) {
|
||||
// _image = this->image3x;
|
||||
// } else if (quality >= 2 && this->image2x != nullptr) {
|
||||
// _image = this->image2x;
|
||||
// } else {
|
||||
// _image = this->image1x;
|
||||
// }
|
||||
|
||||
// return _image;
|
||||
//}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/optional.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
// class EmoteMap
|
||||
//{
|
||||
// public:
|
||||
// void add(Emote emote);
|
||||
// void remove(const Emote &emote);
|
||||
// void remove(const QString &name);
|
||||
|
||||
// private:
|
||||
//};
|
||||
|
||||
// using EmoteMap = ConcurrentMap<QString, EmoteData>;
|
||||
|
||||
} // namespace chatterino
|
||||
+166
-127
@@ -1,8 +1,10 @@
|
||||
#include "messages/Image.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "common/NetworkRequest.hpp"
|
||||
#include "debug/AssertInGuiThread.hpp"
|
||||
#include "debug/Benchmark.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
@@ -15,119 +17,171 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTimer>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
const QPixmap *getPixmap(const Pixmap &pixmap)
|
||||
{
|
||||
if (pixmap.which() == 0)
|
||||
return boost::get<const QPixmap *>(pixmap);
|
||||
else
|
||||
return boost::get<std::unique_ptr<QPixmap>>(pixmap).get();
|
||||
}
|
||||
// Frames
|
||||
Frames::Frames()
|
||||
{
|
||||
DebugCount::increase("images");
|
||||
}
|
||||
|
||||
// Frames
|
||||
Frames::Frames()
|
||||
{
|
||||
DebugCount::increase("images");
|
||||
}
|
||||
Frames::Frames(const QVector<Frame<QPixmap>> &frames)
|
||||
: items_(frames)
|
||||
{
|
||||
assertInGuiThread();
|
||||
DebugCount::increase("images");
|
||||
|
||||
Frames::Frames(std::vector<Frame> &&frames)
|
||||
: items_(std::move(frames))
|
||||
{
|
||||
DebugCount::increase("images");
|
||||
if (this->animated()) DebugCount::increase("animated images");
|
||||
}
|
||||
if (this->animated()) {
|
||||
DebugCount::increase("animated images");
|
||||
|
||||
Frames::~Frames()
|
||||
{
|
||||
DebugCount::decrease("images");
|
||||
if (this->animated()) DebugCount::decrease("animated images");
|
||||
}
|
||||
|
||||
void Frames::advance()
|
||||
{
|
||||
this->durationOffset_ += GIF_FRAME_LENGTH;
|
||||
|
||||
while (true) {
|
||||
this->index_ %= this->items_.size();
|
||||
if (this->durationOffset_ > this->items_[this->index_].duration) {
|
||||
this->durationOffset_ -= this->items_[this->index_].duration;
|
||||
this->index_ = (this->index_ + 1) % this->items_.size();
|
||||
} else {
|
||||
break;
|
||||
this->gifTimerConnection_ =
|
||||
getApp()->emotes->gifTimer.signal.connect(
|
||||
[this] { this->advance(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Frames::animated() const
|
||||
{
|
||||
return this->items_.size() > 1;
|
||||
}
|
||||
Frames::~Frames()
|
||||
{
|
||||
assertInGuiThread();
|
||||
DebugCount::decrease("images");
|
||||
|
||||
const QPixmap *Frames::current() const
|
||||
{
|
||||
if (this->items_.size() == 0) return nullptr;
|
||||
return getPixmap(this->items_[this->index_].pixmap);
|
||||
}
|
||||
if (this->animated()) {
|
||||
DebugCount::decrease("animated images");
|
||||
}
|
||||
|
||||
const QPixmap *Frames::first() const
|
||||
{
|
||||
if (this->items_.size() == 0) return nullptr;
|
||||
return getPixmap(this->items_.front().pixmap);
|
||||
}
|
||||
this->gifTimerConnection_.disconnect();
|
||||
}
|
||||
|
||||
// functions
|
||||
std::vector<Frame> readFrames(QImageReader &reader, const Url &url)
|
||||
{
|
||||
std::vector<Frame> frames;
|
||||
void Frames::advance()
|
||||
{
|
||||
this->durationOffset_ += GIF_FRAME_LENGTH;
|
||||
|
||||
while (true) {
|
||||
this->index_ %= this->items_.size();
|
||||
|
||||
if (this->index_ >= this->items_.size()) {
|
||||
this->index_ = this->index_;
|
||||
}
|
||||
|
||||
if (this->durationOffset_ > this->items_[this->index_].duration) {
|
||||
this->durationOffset_ -= this->items_[this->index_].duration;
|
||||
this->index_ = (this->index_ + 1) % this->items_.size();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Frames::animated() const
|
||||
{
|
||||
return this->items_.size() > 1;
|
||||
}
|
||||
|
||||
boost::optional<QPixmap> Frames::current() const
|
||||
{
|
||||
if (this->items_.size() == 0) return boost::none;
|
||||
return this->items_[this->index_].image;
|
||||
}
|
||||
|
||||
boost::optional<QPixmap> Frames::first() const
|
||||
{
|
||||
if (this->items_.size() == 0) return boost::none;
|
||||
return this->items_.front().image;
|
||||
}
|
||||
|
||||
// functions
|
||||
QVector<Frame<QImage>> readFrames(QImageReader &reader, const Url &url)
|
||||
{
|
||||
QVector<Frame<QImage>> frames;
|
||||
|
||||
if (reader.imageCount() == 0) {
|
||||
log("Error while reading image {}: '{}'", url.string,
|
||||
reader.errorString());
|
||||
return frames;
|
||||
}
|
||||
|
||||
QImage image;
|
||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||
if (reader.read(&image)) {
|
||||
QPixmap::fromImage(image);
|
||||
|
||||
int duration = std::max(20, reader.nextImageDelay());
|
||||
frames.push_back(Frame<QImage>{image, duration});
|
||||
}
|
||||
}
|
||||
|
||||
if (frames.size() == 0) {
|
||||
log("Error while reading image {}: '{}'", url.string,
|
||||
reader.errorString());
|
||||
}
|
||||
|
||||
if (reader.imageCount() <= 0) {
|
||||
Log("Error while reading image {}: '{}'", url.string,
|
||||
reader.errorString());
|
||||
return frames;
|
||||
}
|
||||
|
||||
QImage image;
|
||||
for (int index = 0; index < reader.imageCount(); ++index) {
|
||||
if (reader.read(&image)) {
|
||||
auto pixmap = std::make_unique<QPixmap>(QPixmap::fromImage(image));
|
||||
// parsed
|
||||
template <typename Assign>
|
||||
void assignDelayed(
|
||||
std::queue<std::pair<Assign, QVector<Frame<QPixmap>>>> &queued,
|
||||
std::mutex &mutex, std::atomic_bool &loadedEventQueued)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
int i = 0;
|
||||
|
||||
int duration = std::max(20, reader.nextImageDelay());
|
||||
frames.push_back(Frame{std::move(pixmap), duration});
|
||||
while (!queued.empty()) {
|
||||
queued.front().first(queued.front().second);
|
||||
queued.pop();
|
||||
|
||||
if (++i > 50) {
|
||||
QTimer::singleShot(3, [&] {
|
||||
assignDelayed(queued, mutex, loadedEventQueued);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
getApp()->windows->forceLayoutChannelViews();
|
||||
loadedEventQueued = false;
|
||||
}
|
||||
|
||||
if (frames.size() != 0) {
|
||||
Log("Error while reading image {}: '{}'", url.string,
|
||||
reader.errorString());
|
||||
template <typename Assign>
|
||||
auto makeConvertCallback(const QVector<Frame<QImage>> &parsed,
|
||||
Assign assign)
|
||||
{
|
||||
return [parsed, assign] {
|
||||
// convert to pixmap
|
||||
auto frames = QVector<Frame<QPixmap>>();
|
||||
std::transform(parsed.begin(), parsed.end(),
|
||||
std::back_inserter(frames), [](auto &frame) {
|
||||
return Frame<QPixmap>{
|
||||
QPixmap::fromImage(frame.image),
|
||||
frame.duration};
|
||||
});
|
||||
|
||||
// put into stack
|
||||
static std::queue<std::pair<Assign, QVector<Frame<QPixmap>>>>
|
||||
queued;
|
||||
static std::mutex mutex;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
queued.emplace(assign, frames);
|
||||
|
||||
static std::atomic_bool loadedEventQueued{false};
|
||||
|
||||
if (!loadedEventQueued) {
|
||||
loadedEventQueued = true;
|
||||
|
||||
QTimer::singleShot(100, [=] {
|
||||
assignDelayed(queued, mutex, loadedEventQueued);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
void queueLoadedEvent()
|
||||
{
|
||||
static auto eventQueued = false;
|
||||
|
||||
if (!eventQueued) {
|
||||
eventQueued = true;
|
||||
|
||||
QTimer::singleShot(250, [] {
|
||||
getApp()->windows->incGeneration();
|
||||
getApp()->windows->layoutChannelViews();
|
||||
eventQueued = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// IMAGE2
|
||||
std::atomic<bool> Image::loadedEventQueued{false};
|
||||
|
||||
ImagePtr Image::fromUrl(const Url &url, qreal scale)
|
||||
{
|
||||
static std::unordered_map<Url, std::weak_ptr<Image>> cache;
|
||||
@@ -140,18 +194,13 @@ ImagePtr Image::fromUrl(const Url &url, qreal scale)
|
||||
if (!shared) {
|
||||
cache[url] = shared = ImagePtr(new Image(url, scale));
|
||||
} else {
|
||||
Warn("same image loaded multiple times: {}", url.string);
|
||||
// Warn("same image loaded multiple times: {}", url.string);
|
||||
}
|
||||
|
||||
return shared;
|
||||
}
|
||||
|
||||
ImagePtr Image::fromOwningPixmap(std::unique_ptr<QPixmap> pixmap, qreal scale)
|
||||
{
|
||||
return ImagePtr(new Image(std::move(pixmap), scale));
|
||||
}
|
||||
|
||||
ImagePtr Image::fromNonOwningPixmap(QPixmap *pixmap, qreal scale)
|
||||
ImagePtr Image::fromPixmap(const QPixmap &pixmap, qreal scale)
|
||||
{
|
||||
return ImagePtr(new Image(pixmap, scale));
|
||||
}
|
||||
@@ -171,23 +220,15 @@ Image::Image(const Url &url, qreal scale)
|
||||
: url_(url)
|
||||
, scale_(scale)
|
||||
, shouldLoad_(true)
|
||||
, frames_(std::make_unique<Frames>())
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(std::unique_ptr<QPixmap> owning, qreal scale)
|
||||
Image::Image(const QPixmap &pixmap, qreal scale)
|
||||
: scale_(scale)
|
||||
, frames_(std::make_unique<Frames>(
|
||||
QVector<Frame<QPixmap>>{Frame<QPixmap>{pixmap, 1}}))
|
||||
{
|
||||
std::vector<Frame> vec;
|
||||
vec.push_back(Frame{std::move(owning)});
|
||||
this->frames_ = std::move(vec);
|
||||
}
|
||||
|
||||
Image::Image(QPixmap *nonOwning, qreal scale)
|
||||
: scale_(scale)
|
||||
{
|
||||
std::vector<Frame> vec;
|
||||
vec.push_back(Frame{nonOwning});
|
||||
this->frames_ = std::move(vec);
|
||||
}
|
||||
|
||||
const Url &Image::url() const
|
||||
@@ -195,7 +236,7 @@ const Url &Image::url() const
|
||||
return this->url_;
|
||||
}
|
||||
|
||||
const QPixmap *Image::pixmap() const
|
||||
boost::optional<QPixmap> Image::pixmap() const
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
@@ -204,7 +245,7 @@ const QPixmap *Image::pixmap() const
|
||||
const_cast<Image *>(this)->load();
|
||||
}
|
||||
|
||||
return this->frames_.current();
|
||||
return this->frames_->current();
|
||||
}
|
||||
|
||||
qreal Image::scale() const
|
||||
@@ -212,7 +253,7 @@ qreal Image::scale() const
|
||||
return this->scale_;
|
||||
}
|
||||
|
||||
bool Image::empty() const
|
||||
bool Image::isEmpty() const
|
||||
{
|
||||
return this->empty_;
|
||||
}
|
||||
@@ -221,14 +262,14 @@ bool Image::animated() const
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
return this->frames_.animated();
|
||||
return this->frames_->animated();
|
||||
}
|
||||
|
||||
int Image::width() const
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
if (auto pixmap = this->frames_.first())
|
||||
if (auto pixmap = this->frames_->first())
|
||||
return pixmap->width() * this->scale_;
|
||||
else
|
||||
return 16;
|
||||
@@ -238,7 +279,7 @@ int Image::height() const
|
||||
{
|
||||
assertInGuiThread();
|
||||
|
||||
if (auto pixmap = this->frames_.first())
|
||||
if (auto pixmap = this->frames_->first())
|
||||
return pixmap->height() * this->scale_;
|
||||
else
|
||||
return 16;
|
||||
@@ -247,39 +288,37 @@ int Image::height() const
|
||||
void Image::load()
|
||||
{
|
||||
NetworkRequest req(this->url().string);
|
||||
req.setExecuteConcurrently(true);
|
||||
req.setCaller(&this->object_);
|
||||
req.setUseQuickLoadCache(true);
|
||||
req.onSuccess([this, weak = weakOf(this)](auto result) -> Outcome {
|
||||
assertInGuiThread();
|
||||
|
||||
req.onSuccess([that = this, weak = weakOf(this)](auto result) -> Outcome {
|
||||
auto shared = weak.lock();
|
||||
if (!shared) return Failure;
|
||||
|
||||
auto data = result.getData();
|
||||
|
||||
// const cast since we are only reading from it
|
||||
QBuffer buffer(const_cast<QByteArray *>(&result.getData()));
|
||||
QBuffer buffer(const_cast<QByteArray *>(&data));
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QImageReader reader(&buffer);
|
||||
auto parsed = readFrames(reader, that->url());
|
||||
|
||||
postToThread(makeConvertCallback(parsed, [weak](auto frames) {
|
||||
if (auto shared = weak.lock())
|
||||
shared->frames_ = std::make_unique<Frames>(frames);
|
||||
}));
|
||||
|
||||
this->frames_ = readFrames(reader, this->url());
|
||||
return Success;
|
||||
});
|
||||
req.onError([this, weak = weakOf(this)](int) {
|
||||
auto shared = weak.lock();
|
||||
if (!shared) return false;
|
||||
|
||||
this->frames_ = std::vector<Frame>();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
req.execute();
|
||||
}
|
||||
|
||||
bool Image::operator==(const Image &other) const
|
||||
{
|
||||
if (this->empty() && other.empty()) return true;
|
||||
if (this->isEmpty() && other.isEmpty()) return true;
|
||||
if (!this->url_.string.isEmpty() && this->url_ == other.url_) return true;
|
||||
if (this->frames_.first() == other.frames_.first()) return true;
|
||||
if (this->frames_->first() == other.frames_->first()) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+31
-34
@@ -1,43 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Common.hpp"
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
#include <atomic>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
#include "common/Aliases.hpp"
|
||||
#include "common/NullablePtr.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace {
|
||||
using Pixmap = boost::variant<const QPixmap *, std::unique_ptr<QPixmap>>;
|
||||
struct Frame {
|
||||
Pixmap pixmap;
|
||||
int duration;
|
||||
};
|
||||
class Frames
|
||||
{
|
||||
public:
|
||||
Frames();
|
||||
Frames(std::vector<Frame> &&frames);
|
||||
~Frames();
|
||||
Frames(Frames &&other) = default;
|
||||
Frames &operator=(Frames &&other) = default;
|
||||
template <typename Image>
|
||||
struct Frame {
|
||||
Image image;
|
||||
int duration;
|
||||
};
|
||||
class Frames : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
Frames();
|
||||
Frames(const QVector<Frame<QPixmap>> &frames);
|
||||
~Frames();
|
||||
|
||||
bool animated() const;
|
||||
void advance();
|
||||
const QPixmap *current() const;
|
||||
const QPixmap *first() const;
|
||||
bool animated() const;
|
||||
void advance();
|
||||
boost::optional<QPixmap> current() const;
|
||||
boost::optional<QPixmap> first() const;
|
||||
|
||||
private:
|
||||
std::vector<Frame> items_;
|
||||
int index_{0};
|
||||
int durationOffset_{0};
|
||||
};
|
||||
private:
|
||||
QVector<Frame<QPixmap>> items_;
|
||||
int index_{0};
|
||||
int durationOffset_{0};
|
||||
pajlada::Signals::Connection gifTimerConnection_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class Image;
|
||||
@@ -47,15 +49,13 @@ class Image : public std::enable_shared_from_this<Image>, boost::noncopyable
|
||||
{
|
||||
public:
|
||||
static ImagePtr fromUrl(const Url &url, qreal scale = 1);
|
||||
static ImagePtr fromOwningPixmap(std::unique_ptr<QPixmap> pixmap,
|
||||
qreal scale = 1);
|
||||
static ImagePtr fromNonOwningPixmap(QPixmap *pixmap, qreal scale = 1);
|
||||
static ImagePtr fromPixmap(const QPixmap &pixmap, qreal scale = 1);
|
||||
static ImagePtr getEmpty();
|
||||
|
||||
const Url &url() const;
|
||||
const QPixmap *pixmap() const;
|
||||
boost::optional<QPixmap> pixmap() const;
|
||||
qreal scale() const;
|
||||
bool empty() const;
|
||||
bool isEmpty() const;
|
||||
int width() const;
|
||||
int height() const;
|
||||
bool animated() const;
|
||||
@@ -66,8 +66,7 @@ public:
|
||||
private:
|
||||
Image();
|
||||
Image(const Url &url, qreal scale);
|
||||
Image(std::unique_ptr<QPixmap> owning, qreal scale);
|
||||
Image(QPixmap *nonOwning, qreal scale);
|
||||
Image(const QPixmap &nonOwning, qreal scale);
|
||||
|
||||
void load();
|
||||
|
||||
@@ -75,9 +74,7 @@ private:
|
||||
qreal scale_{1};
|
||||
bool empty_{false};
|
||||
bool shouldLoad_{false};
|
||||
Frames frames_{};
|
||||
std::unique_ptr<Frames> frames_{};
|
||||
QObject object_{};
|
||||
|
||||
static std::atomic<bool> loadedEventQueued;
|
||||
};
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "ImageSet.hpp"
|
||||
#include "messages/ImageSet.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
@@ -60,23 +58,19 @@ const ImagePtr &ImageSet::getImage3() const
|
||||
|
||||
const ImagePtr &ImageSet::getImage(float scale) const
|
||||
{
|
||||
int quality = getSettings()->preferredEmoteQuality;
|
||||
int quality = 1;
|
||||
|
||||
if (!quality) {
|
||||
if (scale > 3.999)
|
||||
quality = 3;
|
||||
else if (scale > 1.999)
|
||||
quality = 2;
|
||||
else
|
||||
scale = 1;
|
||||
}
|
||||
if (scale > 2.999)
|
||||
quality = 3;
|
||||
else if (scale > 1.5)
|
||||
quality = 2;
|
||||
|
||||
if (!this->imageX3_->empty() && quality == 3) {
|
||||
if (!this->imageX3_->isEmpty() && quality == 3) {
|
||||
return this->imageX3_;
|
||||
}
|
||||
|
||||
if (!this->imageX2_->empty() && quality == 2) {
|
||||
return this->imageX3_;
|
||||
if (!this->imageX2_->isEmpty() && quality == 2) {
|
||||
return this->imageX2_;
|
||||
}
|
||||
|
||||
return this->imageX1_;
|
||||
|
||||
@@ -21,8 +21,6 @@ public:
|
||||
|
||||
const ImagePtr &getImage(float scale) const;
|
||||
|
||||
ImagePtr getImage(float scale);
|
||||
|
||||
bool operator==(const ImageSet &other) const;
|
||||
bool operator!=(const ImageSet &other) const;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <common/Common.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
@@ -14,6 +13,7 @@ public:
|
||||
UserInfo,
|
||||
UserTimeout,
|
||||
UserBan,
|
||||
UserWhisper,
|
||||
InsertText,
|
||||
ShowMessage,
|
||||
UserAction,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "providers/twitch/PubsubActions.hpp"
|
||||
#include "widgets/helper/ScrollbarHighlight.hpp"
|
||||
|
||||
#include <QTime>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
class MessageElement;
|
||||
|
||||
enum class MessageFlag : uint16_t {
|
||||
None = 0,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "MessageBuilder.hpp"
|
||||
|
||||
#include "common/LinkParser.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "providers/twitch/PubsubActions.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/Resources.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
@@ -17,7 +20,7 @@ MessagePtr makeSystemMessage(const QString &text)
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder()
|
||||
: message_(std::make_unique<Message>())
|
||||
: message_(std::make_shared<Message>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -82,6 +85,7 @@ MessageBuilder::MessageBuilder(TimeoutMessageTag, const QString &username,
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(const BanAction &action, uint32_t count)
|
||||
: MessageBuilder()
|
||||
{
|
||||
this->emplace<TimestampElement>();
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
@@ -127,6 +131,7 @@ MessageBuilder::MessageBuilder(const BanAction &action, uint32_t count)
|
||||
}
|
||||
|
||||
MessageBuilder::MessageBuilder(const UnbanAction &action)
|
||||
: MessageBuilder()
|
||||
{
|
||||
this->emplace<TimestampElement>();
|
||||
this->message().flags.set(MessageFlag::System);
|
||||
@@ -163,7 +168,9 @@ Message &MessageBuilder::message()
|
||||
|
||||
MessagePtr MessageBuilder::release()
|
||||
{
|
||||
return MessagePtr(this->message_.release());
|
||||
std::shared_ptr<Message> ptr;
|
||||
this->message_.swap(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void MessageBuilder::append(std::unique_ptr<MessageElement> element)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <ctime>
|
||||
|
||||
namespace chatterino {
|
||||
struct BanAction;
|
||||
struct UnbanAction;
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
|
||||
struct SystemMessageTag {
|
||||
};
|
||||
@@ -16,6 +20,14 @@ const TimeoutMessageTag timeoutMessage{};
|
||||
|
||||
MessagePtr makeSystemMessage(const QString &text);
|
||||
|
||||
struct MessageParseArgs {
|
||||
bool disablePingSounds = false;
|
||||
bool isReceivedWhisper = false;
|
||||
bool isSentWhisper = false;
|
||||
bool trimSubscriberUsername = false;
|
||||
bool isStaffOrBroadcaster = false;
|
||||
};
|
||||
|
||||
class MessageBuilder
|
||||
{
|
||||
public:
|
||||
@@ -48,7 +60,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Message> message_;
|
||||
std::shared_ptr<Message> message_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "MessageColor.hpp"
|
||||
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
MessageColor::MessageColor(const QColor &color)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
namespace chatterino {
|
||||
class Theme;
|
||||
|
||||
struct MessageColor {
|
||||
enum Type { Custom, Text, Link, System };
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "MessageContainer.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
MessageContainer::MessageContainer()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class MessageContainer
|
||||
{
|
||||
public:
|
||||
MessageContainer();
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "messages/MessageElement.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "common/Emotemap.hpp"
|
||||
#include "controllers/moderationactions/ModerationActions.hpp"
|
||||
#include "debug/Benchmark.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
#include "messages/layouts/MessageLayoutContainer.hpp"
|
||||
#include "messages/layouts/MessageLayoutElement.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
@@ -102,7 +103,7 @@ void EmoteElement::addToContainer(MessageLayoutContainer &container,
|
||||
if (flags.hasAny(this->getFlags())) {
|
||||
if (flags.has(MessageElementFlag::EmoteImages)) {
|
||||
auto image = this->emote_->images.getImage(container.getScale());
|
||||
if (image->empty()) return;
|
||||
if (image->isEmpty()) return;
|
||||
|
||||
auto size = QSize(int(container.getScale() * image->width()),
|
||||
int(container.getScale() * image->height()));
|
||||
@@ -224,8 +225,8 @@ void TimestampElement::addToContainer(MessageLayoutContainer &container,
|
||||
{
|
||||
if (flags.hasAny(this->getFlags())) {
|
||||
auto app = getApp();
|
||||
if (app->settings->timestampFormat != this->format_) {
|
||||
this->format_ = app->settings->timestampFormat.getValue();
|
||||
if (getSettings()->timestampFormat != this->format_) {
|
||||
this->format_ = getSettings()->timestampFormat.getValue();
|
||||
this->element_.reset(this->formatTime(this->time_));
|
||||
}
|
||||
|
||||
@@ -237,7 +238,7 @@ TextElement *TimestampElement::formatTime(const QTime &time)
|
||||
{
|
||||
static QLocale locale("en_US");
|
||||
|
||||
QString format = locale.toString(time, getApp()->settings->timestampFormat);
|
||||
QString format = locale.toString(time, getSettings()->timestampFormat);
|
||||
|
||||
return new TextElement(format, MessageElementFlag::Timestamp,
|
||||
MessageColor::System, FontStyle::ChatMedium);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Emotemap.hpp"
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/Link.hpp"
|
||||
#include "messages/MessageColor.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
@@ -12,14 +9,20 @@
|
||||
#include <QString>
|
||||
#include <QTime>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace chatterino {
|
||||
class Channel;
|
||||
struct MessageLayoutContainer;
|
||||
|
||||
class Image;
|
||||
using ImagePtr = std::shared_ptr<Image>;
|
||||
|
||||
struct Emote;
|
||||
using EmotePtr = std::shared_ptr<const Emote>;
|
||||
|
||||
enum class MessageElementFlag {
|
||||
None = 0,
|
||||
Misc = (1 << 0),
|
||||
|
||||
@@ -2,12 +2,4 @@
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct MessageParseArgs {
|
||||
bool disablePingSounds = false;
|
||||
bool isReceivedWhisper = false;
|
||||
bool isSentWhisper = false;
|
||||
bool trimSubscriberUsername = false;
|
||||
bool isStaffOrBroadcaster = false;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace chatterino {
|
||||
@@ -23,14 +24,8 @@ struct SelectionItem {
|
||||
|
||||
bool operator<(const SelectionItem &b) const
|
||||
{
|
||||
if (this->messageIndex < b.messageIndex) {
|
||||
return true;
|
||||
}
|
||||
if (this->messageIndex == b.messageIndex &&
|
||||
this->charIndex < b.charIndex) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return std::tie(this->messageIndex, this->charIndex) <
|
||||
std::tie(b.messageIndex, b.charIndex);
|
||||
}
|
||||
|
||||
bool operator>(const SelectionItem &b) const
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "debug/Benchmark.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "messages/layouts/MessageLayoutContainer.hpp"
|
||||
#include "singletons/Emotes.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "singletons/WindowManager.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
|
||||
@@ -24,6 +28,7 @@ namespace chatterino {
|
||||
MessageLayout::MessageLayout(MessagePtr message)
|
||||
: message_(message)
|
||||
, buffer_(nullptr)
|
||||
, container_(std::make_shared<MessageLayoutContainer>())
|
||||
{
|
||||
DebugCount::increase("message layout");
|
||||
}
|
||||
@@ -41,7 +46,7 @@ const Message *MessageLayout::getMessage()
|
||||
// Height
|
||||
int MessageLayout::getHeight() const
|
||||
{
|
||||
return container_.getHeight();
|
||||
return container_->getHeight();
|
||||
}
|
||||
|
||||
// Layout
|
||||
@@ -68,7 +73,7 @@ bool MessageLayout::layout(int width, float scale, MessageElementFlags flags)
|
||||
|
||||
// check if work mask changed
|
||||
layoutRequired |= this->currentWordFlags_ != flags;
|
||||
this->currentWordFlags_ = flags; // app->settings->getWordTypeMask();
|
||||
this->currentWordFlags_ = flags; // getSettings()->getWordTypeMask();
|
||||
|
||||
// check if layout was requested manually
|
||||
layoutRequired |= this->flags.has(MessageLayoutFlag::RequiresLayout);
|
||||
@@ -82,9 +87,9 @@ bool MessageLayout::layout(int width, float scale, MessageElementFlags flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
int oldHeight = this->container_.getHeight();
|
||||
int oldHeight = this->container_->getHeight();
|
||||
this->actuallyLayout(width, flags);
|
||||
if (widthChanged || this->container_.getHeight() != oldHeight) {
|
||||
if (widthChanged || this->container_->getHeight() != oldHeight) {
|
||||
this->deleteBuffer();
|
||||
}
|
||||
this->invalidateBuffer();
|
||||
@@ -103,22 +108,22 @@ void MessageLayout::actuallyLayout(int width, MessageElementFlags _flags)
|
||||
messageFlags.unset(MessageFlag::Collapsed);
|
||||
}
|
||||
|
||||
this->container_.begin(width, this->scale_, messageFlags);
|
||||
this->container_->begin(width, this->scale_, messageFlags);
|
||||
|
||||
for (const auto &element : this->message_->elements) {
|
||||
element->addToContainer(this->container_, _flags);
|
||||
element->addToContainer(*this->container_, _flags);
|
||||
}
|
||||
|
||||
if (this->height_ != this->container_.getHeight()) {
|
||||
if (this->height_ != this->container_->getHeight()) {
|
||||
this->deleteBuffer();
|
||||
}
|
||||
|
||||
this->container_.end();
|
||||
this->height_ = this->container_.getHeight();
|
||||
this->container_->end();
|
||||
this->height_ = this->container_->getHeight();
|
||||
|
||||
// collapsed state
|
||||
this->flags.unset(MessageLayoutFlag::Collapsed);
|
||||
if (this->container_.isCollapsed()) {
|
||||
if (this->container_->isCollapsed()) {
|
||||
this->flags.set(MessageLayoutFlag::Collapsed);
|
||||
}
|
||||
}
|
||||
@@ -135,11 +140,12 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex,
|
||||
if (!pixmap) {
|
||||
#ifdef Q_OS_MACOS
|
||||
pixmap = new QPixmap(int(width * painter.device()->devicePixelRatioF()),
|
||||
int(container_.getHeight() *
|
||||
int(container_->getHeight() *
|
||||
painter.device()->devicePixelRatioF()));
|
||||
pixmap->setDevicePixelRatio(painter.device()->devicePixelRatioF());
|
||||
#else
|
||||
pixmap = new QPixmap(width, std::max(16, this->container_.getHeight()));
|
||||
pixmap =
|
||||
new QPixmap(width, std::max(16, this->container_->getHeight()));
|
||||
#endif
|
||||
|
||||
this->buffer_ = std::shared_ptr<QPixmap>(pixmap);
|
||||
@@ -157,7 +163,7 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex,
|
||||
// this->container.getHeight(), *pixmap);
|
||||
|
||||
// draw gif emotes
|
||||
this->container_.paintAnimatedElements(painter, y);
|
||||
this->container_->paintAnimatedElements(painter, y);
|
||||
|
||||
// draw disabled
|
||||
if (this->message_->flags.has(MessageFlag::Disabled)) {
|
||||
@@ -167,12 +173,12 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex,
|
||||
|
||||
// draw selection
|
||||
if (!selection.isEmpty()) {
|
||||
this->container_.paintSelection(painter, messageIndex, selection, y);
|
||||
this->container_->paintSelection(painter, messageIndex, selection, y);
|
||||
}
|
||||
|
||||
// draw message seperation line
|
||||
if (app->settings->separateMessages.getValue()) {
|
||||
painter.fillRect(0, y, this->container_.getWidth(), 1,
|
||||
if (getSettings()->separateMessages.getValue()) {
|
||||
painter.fillRect(0, y, this->container_->getWidth(), 1,
|
||||
app->themes->splits.messageSeperator);
|
||||
}
|
||||
|
||||
@@ -184,9 +190,9 @@ void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex,
|
||||
: app->themes->tabs.selected.backgrounds.unfocused.color();
|
||||
|
||||
QBrush brush(color, static_cast<Qt::BrushStyle>(
|
||||
app->settings->lastMessagePattern.getValue()));
|
||||
getSettings()->lastMessagePattern.getValue()));
|
||||
|
||||
painter.fillRect(0, y + this->container_.getHeight() - 1,
|
||||
painter.fillRect(0, y + this->container_->getHeight() - 1,
|
||||
pixmap->width(), 1, brush);
|
||||
}
|
||||
|
||||
@@ -208,7 +214,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
|
||||
backgroundColor = app->themes->messages.backgrounds.highlighted;
|
||||
} else if (this->message_->flags.has(MessageFlag::Subscription)) {
|
||||
backgroundColor = app->themes->messages.backgrounds.subscription;
|
||||
} else if (app->settings->alternateMessageBackground.getValue() &&
|
||||
} else if (getSettings()->alternateMessageBackground.getValue() &&
|
||||
this->flags.has(MessageLayoutFlag::AlternateBackground)) {
|
||||
backgroundColor = app->themes->messages.backgrounds.alternate;
|
||||
} else {
|
||||
@@ -217,7 +223,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
|
||||
painter.fillRect(buffer->rect(), backgroundColor);
|
||||
|
||||
// draw message
|
||||
this->container_.paintElements(painter);
|
||||
this->container_->paintElements(painter);
|
||||
|
||||
#ifdef FOURTF
|
||||
// debug
|
||||
@@ -252,7 +258,7 @@ void MessageLayout::deleteCache()
|
||||
this->deleteBuffer();
|
||||
|
||||
#ifdef XD
|
||||
this->container_.clear();
|
||||
this->container_->clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -265,22 +271,23 @@ void MessageLayout::deleteCache()
|
||||
const MessageLayoutElement *MessageLayout::getElementAt(QPoint point)
|
||||
{
|
||||
// go through all words and return the first one that contains the point.
|
||||
return this->container_.getElementAt(point);
|
||||
return this->container_->getElementAt(point);
|
||||
}
|
||||
|
||||
int MessageLayout::getLastCharacterIndex() const
|
||||
{
|
||||
return this->container_.getLastCharacterIndex();
|
||||
return this->container_->getLastCharacterIndex();
|
||||
}
|
||||
|
||||
int MessageLayout::getSelectionIndex(QPoint position)
|
||||
{
|
||||
return this->container_.getSelectionIndex(position);
|
||||
return this->container_->getSelectionIndex(position);
|
||||
}
|
||||
|
||||
void MessageLayout::addSelectionText(QString &str, int from, int to)
|
||||
void MessageLayout::addSelectionText(QString &str, int from, int to,
|
||||
CopyMode copymode)
|
||||
{
|
||||
this->container_.addSelectionText(str, from, to);
|
||||
this->container_->addSelectionText(str, from, to, copymode);
|
||||
}
|
||||
|
||||
} // namespace chatterino
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/Common.hpp"
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/Selection.hpp"
|
||||
#include "messages/layouts/MessageLayoutContainer.hpp"
|
||||
#include "messages/layouts/MessageLayoutElement.hpp"
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct Message;
|
||||
using MessagePtr = std::shared_ptr<const Message>;
|
||||
|
||||
struct Selection;
|
||||
struct MessageLayoutContainer;
|
||||
class MessageLayoutElement;
|
||||
|
||||
enum class MessageElementFlag;
|
||||
using MessageElementFlags = FlagsEnum<MessageElementFlag>;
|
||||
|
||||
enum class MessageLayoutFlag : uint8_t {
|
||||
RequiresBufferUpdate = 1 << 1,
|
||||
RequiresLayout = 1 << 2,
|
||||
@@ -31,13 +37,10 @@ public:
|
||||
|
||||
const Message *getMessage();
|
||||
|
||||
// Height
|
||||
int getHeight() const;
|
||||
|
||||
// Flags
|
||||
MessageLayoutFlags flags;
|
||||
|
||||
// Layout
|
||||
bool layout(int width, float scale_, MessageElementFlags flags);
|
||||
|
||||
// Painting
|
||||
@@ -52,7 +55,8 @@ public:
|
||||
const MessageLayoutElement *getElementAt(QPoint point);
|
||||
int getLastCharacterIndex() const;
|
||||
int getSelectionIndex(QPoint position);
|
||||
void addSelectionText(QString &str, int from = 0, int to = INT_MAX);
|
||||
void addSelectionText(QString &str, int from = 0, int to = INT_MAX,
|
||||
CopyMode copymode = CopyMode::Everything);
|
||||
|
||||
// Misc
|
||||
bool isDisabled() const;
|
||||
@@ -60,7 +64,7 @@ public:
|
||||
private:
|
||||
// variables
|
||||
MessagePtr message_;
|
||||
MessageLayoutContainer container_;
|
||||
std::shared_ptr<MessageLayoutContainer> container_;
|
||||
std::shared_ptr<QPixmap> buffer_ = nullptr;
|
||||
bool bufferValid_ = false;
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
#include "MessageLayoutContainer.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "MessageLayoutElement.hpp"
|
||||
#include "messages/Message.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "messages/Selection.hpp"
|
||||
#include "messages/layouts/MessageLayoutElement.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
#include "singletons/Settings.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
#define COMPACT_EMOTES_OFFSET 6
|
||||
#define MAX_UNCOLLAPSED_LINES \
|
||||
(getApp()->settings->collpseMessagesMinLines.getValue())
|
||||
(getSettings()->collpseMessagesMinLines.getValue())
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
@@ -126,9 +130,10 @@ void MessageLayoutContainer::breakLine()
|
||||
int xOffset = 0;
|
||||
|
||||
if (this->flags_.has(MessageFlag::Centered) && this->elements_.size() > 0) {
|
||||
xOffset = (width_ - this->elements_.at(this->elements_.size() - 1)
|
||||
->getRect()
|
||||
.right()) /
|
||||
xOffset = (width_ - this->elements_.at(0)->getRect().left() -
|
||||
this->elements_.at(this->elements_.size() - 1)
|
||||
->getRect()
|
||||
.right()) /
|
||||
2;
|
||||
}
|
||||
|
||||
@@ -230,7 +235,7 @@ void MessageLayoutContainer::end()
|
||||
|
||||
bool MessageLayoutContainer::canCollapse()
|
||||
{
|
||||
return getApp()->settings->collpseMessagesMinLines.getValue() > 0 &&
|
||||
return getSettings()->collpseMessagesMinLines.getValue() > 0 &&
|
||||
this->flags_.has(MessageFlag::Collapsed);
|
||||
}
|
||||
|
||||
@@ -500,33 +505,41 @@ int MessageLayoutContainer::getLastCharacterIndex() const
|
||||
return this->lines_.back().endCharIndex;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::addSelectionText(QString &str, int from, int to)
|
||||
void MessageLayoutContainer::addSelectionText(QString &str, int from, int to,
|
||||
CopyMode copymode)
|
||||
{
|
||||
int index = 0;
|
||||
bool first = true;
|
||||
|
||||
for (std::unique_ptr<MessageLayoutElement> &ele : this->elements_) {
|
||||
int c = ele->getSelectionIndexCount();
|
||||
for (auto &element : this->elements_) {
|
||||
if (copymode == CopyMode::OnlyTextAndEmotes) {
|
||||
if (element->getCreator().getFlags().hasAny(
|
||||
{MessageElementFlag::Timestamp,
|
||||
MessageElementFlag::Username, MessageElementFlag::Badges}))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto indexCount = element->getSelectionIndexCount();
|
||||
|
||||
if (first) {
|
||||
if (index + c > from) {
|
||||
ele->addCopyTextToString(str, from - index, to - index);
|
||||
if (index + indexCount > from) {
|
||||
element->addCopyTextToString(str, from - index, to - index);
|
||||
first = false;
|
||||
|
||||
if (index + c > to) {
|
||||
if (index + indexCount > to) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (index + c > to) {
|
||||
ele->addCopyTextToString(str, 0, to - index);
|
||||
if (index + indexCount > to) {
|
||||
element->addCopyTextToString(str, 0, to - index);
|
||||
break;
|
||||
} else {
|
||||
ele->addCopyTextToString(str);
|
||||
element->addCopyTextToString(str);
|
||||
}
|
||||
}
|
||||
|
||||
index += c;
|
||||
index += indexCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "messages/Message.hpp"
|
||||
#include "common/Common.hpp"
|
||||
#include "common/FlagsEnum.hpp"
|
||||
#include "messages/Selection.hpp"
|
||||
#include "messages/layouts/MessageLayoutElement.hpp"
|
||||
|
||||
class QPainter;
|
||||
|
||||
namespace chatterino {
|
||||
class MessageLayoutElement;
|
||||
|
||||
enum class MessageFlag : uint16_t;
|
||||
using MessageFlags = FlagsEnum<MessageFlag>;
|
||||
|
||||
struct Margin {
|
||||
int top;
|
||||
@@ -72,7 +75,7 @@ struct MessageLayoutContainer {
|
||||
// selection
|
||||
int getSelectionIndex(QPoint point);
|
||||
int getLastCharacterIndex() const;
|
||||
void addSelectionText(QString &str, int from, int to);
|
||||
void addSelectionText(QString &str, int from, int to, CopyMode copymode);
|
||||
|
||||
bool isCollapsed();
|
||||
|
||||
@@ -92,7 +95,7 @@ private:
|
||||
// variables
|
||||
float scale_ = 1.f;
|
||||
int width_ = 0;
|
||||
MessageFlags flags_ = MessageFlag::None;
|
||||
MessageFlags flags_{};
|
||||
int line_ = 0;
|
||||
int height_ = 0;
|
||||
int currentX_ = 0;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "messages/layouts/MessageLayoutElement.hpp"
|
||||
|
||||
#include "Application.hpp"
|
||||
#include "messages/Emote.hpp"
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/MessageElement.hpp"
|
||||
#include "singletons/Theme.hpp"
|
||||
#include "util/DebugCount.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
@@ -75,11 +78,12 @@ ImageLayoutElement::ImageLayoutElement(MessageElement &creator, ImagePtr image,
|
||||
void ImageLayoutElement::addCopyTextToString(QString &str, int from,
|
||||
int to) const
|
||||
{
|
||||
// str += this->image_->getCopyString();
|
||||
str += "not implemented";
|
||||
|
||||
if (this->hasTrailingSpace()) {
|
||||
str += " ";
|
||||
const auto *emoteElement = dynamic_cast<EmoteElement *>(&this->getCreator());
|
||||
if (emoteElement) {
|
||||
str += emoteElement->getEmote()->getCopyString();
|
||||
if (this->hasTrailingSpace()) {
|
||||
str += " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <climits>
|
||||
|
||||
#include "messages/Image.hpp"
|
||||
#include "messages/Link.hpp"
|
||||
#include "messages/MessageColor.hpp"
|
||||
#include "singletons/Fonts.hpp"
|
||||
|
||||
class QPainter;
|
||||
|
||||
namespace chatterino {
|
||||
class MessageElement;
|
||||
class Image;
|
||||
using ImagePtr = std::shared_ptr<Image>;
|
||||
enum class FontStyle : uint8_t;
|
||||
|
||||
class MessageLayoutElement : boost::noncopyable
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user