mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
refactoring
This commit is contained in:
parent
8ef492d7ae
commit
96db82e867
114 changed files with 5554 additions and 3703 deletions
13
account.cpp
13
account.cpp
|
@ -1,13 +0,0 @@
|
||||||
#include "account.h"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
Account Account::anon("justinfan123", "", "");
|
|
||||||
|
|
||||||
Account::Account(QString username, QString oauthToken, QString oauthClient)
|
|
||||||
{
|
|
||||||
this->oauthClient = oauthClient;
|
|
||||||
this->oauthToken = oauthToken;
|
|
||||||
this->username = username;
|
|
||||||
}
|
|
||||||
}
|
|
52
account.h
52
account.h
|
@ -1,52 +0,0 @@
|
||||||
#ifndef ACCOUNT_H
|
|
||||||
#define ACCOUNT_H
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
class Account
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Account(QString username, QString oauthToken, QString oauthClient);
|
|
||||||
|
|
||||||
static const Account *
|
|
||||||
getAnon()
|
|
||||||
{
|
|
||||||
return &anon;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getUsername() const
|
|
||||||
{
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getOauthToken() const
|
|
||||||
{
|
|
||||||
return oauthToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getOauthClient() const
|
|
||||||
{
|
|
||||||
return oauthClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
isAnon() const
|
|
||||||
{
|
|
||||||
return username.startsWith("justinfan");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static Account anon;
|
|
||||||
|
|
||||||
QString username;
|
|
||||||
QString oauthClient;
|
|
||||||
QString oauthToken;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ACCOUNT_H
|
|
|
@ -7,19 +7,16 @@
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#define async_exec(a) \
|
#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
||||||
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
|
||||||
|
|
||||||
class LambdaRunnable : public QRunnable
|
class LambdaRunnable : public QRunnable {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
LambdaRunnable(std::function<void()> action)
|
LambdaRunnable(std::function<void()> action)
|
||||||
{
|
{
|
||||||
this->action = action;
|
this->action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void run()
|
||||||
run()
|
|
||||||
{
|
{
|
||||||
this->action();
|
this->action();
|
||||||
}
|
}
|
||||||
|
|
263
channel.cpp
263
channel.cpp
|
@ -1,8 +1,9 @@
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "logging/loggingmanager.h"
|
#include "logging/loggingmanager.h"
|
||||||
#include "messages/message.h"
|
#include "messages/message.h"
|
||||||
#include "windows.h"
|
#include "util/urlfetch.h"
|
||||||
|
#include "windowmanager.h"
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
@ -14,133 +15,177 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
Channel::Channel(const QString &channel)
|
Channel::Channel(const QString &channel)
|
||||||
: messages()
|
: _messages()
|
||||||
, name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1)
|
, _name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) : channel)
|
||||||
: channel)
|
, _bttvChannelEmotes()
|
||||||
, bttvChannelEmotes()
|
, _ffzChannelEmotes()
|
||||||
, ffzChannelEmotes()
|
, _subLink("https://www.twitch.tv/" + _name + "/subscribe?ref=in_chat_subscriber_link")
|
||||||
, subLink("https://www.twitch.tv/" + name +
|
, _channelLink("https://twitch.tv/" + _name)
|
||||||
"/subscribe?ref=in_chat_subscriber_link")
|
, _popoutPlayerLink("https://player.twitch.tv/?channel=" + _name)
|
||||||
, channelLink("https://twitch.tv/" + name)
|
//, _loggingChannel(logging::get(_name))
|
||||||
, popoutPlayerLink("https://player.twitch.tv/?channel=" + name)
|
|
||||||
, loggingChannel(logging::get(name))
|
|
||||||
{
|
{
|
||||||
reloadChannelEmotes();
|
reloadChannelEmotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
//
|
||||||
Channel::reloadBttvEmotes()
|
// properties
|
||||||
|
//
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getBttvChannelEmotes()
|
||||||
{
|
{
|
||||||
// bttv
|
return _bttvChannelEmotes;
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
|
||||||
|
|
||||||
QUrl url("https://api.betterttv.net/2/channels/" + this->name);
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
|
|
||||||
QNetworkReply *reply = manager->get(request);
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
|
||||||
QByteArray data = reply->readAll();
|
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
|
||||||
QJsonObject root = jsonDoc.object();
|
|
||||||
|
|
||||||
auto emotes = root.value("emotes").toArray();
|
|
||||||
|
|
||||||
QString _template = "https:" + root.value("urlTemplate").toString();
|
|
||||||
|
|
||||||
for (const QJsonValue &emote : emotes) {
|
|
||||||
QString id = emote.toObject().value("id").toString();
|
|
||||||
QString code = emote.toObject().value("code").toString();
|
|
||||||
// emote.value("imageType").toString();
|
|
||||||
|
|
||||||
QString tmp = _template;
|
|
||||||
tmp.detach();
|
|
||||||
QString url =
|
|
||||||
tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
|
||||||
|
|
||||||
this->getBttvChannelEmotes().insert(
|
|
||||||
code, Emotes::getBttvChannelEmoteFromCaches().getOrAdd(
|
|
||||||
id, [&code, &url] {
|
|
||||||
return new messages::LazyLoadedImage(
|
|
||||||
url, 1, code,
|
|
||||||
code + "\nChannel Bttv Emote");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
manager->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getFfzChannelEmotes()
|
||||||
Channel::reloadFfzEmotes()
|
|
||||||
{
|
{
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
return _ffzChannelEmotes;
|
||||||
|
|
||||||
QUrl url("http://api.frankerfacez.com/v1/room/" + this->name);
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
|
|
||||||
QNetworkReply *reply = manager->get(request);
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
|
||||||
QByteArray data = reply->readAll();
|
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
|
||||||
QJsonObject root = jsonDoc.object();
|
|
||||||
|
|
||||||
auto sets = root.value("sets").toObject();
|
|
||||||
|
|
||||||
for (const QJsonValue &set : sets) {
|
|
||||||
auto emoticons = set.toObject().value("emoticons").toArray();
|
|
||||||
|
|
||||||
for (const QJsonValue &emote : emoticons) {
|
|
||||||
QJsonObject object = emote.toObject();
|
|
||||||
|
|
||||||
// margins
|
|
||||||
|
|
||||||
int id = object.value("id").toInt();
|
|
||||||
QString code = object.value("name").toString();
|
|
||||||
|
|
||||||
QJsonObject urls = object.value("urls").toObject();
|
|
||||||
QString url1 = "http:" + urls.value("1").toString();
|
|
||||||
|
|
||||||
this->getFfzChannelEmotes().insert(
|
|
||||||
code, Emotes::getFfzChannelEmoteFromCaches().getOrAdd(
|
|
||||||
id, [&code, &url1] {
|
|
||||||
return new messages::LazyLoadedImage(
|
|
||||||
url1, 1, code,
|
|
||||||
code + "\nGlobal Ffz Emote");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
manager->deleteLater();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool Channel::isEmpty() const
|
||||||
Channel::addMessage(std::shared_ptr<messages::Message> message)
|
|
||||||
{
|
{
|
||||||
std::shared_ptr<messages::Message> deleted;
|
return _name.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
if (this->loggingChannel.get() != nullptr) {
|
const QString &Channel::getName() const
|
||||||
this->loggingChannel->append(message);
|
{
|
||||||
}
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->messages.appendItem(message, deleted)) {
|
int Channel::getRoomID() const
|
||||||
this->messageRemovedFromStart(deleted);
|
{
|
||||||
|
return _roomID;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Channel::getSubLink() const
|
||||||
|
{
|
||||||
|
return _subLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Channel::getChannelLink() const
|
||||||
|
{
|
||||||
|
return _channelLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Channel::getPopoutPlayerLink() const
|
||||||
|
{
|
||||||
|
return _popoutPlayerLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Channel::getIsLive() const
|
||||||
|
{
|
||||||
|
return _isLive;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Channel::getStreamViewerCount() const
|
||||||
|
{
|
||||||
|
return _streamViewerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Channel::getStreamStatus() const
|
||||||
|
{
|
||||||
|
return _streamStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Channel::getStreamGame() const
|
||||||
|
{
|
||||||
|
return _streamGame;
|
||||||
|
}
|
||||||
|
|
||||||
|
messages::LimitedQueueSnapshot<messages::SharedMessage> Channel::getMessageSnapshot()
|
||||||
|
{
|
||||||
|
return _messages.getSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// methods
|
||||||
|
//
|
||||||
|
void Channel::addMessage(std::shared_ptr<Message> message)
|
||||||
|
{
|
||||||
|
std::shared_ptr<Message> deleted;
|
||||||
|
|
||||||
|
// if (_loggingChannel.get() != nullptr) {
|
||||||
|
// _loggingChannel->append(message);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (_messages.appendItem(message, deleted)) {
|
||||||
|
messageRemovedFromStart(deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->messageAppended(message);
|
this->messageAppended(message);
|
||||||
|
|
||||||
Windows::repaintVisibleChatWidgets(this);
|
WindowManager::repaintVisibleChatWidgets(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
void Channel::reloadChannelEmotes()
|
||||||
|
{
|
||||||
|
reloadBttvEmotes();
|
||||||
|
reloadFfzEmotes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::reloadBttvEmotes()
|
||||||
|
{
|
||||||
|
util::urlJsonFetch(
|
||||||
|
"https://api.betterttv.net/2/channels/" + _name, [this](QJsonObject &rootNode) {
|
||||||
|
auto emotesNode = rootNode.value("emotes").toArray();
|
||||||
|
|
||||||
|
QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString();
|
||||||
|
|
||||||
|
for (const QJsonValue &emoteNode : emotesNode) {
|
||||||
|
QJsonObject emoteObject = emoteNode.toObject();
|
||||||
|
|
||||||
|
QString id = emoteObject.value("id").toString();
|
||||||
|
QString code = emoteObject.value("code").toString();
|
||||||
|
// emoteObject.value("imageType").toString();
|
||||||
|
|
||||||
|
QString link = linkTemplate;
|
||||||
|
link.detach();
|
||||||
|
|
||||||
|
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||||
|
|
||||||
|
auto emote = EmoteManager::getInstance().getBttvChannelEmoteFromCaches().getOrAdd(
|
||||||
|
id, [&code, &link] {
|
||||||
|
return new LazyLoadedImage(link, 1, code, code + "\nChannel Bttv Emote");
|
||||||
|
});
|
||||||
|
|
||||||
|
this->getBttvChannelEmotes().insert(code, emote);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Channel::reloadFfzEmotes()
|
||||||
|
{
|
||||||
|
util::urlJsonFetch("http://api.frankerfacez.com/v1/room/" + _name, [this](
|
||||||
|
QJsonObject &rootNode) {
|
||||||
|
auto setsNode = rootNode.value("sets").toObject();
|
||||||
|
|
||||||
|
for (const QJsonValue &setNode : setsNode) {
|
||||||
|
auto emotesNode = setNode.toObject().value("emoticons").toArray();
|
||||||
|
|
||||||
|
for (const QJsonValue &emoteNode : emotesNode) {
|
||||||
|
QJsonObject emoteObject = emoteNode.toObject();
|
||||||
|
|
||||||
|
// margins
|
||||||
|
|
||||||
|
int id = emoteObject.value("id").toInt();
|
||||||
|
QString code = emoteObject.value("name").toString();
|
||||||
|
|
||||||
|
QJsonObject urls = emoteObject.value("urls").toObject();
|
||||||
|
QString url1 = "http:" + urls.value("1").toString();
|
||||||
|
|
||||||
|
auto emote = EmoteManager::getInstance().getFfzChannelEmoteFromCaches().getOrAdd(
|
||||||
|
id, [&code, &url1] {
|
||||||
|
return new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote");
|
||||||
|
});
|
||||||
|
|
||||||
|
getFfzChannelEmotes().insert(code, emote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
132
channel.h
132
channel.h
|
@ -18,119 +18,59 @@ namespace messages {
|
||||||
class Message;
|
class Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChannelManager;
|
||||||
|
|
||||||
|
typedef std::shared_ptr<Channel> SharedChannel;
|
||||||
|
|
||||||
class Channel
|
class Channel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Channel(const QString &channel);
|
Channel(const QString &channel);
|
||||||
|
|
||||||
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)>
|
boost::signals2::signal<void(messages::SharedMessage &)> messageRemovedFromStart;
|
||||||
messageRemovedFromStart;
|
boost::signals2::signal<void(messages::SharedMessage &)> messageAppended;
|
||||||
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)>
|
|
||||||
messageAppended;
|
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getBttvChannelEmotes();
|
||||||
getBttvChannelEmotes()
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getFfzChannelEmotes();
|
||||||
{
|
|
||||||
return bttvChannelEmotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
bool isEmpty() const;
|
||||||
getFfzChannelEmotes()
|
const QString &getName() const;
|
||||||
{
|
int getRoomID() const;
|
||||||
return ffzChannelEmotes;
|
const QString &getSubLink() const;
|
||||||
}
|
const QString &getChannelLink() const;
|
||||||
|
const QString &getPopoutPlayerLink() const;
|
||||||
bool
|
bool getIsLive() const;
|
||||||
isEmpty() const
|
int getStreamViewerCount() const;
|
||||||
{
|
const QString &getStreamStatus() const;
|
||||||
return name.isEmpty();
|
const QString &getStreamGame() const;
|
||||||
}
|
messages::LimitedQueueSnapshot<messages::SharedMessage> getMessageSnapshot();
|
||||||
|
|
||||||
const QString &
|
|
||||||
getName() const
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getRoomID() const
|
|
||||||
{
|
|
||||||
return roomID;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getSubLink() const
|
|
||||||
{
|
|
||||||
return subLink;
|
|
||||||
}
|
|
||||||
const QString &
|
|
||||||
getChannelLink() const
|
|
||||||
{
|
|
||||||
return channelLink;
|
|
||||||
}
|
|
||||||
const QString &
|
|
||||||
getPopoutPlayerLink() const
|
|
||||||
{
|
|
||||||
return popoutPlayerLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
getIsLive() const
|
|
||||||
{
|
|
||||||
return isLive;
|
|
||||||
}
|
|
||||||
int
|
|
||||||
getStreamViewerCount() const
|
|
||||||
{
|
|
||||||
return streamViewerCount;
|
|
||||||
}
|
|
||||||
const QString &
|
|
||||||
getStreamStatus() const
|
|
||||||
{
|
|
||||||
return streamStatus;
|
|
||||||
}
|
|
||||||
const QString &
|
|
||||||
getStreamGame() const
|
|
||||||
{
|
|
||||||
return streamGame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
messages::LimitedQueueSnapshot<std::shared_ptr<messages::Message>>
|
void addMessage(messages::SharedMessage message);
|
||||||
getMessageSnapshot()
|
void reloadChannelEmotes();
|
||||||
{
|
|
||||||
return messages.getSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMessage(std::shared_ptr<messages::Message> message);
|
|
||||||
|
|
||||||
void
|
|
||||||
reloadChannelEmotes()
|
|
||||||
{
|
|
||||||
this->reloadBttvEmotes();
|
|
||||||
this->reloadFfzEmotes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
messages::LimitedQueue<std::shared_ptr<messages::Message>> messages;
|
// variabeles
|
||||||
|
messages::LimitedQueue<messages::SharedMessage> _messages;
|
||||||
|
|
||||||
QString name;
|
QString _name;
|
||||||
int roomID;
|
int _roomID;
|
||||||
|
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmotes;
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzChannelEmotes;
|
||||||
|
|
||||||
QString subLink;
|
QString _subLink;
|
||||||
QString channelLink;
|
QString _channelLink;
|
||||||
QString popoutPlayerLink;
|
QString _popoutPlayerLink;
|
||||||
|
|
||||||
bool isLive;
|
bool _isLive;
|
||||||
int streamViewerCount;
|
int _streamViewerCount;
|
||||||
QString streamStatus;
|
QString _streamStatus;
|
||||||
QString streamGame;
|
QString _streamGame;
|
||||||
std::shared_ptr<logging::Channel> loggingChannel;
|
// std::shared_ptr<logging::Channel> _loggingChannel;
|
||||||
|
|
||||||
|
// methods
|
||||||
void reloadBttvEmotes();
|
void reloadBttvEmotes();
|
||||||
void reloadFfzEmotes();
|
void reloadFfzEmotes();
|
||||||
};
|
};
|
||||||
|
|
122
channelmanager.cpp
Normal file
122
channelmanager.cpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#include "channelmanager.h"
|
||||||
|
#include "ircmanager.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
ChannelManager ChannelManager::instance;
|
||||||
|
|
||||||
|
ChannelManager::ChannelManager()
|
||||||
|
: _channels()
|
||||||
|
, _channelsMutex()
|
||||||
|
, _whispers(new Channel(QString("/whispers")))
|
||||||
|
, _mentions(new Channel(QString("/mentions")))
|
||||||
|
, _empty(new Channel(QString("")))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedChannel ChannelManager::getWhispers()
|
||||||
|
{
|
||||||
|
return _whispers;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedChannel ChannelManager::getMentions()
|
||||||
|
{
|
||||||
|
return _mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedChannel ChannelManager::getEmpty()
|
||||||
|
{
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<SharedChannel> ChannelManager::getItems()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_channelsMutex);
|
||||||
|
|
||||||
|
std::vector<SharedChannel> items;
|
||||||
|
|
||||||
|
for (auto &item : _channels.values()) {
|
||||||
|
items.push_back(std::get<0>(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedChannel ChannelManager::addChannel(const QString &channel)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_channelsMutex);
|
||||||
|
|
||||||
|
QString channelName = channel.toLower();
|
||||||
|
|
||||||
|
if (channel.length() > 1 && channel.at(0) == '/') {
|
||||||
|
return getChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = _channels.find(channelName);
|
||||||
|
|
||||||
|
if (it == _channels.end()) {
|
||||||
|
auto channel = SharedChannel(new Channel(channelName));
|
||||||
|
_channels.insert(channelName, std::make_tuple(channel, 1));
|
||||||
|
|
||||||
|
IrcManager::getInstance().sendJoin(channelName);
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::get<1>(it.value())++;
|
||||||
|
|
||||||
|
return std::get<0>(it.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Channel> ChannelManager::getChannel(const QString &channel)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_channelsMutex);
|
||||||
|
|
||||||
|
QString c = channel.toLower();
|
||||||
|
|
||||||
|
if (channel.length() > 1 && channel.at(0) == '/') {
|
||||||
|
if (c == "/whispers") {
|
||||||
|
return _whispers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == "/mentions") {
|
||||||
|
return _mentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto a = _channels.find(c);
|
||||||
|
|
||||||
|
if (a == _channels.end()) {
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::get<0>(a.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelManager::removeChannel(const QString &channel)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&_channelsMutex);
|
||||||
|
|
||||||
|
if (channel.length() > 1 && channel.at(0) == '/') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString c = channel.toLower();
|
||||||
|
|
||||||
|
auto a = _channels.find(c);
|
||||||
|
|
||||||
|
if (a == _channels.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::get<1>(a.value())--;
|
||||||
|
|
||||||
|
if (std::get<1>(a.value()) == 0) {
|
||||||
|
IrcManager::getInstance().partChannel(c);
|
||||||
|
_channels.remove(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
41
channelmanager.h
Normal file
41
channelmanager.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef CHANNELS_H
|
||||||
|
#define CHANNELS_H
|
||||||
|
|
||||||
|
#include "channel.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class ChannelManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static ChannelManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedChannel getWhispers();
|
||||||
|
SharedChannel getMentions();
|
||||||
|
SharedChannel getEmpty();
|
||||||
|
|
||||||
|
const std::vector<SharedChannel> getItems();
|
||||||
|
|
||||||
|
SharedChannel addChannel(const QString &channel);
|
||||||
|
SharedChannel getChannel(const QString &channel);
|
||||||
|
void removeChannel(const QString &channel);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ChannelManager instance;
|
||||||
|
|
||||||
|
ChannelManager();
|
||||||
|
|
||||||
|
QMap<QString, std::tuple<SharedChannel, int>> _channels;
|
||||||
|
QMutex _channelsMutex;
|
||||||
|
|
||||||
|
SharedChannel _whispers;
|
||||||
|
SharedChannel _mentions;
|
||||||
|
SharedChannel _empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace chatterino
|
||||||
|
|
||||||
|
#endif // CHANNELS_H
|
133
channels.cpp
133
channels.cpp
|
@ -1,133 +0,0 @@
|
||||||
#include "channels.h"
|
|
||||||
#include "ircmanager.h"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
static std::shared_ptr<Channel> whispers(nullptr);
|
|
||||||
static std::shared_ptr<Channel> mentions(nullptr);
|
|
||||||
static std::shared_ptr<Channel> empty(nullptr);
|
|
||||||
|
|
||||||
QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> Channels::channels;
|
|
||||||
QMutex Channels::channelsMutex;
|
|
||||||
|
|
||||||
void
|
|
||||||
initChannels()
|
|
||||||
{
|
|
||||||
whispers.reset(new Channel(QString("/whispers")));
|
|
||||||
mentions.reset(new Channel(QString("/mentions")));
|
|
||||||
empty.reset(new Channel(QString("")));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
Channels::getWhispers()
|
|
||||||
{
|
|
||||||
return whispers;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
Channels::getMentions()
|
|
||||||
{
|
|
||||||
return mentions;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
Channels::getEmpty()
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::shared_ptr<Channel>>
|
|
||||||
Channels::getItems()
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&Channels::channelsMutex);
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Channel>> items;
|
|
||||||
|
|
||||||
for (auto &item : Channels::channels.values()) {
|
|
||||||
items.push_back(std::get<0>(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
Channels::addChannel(const QString &channel)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&Channels::channelsMutex);
|
|
||||||
|
|
||||||
QString channelName = channel.toLower();
|
|
||||||
|
|
||||||
if (channel.length() > 1 && channel.at(0) == '/') {
|
|
||||||
return getChannel(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = Channels::channels.find(channelName);
|
|
||||||
|
|
||||||
if (it == Channels::channels.end()) {
|
|
||||||
auto channel = std::shared_ptr<Channel>(new Channel(channelName));
|
|
||||||
Channels::channels.insert(channelName, std::make_tuple(channel, 1));
|
|
||||||
|
|
||||||
IrcManager::sendJoin(channelName);
|
|
||||||
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::get<1>(it.value())++;
|
|
||||||
|
|
||||||
return std::get<0>(it.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
Channels::getChannel(const QString &channel)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&Channels::channelsMutex);
|
|
||||||
|
|
||||||
QString c = channel.toLower();
|
|
||||||
|
|
||||||
if (channel.length() > 1 && channel.at(0) == '/') {
|
|
||||||
if (c == "/whispers") {
|
|
||||||
return whispers;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == "/mentions") {
|
|
||||||
return mentions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto a = Channels::channels.find(c);
|
|
||||||
|
|
||||||
if (a == Channels::channels.end()) {
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::get<0>(a.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Channels::removeChannel(const QString &channel)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&Channels::channelsMutex);
|
|
||||||
|
|
||||||
if (channel.length() > 1 && channel.at(0) == '/') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString c = channel.toLower();
|
|
||||||
|
|
||||||
auto a = Channels::channels.find(c);
|
|
||||||
|
|
||||||
if (a == Channels::channels.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::get<1>(a.value())--;
|
|
||||||
|
|
||||||
if (std::get<1>(a.value()) == 0) {
|
|
||||||
IrcManager::partChannel(c);
|
|
||||||
Channels::channels.remove(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
34
channels.h
34
channels.h
|
@ -1,34 +0,0 @@
|
||||||
#ifndef CHANNELS_H
|
|
||||||
#define CHANNELS_H
|
|
||||||
|
|
||||||
#include "channel.h"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
void initChannels();
|
|
||||||
|
|
||||||
class Channels
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static std::shared_ptr<Channel> getWhispers();
|
|
||||||
static std::shared_ptr<Channel> getMentions();
|
|
||||||
static std::shared_ptr<Channel> getEmpty();
|
|
||||||
|
|
||||||
const static std::vector<std::shared_ptr<Channel>> getItems();
|
|
||||||
|
|
||||||
static std::shared_ptr<Channel> addChannel(const QString &channel);
|
|
||||||
static std::shared_ptr<Channel> getChannel(const QString &channel);
|
|
||||||
static void removeChannel(const QString &channel);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Channels()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static QMap<QString, std::tuple<std::shared_ptr<Channel>, int>> channels;
|
|
||||||
static QMutex channelsMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
||||||
|
|
||||||
#endif // CHANNELS_H
|
|
|
@ -17,17 +17,14 @@ TARGET = chatterino
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
DEFINES += QT_DEPRECATED_WARNINGS
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
DEFINES += IRC_NAMESPACE=Communi
|
||||||
|
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
SOURCES += main.cpp\
|
SOURCES += main.cpp\
|
||||||
account.cpp \
|
|
||||||
channel.cpp \
|
channel.cpp \
|
||||||
channels.cpp \
|
|
||||||
colorscheme.cpp \
|
colorscheme.cpp \
|
||||||
emojis.cpp \
|
emojis.cpp \
|
||||||
emotes.cpp \
|
|
||||||
fonts.cpp \
|
|
||||||
ircmanager.cpp \
|
ircmanager.cpp \
|
||||||
messages/lazyloadedimage.cpp \
|
messages/lazyloadedimage.cpp \
|
||||||
messages/link.cpp \
|
messages/link.cpp \
|
||||||
|
@ -35,7 +32,6 @@ SOURCES += main.cpp\
|
||||||
messages/word.cpp \
|
messages/word.cpp \
|
||||||
messages/wordpart.cpp \
|
messages/wordpart.cpp \
|
||||||
resources.cpp \
|
resources.cpp \
|
||||||
settings.cpp \
|
|
||||||
widgets/chatwidget.cpp \
|
widgets/chatwidget.cpp \
|
||||||
widgets/chatwidgetheader.cpp \
|
widgets/chatwidgetheader.cpp \
|
||||||
widgets/chatwidgetheaderbutton.cpp \
|
widgets/chatwidgetheaderbutton.cpp \
|
||||||
|
@ -52,21 +48,31 @@ SOURCES += main.cpp\
|
||||||
widgets/settingsdialog.cpp \
|
widgets/settingsdialog.cpp \
|
||||||
widgets/settingsdialogtab.cpp \
|
widgets/settingsdialogtab.cpp \
|
||||||
widgets/textinputdialog.cpp \
|
widgets/textinputdialog.cpp \
|
||||||
windows.cpp \
|
|
||||||
messages/messageref.cpp \
|
messages/messageref.cpp \
|
||||||
logging/loggingmanager.cpp \
|
logging/loggingmanager.cpp \
|
||||||
logging/loggingchannel.cpp
|
logging/loggingchannel.cpp \
|
||||||
|
windowmanager.cpp \
|
||||||
|
channelmanager.cpp \
|
||||||
|
fontmanager.cpp \
|
||||||
|
settingsmanager.cpp \
|
||||||
|
emotemanager.cpp \
|
||||||
|
usermanager.cpp \
|
||||||
|
twitch/twitchuser.cpp \
|
||||||
|
messages/messagebuilder.cpp \
|
||||||
|
twitch/twitchmessagebuilder.cpp \
|
||||||
|
ircuser2.cpp \
|
||||||
|
twitch/twitchparsemessage.cpp \
|
||||||
|
widgets/fancybutton.cpp \
|
||||||
|
widgets/titlebar.cpp \
|
||||||
|
widgets/userpopupwidget.cpp
|
||||||
|
|
||||||
HEADERS += account.h \
|
HEADERS += \
|
||||||
asyncexec.h \
|
asyncexec.h \
|
||||||
channel.h \
|
channel.h \
|
||||||
channels.h \
|
|
||||||
colorscheme.h \
|
colorscheme.h \
|
||||||
common.h \
|
common.h \
|
||||||
concurrentmap.h \
|
concurrentmap.h \
|
||||||
emojis.h \
|
emojis.h \
|
||||||
emotes.h \
|
|
||||||
fonts.h \
|
|
||||||
ircmanager.h \
|
ircmanager.h \
|
||||||
messages/lazyloadedimage.h \
|
messages/lazyloadedimage.h \
|
||||||
messages/link.h \
|
messages/link.h \
|
||||||
|
@ -75,8 +81,7 @@ HEADERS += account.h \
|
||||||
messages/wordpart.h \
|
messages/wordpart.h \
|
||||||
resources.h \
|
resources.h \
|
||||||
setting.h \
|
setting.h \
|
||||||
settings.h \
|
twitch/emotevalue.h \
|
||||||
twitchemotevalue.h \
|
|
||||||
widgets/chatwidget.h \
|
widgets/chatwidget.h \
|
||||||
widgets/chatwidgetheader.h \
|
widgets/chatwidgetheader.h \
|
||||||
widgets/chatwidgetheaderbutton.h \
|
widgets/chatwidgetheaderbutton.h \
|
||||||
|
@ -94,14 +99,29 @@ HEADERS += account.h \
|
||||||
widgets/settingsdialogtab.h \
|
widgets/settingsdialogtab.h \
|
||||||
widgets/signallabel.h \
|
widgets/signallabel.h \
|
||||||
widgets/textinputdialog.h \
|
widgets/textinputdialog.h \
|
||||||
windows.h \
|
|
||||||
widgets/resizingtextedit.h \
|
widgets/resizingtextedit.h \
|
||||||
settingssnapshot.h \
|
settingssnapshot.h \
|
||||||
messages/limitedqueue.h \
|
messages/limitedqueue.h \
|
||||||
messages/limitedqueuesnapshot.h \
|
messages/limitedqueuesnapshot.h \
|
||||||
messages/messageref.h \
|
messages/messageref.h \
|
||||||
logging/loggingmanager.h \
|
logging/loggingmanager.h \
|
||||||
logging/loggingchannel.h
|
logging/loggingchannel.h \
|
||||||
|
channelmanager.h \
|
||||||
|
windowmanager.h \
|
||||||
|
settingsmanager.h \
|
||||||
|
fontmanager.h \
|
||||||
|
emotemanager.h \
|
||||||
|
util/urlfetch.h \
|
||||||
|
usermanager.h \
|
||||||
|
twitch/twitchuser.h \
|
||||||
|
messages/messageparseargs.h \
|
||||||
|
messages/messagebuilder.h \
|
||||||
|
twitch/twitchmessagebuilder.h \
|
||||||
|
ircuser2.h \
|
||||||
|
twitch/twitchparsemessage.h \
|
||||||
|
widgets/fancybutton.h \
|
||||||
|
widgets/titlebar.h \
|
||||||
|
widgets/userpopupwidget.h
|
||||||
|
|
||||||
PRECOMPILED_HEADER =
|
PRECOMPILED_HEADER =
|
||||||
|
|
||||||
|
@ -112,9 +132,29 @@ DISTFILES +=
|
||||||
|
|
||||||
# Include boost
|
# Include boost
|
||||||
win32 {
|
win32 {
|
||||||
INCLUDEPATH += C:\local\boost\
|
INCLUDEPATH += C:\local\boost\
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optional dependency on windows sdk 7.1
|
||||||
|
win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) {
|
||||||
|
LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \
|
||||||
|
-ldwmapi \
|
||||||
|
-lgdi32
|
||||||
|
|
||||||
|
SOURCES += platform/borderless/qwinwidget.cpp \
|
||||||
|
platform/borderless/winnativewindow.cpp \
|
||||||
|
platform/borderless/widget.cpp
|
||||||
|
|
||||||
|
HEADERS += platform/borderless/qwinwidget.h \
|
||||||
|
platform/borderless/winnativewindow.h \
|
||||||
|
platform/borderless/widget.h
|
||||||
|
|
||||||
|
DEFINES += "USEWINSDK"
|
||||||
}
|
}
|
||||||
|
|
||||||
macx {
|
macx {
|
||||||
INCLUDEPATH += /usr/local/include
|
INCLUDEPATH += /usr/local/include
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
forms/userpopup.ui
|
||||||
|
|
|
@ -7,7 +7,7 @@ Language: Cpp
|
||||||
SpacesBeforeTrailingComments: 2
|
SpacesBeforeTrailingComments: 2
|
||||||
AccessModifierOffset: -1
|
AccessModifierOffset: -1
|
||||||
AlignEscapedNewlinesLeft: true
|
AlignEscapedNewlinesLeft: true
|
||||||
AlwaysBreakAfterDefinitionReturnType: true
|
AlwaysBreakAfterDefinitionReturnType: false
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
BreakConstructorInitializersBeforeComma: true
|
BreakConstructorInitializersBeforeComma: true
|
||||||
# BreakBeforeBraces: Linux
|
# BreakBeforeBraces: Linux
|
||||||
|
@ -26,3 +26,4 @@ BraceWrapping: {
|
||||||
AfterFunction: 'true'
|
AfterFunction: 'true'
|
||||||
BeforeCatch: 'false'
|
BeforeCatch: 'false'
|
||||||
}
|
}
|
||||||
|
ColumnLimit: 100
|
|
@ -1,38 +1,36 @@
|
||||||
#define LOOKUP_COLOR_COUNT 360
|
#define LOOKUP_COLOR_COUNT 360
|
||||||
|
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
#include "windows.h"
|
#include "windowmanager.h"
|
||||||
|
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
void
|
void ColorScheme::init()
|
||||||
ColorScheme::init()
|
|
||||||
{
|
{
|
||||||
static bool initiated = false;
|
static bool initiated = false;
|
||||||
|
|
||||||
if (!initiated) {
|
if (!initiated) {
|
||||||
initiated = true;
|
initiated = true;
|
||||||
ColorScheme::getInstance().update();
|
ColorScheme::getInstance().update();
|
||||||
Settings::getInstance().theme.valueChanged.connect(
|
SettingsManager::getInstance().theme.valueChanged.connect(
|
||||||
[](const QString &) { ColorScheme::getInstance().update(); });
|
[](const QString &) { ColorScheme::getInstance().update(); });
|
||||||
Settings::getInstance().themeHue.valueChanged.connect(
|
SettingsManager::getInstance().themeHue.valueChanged.connect(
|
||||||
[](const float &) { ColorScheme::getInstance().update(); });
|
[](const float &) { ColorScheme::getInstance().update(); });
|
||||||
|
|
||||||
ColorScheme::getInstance().updated.connect(
|
ColorScheme::getInstance().updated.connect(
|
||||||
[] { Windows::repaintVisibleChatWidgets(); });
|
[] { WindowManager::repaintVisibleChatWidgets(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ColorScheme::update()
|
||||||
ColorScheme::update()
|
|
||||||
{
|
{
|
||||||
QString theme = Settings::getInstance().theme.get();
|
QString theme = SettingsManager::getInstance().theme.get();
|
||||||
theme = theme.toLower();
|
theme = theme.toLower();
|
||||||
|
|
||||||
qreal hue = Settings::getInstance().themeHue.get();
|
qreal hue = SettingsManager::getInstance().themeHue.get();
|
||||||
|
|
||||||
if (theme == "light") {
|
if (theme == "light") {
|
||||||
setColors(hue, 0.8);
|
setColors(hue, 0.8);
|
||||||
|
@ -47,33 +45,40 @@ ColorScheme::update()
|
||||||
|
|
||||||
// hue: theme color (0 - 1)
|
// hue: theme color (0 - 1)
|
||||||
// multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black
|
// multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black
|
||||||
void
|
void ColorScheme::setColors(float hue, float multiplyer)
|
||||||
ColorScheme::setColors(float hue, float multiplyer)
|
|
||||||
{
|
{
|
||||||
IsLightTheme = multiplyer > 0;
|
IsLightTheme = multiplyer > 0;
|
||||||
|
bool hasDarkBorder = false;
|
||||||
|
|
||||||
SystemMessageColor = QColor(140, 127, 127);
|
SystemMessageColor = QColor(140, 127, 127);
|
||||||
|
|
||||||
auto isLightTheme = IsLightTheme;
|
auto isLightTheme = IsLightTheme;
|
||||||
|
|
||||||
auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l,
|
auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, qreal a = 1.0) {
|
||||||
qreal a = 1.0) {
|
|
||||||
return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a);
|
return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a);
|
||||||
};
|
};
|
||||||
|
|
||||||
DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.3);
|
DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.6);
|
||||||
|
|
||||||
Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255);
|
Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255);
|
||||||
|
|
||||||
// tab
|
// tab
|
||||||
TabPanelBackground = QColor(255, 255, 255);
|
if (hasDarkBorder) {
|
||||||
TabBackground = QColor(255, 255, 255);
|
// TabPanelBackground = getColor(hue, 0, 0.8);
|
||||||
TabHoverBackground = getColor(hue, 0, 0.05);
|
// TabBackground = getColor(hue, 0, 0.8);
|
||||||
|
// TabHoverBackground = getColor(hue, 0, 0.8);
|
||||||
|
} else {
|
||||||
|
TabPanelBackground = QColor(255, 255, 255);
|
||||||
|
TabBackground = QColor(255, 255, 255);
|
||||||
|
TabHoverBackground = getColor(hue, 0, 0.05);
|
||||||
|
}
|
||||||
TabSelectedBackground = getColor(hue, 0.5, 0.5);
|
TabSelectedBackground = getColor(hue, 0.5, 0.5);
|
||||||
TabHighlightedBackground = getColor(hue, 0.5, 0.2);
|
TabHighlightedBackground = getColor(hue, 0.5, 0.2);
|
||||||
TabNewMessageBackground =
|
TabNewMessageBackground = QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern);
|
||||||
QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern);
|
if (hasDarkBorder)
|
||||||
TabText = QColor(0, 0, 0);
|
// TabText = QColor(210, 210, 210);
|
||||||
|
// TabHoverText = QColor(210, 210, 210);
|
||||||
|
TabText = QColor(0, 0, 0);
|
||||||
TabHoverText = QColor(0, 0, 0);
|
TabHoverText = QColor(0, 0, 0);
|
||||||
TabSelectedText = QColor(255, 255, 255);
|
TabSelectedText = QColor(255, 255, 255);
|
||||||
TabHighlightedText = QColor(0, 0, 0);
|
TabHighlightedText = QColor(0, 0, 0);
|
||||||
|
@ -103,17 +108,14 @@ ColorScheme::setColors(float hue, float multiplyer)
|
||||||
fillLookupTableValues(this->minLookupTable, 0.833, 1.000, 0.30, 0.33);
|
fillLookupTableValues(this->minLookupTable, 0.833, 1.000, 0.30, 0.33);
|
||||||
|
|
||||||
// stylesheet
|
// stylesheet
|
||||||
InputStyleSheet =
|
InputStyleSheet = "background:" + ChatInputBackground.name() + ";" +
|
||||||
"background:" + ChatInputBackground.name() + ";" +
|
"border:" + TabSelectedBackground.name() + ";" + "color:" + Text.name() +
|
||||||
"border:" + TabSelectedBackground.name() + ";" +
|
";" + "selection-background-color:" + TabSelectedBackground.name();
|
||||||
"color:" + Text.name() + ";" +
|
|
||||||
"selection-background-color:" + TabSelectedBackground.name();
|
|
||||||
|
|
||||||
updated();
|
updated();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from,
|
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
|
||||||
qreal to, qreal fromValue,
|
|
||||||
qreal toValue)
|
qreal toValue)
|
||||||
{
|
{
|
||||||
qreal diff = toValue - fromValue;
|
qreal diff = toValue - fromValue;
|
||||||
|
@ -127,8 +129,7 @@ void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ColorScheme::normalizeColor(QColor &color)
|
||||||
ColorScheme::normalizeColor(QColor &color)
|
|
||||||
{
|
{
|
||||||
// qreal l = color.lightnessF();
|
// qreal l = color.lightnessF();
|
||||||
// qreal s = color.saturationF();
|
// qreal s = color.saturationF();
|
||||||
|
|
|
@ -61,8 +61,7 @@ public:
|
||||||
const int HighlightColorCount = 3;
|
const int HighlightColorCount = 3;
|
||||||
QColor HighlightColors[3];
|
QColor HighlightColors[3];
|
||||||
|
|
||||||
static ColorScheme &
|
static ColorScheme &getInstance()
|
||||||
getInstance()
|
|
||||||
{
|
{
|
||||||
static ColorScheme instance;
|
static ColorScheme instance;
|
||||||
|
|
||||||
|
@ -87,8 +86,8 @@ private:
|
||||||
qreal middleLookupTable[360] = {};
|
qreal middleLookupTable[360] = {};
|
||||||
qreal minLookupTable[360] = {};
|
qreal minLookupTable[360] = {};
|
||||||
|
|
||||||
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to,
|
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
|
||||||
qreal fromValue, qreal toValue);
|
qreal toValue);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,17 +14,16 @@ class ConcurrentMap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ConcurrentMap()
|
ConcurrentMap()
|
||||||
: map()
|
: _map()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool tryGet(const TKey &name, TValue &value) const
|
||||||
tryGet(const TKey &name, TValue &value) const
|
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&this->mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
auto a = map.find(name);
|
auto a = _map.find(name);
|
||||||
if (a == map.end()) {
|
if (a == _map.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,40 +32,37 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue
|
TValue getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
||||||
getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&this->mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
auto a = map.find(name);
|
auto a = _map.find(name);
|
||||||
if (a == map.end()) {
|
if (a == _map.end()) {
|
||||||
TValue value = addLambda();
|
TValue value = addLambda();
|
||||||
map.insert(name, value);
|
_map.insert(name, value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.value();
|
return a.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void clear()
|
||||||
clear()
|
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&this->mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
map.clear();
|
_map.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void insert(const TKey &name, const TValue &value)
|
||||||
insert(const TKey &name, const TValue &value)
|
|
||||||
{
|
{
|
||||||
QMutexLocker lock(&this->mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
map.insert(name, value);
|
_map.insert(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable QMutex mutex;
|
mutable QMutex _mutex;
|
||||||
QMap<TKey, TValue> map;
|
QMap<TKey, TValue> _map;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
45
emojis.cpp
45
emojis.cpp
|
@ -1,5 +1,5 @@
|
||||||
#include "emojis.h"
|
#include "emojis.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
|
@ -15,17 +15,14 @@ QMap<QChar, QMap<QString, QString>> Emojis::firstEmojiChars;
|
||||||
|
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache;
|
ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache;
|
||||||
|
|
||||||
QString
|
QString Emojis::replaceShortCodes(const QString &text)
|
||||||
Emojis::replaceShortCodes(const QString &text)
|
|
||||||
{
|
{
|
||||||
// TODO: Implement this xD
|
// TODO: Implement this xD
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Emojis::parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||||
Emojis::parseEmojis(
|
const QString &text)
|
||||||
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
|
||||||
const QString &text)
|
|
||||||
{
|
{
|
||||||
long lastSlice = 0;
|
long lastSlice = 0;
|
||||||
|
|
||||||
|
@ -44,21 +41,14 @@ Emojis::parseEmojis(
|
||||||
emojiIter.value() + ".png";
|
emojiIter.value() + ".png";
|
||||||
|
|
||||||
if (i - lastSlice != 0) {
|
if (i - lastSlice != 0) {
|
||||||
vector.push_back(
|
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||||
std::tuple<messages::LazyLoadedImage *,
|
NULL, text.mid(lastSlice, i - lastSlice)));
|
||||||
QString>(
|
|
||||||
NULL, text.mid(lastSlice, i - lastSlice)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector.push_back(
|
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||||
std::tuple<messages::LazyLoadedImage *, QString>(
|
imageCache.getOrAdd(
|
||||||
imageCache.getOrAdd(
|
url, [&url] { return new messages::LazyLoadedImage(url, 0.35); }),
|
||||||
url,
|
QString()));
|
||||||
[&url] {
|
|
||||||
return new messages::LazyLoadedImage(
|
|
||||||
url, 0.35);
|
|
||||||
}),
|
|
||||||
QString()));
|
|
||||||
|
|
||||||
i += j - 1;
|
i += j - 1;
|
||||||
|
|
||||||
|
@ -72,13 +62,12 @@ Emojis::parseEmojis(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastSlice < text.length()) {
|
if (lastSlice < text.length()) {
|
||||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
vector.push_back(
|
||||||
NULL, text.mid(lastSlice)));
|
std::tuple<messages::LazyLoadedImage *, QString>(NULL, text.mid(lastSlice)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Emojis::loadEmojis()
|
||||||
Emojis::loadEmojis()
|
|
||||||
{
|
{
|
||||||
QFile file(":/emojidata.txt");
|
QFile file(":/emojidata.txt");
|
||||||
file.open(QFile::ReadOnly);
|
file.open(QFile::ReadOnly);
|
||||||
|
@ -106,8 +95,7 @@ Emojis::loadEmojis()
|
||||||
emotes[i++] = QString(item).toUInt(nullptr, 16);
|
emotes[i++] = QString(item).toUInt(nullptr, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
shortCodeToEmoji.insert(
|
shortCodeToEmoji.insert(a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
|
||||||
a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
|
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
|
||||||
|
@ -122,9 +110,8 @@ Emojis::loadEmojis()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstEmojiChars.insert(
|
firstEmojiChars.insert(emoji.first.at(0),
|
||||||
emoji.first.at(0),
|
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
|
||||||
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
emojis.h
5
emojis.h
|
@ -14,9 +14,8 @@ namespace chatterino {
|
||||||
class Emojis
|
class Emojis
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void parseEmojis(
|
static void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||||
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
const QString &text);
|
||||||
const QString &text);
|
|
||||||
|
|
||||||
static void loadEmojis();
|
static void loadEmojis();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -11,41 +11,77 @@
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0"
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
QString Emotes::twitchEmoteTemplate(
|
EmoteManager EmoteManager::instance;
|
||||||
"https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0");
|
|
||||||
|
|
||||||
ConcurrentMap<QString, TwitchEmoteValue *> Emotes::twitchEmotes;
|
EmoteManager::EmoteManager()
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::bttvEmotes;
|
: _twitchEmotes()
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::ffzEmotes;
|
, _bttvEmotes()
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::chatterinoEmotes;
|
, _ffzEmotes()
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *>
|
, _chatterinoEmotes()
|
||||||
Emotes::bttvChannelEmoteFromCaches;
|
, _bttvChannelEmoteFromCaches()
|
||||||
ConcurrentMap<int, messages::LazyLoadedImage *>
|
, _ffzChannelEmoteFromCaches()
|
||||||
Emotes::ffzChannelEmoteFromCaches;
|
, _twitchEmoteFromCache()
|
||||||
ConcurrentMap<long, messages::LazyLoadedImage *> Emotes::twitchEmoteFromCache;
|
, _miscImageFromCache()
|
||||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::miscImageFromCache;
|
, _gifUpdateTimerSignal()
|
||||||
boost::signals2::signal<void()> Emotes::gifUpdateTimerSignal;
|
, _gifUpdateTimer()
|
||||||
|
, _gifUpdateTimerInitiated(false)
|
||||||
QTimer Emotes::gifUpdateTimer;
|
, _generation(0)
|
||||||
bool Emotes::gifUpdateTimerInitiated(false);
|
|
||||||
|
|
||||||
int Emotes::generation = 0;
|
|
||||||
|
|
||||||
Emotes::Emotes()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
|
||||||
Emotes::loadGlobalEmotes()
|
{
|
||||||
|
return _twitchEmotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getBttvEmotes()
|
||||||
|
{
|
||||||
|
return _bttvEmotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getFfzEmotes()
|
||||||
|
{
|
||||||
|
return _ffzEmotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getChatterinoEmotes()
|
||||||
|
{
|
||||||
|
return _chatterinoEmotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getBttvChannelEmoteFromCaches()
|
||||||
|
{
|
||||||
|
return _bttvChannelEmoteFromCaches;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<int, messages::LazyLoadedImage *> &EmoteManager::getFfzChannelEmoteFromCaches()
|
||||||
|
{
|
||||||
|
return _ffzChannelEmoteFromCaches;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<long, messages::LazyLoadedImage *> &EmoteManager::getTwitchEmoteFromCache()
|
||||||
|
{
|
||||||
|
return _twitchEmoteFromCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getMiscImageFromCache()
|
||||||
|
{
|
||||||
|
return _miscImageFromCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmoteManager::loadGlobalEmotes()
|
||||||
{
|
{
|
||||||
loadBttvEmotes();
|
loadBttvEmotes();
|
||||||
loadFfzEmotes();
|
loadFfzEmotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void EmoteManager::loadBttvEmotes()
|
||||||
Emotes::loadBttvEmotes()
|
|
||||||
{
|
{
|
||||||
// bttv
|
// bttv
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
@ -63,21 +99,19 @@ Emotes::loadBttvEmotes()
|
||||||
|
|
||||||
auto emotes = root.value("emotes").toArray();
|
auto emotes = root.value("emotes").toArray();
|
||||||
|
|
||||||
QString _template = "https:" + root.value("urlTemplate").toString();
|
QString linkTemplate = "https:" + root.value("urlTemplate").toString();
|
||||||
|
|
||||||
for (const QJsonValue &emote : emotes) {
|
for (const QJsonValue &emote : emotes) {
|
||||||
QString id = emote.toObject().value("id").toString();
|
QString id = emote.toObject().value("id").toString();
|
||||||
QString code = emote.toObject().value("code").toString();
|
QString code = emote.toObject().value("code").toString();
|
||||||
// emote.value("imageType").toString();
|
// emote.value("imageType").toString();
|
||||||
|
|
||||||
QString tmp = _template;
|
QString tmp = linkTemplate;
|
||||||
tmp.detach();
|
tmp.detach();
|
||||||
QString url =
|
QString url = tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||||
tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
|
||||||
|
|
||||||
Emotes::getBttvEmotes().insert(
|
EmoteManager::getBttvEmotes().insert(
|
||||||
code, new messages::LazyLoadedImage(
|
code, new LazyLoadedImage(url, 1, code, code + "\nGlobal Bttv Emote"));
|
||||||
url, 1, code, code + "\nGlobal Bttv Emote"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +120,7 @@ Emotes::loadBttvEmotes()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void EmoteManager::loadFfzEmotes()
|
||||||
Emotes::loadFfzEmotes()
|
|
||||||
{
|
{
|
||||||
// ffz
|
// ffz
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
@ -113,15 +146,14 @@ Emotes::loadFfzEmotes()
|
||||||
|
|
||||||
// margins
|
// margins
|
||||||
|
|
||||||
int id = object.value("id").toInt();
|
// int id = object.value("id").toInt();
|
||||||
QString code = object.value("name").toString();
|
QString code = object.value("name").toString();
|
||||||
|
|
||||||
QJsonObject urls = object.value("urls").toObject();
|
QJsonObject urls = object.value("urls").toObject();
|
||||||
QString url1 = "http:" + urls.value("1").toString();
|
QString url1 = "http:" + urls.value("1").toString();
|
||||||
|
|
||||||
Emotes::getBttvEmotes().insert(
|
EmoteManager::getBttvEmotes().insert(
|
||||||
code, new messages::LazyLoadedImage(
|
code, new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote"));
|
||||||
url1, 1, code, code + "\nGlobal Ffz Emote"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,41 +163,34 @@ Emotes::loadFfzEmotes()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
messages::LazyLoadedImage *
|
LazyLoadedImage *EmoteManager::getTwitchEmoteById(const QString &name, long id)
|
||||||
Emotes::getTwitchEmoteById(const QString &name, long id)
|
|
||||||
{
|
{
|
||||||
qDebug() << "loading twitch emote: " << id;
|
return EmoteManager::_twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
|
||||||
|
qDebug() << "added twitch emote: " << id;
|
||||||
return Emotes::twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
|
|
||||||
qDebug() << "loading twitch emote: " << id;
|
|
||||||
qreal scale;
|
qreal scale;
|
||||||
QString url = getTwitchEmoteLink(id, scale);
|
QString url = getTwitchEmoteLink(id, scale);
|
||||||
return new messages::LazyLoadedImage(url, scale, name,
|
return new LazyLoadedImage(url, scale, name, name + "\nTwitch Emote");
|
||||||
name + "\nTwitch Emote");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString EmoteManager::getTwitchEmoteLink(long id, qreal &scale)
|
||||||
Emotes::getTwitchEmoteLink(long id, qreal &scale)
|
|
||||||
{
|
{
|
||||||
scale = .5;
|
scale = .5;
|
||||||
|
|
||||||
QString value = Emotes::twitchEmoteTemplate;
|
QString value = TWITCH_EMOTE_TEMPLATE;
|
||||||
|
|
||||||
value.detach();
|
value.detach();
|
||||||
|
|
||||||
return value.replace("{id}", QString::number(id)).replace("{scale}", "2");
|
return value.replace("{id}", QString::number(id)).replace("{scale}", "2");
|
||||||
}
|
}
|
||||||
|
|
||||||
messages::LazyLoadedImage *
|
LazyLoadedImage *EmoteManager::getCheerImage(long long amount, bool animated)
|
||||||
Emotes::getCheerImage(long long amount, bool animated)
|
|
||||||
{
|
{
|
||||||
// TODO: fix this xD
|
// TODO: fix this xD
|
||||||
return getCheerBadge(amount);
|
return getCheerBadge(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
messages::LazyLoadedImage *
|
LazyLoadedImage *EmoteManager::getCheerBadge(long long amount)
|
||||||
Emotes::getCheerBadge(long long amount)
|
|
||||||
{
|
{
|
||||||
if (amount >= 100000) {
|
if (amount >= 100000) {
|
||||||
return Resources::getCheerBadge100000();
|
return Resources::getCheerBadge100000();
|
98
emotemanager.h
Normal file
98
emotemanager.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef EMOTES_H
|
||||||
|
#define EMOTES_H
|
||||||
|
|
||||||
|
#define GIF_FRAME_LENGTH 33
|
||||||
|
|
||||||
|
#include "concurrentmap.h"
|
||||||
|
#include "messages/lazyloadedimage.h"
|
||||||
|
#include "twitch/emotevalue.h"
|
||||||
|
#include "windowmanager.h"
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
class EmoteManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static EmoteManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentMap<QString, twitch::EmoteValue *> &getTwitchEmotes();
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getBttvEmotes();
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getFfzEmotes();
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getChatterinoEmotes();
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getBttvChannelEmoteFromCaches();
|
||||||
|
ConcurrentMap<int, messages::LazyLoadedImage *> &getFfzChannelEmoteFromCaches();
|
||||||
|
ConcurrentMap<long, messages::LazyLoadedImage *> &getTwitchEmoteFromCache();
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> &getMiscImageFromCache();
|
||||||
|
|
||||||
|
void loadGlobalEmotes();
|
||||||
|
|
||||||
|
messages::LazyLoadedImage *getCheerImage(long long int amount, bool animated);
|
||||||
|
messages::LazyLoadedImage *getCheerBadge(long long int amount);
|
||||||
|
|
||||||
|
messages::LazyLoadedImage *getTwitchEmoteById(const QString &name, long int id);
|
||||||
|
|
||||||
|
int getGeneration()
|
||||||
|
{
|
||||||
|
return _generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incGeneration()
|
||||||
|
{
|
||||||
|
_generation++;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::signals2::signal<void()> &getGifUpdateSignal()
|
||||||
|
{
|
||||||
|
if (!_gifUpdateTimerInitiated) {
|
||||||
|
_gifUpdateTimerInitiated = true;
|
||||||
|
|
||||||
|
_gifUpdateTimer.setInterval(30);
|
||||||
|
_gifUpdateTimer.start();
|
||||||
|
|
||||||
|
QObject::connect(&_gifUpdateTimer, &QTimer::timeout, [this] {
|
||||||
|
_gifUpdateTimerSignal();
|
||||||
|
WindowManager::repaintGifEmotes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return _gifUpdateTimerSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static EmoteManager instance;
|
||||||
|
|
||||||
|
EmoteManager();
|
||||||
|
|
||||||
|
// variables
|
||||||
|
ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes;
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvEmotes;
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzEmotes;
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _chatterinoEmotes;
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmoteFromCaches;
|
||||||
|
ConcurrentMap<int, messages::LazyLoadedImage *> _ffzChannelEmoteFromCaches;
|
||||||
|
ConcurrentMap<long, messages::LazyLoadedImage *> _twitchEmoteFromCache;
|
||||||
|
ConcurrentMap<QString, messages::LazyLoadedImage *> _miscImageFromCache;
|
||||||
|
|
||||||
|
QTimer _gifUpdateTimer;
|
||||||
|
bool _gifUpdateTimerInitiated;
|
||||||
|
|
||||||
|
int _generation;
|
||||||
|
|
||||||
|
boost::signals2::signal<void()> _gifUpdateTimerSignal;
|
||||||
|
|
||||||
|
// methods
|
||||||
|
static QString getTwitchEmoteLink(long id, qreal &scale);
|
||||||
|
|
||||||
|
void loadFfzEmotes();
|
||||||
|
void loadBttvEmotes();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EMOTES_H
|
140
emotes.h
140
emotes.h
|
@ -1,140 +0,0 @@
|
||||||
#ifndef EMOTES_H
|
|
||||||
#define EMOTES_H
|
|
||||||
|
|
||||||
#define GIF_FRAME_LENGTH 33
|
|
||||||
|
|
||||||
#include "concurrentmap.h"
|
|
||||||
#include "messages/lazyloadedimage.h"
|
|
||||||
#include "twitchemotevalue.h"
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
#include <QMap>
|
|
||||||
#include <QMutex>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <boost/signals2.hpp>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
class Emotes
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static ConcurrentMap<QString, TwitchEmoteValue *> &
|
|
||||||
getTwitchEmotes()
|
|
||||||
{
|
|
||||||
return twitchEmotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
|
||||||
getBttvEmotes()
|
|
||||||
{
|
|
||||||
return bttvEmotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
|
||||||
getFfzEmotes()
|
|
||||||
{
|
|
||||||
return ffzEmotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
|
||||||
getChatterinoEmotes()
|
|
||||||
{
|
|
||||||
return chatterinoEmotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
|
||||||
getBttvChannelEmoteFromCaches()
|
|
||||||
{
|
|
||||||
return bttvChannelEmoteFromCaches;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<int, messages::LazyLoadedImage *> &
|
|
||||||
getFfzChannelEmoteFromCaches()
|
|
||||||
{
|
|
||||||
return ffzChannelEmoteFromCaches;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<long, messages::LazyLoadedImage *> &
|
|
||||||
getTwitchEmoteFromCache()
|
|
||||||
{
|
|
||||||
return twitchEmoteFromCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
|
||||||
getMiscImageFromCache()
|
|
||||||
{
|
|
||||||
return miscImageFromCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void loadGlobalEmotes();
|
|
||||||
|
|
||||||
static messages::LazyLoadedImage *getCheerImage(long long int amount,
|
|
||||||
bool animated);
|
|
||||||
static messages::LazyLoadedImage *getCheerBadge(long long int amount);
|
|
||||||
|
|
||||||
static messages::LazyLoadedImage *getTwitchEmoteById(const QString &name,
|
|
||||||
long int id);
|
|
||||||
|
|
||||||
static int
|
|
||||||
getGeneration()
|
|
||||||
{
|
|
||||||
return generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
incGeneration()
|
|
||||||
{
|
|
||||||
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:
|
|
||||||
Emotes();
|
|
||||||
|
|
||||||
static QString twitchEmoteTemplate;
|
|
||||||
|
|
||||||
static ConcurrentMap<QString, TwitchEmoteValue *> twitchEmotes;
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> bttvEmotes;
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> ffzEmotes;
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *> chatterinoEmotes;
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *>
|
|
||||||
bttvChannelEmoteFromCaches;
|
|
||||||
static ConcurrentMap<int, messages::LazyLoadedImage *>
|
|
||||||
ffzChannelEmoteFromCaches;
|
|
||||||
static ConcurrentMap<long, messages::LazyLoadedImage *>
|
|
||||||
twitchEmoteFromCache;
|
|
||||||
static ConcurrentMap<QString, messages::LazyLoadedImage *>
|
|
||||||
miscImageFromCache;
|
|
||||||
|
|
||||||
static QString getTwitchEmoteLink(long id, qreal &scale);
|
|
||||||
|
|
||||||
static QTimer gifUpdateTimer;
|
|
||||||
static bool gifUpdateTimerInitiated;
|
|
||||||
|
|
||||||
static void loadFfzEmotes();
|
|
||||||
static void loadBttvEmotes();
|
|
||||||
|
|
||||||
static int generation;
|
|
||||||
|
|
||||||
static boost::signals2::signal<void()> gifUpdateTimerSignal;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // EMOTES_H
|
|
62
fontmanager.cpp
Normal file
62
fontmanager.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include "fontmanager.h"
|
||||||
|
|
||||||
|
#define DEFAULT_FONT "Arial"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
FontManager FontManager::instance;
|
||||||
|
|
||||||
|
FontManager::FontManager()
|
||||||
|
: _generation(0)
|
||||||
|
{
|
||||||
|
_medium = new QFont(DEFAULT_FONT, 14);
|
||||||
|
_mediumBold = new QFont(DEFAULT_FONT, 14);
|
||||||
|
_mediumItalic = new QFont(DEFAULT_FONT, 14);
|
||||||
|
_small = new QFont(DEFAULT_FONT, 10);
|
||||||
|
_large = new QFont(DEFAULT_FONT, 16);
|
||||||
|
_veryLarge = new QFont(DEFAULT_FONT, 18);
|
||||||
|
|
||||||
|
_metricsMedium = new QFontMetrics(*_medium);
|
||||||
|
_metricsMediumBold = new QFontMetrics(*_mediumBold);
|
||||||
|
_metricsMediumItalic = new QFontMetrics(*_mediumItalic);
|
||||||
|
_metricsSmall = new QFontMetrics(*_small);
|
||||||
|
_metricsLarge = new QFontMetrics(*_large);
|
||||||
|
_metricsVeryLarge = new QFontMetrics(*_veryLarge);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont &FontManager::getFont(Type type)
|
||||||
|
{
|
||||||
|
if (type == Medium)
|
||||||
|
return *_medium;
|
||||||
|
if (type == MediumBold)
|
||||||
|
return *_mediumBold;
|
||||||
|
if (type == MediumItalic)
|
||||||
|
return *_mediumItalic;
|
||||||
|
if (type == Small)
|
||||||
|
return *_small;
|
||||||
|
if (type == Large)
|
||||||
|
return *_large;
|
||||||
|
if (type == VeryLarge)
|
||||||
|
return *_veryLarge;
|
||||||
|
|
||||||
|
return *_medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFontMetrics &FontManager::getFontMetrics(Type type)
|
||||||
|
{
|
||||||
|
if (type == Medium)
|
||||||
|
return *_metricsMedium;
|
||||||
|
if (type == MediumBold)
|
||||||
|
return *_metricsMediumBold;
|
||||||
|
if (type == MediumItalic)
|
||||||
|
return *_metricsMediumItalic;
|
||||||
|
if (type == Small)
|
||||||
|
return *_metricsSmall;
|
||||||
|
if (type == Large)
|
||||||
|
return *_metricsLarge;
|
||||||
|
if (type == VeryLarge)
|
||||||
|
return *_metricsVeryLarge;
|
||||||
|
|
||||||
|
return *_metricsMedium;
|
||||||
|
}
|
||||||
|
}
|
55
fontmanager.h
Normal file
55
fontmanager.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef FONTS_H
|
||||||
|
#define FONTS_H
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class FontManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type : char { Medium, MediumBold, MediumItalic, Small, Large, VeryLarge };
|
||||||
|
|
||||||
|
static FontManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont &getFont(Type type);
|
||||||
|
QFontMetrics &getFontMetrics(Type type);
|
||||||
|
|
||||||
|
int getGeneration()
|
||||||
|
{
|
||||||
|
return _generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incGeneration()
|
||||||
|
{
|
||||||
|
_generation++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static FontManager instance;
|
||||||
|
|
||||||
|
FontManager();
|
||||||
|
|
||||||
|
QFont *_medium;
|
||||||
|
QFont *_mediumBold;
|
||||||
|
QFont *_mediumItalic;
|
||||||
|
QFont *_small;
|
||||||
|
QFont *_large;
|
||||||
|
QFont *_veryLarge;
|
||||||
|
|
||||||
|
QFontMetrics *_metricsMedium;
|
||||||
|
QFontMetrics *_metricsMediumBold;
|
||||||
|
QFontMetrics *_metricsMediumItalic;
|
||||||
|
QFontMetrics *_metricsSmall;
|
||||||
|
QFontMetrics *_metricsLarge;
|
||||||
|
QFontMetrics *_metricsVeryLarge;
|
||||||
|
|
||||||
|
int _generation;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FONTS_H
|
64
fonts.cpp
64
fonts.cpp
|
@ -1,64 +0,0 @@
|
||||||
#include "fonts.h"
|
|
||||||
|
|
||||||
#define DEFAULT_FONT "Arial"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
QFont *Fonts::medium = new QFont(DEFAULT_FONT, 14);
|
|
||||||
QFont *Fonts::mediumBold = new QFont(DEFAULT_FONT, 14);
|
|
||||||
QFont *Fonts::mediumItalic = new QFont(DEFAULT_FONT, 14);
|
|
||||||
QFont *Fonts::small = new QFont(DEFAULT_FONT, 10);
|
|
||||||
QFont *Fonts::large = new QFont(DEFAULT_FONT, 16);
|
|
||||||
QFont *Fonts::veryLarge = new QFont(DEFAULT_FONT, 18);
|
|
||||||
|
|
||||||
QFontMetrics *Fonts::metricsMedium = new QFontMetrics(*medium);
|
|
||||||
QFontMetrics *Fonts::metricsMediumBold = new QFontMetrics(*mediumBold);
|
|
||||||
QFontMetrics *Fonts::metricsMediumItalic = new QFontMetrics(*mediumItalic);
|
|
||||||
QFontMetrics *Fonts::metricsSmall = new QFontMetrics(*small);
|
|
||||||
QFontMetrics *Fonts::metricsLarge = new QFontMetrics(*large);
|
|
||||||
QFontMetrics *Fonts::metricsVeryLarge = new QFontMetrics(*veryLarge);
|
|
||||||
|
|
||||||
int Fonts::generation = 0;
|
|
||||||
|
|
||||||
Fonts::Fonts()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QFont &
|
|
||||||
Fonts::getFont(Type type)
|
|
||||||
{
|
|
||||||
if (type == Medium)
|
|
||||||
return *medium;
|
|
||||||
if (type == MediumBold)
|
|
||||||
return *mediumBold;
|
|
||||||
if (type == MediumItalic)
|
|
||||||
return *mediumItalic;
|
|
||||||
if (type == Small)
|
|
||||||
return *small;
|
|
||||||
if (type == Large)
|
|
||||||
return *large;
|
|
||||||
if (type == VeryLarge)
|
|
||||||
return *veryLarge;
|
|
||||||
|
|
||||||
return *medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFontMetrics &
|
|
||||||
Fonts::getFontMetrics(Type type)
|
|
||||||
{
|
|
||||||
if (type == Medium)
|
|
||||||
return *metricsMedium;
|
|
||||||
if (type == MediumBold)
|
|
||||||
return *metricsMediumBold;
|
|
||||||
if (type == MediumItalic)
|
|
||||||
return *metricsMediumItalic;
|
|
||||||
if (type == Small)
|
|
||||||
return *metricsSmall;
|
|
||||||
if (type == Large)
|
|
||||||
return *metricsLarge;
|
|
||||||
if (type == VeryLarge)
|
|
||||||
return *metricsVeryLarge;
|
|
||||||
|
|
||||||
return *metricsMedium;
|
|
||||||
}
|
|
||||||
}
|
|
57
fonts.h
57
fonts.h
|
@ -1,57 +0,0 @@
|
||||||
#ifndef FONTS_H
|
|
||||||
#define FONTS_H
|
|
||||||
|
|
||||||
#include <QFont>
|
|
||||||
#include <QFontMetrics>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
class Fonts
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Type : char {
|
|
||||||
Medium,
|
|
||||||
MediumBold,
|
|
||||||
MediumItalic,
|
|
||||||
Small,
|
|
||||||
Large,
|
|
||||||
VeryLarge
|
|
||||||
};
|
|
||||||
|
|
||||||
static QFont &getFont(Type type);
|
|
||||||
static QFontMetrics &getFontMetrics(Type type);
|
|
||||||
|
|
||||||
static int
|
|
||||||
getGeneration()
|
|
||||||
{
|
|
||||||
return generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
incGeneration()
|
|
||||||
{
|
|
||||||
generation++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Fonts();
|
|
||||||
|
|
||||||
static QFont *medium;
|
|
||||||
static QFont *mediumBold;
|
|
||||||
static QFont *mediumItalic;
|
|
||||||
static QFont *small;
|
|
||||||
static QFont *large;
|
|
||||||
static QFont *veryLarge;
|
|
||||||
|
|
||||||
static QFontMetrics *metricsMedium;
|
|
||||||
static QFontMetrics *metricsMediumBold;
|
|
||||||
static QFontMetrics *metricsMediumItalic;
|
|
||||||
static QFontMetrics *metricsSmall;
|
|
||||||
static QFontMetrics *metricsLarge;
|
|
||||||
static QFontMetrics *metricsVeryLarge;
|
|
||||||
|
|
||||||
static int generation;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FONTS_H
|
|
124
forms/userpopup.ui
Normal file
124
forms/userpopup.ui
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>UserPopup</class>
|
||||||
|
<widget class="QWidget" name="UserPopup">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>131</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>UserPopup</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QPushButton" name="btnPurge">
|
||||||
|
<property name="text">
|
||||||
|
<string>Purge</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" rowspan="2" colspan="2">
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Views</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="lblViews">
|
||||||
|
<property name="text">
|
||||||
|
<string>420</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Followers</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="lblFollowers">
|
||||||
|
<property name="text">
|
||||||
|
<string>69</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Account Age</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="lblAccountAge">
|
||||||
|
<property name="text">
|
||||||
|
<string>6 years</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lblUsername">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>14</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>USERNAME</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnClose">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" rowspan="3">
|
||||||
|
<widget class="QLabel" name="lblAvatar">
|
||||||
|
<property name="text">
|
||||||
|
<string>AVATAR</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
257
ircmanager.cpp
257
ircmanager.cpp
|
@ -1,73 +1,89 @@
|
||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "asyncexec.h"
|
#include "asyncexec.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "channels.h"
|
#include "channelmanager.h"
|
||||||
|
#include "messages/messageparseargs.h"
|
||||||
|
#include "twitch/twitchmessagebuilder.h"
|
||||||
|
#include "twitch/twitchparsemessage.h"
|
||||||
|
#include "twitch/twitchuser.h"
|
||||||
|
#include "usermanager.h"
|
||||||
|
|
||||||
#include <irccommand.h>
|
#include <irccommand.h>
|
||||||
#include <ircconnection.h>
|
#include <ircconnection.h>
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
Account *IrcManager::account = nullptr;
|
IrcManager IrcManager::instance;
|
||||||
std::shared_ptr<IrcConnection> IrcManager::connection;
|
|
||||||
QMutex IrcManager::connectionMutex;
|
|
||||||
long IrcManager::connectionGeneration = 0;
|
|
||||||
const QString IrcManager::defaultClientId = "7ue61iz46fz11y3cugd0l3tawb4taal";
|
|
||||||
QNetworkAccessManager IrcManager::accessManager;
|
|
||||||
|
|
||||||
QMap<QString, bool> IrcManager::twitchBlockedUsers;
|
const QString IrcManager::defaultClientId("7ue61iz46fz11y3cugd0l3tawb4taal");
|
||||||
QMutex IrcManager::twitchBlockedUsersMutex;
|
|
||||||
|
|
||||||
IrcManager::IrcManager()
|
IrcManager::IrcManager()
|
||||||
|
: _account(AccountManager::getInstance().getAnon())
|
||||||
|
, _connection()
|
||||||
|
, _connectionMutex()
|
||||||
|
, _connectionGeneration(0)
|
||||||
|
, _twitchBlockedUsers()
|
||||||
|
, _twitchBlockedUsersMutex()
|
||||||
|
, _accessManager()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
const twitch::TwitchUser &IrcManager::getUser() const
|
||||||
IrcManager::connect()
|
{
|
||||||
|
return _account;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IrcManager::setUser(const twitch::TwitchUser &account)
|
||||||
|
{
|
||||||
|
_account = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IrcManager::connect()
|
||||||
{
|
{
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
async_exec([] { beginConnecting(); });
|
async_exec([this] { beginConnecting(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::beginConnecting()
|
||||||
IrcManager::beginConnecting()
|
|
||||||
{
|
{
|
||||||
IrcManager::account = const_cast<Account *>(Account::getAnon());
|
int generation = ++IrcManager::_connectionGeneration;
|
||||||
|
|
||||||
int generation = ++IrcManager::connectionGeneration;
|
Communi::IrcConnection *c = new Communi::IrcConnection;
|
||||||
|
|
||||||
auto c = new IrcConnection();
|
QObject::connect(c, &Communi::IrcConnection::messageReceived, this,
|
||||||
|
&IrcManager::messageReceived);
|
||||||
|
QObject::connect(c, &Communi::IrcConnection::privateMessageReceived, this,
|
||||||
|
&IrcManager::privateMessageReceived);
|
||||||
|
|
||||||
QObject::connect(c, &IrcConnection::messageReceived, &messageReceived);
|
if (_account.isAnon()) {
|
||||||
QObject::connect(c, &IrcConnection::privateMessageReceived,
|
|
||||||
&privateMessageReceived);
|
|
||||||
|
|
||||||
if (account->isAnon()) {
|
|
||||||
// fetch ignored users
|
// fetch ignored users
|
||||||
QString username = account->getUsername();
|
QString username = _account.getUserName();
|
||||||
QString oauthClient = account->getOauthClient();
|
QString oauthClient = _account.getOAuthClient();
|
||||||
QString oauthToken = account->getOauthToken();
|
QString oauthToken = _account.getOAuthToken();
|
||||||
|
|
||||||
{
|
{
|
||||||
QString nextLink = "https://api.twitch.tv/kraken/users/" +
|
QString nextLink = "https://api.twitch.tv/kraken/users/" + username +
|
||||||
username + "/blocks?limit=" + 100 +
|
"/blocks?limit=" + 100 + "&client_id=" + oauthClient;
|
||||||
"&client_id=" + oauthClient;
|
|
||||||
|
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
|
QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
|
||||||
QNetworkReply *reply = manager->get(req);
|
QNetworkReply *reply = manager->get(req);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
_twitchBlockedUsersMutex.lock();
|
||||||
IrcManager::twitchBlockedUsers.clear();
|
_twitchBlockedUsers.clear();
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
_twitchBlockedUsersMutex.unlock();
|
||||||
|
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||||
|
@ -78,15 +94,13 @@ IrcManager::beginConnecting()
|
||||||
|
|
||||||
auto blocks = root.value("blocks").toArray();
|
auto blocks = root.value("blocks").toArray();
|
||||||
|
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
_twitchBlockedUsersMutex.lock();
|
||||||
for (QJsonValue block : blocks) {
|
for (QJsonValue block : blocks) {
|
||||||
QJsonObject user =
|
QJsonObject user = block.toObject().value("user").toObject();
|
||||||
block.toObject().value("user").toObject();
|
|
||||||
// display_name
|
// display_name
|
||||||
IrcManager::twitchBlockedUsers.insert(
|
_twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
|
||||||
user.value("name").toString().toLower(), true);
|
|
||||||
}
|
}
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
_twitchBlockedUsersMutex.unlock();
|
||||||
|
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
});
|
});
|
||||||
|
@ -94,10 +108,10 @@ IrcManager::beginConnecting()
|
||||||
|
|
||||||
// fetch available twitch emtoes
|
// fetch available twitch emtoes
|
||||||
{
|
{
|
||||||
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" +
|
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username +
|
||||||
username + "/emotes?oauth_token=" +
|
"/emotes?oauth_token=" + oauthToken +
|
||||||
oauthToken + "&client_id=" + oauthClient));
|
"&client_id=" + oauthClient));
|
||||||
QNetworkReply *reply = IrcManager::accessManager.get(req);
|
QNetworkReply *reply = _accessManager.get(req);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
QByteArray data = reply->readAll();
|
QByteArray data = reply->readAll();
|
||||||
|
@ -109,15 +123,13 @@ IrcManager::beginConnecting()
|
||||||
|
|
||||||
auto blocks = root.value("blocks").toArray();
|
auto blocks = root.value("blocks").toArray();
|
||||||
|
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
_twitchBlockedUsersMutex.lock();
|
||||||
for (QJsonValue block : blocks) {
|
for (QJsonValue block : blocks) {
|
||||||
QJsonObject user =
|
QJsonObject user = block.toObject().value("user").toObject();
|
||||||
block.toObject().value("user").toObject();
|
|
||||||
// display_name
|
// display_name
|
||||||
IrcManager::twitchBlockedUsers.insert(
|
_twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
|
||||||
user.value("name").toString().toLower(), true);
|
|
||||||
}
|
}
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
_twitchBlockedUsersMutex.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,19 +141,17 @@ IrcManager::beginConnecting()
|
||||||
c->setNickName("justinfan123");
|
c->setNickName("justinfan123");
|
||||||
c->setRealName("justinfan123");
|
c->setRealName("justinfan123");
|
||||||
|
|
||||||
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
||||||
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
||||||
|
|
||||||
QMutexLocker locker(&IrcManager::connectionMutex);
|
QMutexLocker locker(&_connectionMutex);
|
||||||
|
|
||||||
if (generation == IrcManager::connectionGeneration) {
|
if (generation == _connectionGeneration) {
|
||||||
c->moveToThread(QCoreApplication::instance()->thread());
|
c->moveToThread(QCoreApplication::instance()->thread());
|
||||||
IrcManager::connection = std::shared_ptr<IrcConnection>(c);
|
_connection = std::shared_ptr<Communi::IrcConnection>(c);
|
||||||
|
|
||||||
auto channels = Channels::getItems();
|
for (auto &channel : ChannelManager::getInstance().getItems()) {
|
||||||
|
c->sendRaw("JOIN #" + channel->getName());
|
||||||
for (auto &channel : channels) {
|
|
||||||
c->sendRaw("JOIN #" + channel.get()->getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c->open();
|
c->open();
|
||||||
|
@ -150,53 +160,50 @@ IrcManager::beginConnecting()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::disconnect()
|
||||||
IrcManager::disconnect()
|
|
||||||
{
|
{
|
||||||
IrcManager::connectionMutex.lock();
|
_connectionMutex.lock();
|
||||||
|
|
||||||
auto c = IrcManager::connection;
|
auto c = _connection;
|
||||||
if (IrcManager::connection.get() != NULL) {
|
if (_connection.get() != NULL) {
|
||||||
IrcManager::connection = std::shared_ptr<IrcConnection>();
|
_connection = std::shared_ptr<Communi::IrcConnection>();
|
||||||
}
|
}
|
||||||
|
|
||||||
IrcManager::connectionMutex.unlock();
|
_connectionMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::send(QString raw)
|
||||||
IrcManager::send(QString raw)
|
|
||||||
{
|
{
|
||||||
IrcManager::connectionMutex.lock();
|
_connectionMutex.lock();
|
||||||
IrcManager::connection->sendRaw(raw);
|
|
||||||
IrcManager::connectionMutex.unlock();
|
_connection->sendRaw(raw);
|
||||||
|
|
||||||
|
_connectionMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::sendJoin(const QString &channel)
|
||||||
IrcManager::sendJoin(const QString &channel)
|
|
||||||
{
|
{
|
||||||
IrcManager::connectionMutex.lock();
|
_connectionMutex.lock();
|
||||||
|
|
||||||
if (IrcManager::connection.get() != NULL) {
|
if (_connection.get() != NULL) {
|
||||||
IrcManager::connection.get()->sendRaw("JOIN #" + channel);
|
_connection.get()->sendRaw("JOIN #" + channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
IrcManager::connectionMutex.unlock();
|
_connectionMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::partChannel(const QString &channel)
|
||||||
IrcManager::partChannel(const QString &channel)
|
|
||||||
{
|
{
|
||||||
IrcManager::connectionMutex.lock();
|
_connectionMutex.lock();
|
||||||
|
|
||||||
if (IrcManager::connection.get() != NULL) {
|
if (_connection.get() != NULL) {
|
||||||
IrcManager::connection.get()->sendRaw("PART #" + channel);
|
_connection.get()->sendRaw("PART #" + channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
IrcManager::connectionMutex.unlock();
|
_connectionMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::messageReceived(Communi::IrcMessage *message)
|
||||||
IrcManager::messageReceived(IrcMessage *message)
|
|
||||||
{
|
{
|
||||||
// qInfo(message->command().toStdString().c_str());
|
// qInfo(message->command().toStdString().c_str());
|
||||||
|
|
||||||
|
@ -211,63 +218,51 @@ IrcManager::messageReceived(IrcMessage *message)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
||||||
IrcManager::privateMessageReceived(IrcPrivateMessage *message)
|
|
||||||
{
|
{
|
||||||
// qInfo(message->content().toStdString().c_str());
|
auto c = ChannelManager::getInstance().getChannel(message->target().mid(1));
|
||||||
|
|
||||||
auto c = Channels::getChannel(message->target().mid(1));
|
|
||||||
|
|
||||||
if (c != NULL) {
|
if (c != NULL) {
|
||||||
c->addMessage(std::shared_ptr<messages::Message>(
|
messages::MessageParseArgs args;
|
||||||
new messages::Message(*message, *c)));
|
|
||||||
|
c->addMessage(twitch::TwitchMessageBuilder::parse(message, c.get(), args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool IrcManager::isTwitchBlockedUser(QString const &username)
|
||||||
IrcManager::isTwitchBlockedUser(QString const &username)
|
|
||||||
{
|
{
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
QMutexLocker locker(&_twitchBlockedUsersMutex);
|
||||||
|
|
||||||
auto iterator = IrcManager::twitchBlockedUsers.find(username);
|
auto iterator = _twitchBlockedUsers.find(username);
|
||||||
|
|
||||||
if (iterator == IrcManager::twitchBlockedUsers.end()) {
|
return iterator != _twitchBlockedUsers.end();
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
|
||||||
IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
|
|
||||||
{
|
{
|
||||||
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() +
|
QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
|
||||||
"/blocks/" + username +
|
username + "?oauth_token=" + _account.getOAuthToken() +
|
||||||
"?oauth_token=" + account->getOauthToken() +
|
"&client_id=" + _account.getOAuthClient());
|
||||||
"&client_id=" + account->getOauthClient());
|
|
||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
auto reply = IrcManager::accessManager.put(request, QByteArray());
|
auto reply = _accessManager.put(request, QByteArray());
|
||||||
reply->waitForReadyRead(10000);
|
reply->waitForReadyRead(10000);
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
_twitchBlockedUsersMutex.lock();
|
||||||
IrcManager::twitchBlockedUsers.insert(username, true);
|
_twitchBlockedUsers.insert(username, true);
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
_twitchBlockedUsersMutex.unlock();
|
||||||
|
|
||||||
delete reply;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage = "Error while ignoring user \"" + username +
|
reply->deleteLater();
|
||||||
"\": " + reply->errorString();
|
|
||||||
|
errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::addIgnoredUser(QString const &username)
|
||||||
IrcManager::addIgnoredUser(QString const &username)
|
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!tryAddIgnoredUser(username, errorMessage)) {
|
if (!tryAddIgnoredUser(username, errorMessage)) {
|
||||||
|
@ -275,38 +270,40 @@ IrcManager::addIgnoredUser(QString const &username)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
|
||||||
IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
|
|
||||||
{
|
{
|
||||||
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() +
|
QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
|
||||||
"/blocks/" + username +
|
username + "?oauth_token=" + _account.getOAuthToken() +
|
||||||
"?oauth_token=" + account->getOauthToken() +
|
"&client_id=" + _account.getOAuthClient());
|
||||||
"&client_id=" + account->getOauthClient());
|
|
||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
auto reply = IrcManager::accessManager.deleteResource(request);
|
auto reply = _accessManager.deleteResource(request);
|
||||||
reply->waitForReadyRead(10000);
|
reply->waitForReadyRead(10000);
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
IrcManager::twitchBlockedUsersMutex.lock();
|
_twitchBlockedUsersMutex.lock();
|
||||||
IrcManager::twitchBlockedUsers.remove(username);
|
_twitchBlockedUsers.remove(username);
|
||||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
_twitchBlockedUsersMutex.unlock();
|
||||||
|
|
||||||
delete reply;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage = "Error while unignoring user \"" + username +
|
reply->deleteLater();
|
||||||
"\": " + reply->errorString();
|
|
||||||
|
errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void IrcManager::removeIgnoredUser(QString const &username)
|
||||||
IrcManager::removeIgnoredUser(QString const &username)
|
|
||||||
{
|
{
|
||||||
QString errorMessage;
|
QString errorMessage;
|
||||||
if (!tryRemoveIgnoredUser(username, errorMessage)) {
|
if (!tryRemoveIgnoredUser(username, errorMessage)) {
|
||||||
// TODO: Implement IrcManager::removeIgnoredUser
|
// TODO: Implement IrcManager::removeIgnoredUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QNetworkAccessManager &IrcManager::getAccessManager()
|
||||||
|
{
|
||||||
|
return _accessManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
55
ircmanager.h
55
ircmanager.h
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#define TWITCH_MAX_MESSAGELENGTH 500
|
#define TWITCH_MAX_MESSAGELENGTH 500
|
||||||
|
|
||||||
#include "account.h"
|
|
||||||
#include "messages/message.h"
|
#include "messages/message.h"
|
||||||
|
#include "twitch/twitchuser.h"
|
||||||
|
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
@ -16,50 +16,59 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class IrcManager
|
class IrcManager : public QObject
|
||||||
{
|
{
|
||||||
public:
|
Q_OBJECT
|
||||||
static void connect();
|
|
||||||
static void disconnect();
|
|
||||||
|
|
||||||
static void send(QString raw);
|
public:
|
||||||
|
static IrcManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
static const QString defaultClientId;
|
static const QString defaultClientId;
|
||||||
|
|
||||||
|
void connect();
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
void send(QString raw);
|
||||||
|
|
||||||
bool isTwitchBlockedUser(QString const &username);
|
bool isTwitchBlockedUser(QString const &username);
|
||||||
bool tryAddIgnoredUser(QString const &username, QString &errorMessage);
|
bool tryAddIgnoredUser(QString const &username, QString &errorMessage);
|
||||||
void addIgnoredUser(QString const &username);
|
void addIgnoredUser(QString const &username);
|
||||||
bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage);
|
bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage);
|
||||||
void removeIgnoredUser(QString const &username);
|
void removeIgnoredUser(QString const &username);
|
||||||
|
|
||||||
static Account *account;
|
QNetworkAccessManager &getAccessManager();
|
||||||
|
|
||||||
static QNetworkAccessManager &
|
void sendJoin(const QString &channel);
|
||||||
getAccessManager()
|
|
||||||
{
|
|
||||||
return accessManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendJoin(const QString &channel);
|
void partChannel(const QString &channel);
|
||||||
|
|
||||||
static void partChannel(const QString &channel);
|
const twitch::TwitchUser &getUser() const;
|
||||||
|
void setUser(const twitch::TwitchUser &account);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static IrcManager instance;
|
||||||
IrcManager();
|
IrcManager();
|
||||||
|
|
||||||
static QMap<QString, bool> twitchBlockedUsers;
|
// variables
|
||||||
static QMutex twitchBlockedUsersMutex;
|
twitch::TwitchUser _account;
|
||||||
|
|
||||||
static QNetworkAccessManager accessManager;
|
std::shared_ptr<Communi::IrcConnection> _connection;
|
||||||
|
QMutex _connectionMutex;
|
||||||
|
long _connectionGeneration;
|
||||||
|
|
||||||
static void beginConnecting();
|
QMap<QString, bool> _twitchBlockedUsers;
|
||||||
|
QMutex _twitchBlockedUsersMutex;
|
||||||
|
|
||||||
static std::shared_ptr<IrcConnection> connection;
|
QNetworkAccessManager _accessManager;
|
||||||
static QMutex connectionMutex;
|
|
||||||
static long connectionGeneration;
|
|
||||||
|
|
||||||
static void messageReceived(IrcMessage *message);
|
// methods
|
||||||
static void privateMessageReceived(IrcPrivateMessage *message);
|
void beginConnecting();
|
||||||
|
|
||||||
|
void messageReceived(Communi::IrcMessage *message);
|
||||||
|
void privateMessageReceived(Communi::IrcPrivateMessage *message);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
ircuser2.cpp
Normal file
33
ircuser2.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include "ircuser2.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
IrcUser2::IrcUser2(const QString &userName, const QString &nickName, const QString &realName,
|
||||||
|
const QString &password)
|
||||||
|
: _userName(userName)
|
||||||
|
, _nickName(nickName)
|
||||||
|
, _realName(realName)
|
||||||
|
, _password(password)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &IrcUser2::getUserName() const
|
||||||
|
{
|
||||||
|
return _userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &IrcUser2::getNickName() const
|
||||||
|
{
|
||||||
|
return _nickName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &IrcUser2::getRealName() const
|
||||||
|
{
|
||||||
|
return _realName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &IrcUser2::getPassword() const
|
||||||
|
{
|
||||||
|
return _password;
|
||||||
|
}
|
||||||
|
}
|
27
ircuser2.h
Normal file
27
ircuser2.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef IRCUSER_H
|
||||||
|
#define IRCUSER_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class IrcUser2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IrcUser2(const QString &userName, const QString &nickName, const QString &realName,
|
||||||
|
const QString &password);
|
||||||
|
|
||||||
|
const QString &getUserName() const;
|
||||||
|
const QString &getNickName() const;
|
||||||
|
const QString &getRealName() const;
|
||||||
|
const QString &getPassword() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _userName;
|
||||||
|
QString _nickName;
|
||||||
|
QString _realName;
|
||||||
|
QString _password;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // IRCUSER_H
|
|
@ -14,12 +14,10 @@ Channel::Channel(const QString &_channelName, const QString &_baseDirectory)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
|
|
||||||
this->fileName =
|
this->fileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
|
||||||
this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
|
|
||||||
|
|
||||||
// Open file handle to log file of current date
|
// Open file handle to log file of current date
|
||||||
this->fileHandle.setFileName(this->baseDirectory + QDir::separator() +
|
this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileName);
|
||||||
this->fileName);
|
|
||||||
|
|
||||||
this->fileHandle.open(QIODevice::Append);
|
this->fileHandle.open(QIODevice::Append);
|
||||||
this->appendLine(this->generateOpeningString(now));
|
this->appendLine(this->generateOpeningString(now));
|
||||||
|
@ -31,8 +29,7 @@ Channel::~Channel()
|
||||||
this->fileHandle.close();
|
this->fileHandle.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Channel::append(std::shared_ptr<messages::Message> message)
|
||||||
Channel::append(std::shared_ptr<messages::Message> message)
|
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
@ -47,8 +44,7 @@ Channel::append(std::shared_ptr<messages::Message> message)
|
||||||
this->appendLine(str);
|
this->appendLine(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString Channel::generateOpeningString(const QDateTime &now) const
|
||||||
Channel::generateOpeningString(const QDateTime &now) const
|
|
||||||
{
|
{
|
||||||
QString ret = QLatin1Literal("# Start logging at ");
|
QString ret = QLatin1Literal("# Start logging at ");
|
||||||
|
|
||||||
|
@ -59,8 +55,7 @@ Channel::generateOpeningString(const QDateTime &now) const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString Channel::generateClosingString(const QDateTime &now) const
|
||||||
Channel::generateClosingString(const QDateTime &now) const
|
|
||||||
{
|
{
|
||||||
QString ret = QLatin1Literal("# Stop logging at ");
|
QString ret = QLatin1Literal("# Stop logging at ");
|
||||||
|
|
||||||
|
@ -71,8 +66,7 @@ Channel::generateClosingString(const QDateTime &now) const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Channel::appendLine(const QString &line)
|
||||||
Channel::appendLine(const QString &line)
|
|
||||||
{
|
{
|
||||||
this->fileHandle.write(line.toUtf8());
|
this->fileHandle.write(line.toUtf8());
|
||||||
this->fileHandle.flush();
|
this->fileHandle.flush();
|
||||||
|
|
|
@ -15,17 +15,14 @@ namespace logging {
|
||||||
class Channel
|
class Channel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Channel(const QString &_channelName,
|
explicit Channel(const QString &_channelName, const QString &_baseDirectory);
|
||||||
const QString &_baseDirectory);
|
|
||||||
~Channel();
|
~Channel();
|
||||||
|
|
||||||
void append(std::shared_ptr<messages::Message> message);
|
void append(std::shared_ptr<messages::Message> message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString generateOpeningString(
|
QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||||
const QDateTime &now = QDateTime::currentDateTime()) const;
|
QString generateClosingString(const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||||
QString generateClosingString(
|
|
||||||
const QDateTime &now = QDateTime::currentDateTime()) const;
|
|
||||||
|
|
||||||
void appendLine(const QString &line);
|
void appendLine(const QString &line);
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,11 @@ static QString mentionsBasePath;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::weak_ptr<Channel>> channels;
|
std::unordered_map<std::string, std::weak_ptr<Channel>> channels;
|
||||||
|
|
||||||
void
|
void init()
|
||||||
init()
|
|
||||||
{
|
{
|
||||||
// Make sure all folders are properly created
|
// Make sure all folders are properly created
|
||||||
logBasePath =
|
logBasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
||||||
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
QDir::separator() + "Logs";
|
||||||
QDir::separator() + "Logs";
|
|
||||||
channelBasePath = logBasePath + QDir::separator() + "Channels";
|
channelBasePath = logBasePath + QDir::separator() + "Channels";
|
||||||
whispersBasePath = logBasePath + QDir::separator() + "Whispers";
|
whispersBasePath = logBasePath + QDir::separator() + "Whispers";
|
||||||
mentionsBasePath = logBasePath + QDir::separator() + "Mentions";
|
mentionsBasePath = logBasePath + QDir::separator() + "Mentions";
|
||||||
|
@ -55,8 +53,7 @@ init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QString &
|
static const QString &getBaseDirectory(const QString &channelName)
|
||||||
getBaseDirectory(const QString &channelName)
|
|
||||||
{
|
{
|
||||||
if (channelName == "/whispers") {
|
if (channelName == "/whispers") {
|
||||||
return whispersBasePath;
|
return whispersBasePath;
|
||||||
|
@ -67,8 +64,7 @@ getBaseDirectory(const QString &channelName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
std::shared_ptr<Channel> get(const QString &channelName)
|
||||||
get(const QString &channelName)
|
|
||||||
{
|
{
|
||||||
if (channelName.isEmpty()) {
|
if (channelName.isEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
23
main.cpp
23
main.cpp
|
@ -1,13 +1,13 @@
|
||||||
#include "channels.h"
|
#include "channelmanager.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "emojis.h"
|
#include "emojis.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "logging/loggingmanager.h"
|
#include "logging/loggingmanager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
#include "widgets/mainwindow.h"
|
#include "widgets/mainwindow.h"
|
||||||
#include "windows.h"
|
#include "windowmanager.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
@ -22,26 +22,25 @@ main(int argc, char *argv[])
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
chatterino::logging::init();
|
chatterino::logging::init();
|
||||||
chatterino::initChannels();
|
SettingsManager::getInstance().load();
|
||||||
Settings::getInstance().load();
|
|
||||||
Resources::load();
|
Resources::load();
|
||||||
Emojis::loadEmojis();
|
Emojis::loadEmojis();
|
||||||
Emotes::loadGlobalEmotes();
|
EmoteManager::getInstance().loadGlobalEmotes();
|
||||||
|
|
||||||
ColorScheme::getInstance().init();
|
ColorScheme::getInstance().init();
|
||||||
|
|
||||||
Windows::load();
|
WindowManager::load();
|
||||||
|
|
||||||
MainWindow &w = Windows::getMainWindow();
|
MainWindow &w = WindowManager::getMainWindow();
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
IrcManager::connect();
|
IrcManager::getInstance().connect();
|
||||||
|
|
||||||
int ret = a.exec();
|
int ret = a.exec();
|
||||||
|
|
||||||
Settings::getInstance().save();
|
SettingsManager::getInstance().save();
|
||||||
|
|
||||||
Windows::save();
|
WindowManager::save();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#include "messages/lazyloadedimage.h"
|
#include "messages/lazyloadedimage.h"
|
||||||
|
|
||||||
#include "asyncexec.h"
|
#include "asyncexec.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "windows.h"
|
#include "windowmanager.h"
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
|
@ -16,9 +16,8 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name,
|
||||||
const QString &name, const QString &tooltip,
|
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||||
const QMargins &margin, bool isHat)
|
|
||||||
: currentPixmap(NULL)
|
: currentPixmap(NULL)
|
||||||
, allFrames()
|
, allFrames()
|
||||||
, currentFrame(0)
|
, currentFrame(0)
|
||||||
|
@ -34,9 +33,8 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name,
|
||||||
const QString &name, const QString &tooltip,
|
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||||
const QMargins &margin, bool isHat)
|
|
||||||
: currentPixmap(image)
|
: currentPixmap(image)
|
||||||
, allFrames()
|
, allFrames()
|
||||||
, currentFrame(0)
|
, currentFrame(0)
|
||||||
|
@ -52,8 +50,7 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void LazyLoadedImage::loadImage()
|
||||||
LazyLoadedImage::loadImage()
|
|
||||||
{
|
{
|
||||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
|
||||||
|
@ -92,29 +89,25 @@ LazyLoadedImage::loadImage()
|
||||||
if (allFrames.size() > 1) {
|
if (allFrames.size() > 1) {
|
||||||
this->animated = true;
|
this->animated = true;
|
||||||
|
|
||||||
Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
EmoteManager::getInstance().getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Emotes::incGeneration();
|
EmoteManager::getInstance().incGeneration();
|
||||||
Windows::layoutVisibleChatWidgets();
|
WindowManager::layoutVisibleChatWidgets();
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
manager->deleteLater();
|
manager->deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void LazyLoadedImage::gifUpdateTimout()
|
||||||
LazyLoadedImage::gifUpdateTimout()
|
|
||||||
{
|
{
|
||||||
this->currentFrameOffset += GIF_FRAME_LENGTH;
|
this->currentFrameOffset += GIF_FRAME_LENGTH;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this->currentFrameOffset >
|
if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) {
|
||||||
this->allFrames.at(this->currentFrame).duration) {
|
this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration;
|
||||||
this->currentFrameOffset -=
|
this->currentFrame = (this->currentFrame + 1) % this->allFrames.size();
|
||||||
this->allFrames.at(this->currentFrame).duration;
|
|
||||||
this->currentFrame =
|
|
||||||
(this->currentFrame + 1) % this->allFrames.size();
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,14 @@ namespace messages {
|
||||||
class LazyLoadedImage : QObject
|
class LazyLoadedImage : QObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit LazyLoadedImage(const QString &url, qreal scale = 1,
|
explicit LazyLoadedImage(const QString &url, qreal scale = 1, const QString &name = "",
|
||||||
const QString &name = "",
|
const QString &tooltip = "", const QMargins &margin = QMargins(),
|
||||||
const QString &tooltip = "",
|
|
||||||
const QMargins &margin = QMargins(),
|
|
||||||
bool isHat = false);
|
bool isHat = false);
|
||||||
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1,
|
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, const QString &name = "",
|
||||||
const QString &name = "",
|
const QString &tooltip = "", const QMargins &margin = QMargins(),
|
||||||
const QString &tooltip = "",
|
|
||||||
const QMargins &margin = QMargins(),
|
|
||||||
bool isHat = false);
|
bool isHat = false);
|
||||||
|
|
||||||
const QPixmap *
|
const QPixmap *getPixmap()
|
||||||
getPixmap()
|
|
||||||
{
|
{
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
@ -32,50 +27,42 @@ public:
|
||||||
return currentPixmap;
|
return currentPixmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal
|
qreal getScale() const
|
||||||
getScale() const
|
|
||||||
{
|
{
|
||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &
|
const QString &getUrl() const
|
||||||
getUrl() const
|
|
||||||
{
|
{
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &
|
const QString &getName() const
|
||||||
getName() const
|
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &
|
const QString &getTooltip() const
|
||||||
getTooltip() const
|
|
||||||
{
|
{
|
||||||
return tooltip;
|
return tooltip;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QMargins &
|
const QMargins &getMargin() const
|
||||||
getMargin() const
|
|
||||||
{
|
{
|
||||||
return margin;
|
return margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool getAnimated() const
|
||||||
getAnimated() const
|
|
||||||
{
|
{
|
||||||
return animated;
|
return animated;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool getIsHat() const
|
||||||
getIsHat() const
|
|
||||||
{
|
{
|
||||||
return ishat;
|
return ishat;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int getWidth() const
|
||||||
getWidth() const
|
|
||||||
{
|
{
|
||||||
if (currentPixmap == NULL) {
|
if (currentPixmap == NULL) {
|
||||||
return 16;
|
return 16;
|
||||||
|
@ -83,8 +70,7 @@ public:
|
||||||
return currentPixmap->width();
|
return currentPixmap->width();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int getHeight() const
|
||||||
getHeight() const
|
|
||||||
{
|
{
|
||||||
if (currentPixmap == NULL) {
|
if (currentPixmap == NULL) {
|
||||||
return 16;
|
return 16;
|
||||||
|
|
|
@ -24,8 +24,7 @@ public:
|
||||||
vector->reserve(this->limit + this->buffer);
|
vector->reserve(this->limit + this->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void clear()
|
||||||
clear()
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
|
@ -37,8 +36,7 @@ public:
|
||||||
|
|
||||||
// return true if an item was deleted
|
// return true if an item was deleted
|
||||||
// deleted will be set if the item was deleted
|
// deleted will be set if the item was deleted
|
||||||
bool
|
bool appendItem(const T &item, T &deleted)
|
||||||
appendItem(const T &item, T &deleted)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
|
@ -63,7 +61,7 @@ public:
|
||||||
} else {
|
} else {
|
||||||
deleted = this->vector->at(this->offset);
|
deleted = this->vector->at(this->offset);
|
||||||
|
|
||||||
//append item and increment offset("deleting" first element)
|
// append item and increment offset("deleting" first element)
|
||||||
this->vector->push_back(item);
|
this->vector->push_back(item);
|
||||||
this->offset++;
|
this->offset++;
|
||||||
|
|
||||||
|
@ -77,12 +75,11 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messages::LimitedQueueSnapshot<T>
|
messages::LimitedQueueSnapshot<T> getSnapshot()
|
||||||
getSnapshot()
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex);
|
std::lock_guard<std::mutex> lock(this->mutex);
|
||||||
|
|
||||||
if(vector->size() < limit) {
|
if (vector->size() < limit) {
|
||||||
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size());
|
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size());
|
||||||
} else {
|
} else {
|
||||||
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit);
|
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit);
|
||||||
|
|
|
@ -11,16 +11,14 @@ template <typename T>
|
||||||
class LimitedQueueSnapshot
|
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)
|
|
||||||
: vector(ptr)
|
: vector(ptr)
|
||||||
, offset(offset)
|
, offset(offset)
|
||||||
, length(length)
|
, length(length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int getLength()
|
||||||
getLength()
|
|
||||||
{
|
{
|
||||||
return this->length;
|
return this->length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,20 +23,17 @@ public:
|
||||||
Link();
|
Link();
|
||||||
Link(Type getType, const QString &getValue);
|
Link(Type getType, const QString &getValue);
|
||||||
|
|
||||||
bool
|
bool getIsValid() const
|
||||||
getIsValid() const
|
|
||||||
{
|
{
|
||||||
return type == None;
|
return type == None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type
|
Type getType() const
|
||||||
getType() const
|
|
||||||
{
|
{
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &
|
const QString &getValue() const
|
||||||
getValue() const
|
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "emojis.h"
|
#include "emojis.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "fonts.h"
|
#include "fontmanager.h"
|
||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "messages/link.h"
|
#include "messages/link.h"
|
||||||
#include "qcolor.h"
|
#include "qcolor.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
|
|
||||||
#include <QObjectUserData>
|
#include <QObjectUserData>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
@ -19,396 +19,66 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
QRegularExpression *Message::cheerRegex =
|
|
||||||
new QRegularExpression("cheer[1-9][0-9]*");
|
|
||||||
|
|
||||||
Message::Message(const QString &text)
|
Message::Message(const QString &text)
|
||||||
|
: _words()
|
||||||
{
|
{
|
||||||
words.push_back(Word(text, Word::Text,
|
_words.push_back(
|
||||||
ColorScheme::getInstance().SystemMessageColor, text,
|
Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString()));
|
||||||
QString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel,
|
Message::Message(const std::vector<Word> &words)
|
||||||
bool enablePingSound, bool isReceivedWhisper,
|
: _words(words)
|
||||||
bool isSentWhisper, bool includeChannel)
|
|
||||||
: content(ircMessage.content())
|
|
||||||
{
|
{
|
||||||
this->parseTime = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
auto words = std::vector<Word>();
|
|
||||||
|
|
||||||
auto tags = ircMessage.tags();
|
|
||||||
|
|
||||||
auto iterator = tags.find("id");
|
|
||||||
|
|
||||||
if (iterator != tags.end()) {
|
|
||||||
this->id = iterator.value().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// timestamps
|
|
||||||
iterator = tags.find("tmi-sent-ts");
|
|
||||||
|
|
||||||
std::time_t time = std::time(NULL);
|
|
||||||
|
|
||||||
// if (iterator != tags.end()) {
|
|
||||||
// time = strtoll(iterator.value().toString().toStdString().c_str(),
|
|
||||||
// NULL,
|
|
||||||
// 10);
|
|
||||||
// }
|
|
||||||
|
|
||||||
char timeStampBuffer[69];
|
|
||||||
|
|
||||||
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
|
|
||||||
QString timestamp = QString(timeStampBuffer);
|
|
||||||
|
|
||||||
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
|
|
||||||
QString timestampWithSeconds = QString(timeStampBuffer);
|
|
||||||
|
|
||||||
words.push_back(Word(timestamp, Word::TimestampNoSeconds,
|
|
||||||
ColorScheme::getInstance().SystemMessageColor,
|
|
||||||
QString(), QString()));
|
|
||||||
words.push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds,
|
|
||||||
ColorScheme::getInstance().SystemMessageColor,
|
|
||||||
QString(), QString()));
|
|
||||||
|
|
||||||
// mod buttons
|
|
||||||
static QString buttonBanTooltip("Ban user");
|
|
||||||
static QString buttonTimeoutTooltip("Timeout user");
|
|
||||||
|
|
||||||
words.push_back(Word(Resources::getButtonBan(), Word::ButtonBan, QString(),
|
|
||||||
buttonBanTooltip,
|
|
||||||
Link(Link::UserBan, ircMessage.account())));
|
|
||||||
words.push_back(Word(Resources::getButtonTimeout(), Word::ButtonTimeout,
|
|
||||||
QString(), buttonTimeoutTooltip,
|
|
||||||
Link(Link::UserTimeout, ircMessage.account())));
|
|
||||||
|
|
||||||
// badges
|
|
||||||
iterator = tags.find("badges");
|
|
||||||
|
|
||||||
if (iterator != tags.end()) {
|
|
||||||
auto badges = iterator.value().toString().split(',');
|
|
||||||
|
|
||||||
for (QString badge : badges) {
|
|
||||||
if (badge.startsWith("bits/")) {
|
|
||||||
long long int cheer =
|
|
||||||
strtoll(badge.mid(5).toStdString().c_str(), NULL, 10);
|
|
||||||
words.push_back(Word(
|
|
||||||
Emotes::getCheerBadge(cheer), Word::BadgeCheer, QString(),
|
|
||||||
QString("Twitch Cheer" + QString::number(cheer))));
|
|
||||||
} else if (badge == "staff/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgeStaff(),
|
|
||||||
Word::BadgeStaff, QString(),
|
|
||||||
QString("Twitch Staff")));
|
|
||||||
} else if (badge == "admin/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgeAdmin(),
|
|
||||||
Word::BadgeAdmin, QString(),
|
|
||||||
QString("Twitch Admin")));
|
|
||||||
} else if (badge == "global_mod/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgeGlobalmod(),
|
|
||||||
Word::BadgeGlobalMod, QString(),
|
|
||||||
QString("Global Moderator")));
|
|
||||||
} else if (badge == "moderator/1") {
|
|
||||||
// TODO: implement this xD
|
|
||||||
words.push_back(Word(
|
|
||||||
Resources::getBadgeTurbo(), Word::BadgeModerator, QString(),
|
|
||||||
QString("Channel Moderator"))); // custom badge
|
|
||||||
} else if (badge == "turbo/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgeStaff(),
|
|
||||||
Word::BadgeTurbo, QString(),
|
|
||||||
QString("Turbo Subscriber")));
|
|
||||||
} else if (badge == "broadcaster/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgeBroadcaster(),
|
|
||||||
Word::BadgeBroadcaster, QString(),
|
|
||||||
QString("Channel Broadcaster")));
|
|
||||||
} else if (badge == "premium/1") {
|
|
||||||
words.push_back(Word(Resources::getBadgePremium(),
|
|
||||||
Word::BadgePremium, QString(),
|
|
||||||
QString("Twitch Prime")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// color
|
|
||||||
QColor usernameColor = ColorScheme::getInstance().SystemMessageColor;
|
|
||||||
|
|
||||||
iterator = tags.find("color");
|
|
||||||
if (iterator != tags.end()) {
|
|
||||||
usernameColor = QColor(iterator.value().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// channel name
|
|
||||||
if (includeChannel) {
|
|
||||||
QString channelName("#" + channel.getName());
|
|
||||||
words.push_back(Word(
|
|
||||||
channelName, Word::Misc,
|
|
||||||
ColorScheme::getInstance().SystemMessageColor, QString(channelName),
|
|
||||||
QString(), Link(Link::Url, channel.getName() + "\n" + this->id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// username
|
|
||||||
this->userName = ircMessage.nick();
|
|
||||||
|
|
||||||
if (this->userName.isEmpty()) {
|
|
||||||
this->userName = tags.value(QLatin1String("login")).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString displayName;
|
|
||||||
|
|
||||||
iterator = tags.find("display-name");
|
|
||||||
if (iterator == tags.end()) {
|
|
||||||
displayName = ircMessage.account();
|
|
||||||
} else {
|
|
||||||
displayName = iterator.value().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasLocalizedName =
|
|
||||||
QString::compare(displayName, ircMessage.account()) == 0;
|
|
||||||
QString userDisplayString =
|
|
||||||
displayName +
|
|
||||||
(hasLocalizedName ? (" (" + ircMessage.account() + ")") : QString());
|
|
||||||
|
|
||||||
if (isSentWhisper) {
|
|
||||||
userDisplayString = IrcManager::account->getUsername() + " -> ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isReceivedWhisper) {
|
|
||||||
userDisplayString += " -> " + IrcManager::account->getUsername();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ircMessage.isAction()) {
|
|
||||||
userDisplayString += ": ";
|
|
||||||
}
|
|
||||||
|
|
||||||
words.push_back(Word(userDisplayString, Word::Username, usernameColor,
|
|
||||||
userDisplayString, QString()));
|
|
||||||
|
|
||||||
// highlights
|
|
||||||
// TODO: implement this xD
|
|
||||||
|
|
||||||
// bits
|
|
||||||
QString bits = "";
|
|
||||||
|
|
||||||
iterator = tags.find("bits");
|
|
||||||
if (iterator != tags.end()) {
|
|
||||||
bits = iterator.value().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// twitch emotes
|
|
||||||
std::vector<std::pair<long int, LazyLoadedImage *>> twitchEmotes;
|
|
||||||
|
|
||||||
iterator = tags.find("emotes");
|
|
||||||
|
|
||||||
if (iterator != tags.end()) {
|
|
||||||
auto emotes = iterator.value().toString().split('/');
|
|
||||||
|
|
||||||
for (QString emote : emotes) {
|
|
||||||
if (!emote.contains(':'))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QStringList parameters = emote.split(':');
|
|
||||||
|
|
||||||
if (parameters.length() < 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
long int id = std::stol(parameters.at(0).toStdString(), NULL, 10);
|
|
||||||
|
|
||||||
QStringList occurences = parameters.at(1).split(',');
|
|
||||||
|
|
||||||
for (QString occurence : occurences) {
|
|
||||||
QStringList coords = occurence.split('-');
|
|
||||||
|
|
||||||
if (coords.length() < 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
long int start =
|
|
||||||
std::stol(coords.at(0).toStdString(), NULL, 10);
|
|
||||||
long int end = std::stol(coords.at(1).toStdString(), NULL, 10);
|
|
||||||
|
|
||||||
if (start >= end || start < 0 ||
|
|
||||||
end > ircMessage.content().length())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QString name = ircMessage.content().mid(start, end - start + 1);
|
|
||||||
|
|
||||||
twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
|
|
||||||
start, Emotes::getTwitchEmoteById(name, id)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(twitchEmotes.begin(), twitchEmotes.end(), sortTwitchEmotes);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto currentTwitchEmote = twitchEmotes.begin();
|
|
||||||
|
|
||||||
// words
|
|
||||||
QColor textColor =
|
|
||||||
ircMessage.isAction() ? usernameColor : ColorScheme::getInstance().Text;
|
|
||||||
|
|
||||||
QStringList splits = ircMessage.content().split(' ');
|
|
||||||
|
|
||||||
long int i = 0;
|
|
||||||
|
|
||||||
for (QString split : splits) {
|
|
||||||
// twitch emote
|
|
||||||
if (currentTwitchEmote != twitchEmotes.end() &&
|
|
||||||
currentTwitchEmote->first == i) {
|
|
||||||
words.push_back(Word(currentTwitchEmote->second,
|
|
||||||
Word::TwitchEmoteImage,
|
|
||||||
currentTwitchEmote->second->getName(),
|
|
||||||
currentTwitchEmote->second->getName() +
|
|
||||||
QString("\nTwitch Emote")));
|
|
||||||
words.push_back(Word(currentTwitchEmote->second->getName(),
|
|
||||||
Word::TwitchEmoteText, textColor,
|
|
||||||
currentTwitchEmote->second->getName(),
|
|
||||||
currentTwitchEmote->second->getName() +
|
|
||||||
QString("\nTwitch Emote")));
|
|
||||||
|
|
||||||
i += split.length() + 1;
|
|
||||||
currentTwitchEmote = std::next(currentTwitchEmote);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// split words
|
|
||||||
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
|
|
||||||
|
|
||||||
Emojis::parseEmojis(parsed, split);
|
|
||||||
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
|
|
||||||
LazyLoadedImage *image = std::get<0>(tuple);
|
|
||||||
|
|
||||||
if (image == NULL) { // is text
|
|
||||||
QString string = std::get<1>(tuple);
|
|
||||||
|
|
||||||
// cheers
|
|
||||||
if (!bits.isEmpty() && string.length() >= 6 &&
|
|
||||||
cheerRegex->match(string).isValid()) {
|
|
||||||
auto cheer = string.mid(5).toInt();
|
|
||||||
|
|
||||||
QString color;
|
|
||||||
|
|
||||||
QColor bitsColor;
|
|
||||||
|
|
||||||
if (cheer >= 10000) {
|
|
||||||
color = "red";
|
|
||||||
bitsColor = QColor::fromHslF(0, 1, 0.5);
|
|
||||||
} else if (cheer >= 5000) {
|
|
||||||
color = "blue";
|
|
||||||
bitsColor = QColor::fromHslF(0.61, 1, 0.4);
|
|
||||||
} else if (cheer >= 1000) {
|
|
||||||
color = "green";
|
|
||||||
bitsColor = QColor::fromHslF(0.5, 1, 0.5);
|
|
||||||
} else if (cheer >= 100) {
|
|
||||||
color = "purple";
|
|
||||||
bitsColor = QColor::fromHslF(0.8, 1, 0.5);
|
|
||||||
} else {
|
|
||||||
color = "gray";
|
|
||||||
bitsColor = QColor::fromHslF(0.5f, 0.5f, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString bitsLinkAnimated = QString(
|
|
||||||
"http://static-cdn.jtvnw.net/bits/dark/animated/" +
|
|
||||||
color + "/1");
|
|
||||||
QString bitsLink = QString(
|
|
||||||
"http://static-cdn.jtvnw.net/bits/dark/static/" +
|
|
||||||
color + "/1");
|
|
||||||
|
|
||||||
LazyLoadedImage *imageAnimated =
|
|
||||||
Emotes::getMiscImageFromCache().getOrAdd(
|
|
||||||
bitsLinkAnimated, [&bitsLinkAnimated] {
|
|
||||||
return new LazyLoadedImage(bitsLinkAnimated);
|
|
||||||
});
|
|
||||||
LazyLoadedImage *image =
|
|
||||||
Emotes::getMiscImageFromCache().getOrAdd(
|
|
||||||
bitsLink, [&bitsLink] {
|
|
||||||
return new LazyLoadedImage(bitsLink);
|
|
||||||
});
|
|
||||||
|
|
||||||
words.push_back(
|
|
||||||
Word(imageAnimated, Word::BitsAnimated,
|
|
||||||
QString("cheer"), QString("Twitch Cheer"),
|
|
||||||
Link(Link::Url,
|
|
||||||
QString("https://blog.twitch.tv/"
|
|
||||||
"introducing-cheering-celebrate-"
|
|
||||||
"together-da62af41fac6"))));
|
|
||||||
words.push_back(
|
|
||||||
Word(image, Word::BitsStatic, QString("cheer"),
|
|
||||||
QString("Twitch Cheer"),
|
|
||||||
Link(Link::Url,
|
|
||||||
QString("https://blog.twitch.tv/"
|
|
||||||
"introducing-cheering-celebrate-"
|
|
||||||
"together-da62af41fac6"))));
|
|
||||||
|
|
||||||
words.push_back(
|
|
||||||
Word(QString("x" + string.mid(5)), Word::BitsAmount,
|
|
||||||
bitsColor, QString(string.mid(5)),
|
|
||||||
QString("Twitch Cheer"),
|
|
||||||
Link(Link::Url,
|
|
||||||
QString("https://blog.twitch.tv/"
|
|
||||||
"introducing-cheering-celebrate-"
|
|
||||||
"together-da62af41fac6"))));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bttv / ffz emotes
|
|
||||||
LazyLoadedImage *bttvEmote;
|
|
||||||
|
|
||||||
// TODO: Implement this (ignored emotes)
|
|
||||||
if (Emotes::getBttvEmotes().tryGet(string, bttvEmote) ||
|
|
||||||
channel.getBttvChannelEmotes().tryGet(string, bttvEmote) ||
|
|
||||||
Emotes::getFfzEmotes().tryGet(string, bttvEmote) ||
|
|
||||||
channel.getFfzChannelEmotes().tryGet(string, bttvEmote) ||
|
|
||||||
Emotes::getChatterinoEmotes().tryGet(string, bttvEmote)) {
|
|
||||||
words.push_back(Word(bttvEmote, Word::BttvEmoteImage,
|
|
||||||
bttvEmote->getName(),
|
|
||||||
bttvEmote->getTooltip(),
|
|
||||||
Link(Link::Url, bttvEmote->getUrl())));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// actually just a word
|
|
||||||
QString link = matchLink(string);
|
|
||||||
|
|
||||||
words.push_back(
|
|
||||||
Word(string, Word::Text, textColor, string, QString(),
|
|
||||||
link.isEmpty() ? Link() : Link(Link::Url, link)));
|
|
||||||
} else { // is emoji
|
|
||||||
static QString emojiTooltip("Emoji");
|
|
||||||
|
|
||||||
words.push_back(Word(image, Word::EmojiImage, image->getName(),
|
|
||||||
emojiTooltip));
|
|
||||||
Word(image->getName(), Word::EmojiText, textColor,
|
|
||||||
image->getName(), emojiTooltip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i += split.length() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->words = words;
|
|
||||||
|
|
||||||
// TODO: Implement this xD
|
|
||||||
// if (!isReceivedWhisper &&
|
|
||||||
// AppSettings.HighlightIgnoredUsers.ContainsKey(Username))
|
|
||||||
// {
|
|
||||||
// HighlightTab = false;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
bool Message::getCanHighlightTab() const
|
||||||
Message::matchLink(const QString &string)
|
|
||||||
{
|
{
|
||||||
// TODO: Implement this xD
|
return _highlightTab;
|
||||||
return QString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
const QString &Message::getTimeoutUser() const
|
||||||
Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage *> &a,
|
|
||||||
const std::pair<long int, LazyLoadedImage *> &b)
|
|
||||||
{
|
{
|
||||||
return a.first < b.first;
|
return _timeoutUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Message::getTimeoutCount() const
|
||||||
|
{
|
||||||
|
return _timeoutCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Message::getUserName() const
|
||||||
|
{
|
||||||
|
return _userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Message::getDisplayName() const
|
||||||
|
{
|
||||||
|
return _displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Message::getContent() const
|
||||||
|
{
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::chrono::time_point<std::chrono::system_clock> &Message::getParseTime() const
|
||||||
|
{
|
||||||
|
return _parseTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Word> &Message::getWords()
|
||||||
|
{
|
||||||
|
return _words;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Message::isDisabled() const
|
||||||
|
{
|
||||||
|
return _isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Message::getId() const
|
||||||
|
{
|
||||||
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef MESSAGE_H
|
#ifndef MESSAGE_H
|
||||||
#define MESSAGE_H
|
#define MESSAGE_H
|
||||||
|
|
||||||
|
#include "messages/message.h"
|
||||||
|
#include "messages/messageparseargs.h"
|
||||||
#include "messages/word.h"
|
#include "messages/word.h"
|
||||||
#include "messages/wordpart.h"
|
#include "messages/wordpart.h"
|
||||||
|
|
||||||
|
@ -8,84 +10,33 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Channel;
|
class Channel;
|
||||||
|
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
class Message;
|
||||||
|
|
||||||
|
typedef std::shared_ptr<Message> SharedMessage;
|
||||||
|
|
||||||
class Message
|
class Message
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Message(const QString &text);
|
Message(const QString &text);
|
||||||
Message(const IrcPrivateMessage &ircMessage, Channel &channel,
|
Message(const std::vector<messages::Word> &words);
|
||||||
bool enablePingSound = true, bool isReceivedWhisper = false,
|
|
||||||
bool isSentWhisper = false, bool includeChannel = false);
|
|
||||||
|
|
||||||
~Message()
|
bool getCanHighlightTab() const;
|
||||||
{
|
const QString &getTimeoutUser() const;
|
||||||
}
|
int getTimeoutCount() const;
|
||||||
|
const QString &getUserName() const;
|
||||||
bool
|
const QString &getDisplayName() const;
|
||||||
getCanHighlightTab() const
|
const QString &getContent() const;
|
||||||
{
|
const std::chrono::time_point<std::chrono::system_clock> &getParseTime() const;
|
||||||
return highlightTab;
|
std::vector<Word> &getWords();
|
||||||
}
|
bool isDisabled() const;
|
||||||
|
const QString &getId() const;
|
||||||
const QString &
|
|
||||||
getTimeoutUser() const
|
|
||||||
{
|
|
||||||
return timeoutUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getTimeoutCount() const
|
|
||||||
{
|
|
||||||
return timeoutCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getUserName() const
|
|
||||||
{
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getDisplayName() const
|
|
||||||
{
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const QString &
|
|
||||||
getContent() const
|
|
||||||
{
|
|
||||||
return this->content;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const std::chrono::time_point<std::chrono::system_clock> &
|
|
||||||
getParseTime() const
|
|
||||||
{
|
|
||||||
return this->parseTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Word> &
|
|
||||||
getWords()
|
|
||||||
{
|
|
||||||
return words;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
getDisabled() const
|
|
||||||
{
|
|
||||||
return disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getId() const
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static LazyLoadedImage *badgeStaff;
|
static LazyLoadedImage *badgeStaff;
|
||||||
|
@ -98,24 +49,18 @@ private:
|
||||||
|
|
||||||
static QRegularExpression *cheerRegex;
|
static QRegularExpression *cheerRegex;
|
||||||
|
|
||||||
bool highlightTab = false;
|
bool _highlightTab = false;
|
||||||
QString timeoutUser = "";
|
QString _timeoutUser = "";
|
||||||
int timeoutCount = 0;
|
int _timeoutCount = 0;
|
||||||
bool disabled = false;
|
bool _isDisabled = false;
|
||||||
std::chrono::time_point<std::chrono::system_clock> parseTime;
|
std::chrono::time_point<std::chrono::system_clock> _parseTime;
|
||||||
|
|
||||||
QString userName = "";
|
QString _userName = "";
|
||||||
QString displayName = "";
|
QString _displayName = "";
|
||||||
QString content;
|
QString _content;
|
||||||
QString id = "";
|
QString _id = "";
|
||||||
|
|
||||||
std::vector<Word> words;
|
std::vector<Word> _words;
|
||||||
|
|
||||||
static QString matchLink(const QString &string);
|
|
||||||
|
|
||||||
static bool sortTwitchEmotes(
|
|
||||||
const std::pair<long int, LazyLoadedImage *> &a,
|
|
||||||
const std::pair<long int, LazyLoadedImage *> &b);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|
54
messages/messagebuilder.cpp
Normal file
54
messages/messagebuilder.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "messagebuilder.h"
|
||||||
|
#include "colorscheme.h"
|
||||||
|
#include "emotemanager.h"
|
||||||
|
#include "resources.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace messages {
|
||||||
|
|
||||||
|
MessageBuilder::MessageBuilder()
|
||||||
|
: _words()
|
||||||
|
{
|
||||||
|
_parseTime = std::chrono::system_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMessage MessageBuilder::build()
|
||||||
|
{
|
||||||
|
return SharedMessage(new Message(_words));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::appendWord(const Word &word)
|
||||||
|
{
|
||||||
|
_words.push_back(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::appendTimestamp()
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
time(&t);
|
||||||
|
appendTimestamp(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::appendTimestamp(time_t time)
|
||||||
|
{
|
||||||
|
char timeStampBuffer[69];
|
||||||
|
|
||||||
|
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
|
||||||
|
QString timestamp = QString(timeStampBuffer);
|
||||||
|
|
||||||
|
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
|
||||||
|
QString timestampWithSeconds = QString(timeStampBuffer);
|
||||||
|
|
||||||
|
appendWord(Word(timestamp, Word::TimestampNoSeconds,
|
||||||
|
ColorScheme::getInstance().SystemMessageColor, QString(), QString()));
|
||||||
|
appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds,
|
||||||
|
ColorScheme::getInstance().SystemMessageColor, QString(), QString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageBuilder::matchLink(const QString &string)
|
||||||
|
{
|
||||||
|
// TODO: Implement this xD
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
messages/messagebuilder.h
Normal file
29
messages/messagebuilder.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef MESSAGEBUILDER_H
|
||||||
|
#define MESSAGEBUILDER_H
|
||||||
|
|
||||||
|
#include "messages/message.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace messages {
|
||||||
|
|
||||||
|
class MessageBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageBuilder();
|
||||||
|
|
||||||
|
SharedMessage build();
|
||||||
|
|
||||||
|
void appendWord(const Word &word);
|
||||||
|
void appendTimestamp();
|
||||||
|
void appendTimestamp(std::time_t time);
|
||||||
|
|
||||||
|
QString matchLink(const QString &string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Word> _words;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> _parseTime;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MESSAGEBUILDER_H
|
17
messages/messageparseargs.h
Normal file
17
messages/messageparseargs.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef MESSAGEPARSEARGS_H
|
||||||
|
#define MESSAGEPARSEARGS_H
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace messages {
|
||||||
|
|
||||||
|
struct MessageParseArgs {
|
||||||
|
public:
|
||||||
|
bool disablePingSoungs = false;
|
||||||
|
bool isReceivedWhisper = false;
|
||||||
|
bool isSentWhisper = false;
|
||||||
|
bool includeChannelName = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MESSAGEPARSEARGS_H
|
|
@ -1,6 +1,6 @@
|
||||||
#include "messageref.h"
|
#include "messageref.h"
|
||||||
#include "emotes.h"
|
#include "emotemanager.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
@ -9,82 +9,87 @@
|
||||||
#define MARGIN_TOP 8
|
#define MARGIN_TOP 8
|
||||||
#define MARGIN_BOTTOM 8
|
#define MARGIN_BOTTOM 8
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
MessageRef::MessageRef(std::shared_ptr<Message> message)
|
MessageRef::MessageRef(SharedMessage message)
|
||||||
: message(message.get())
|
: _message(message)
|
||||||
, messagePtr(message)
|
, _wordParts()
|
||||||
, wordParts()
|
|
||||||
, buffer()
|
, buffer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
Message *MessageRef::getMessage()
|
||||||
MessageRef::layout(int width, bool enableEmoteMargins)
|
|
||||||
{
|
{
|
||||||
auto &settings = Settings::getInstance();
|
return _message.get();
|
||||||
|
}
|
||||||
|
|
||||||
bool sizeChanged = width != this->currentLayoutWidth;
|
int MessageRef::getHeight() const
|
||||||
bool redraw = width != this->currentLayoutWidth;
|
{
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
|
{
|
||||||
|
auto &settings = SettingsManager::getInstance();
|
||||||
|
|
||||||
|
bool sizeChanged = width != _currentLayoutWidth;
|
||||||
|
bool redraw = width != _currentLayoutWidth;
|
||||||
int spaceWidth = 4;
|
int spaceWidth = 4;
|
||||||
|
|
||||||
{
|
int mediumTextLineHeight =
|
||||||
int mediumTextLineHeight =
|
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
||||||
Fonts::getFontMetrics(Fonts::Medium).height();
|
|
||||||
|
|
||||||
bool recalculateImages =
|
bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration();
|
||||||
this->emoteGeneration != Emotes::getGeneration();
|
bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration();
|
||||||
bool recalculateText = this->fontGeneration != Fonts::getGeneration();
|
bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
||||||
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 || newWordTypes) {
|
// calculate word sizes
|
||||||
this->emoteGeneration = Emotes::getGeneration();
|
if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
|
||||||
this->fontGeneration = Fonts::getGeneration();
|
|
||||||
|
|
||||||
redraw = true;
|
|
||||||
|
|
||||||
for (auto &word : this->message->getWords()) {
|
|
||||||
if (word.isImage()) {
|
|
||||||
if (recalculateImages) {
|
|
||||||
auto &image = word.getImage();
|
|
||||||
|
|
||||||
qreal w = image.getWidth();
|
|
||||||
qreal h = image.getHeight();
|
|
||||||
|
|
||||||
if (scaleEmotesByLineHeight) {
|
|
||||||
word.setSize(
|
|
||||||
w * mediumTextLineHeight / h * emoteScale,
|
|
||||||
mediumTextLineHeight * emoteScale);
|
|
||||||
} else {
|
|
||||||
word.setSize(w * image.getScale() * emoteScale,
|
|
||||||
h * image.getScale() * emoteScale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (recalculateText) {
|
|
||||||
QFontMetrics &metrics = word.getFontMetrics();
|
|
||||||
word.setSize(metrics.width(word.getText()),
|
|
||||||
metrics.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newWordTypes) {
|
|
||||||
this->currentWordTypes = Settings::getInstance().getWordTypeMask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!redraw) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->currentLayoutWidth = width;
|
_emoteGeneration = EmoteManager::getInstance().getGeneration();
|
||||||
|
_fontGeneration = FontManager::getInstance().getGeneration();
|
||||||
|
|
||||||
|
for (auto &word : _message->getWords()) {
|
||||||
|
if (word.isImage()) {
|
||||||
|
if (!recalculateImages) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &image = word.getImage();
|
||||||
|
|
||||||
|
qreal w = image.getWidth();
|
||||||
|
qreal h = image.getHeight();
|
||||||
|
|
||||||
|
if (scaleEmotesByLineHeight) {
|
||||||
|
word.setSize(w * mediumTextLineHeight / h * emoteScale,
|
||||||
|
mediumTextLineHeight * emoteScale);
|
||||||
|
} else {
|
||||||
|
word.setSize(w * image.getScale() * emoteScale, h * image.getScale() * emoteScale);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!recalculateText) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFontMetrics &metrics = word.getFontMetrics();
|
||||||
|
word.setSize(metrics.width(word.getText()), metrics.height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWordTypes) {
|
||||||
|
_currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
// layout
|
||||||
|
_currentLayoutWidth = width;
|
||||||
|
|
||||||
int x = MARGIN_LEFT;
|
int x = MARGIN_LEFT;
|
||||||
int y = MARGIN_TOP;
|
int y = MARGIN_TOP;
|
||||||
|
@ -96,12 +101,11 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
int lineHeight = 0;
|
int lineHeight = 0;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
this->wordParts.clear();
|
_wordParts.clear();
|
||||||
|
|
||||||
uint32_t flags = Settings::getInstance().getWordTypeMask();
|
uint32_t flags = SettingsManager::getInstance().getWordTypeMask();
|
||||||
|
|
||||||
for (auto it = this->message->getWords().begin();
|
for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) {
|
||||||
it != this->message->getWords().end(); ++it) {
|
|
||||||
Word &word = *it;
|
Word &word = *it;
|
||||||
|
|
||||||
if ((word.getType() & flags) == Word::None) {
|
if ((word.getType() & flags) == Word::None) {
|
||||||
|
@ -121,7 +125,7 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
|
|
||||||
// word wrapping
|
// word wrapping
|
||||||
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
|
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
|
||||||
this->alignWordParts(lineStart, lineHeight);
|
alignWordParts(lineStart, lineHeight);
|
||||||
|
|
||||||
y += lineHeight;
|
y += lineHeight;
|
||||||
|
|
||||||
|
@ -144,9 +148,8 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
|
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
|
||||||
QString mid = text.mid(start, i - start - 1);
|
QString mid = text.mid(start, i - start - 1);
|
||||||
|
|
||||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y,
|
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(),
|
||||||
width, word.getHeight(),
|
lineNumber, mid, mid));
|
||||||
lineNumber, mid, mid));
|
|
||||||
|
|
||||||
y += metrics.height();
|
y += metrics.height();
|
||||||
|
|
||||||
|
@ -160,20 +163,19 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
QString mid(text.mid(start));
|
QString mid(text.mid(start));
|
||||||
width = metrics.width(mid);
|
width = metrics.width(mid);
|
||||||
|
|
||||||
this->wordParts.push_back(
|
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||||
WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
word.getHeight(), lineNumber, mid, mid));
|
||||||
word.getHeight(), lineNumber, mid, mid));
|
|
||||||
x = width + MARGIN_LEFT + spaceWidth;
|
x = width + MARGIN_LEFT + spaceWidth;
|
||||||
|
|
||||||
lineHeight = word.getHeight();
|
lineHeight = word.getHeight();
|
||||||
|
|
||||||
lineStart = this->wordParts.size() - 1;
|
lineStart = _wordParts.size() - 1;
|
||||||
|
|
||||||
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(WordPart(word, x, y - word.getHeight(),
|
_wordParts.push_back(
|
||||||
lineNumber, word.getCopyText()));
|
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||||
|
|
||||||
x += word.getWidth() + xOffset;
|
x += word.getWidth() + xOffset;
|
||||||
x += spaceWidth;
|
x += spaceWidth;
|
||||||
|
@ -183,15 +185,14 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
// doesn't fit in the line
|
// doesn't fit in the line
|
||||||
this->alignWordParts(lineStart, lineHeight);
|
alignWordParts(lineStart, lineHeight);
|
||||||
|
|
||||||
y += lineHeight;
|
y += lineHeight;
|
||||||
|
|
||||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT,
|
_wordParts.push_back(
|
||||||
y - word.getHeight(), lineNumber,
|
WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||||
word.getCopyText()));
|
|
||||||
|
|
||||||
lineStart = this->wordParts.size() - 1;
|
lineStart = _wordParts.size() - 1;
|
||||||
|
|
||||||
lineHeight = word.getHeight();
|
lineHeight = word.getHeight();
|
||||||
|
|
||||||
|
@ -202,36 +203,40 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->alignWordParts(lineStart, lineHeight);
|
alignWordParts(lineStart, lineHeight);
|
||||||
|
|
||||||
if (this->height != y + lineHeight) {
|
if (_height != y + lineHeight) {
|
||||||
sizeChanged = true;
|
sizeChanged = true;
|
||||||
this->height = y + lineHeight;
|
_height = y + lineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sizeChanged) {
|
if (sizeChanged) {
|
||||||
this->buffer = nullptr;
|
buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->updateBuffer = true;
|
updateBuffer = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
const std::vector<WordPart> &MessageRef::getWordParts() const
|
||||||
MessageRef::alignWordParts(int lineStart, int lineHeight)
|
|
||||||
{
|
{
|
||||||
for (size_t i = lineStart; i < this->wordParts.size(); i++) {
|
return _wordParts;
|
||||||
WordPart &wordPart2 = this->wordParts.at(i);
|
}
|
||||||
|
|
||||||
|
void MessageRef::alignWordParts(int lineStart, int lineHeight)
|
||||||
|
{
|
||||||
|
for (size_t i = lineStart; i < _wordParts.size(); i++) {
|
||||||
|
WordPart &wordPart2 = _wordParts.at(i);
|
||||||
|
|
||||||
wordPart2.setY(wordPart2.getY() + lineHeight);
|
wordPart2.setY(wordPart2.getY() + lineHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool MessageRef::tryGetWordPart(QPoint point, Word &word)
|
||||||
MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
|
|
||||||
{
|
{
|
||||||
for (messages::WordPart &wordPart : this->wordParts) {
|
// go through all words and return the first one that contains the point.
|
||||||
|
for (WordPart &wordPart : _wordParts) {
|
||||||
if (wordPart.getRect().contains(point)) {
|
if (wordPart.getRect().contains(point)) {
|
||||||
word = wordPart.getWord();
|
word = wordPart.getWord();
|
||||||
return true;
|
return true;
|
||||||
|
@ -241,18 +246,17 @@ MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int MessageRef::getSelectionIndex(QPoint position)
|
||||||
MessageRef::getSelectionIndex(QPoint position)
|
|
||||||
{
|
{
|
||||||
if (this->wordParts.size() == 0) {
|
if (_wordParts.size() == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out in which line the cursor is
|
// find out in which line the cursor is
|
||||||
int lineNumber = 0, lineStart = 0, lineEnd = 0;
|
int lineNumber = 0, lineStart = 0, lineEnd = 0;
|
||||||
|
|
||||||
for (int i = 0; i < this->wordParts.size(); i++) {
|
for (int i = 0; i < _wordParts.size(); i++) {
|
||||||
WordPart &part = this->wordParts[i];
|
WordPart &part = _wordParts[i];
|
||||||
|
|
||||||
// return if curser under the word
|
// return if curser under the word
|
||||||
if (position.y() >= part.getBottom()) {
|
if (position.y() >= part.getBottom()) {
|
||||||
|
@ -271,13 +275,13 @@ MessageRef::getSelectionIndex(QPoint position)
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
for (int i = 0; i < lineStart; i++) {
|
for (int i = 0; i < lineStart; i++) {
|
||||||
WordPart &part = this->wordParts[i];
|
WordPart &part = _wordParts[i];
|
||||||
|
|
||||||
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = lineStart; i < lineEnd; i++) {
|
for (int i = lineStart; i < lineEnd; i++) {
|
||||||
WordPart &part = this->wordParts[i];
|
WordPart &part = _wordParts[i];
|
||||||
|
|
||||||
// curser is left of the word part
|
// curser is left of the word part
|
||||||
if (position.x() < part.getX()) {
|
if (position.x() < part.getX()) {
|
||||||
|
@ -304,8 +308,7 @@ MessageRef::getSelectionIndex(QPoint position)
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
x = part.getX() +
|
x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1);
|
||||||
part.getWord().getFontMetrics().width(text, j + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,9 +318,9 @@ MessageRef::getSelectionIndex(QPoint position)
|
||||||
return index;
|
return index;
|
||||||
|
|
||||||
// go through all the wordparts
|
// go through all the wordparts
|
||||||
// for (int i = 0; i < this->wordParts; i < this->wordParts.size()) {
|
// for (int i = 0; i < wordParts; i < wordParts.size()) {
|
||||||
|
|
||||||
// WordPart &part = this->wordParts[i];
|
// WordPart &part = wordParts[i];
|
||||||
|
|
||||||
// // return if curser under the word
|
// // return if curser under the word
|
||||||
// if (position.y() >= part.getBottom()) {
|
// if (position.y() >= part.getBottom()) {
|
||||||
|
|
|
@ -9,30 +9,21 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
|
class MessageRef;
|
||||||
|
|
||||||
|
typedef std::shared_ptr<MessageRef> SharedMessageRef;
|
||||||
|
|
||||||
class MessageRef
|
class MessageRef
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MessageRef(std::shared_ptr<Message> message);
|
MessageRef(SharedMessage message);
|
||||||
|
|
||||||
Message *
|
Message *getMessage();
|
||||||
getMessage()
|
int getHeight() const;
|
||||||
{
|
|
||||||
return this->message;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getHeight() const
|
|
||||||
{
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool layout(int width, bool enableEmoteMargins = true);
|
bool layout(int width, bool enableEmoteMargins = true);
|
||||||
|
|
||||||
const std::vector<WordPart> &
|
const std::vector<WordPart> &getWordParts() const;
|
||||||
getWordParts() const
|
|
||||||
{
|
|
||||||
return wordParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<QPixmap> buffer = nullptr;
|
std::shared_ptr<QPixmap> buffer = nullptr;
|
||||||
bool updateBuffer = false;
|
bool updateBuffer = false;
|
||||||
|
@ -42,18 +33,18 @@ public:
|
||||||
int getSelectionIndex(QPoint position);
|
int getSelectionIndex(QPoint position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Message *message;
|
// variables
|
||||||
std::shared_ptr<Message> messagePtr;
|
SharedMessage _message;
|
||||||
|
std::vector<messages::WordPart> _wordParts;
|
||||||
|
|
||||||
std::vector<messages::WordPart> wordParts;
|
int _height = 0;
|
||||||
|
|
||||||
int height = 0;
|
int _currentLayoutWidth = -1;
|
||||||
|
int _fontGeneration = -1;
|
||||||
int currentLayoutWidth = -1;
|
int _emoteGeneration = -1;
|
||||||
int fontGeneration = -1;
|
Word::Type _currentWordTypes = Word::None;
|
||||||
int emoteGeneration = -1;
|
|
||||||
Word::Type currentWordTypes = Word::None;
|
|
||||||
|
|
||||||
|
// methods
|
||||||
void alignWordParts(int lineStart, int lineHeight);
|
void alignWordParts(int lineStart, int lineHeight);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,35 +4,131 @@ namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
// Image word
|
// Image word
|
||||||
Word::Word(LazyLoadedImage *image, Type type, const QString ©text,
|
Word::Word(LazyLoadedImage *image, Type type, const QString ©text, const QString &tooltip,
|
||||||
const QString &tooltip, const Link &link)
|
const Link &link)
|
||||||
: image(image)
|
: _image(image)
|
||||||
, text()
|
, _text()
|
||||||
, color()
|
, _color()
|
||||||
, _isImage(true)
|
, _isImage(true)
|
||||||
, type(type)
|
, _type(type)
|
||||||
, copyText(copytext)
|
, _copyText(copytext)
|
||||||
, tooltip(tooltip)
|
, _tooltip(tooltip)
|
||||||
, link(link)
|
, _link(link)
|
||||||
, characterWidthCache()
|
, _characterWidthCache()
|
||||||
{
|
{
|
||||||
image->getWidth(); // professional segfault test
|
image->getWidth(); // professional segfault test
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text word
|
// Text word
|
||||||
Word::Word(const QString &text, Type type, const QColor &color,
|
Word::Word(const QString &text, Type type, const QColor &color, const QString ©text,
|
||||||
const QString ©text, const QString &tooltip, const Link &link)
|
const QString &tooltip, const Link &link)
|
||||||
: image(NULL)
|
: _image(NULL)
|
||||||
, text(text)
|
, _text(text)
|
||||||
, color(color)
|
, _color(color)
|
||||||
, _isImage(false)
|
, _isImage(false)
|
||||||
, type(type)
|
, _type(type)
|
||||||
, copyText(copytext)
|
, _copyText(copytext)
|
||||||
, tooltip(tooltip)
|
, _tooltip(tooltip)
|
||||||
, link(link)
|
, _link(link)
|
||||||
, characterWidthCache()
|
, _characterWidthCache()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LazyLoadedImage &Word::getImage() const
|
||||||
|
{
|
||||||
|
return *_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Word::getText() const
|
||||||
|
{
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Word::getWidth() const
|
||||||
|
{
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Word::getHeight() const
|
||||||
|
{
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Word::setSize(int width, int height)
|
||||||
|
{
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Word::isImage() const
|
||||||
|
{
|
||||||
|
return _isImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Word::isText() const
|
||||||
|
{
|
||||||
|
return !_isImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Word::getCopyText() const
|
||||||
|
{
|
||||||
|
return _copyText;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Word::hasTrailingSpace() const
|
||||||
|
{
|
||||||
|
return _hasTrailingSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFont &Word::getFont() const
|
||||||
|
{
|
||||||
|
return FontManager::getInstance().getFont(_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFontMetrics &Word::getFontMetrics() const
|
||||||
|
{
|
||||||
|
return FontManager::getInstance().getFontMetrics(_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
Word::Type Word::getType() const
|
||||||
|
{
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Word::getTooltip() const
|
||||||
|
{
|
||||||
|
return _tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QColor &Word::getColor() const
|
||||||
|
{
|
||||||
|
return _color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Link &Word::getLink() const
|
||||||
|
{
|
||||||
|
return _link;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Word::getXOffset() const
|
||||||
|
{
|
||||||
|
return _xOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Word::getYOffset() const
|
||||||
|
{
|
||||||
|
return _yOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Word::setOffset(int xOffset, int yOffset)
|
||||||
|
{
|
||||||
|
_xOffset = std::max(0, xOffset);
|
||||||
|
_yOffset = std::max(0, yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<short> &Word::getCharacterWidthCache() const
|
||||||
|
{
|
||||||
|
return _characterWidthCache;
|
||||||
|
}
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
182
messages/word.h
182
messages/word.h
|
@ -1,7 +1,7 @@
|
||||||
#ifndef WORD_H
|
#ifndef WORD_H
|
||||||
#define WORD_H
|
#define WORD_H
|
||||||
|
|
||||||
#include "fonts.h"
|
#include "fontmanager.h"
|
||||||
#include "messages/lazyloadedimage.h"
|
#include "messages/lazyloadedimage.h"
|
||||||
#include "messages/link.h"
|
#include "messages/link.h"
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@ public:
|
||||||
BttvGifEmoteText = (1 << 9),
|
BttvGifEmoteText = (1 << 9),
|
||||||
FfzEmoteImage = (1 << 10),
|
FfzEmoteImage = (1 << 10),
|
||||||
FfzEmoteText = (1 << 11),
|
FfzEmoteText = (1 << 11),
|
||||||
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage |
|
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage,
|
||||||
FfzEmoteImage,
|
|
||||||
|
|
||||||
BitsStatic = (1 << 12),
|
BitsStatic = (1 << 12),
|
||||||
BitsAnimated = (1 << 13),
|
BitsAnimated = (1 << 13),
|
||||||
|
@ -46,9 +45,8 @@ public:
|
||||||
BadgePremium = (1 << 20),
|
BadgePremium = (1 << 20),
|
||||||
BadgeChatterino = (1 << 21),
|
BadgeChatterino = (1 << 21),
|
||||||
BadgeCheer = (1 << 22),
|
BadgeCheer = (1 << 22),
|
||||||
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator |
|
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | BadgeTurbo |
|
||||||
BadgeTurbo | BadgeBroadcaster | BadgePremium |
|
BadgeBroadcaster | BadgePremium | BadgeChatterino | BadgeCheer,
|
||||||
BadgeChatterino | BadgeCheer,
|
|
||||||
|
|
||||||
Username = (1 << 23),
|
Username = (1 << 23),
|
||||||
BitsAmount = (1 << 24),
|
BitsAmount = (1 << 24),
|
||||||
|
@ -59,157 +57,61 @@ public:
|
||||||
EmojiImage = (1 << 27),
|
EmojiImage = (1 << 27),
|
||||||
EmojiText = (1 << 28),
|
EmojiText = (1 << 28),
|
||||||
|
|
||||||
Default = TimestampNoSeconds | Badges | Username | BitsStatic |
|
Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
|
||||||
FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage |
|
BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
|
||||||
TwitchEmoteImage | BitsAmount | Text | ButtonBan |
|
ButtonBan | ButtonTimeout
|
||||||
ButtonTimeout
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Word()
|
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,
|
||||||
const QString ©text, const QString &getTooltip,
|
const QString ©text, const QString &getTooltip, const Link &getLink = Link());
|
||||||
const Link &getLink = Link());
|
|
||||||
|
|
||||||
LazyLoadedImage &
|
LazyLoadedImage &getImage() const;
|
||||||
getImage() const
|
const QString &getText() const;
|
||||||
{
|
int getWidth() const;
|
||||||
return *image;
|
int getHeight() const;
|
||||||
}
|
void setSize(int _width, int _height);
|
||||||
|
|
||||||
const QString &
|
bool isImage() const;
|
||||||
getText() const
|
bool isText() const;
|
||||||
{
|
const QString &getCopyText() const;
|
||||||
return this->text;
|
bool hasTrailingSpace() const;
|
||||||
}
|
QFont &getFont() const;
|
||||||
|
QFontMetrics &getFontMetrics() const;
|
||||||
|
Type getType() const;
|
||||||
|
const QString &getTooltip() const;
|
||||||
|
const QColor &getColor() const;
|
||||||
|
const Link &getLink() const;
|
||||||
|
int getXOffset() const;
|
||||||
|
int getYOffset() const;
|
||||||
|
void setOffset(int _xOffset, int _yOffset);
|
||||||
|
|
||||||
int
|
std::vector<short> &getCharacterWidthCache() const;
|
||||||
getWidth() const
|
|
||||||
{
|
|
||||||
return this->width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getHeight() const
|
|
||||||
{
|
|
||||||
return this->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setSize(int width, int height)
|
|
||||||
{
|
|
||||||
this->width = width;
|
|
||||||
this->height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
isImage() const
|
|
||||||
{
|
|
||||||
return this->_isImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
isText() const
|
|
||||||
{
|
|
||||||
return !this->_isImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getCopyText() const
|
|
||||||
{
|
|
||||||
return this->copyText;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
hasTrailingSpace() const
|
|
||||||
{
|
|
||||||
return this->_hasTrailingSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFont &
|
|
||||||
getFont() const
|
|
||||||
{
|
|
||||||
return Fonts::getFont(this->font);
|
|
||||||
}
|
|
||||||
|
|
||||||
QFontMetrics &
|
|
||||||
getFontMetrics() const
|
|
||||||
{
|
|
||||||
return Fonts::getFontMetrics(this->font);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type
|
|
||||||
getType() const
|
|
||||||
{
|
|
||||||
return this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getTooltip() const
|
|
||||||
{
|
|
||||||
return this->tooltip;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QColor &
|
|
||||||
getColor() const
|
|
||||||
{
|
|
||||||
return this->color;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Link &
|
|
||||||
getLink() const
|
|
||||||
{
|
|
||||||
return this->link;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getXOffset() const
|
|
||||||
{
|
|
||||||
return this->xOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getYOffset() const
|
|
||||||
{
|
|
||||||
return this->yOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setOffset(int xOffset, int yOffset)
|
|
||||||
{
|
|
||||||
this->xOffset = std::max(0, xOffset);
|
|
||||||
this->yOffset = std::max(0, yOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<short> &
|
|
||||||
getCharacterWidthCache() const
|
|
||||||
{
|
|
||||||
return this->characterWidthCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LazyLoadedImage *image;
|
LazyLoadedImage *_image;
|
||||||
QString text;
|
QString _text;
|
||||||
QColor color;
|
QColor _color;
|
||||||
bool _isImage;
|
bool _isImage;
|
||||||
|
|
||||||
Type type;
|
Type _type;
|
||||||
QString copyText;
|
QString _copyText;
|
||||||
QString tooltip;
|
QString _tooltip;
|
||||||
|
|
||||||
int width = 16;
|
int _width = 16;
|
||||||
int height = 16;
|
int _height = 16;
|
||||||
int xOffset = 0;
|
int _xOffset = 0;
|
||||||
int yOffset = 0;
|
int _yOffset = 0;
|
||||||
|
|
||||||
bool _hasTrailingSpace;
|
bool _hasTrailingSpace;
|
||||||
Fonts::Type font = Fonts::Medium;
|
FontManager::Type _font = FontManager::Medium;
|
||||||
Link link;
|
Link _link;
|
||||||
|
|
||||||
mutable std::vector<short> characterWidthCache;
|
mutable std::vector<short> _characterWidthCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace messages
|
} // namespace messages
|
||||||
|
|
|
@ -4,33 +4,103 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace messages {
|
namespace messages {
|
||||||
|
|
||||||
WordPart::WordPart(Word &word, int x, int y, int lineNumber,
|
WordPart::WordPart(Word &word, int x, int y, int lineNumber, const QString ©Text,
|
||||||
const QString ©Text, bool allowTrailingSpace)
|
bool allowTrailingSpace)
|
||||||
: m_word(word)
|
: _word(word)
|
||||||
, copyText(copyText)
|
, _copyText(copyText)
|
||||||
, text(word.isText() ? m_word.getText() : QString())
|
, _text(word.isText() ? _word.getText() : QString())
|
||||||
, x(x)
|
, _x(x)
|
||||||
, y(y)
|
, _y(y)
|
||||||
, width(word.getWidth())
|
, _width(word.getWidth())
|
||||||
, height(word.getHeight())
|
, _height(word.getHeight())
|
||||||
, lineNumber(lineNumber)
|
, _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, int lineNumber,
|
||||||
int lineNumber, const QString ©Text,
|
const QString ©Text, const QString &customText, bool allowTrailingSpace)
|
||||||
const QString &customText, bool allowTrailingSpace)
|
: _word(word)
|
||||||
: m_word(word)
|
, _copyText(copyText)
|
||||||
, copyText(copyText)
|
, _text(customText)
|
||||||
, text(customText)
|
, _x(x)
|
||||||
, x(x)
|
, _y(y)
|
||||||
, y(y)
|
, _width(width)
|
||||||
, width(width)
|
, _height(height)
|
||||||
, height(height)
|
, _lineNumber(lineNumber)
|
||||||
, lineNumber(lineNumber)
|
|
||||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Word &WordPart::getWord() const
|
||||||
|
{
|
||||||
|
return _word;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getWidth() const
|
||||||
|
{
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getHeight() const
|
||||||
|
{
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getX() const
|
||||||
|
{
|
||||||
|
return _x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getY() const
|
||||||
|
{
|
||||||
|
return _y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordPart::setPosition(int x, int y)
|
||||||
|
{
|
||||||
|
_x = x;
|
||||||
|
_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordPart::setY(int y)
|
||||||
|
{
|
||||||
|
_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getRight() const
|
||||||
|
{
|
||||||
|
return _x + _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getBottom() const
|
||||||
|
{
|
||||||
|
return _y + _height;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect WordPart::getRect() const
|
||||||
|
{
|
||||||
|
return QRect(_x, _y, _width, _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString WordPart::getCopyText() const
|
||||||
|
{
|
||||||
|
return _copyText;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::hasTrailingSpace() const
|
||||||
|
{
|
||||||
|
return _trailingSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &WordPart::getText() const
|
||||||
|
{
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WordPart::getLineNumber()
|
||||||
|
{
|
||||||
|
return _lineNumber;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,110 +12,39 @@ class Word;
|
||||||
class WordPart
|
class WordPart
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WordPart(Word &getWord, int getX, int getY, int lineNumber,
|
WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText,
|
||||||
const QString &getCopyText, bool allowTrailingSpace = true);
|
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, int _lineNumber,
|
||||||
int lineNumber, const QString &getCopyText,
|
const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true);
|
||||||
const QString &customText, bool allowTrailingSpace = true);
|
|
||||||
|
|
||||||
const Word &
|
const Word &getWord() const;
|
||||||
getWord() const
|
int getWidth() const;
|
||||||
{
|
int getHeight() const;
|
||||||
return this->m_word;
|
int getX() const;
|
||||||
}
|
int getY() const;
|
||||||
|
void setPosition(int _x, int _y);
|
||||||
int
|
void setY(int _y);
|
||||||
getWidth() const
|
int getRight() const;
|
||||||
{
|
int getBottom() const;
|
||||||
return this->width;
|
QRect getRect() const;
|
||||||
}
|
const QString getCopyText() const;
|
||||||
|
int hasTrailingSpace() const;
|
||||||
int
|
const QString &getText() const;
|
||||||
getHeight() const
|
int getLineNumber();
|
||||||
{
|
|
||||||
return this->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getX() const
|
|
||||||
{
|
|
||||||
return this->x;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getY() const
|
|
||||||
{
|
|
||||||
return this->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setPosition(int x, int y)
|
|
||||||
{
|
|
||||||
this->x = x;
|
|
||||||
this->y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setY(int y)
|
|
||||||
{
|
|
||||||
this->y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getRight() const
|
|
||||||
{
|
|
||||||
return this->x + this->width;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getBottom() const
|
|
||||||
{
|
|
||||||
return this->y + this->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect
|
|
||||||
getRect() const
|
|
||||||
{
|
|
||||||
return QRect(this->x, this->y, this->width, this->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString
|
|
||||||
getCopyText() const
|
|
||||||
{
|
|
||||||
return this->copyText;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
hasTrailingSpace() const
|
|
||||||
{
|
|
||||||
return this->_trailingSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getText() const
|
|
||||||
{
|
|
||||||
return this->text;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getLineNumber()
|
|
||||||
{
|
|
||||||
return this->lineNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Word &m_word;
|
Word &_word;
|
||||||
|
|
||||||
QString copyText;
|
QString _copyText;
|
||||||
QString text;
|
QString _text;
|
||||||
|
|
||||||
int x;
|
int _x;
|
||||||
int y;
|
int _y;
|
||||||
int width;
|
int _width;
|
||||||
int height;
|
int _height;
|
||||||
|
|
||||||
int lineNumber;
|
int _lineNumber;
|
||||||
|
|
||||||
bool _trailingSpace;
|
bool _trailingSpace;
|
||||||
};
|
};
|
||||||
|
|
21
platform/borderless/LICENSE
Normal file
21
platform/borderless/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016 Ian Bannerman
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
555
platform/borderless/qwinwidget.cpp
Normal file
555
platform/borderless/qwinwidget.cpp
Normal file
|
@ -0,0 +1,555 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Solutions component.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:BSD$
|
||||||
|
** You may use this file under the terms of the BSD license as follows:
|
||||||
|
**
|
||||||
|
** "Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are
|
||||||
|
** met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in
|
||||||
|
** the documentation and/or other materials provided with the
|
||||||
|
** distribution.
|
||||||
|
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||||
|
** of its contributors may be used to endorse or promote products derived
|
||||||
|
** from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* File is originally from
|
||||||
|
* https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate/src */
|
||||||
|
/* */
|
||||||
|
/* It has been modified to support borderless window (HTTRANSPARENT) & to remove
|
||||||
|
* pre Qt5 cruft */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
#include "QWinWidget.h"
|
||||||
|
|
||||||
|
#include <qt_windows.h>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QFocusEvent>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class QWinWidget qwinwidget.h
|
||||||
|
\brief The QWinWidget class is a Qt widget that can be child of a
|
||||||
|
native Win32 widget.
|
||||||
|
|
||||||
|
The QWinWidget class is the bridge between an existing application
|
||||||
|
user interface developed using native Win32 APIs or toolkits like
|
||||||
|
MFC, and Qt based GUI elements.
|
||||||
|
|
||||||
|
Using QWinWidget as the parent of QDialogs will ensure that
|
||||||
|
modality, placement and stacking works properly throughout the
|
||||||
|
entire application. If the child widget is a top level window that
|
||||||
|
uses the \c WDestructiveClose flag, QWinWidget will destroy itself
|
||||||
|
when the child window closes down.
|
||||||
|
|
||||||
|
Applications moving to Qt can use QWinWidget to add new
|
||||||
|
functionality, and gradually replace the existing interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
QWinWidget::QWinWidget()
|
||||||
|
: QWidget(nullptr)
|
||||||
|
, m_Layout()
|
||||||
|
, p_Widget(nullptr)
|
||||||
|
, m_ParentNativeWindowHandle(nullptr)
|
||||||
|
, _prevFocus(nullptr)
|
||||||
|
, _reenableParent(false)
|
||||||
|
{
|
||||||
|
// Create a native window and give it geometry values * devicePixelRatio for
|
||||||
|
// HiDPI support
|
||||||
|
p_ParentWinNativeWindow = new WinNativeWindow(
|
||||||
|
1 * window()->devicePixelRatio(), 1 * window()->devicePixelRatio(),
|
||||||
|
1 * window()->devicePixelRatio(), 1 * window()->devicePixelRatio());
|
||||||
|
|
||||||
|
// If you want to set a minimize size for your app, do so here
|
||||||
|
// p_ParentWinNativeWindow->setMinimumSize(1024 *
|
||||||
|
// window()->devicePixelRatio(), 768 * window()->devicePixelRatio());
|
||||||
|
|
||||||
|
// If you want to set a maximum size for your app, do so here
|
||||||
|
// p_ParentWinNativeWindow->setMaximumSize(1024 *
|
||||||
|
// window()->devicePixelRatio(), 768 * window()->devicePixelRatio());
|
||||||
|
|
||||||
|
// Save the native window handle for shorthand use
|
||||||
|
m_ParentNativeWindowHandle = p_ParentWinNativeWindow->hWnd;
|
||||||
|
Q_ASSERT(m_ParentNativeWindowHandle);
|
||||||
|
|
||||||
|
// Create the child window & embed it into the native one
|
||||||
|
if (m_ParentNativeWindowHandle) {
|
||||||
|
SetWindowLong((HWND)winId(), GWL_STYLE,
|
||||||
|
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
|
||||||
|
QWindow *window = windowHandle();
|
||||||
|
window->setProperty("_q_embedded_native_parent_handle",
|
||||||
|
(WId)m_ParentNativeWindowHandle);
|
||||||
|
|
||||||
|
SetParent((HWND)winId(), m_ParentNativeWindowHandle);
|
||||||
|
window->setFlags(Qt::FramelessWindowHint);
|
||||||
|
QEvent e(QEvent::EmbeddingControl);
|
||||||
|
QApplication::sendEvent(this, &e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass along our window handle & widget pointer to WinFramelessWidget so we
|
||||||
|
// can exchange messages
|
||||||
|
p_ParentWinNativeWindow->childWindow = (HWND)winId();
|
||||||
|
p_ParentWinNativeWindow->childWidget = this;
|
||||||
|
|
||||||
|
// Clear margins & spacing & add the layout to prepare for the MainAppWidget
|
||||||
|
setContentsMargins(0, 0, 0, 0);
|
||||||
|
setLayout(&m_Layout);
|
||||||
|
m_Layout.setContentsMargins(0, 0, 0, 0);
|
||||||
|
m_Layout.setSpacing(0);
|
||||||
|
|
||||||
|
// Create the true app widget
|
||||||
|
// p_Widget = new Widget(this);
|
||||||
|
// m_Layout.addWidget(p_Widget);
|
||||||
|
// p_Widget->setParent(this, Qt::Widget);
|
||||||
|
// p_Widget->setVisible(true);
|
||||||
|
|
||||||
|
// Update the BORDERWIDTH value if needed for HiDPI displays
|
||||||
|
BORDERWIDTH = BORDERWIDTH * window()->devicePixelRatio();
|
||||||
|
|
||||||
|
// Update the TOOLBARHEIGHT value to match the height of toolBar * if
|
||||||
|
// needed, the HiDPI display
|
||||||
|
// if (p_Widget->toolBar) {
|
||||||
|
// TOOLBARHEIGHT =
|
||||||
|
// p_Widget->toolBar->height() * window()->devicePixelRatio();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// You need to keep the native window in sync with the Qt window & children,
|
||||||
|
// so wire min/max/close buttons to
|
||||||
|
// slots inside of QWinWidget. QWinWidget can then talk with the native
|
||||||
|
// window as needed
|
||||||
|
// if (p_Widget->minimizeButton) {
|
||||||
|
// connect(p_Widget->minimizeButton, &QPushButton::clicked, this,
|
||||||
|
// &QWinWidget::onMinimizeButtonClicked);
|
||||||
|
// }
|
||||||
|
// if (p_Widget->maximizeButton) {
|
||||||
|
// connect(p_Widget->maximizeButton, &QPushButton::clicked, this,
|
||||||
|
// &QWinWidget::onMaximizeButtonClicked);
|
||||||
|
// }
|
||||||
|
// if (p_Widget->closeButton) {
|
||||||
|
// connect(p_Widget->closeButton, &QPushButton::clicked, this,
|
||||||
|
// &QWinWidget::onCloseButtonClicked);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Send the parent native window a WM_SIZE message to update the widget size
|
||||||
|
SendMessage(m_ParentNativeWindowHandle, WM_SIZE, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destroys this object, freeing all allocated resources.
|
||||||
|
*/
|
||||||
|
QWinWidget::~QWinWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the handle of the native Win32 parent window.
|
||||||
|
*/
|
||||||
|
HWND
|
||||||
|
QWinWidget::getParentWindow() const
|
||||||
|
{
|
||||||
|
return m_ParentNativeWindowHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::childEvent(QChildEvent *e)
|
||||||
|
{
|
||||||
|
QObject *obj = e->child();
|
||||||
|
if (obj->isWidgetType()) {
|
||||||
|
if (e->added()) {
|
||||||
|
if (obj->isWidgetType()) {
|
||||||
|
obj->installEventFilter(this);
|
||||||
|
}
|
||||||
|
} else if (e->removed() && _reenableParent) {
|
||||||
|
_reenableParent = false;
|
||||||
|
EnableWindow(m_ParentNativeWindowHandle, true);
|
||||||
|
obj->removeEventFilter(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QWidget::childEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal */
|
||||||
|
void
|
||||||
|
QWinWidget::saveFocus()
|
||||||
|
{
|
||||||
|
if (!_prevFocus)
|
||||||
|
_prevFocus = ::GetFocus();
|
||||||
|
if (!_prevFocus)
|
||||||
|
_prevFocus = getParentWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Shows this widget. Overrides QWidget::show().
|
||||||
|
|
||||||
|
\sa showCentered()
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::show()
|
||||||
|
{
|
||||||
|
ShowWindow(m_ParentNativeWindowHandle, true);
|
||||||
|
saveFocus();
|
||||||
|
QWidget::show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Centers this widget over the native parent window. Use this
|
||||||
|
function to have Qt toplevel windows (i.e. dialogs) positioned
|
||||||
|
correctly over their native parent windows.
|
||||||
|
|
||||||
|
\code
|
||||||
|
QWinWidget qwin(hParent);
|
||||||
|
qwin.center();
|
||||||
|
|
||||||
|
QMessageBox::information(&qwin, "Caption", "Information Text");
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
This will center the message box over the client area of hParent.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::center()
|
||||||
|
{
|
||||||
|
const QWidget *child = findChild<QWidget *>();
|
||||||
|
if (child && !child->isWindow()) {
|
||||||
|
qWarning("QWinWidget::center: Call this function only for QWinWidgets "
|
||||||
|
"with toplevel children");
|
||||||
|
}
|
||||||
|
RECT r;
|
||||||
|
GetWindowRect(m_ParentNativeWindowHandle, &r);
|
||||||
|
setGeometry((r.right - r.left) / 2 + r.left, (r.bottom - r.top) / 2 + r.top,
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\obsolete
|
||||||
|
|
||||||
|
Call center() instead.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::showCentered()
|
||||||
|
{
|
||||||
|
center();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QWinWidget::setGeometry(int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
p_ParentWinNativeWindow->setGeometry(
|
||||||
|
x * window()->devicePixelRatio(), y * window()->devicePixelRatio(),
|
||||||
|
w * window()->devicePixelRatio(), h * window()->devicePixelRatio());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the focus to the window that had the focus before this widget
|
||||||
|
was shown, or if there was no previous window, sets the focus to
|
||||||
|
the parent window.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::resetFocus()
|
||||||
|
{
|
||||||
|
if (_prevFocus)
|
||||||
|
::SetFocus(_prevFocus);
|
||||||
|
else
|
||||||
|
::SetFocus(getParentWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the parent native window to minimize
|
||||||
|
void
|
||||||
|
QWinWidget::onMinimizeButtonClicked()
|
||||||
|
{
|
||||||
|
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the parent native window to maximize or restore as appropriate
|
||||||
|
void
|
||||||
|
QWinWidget::onMaximizeButtonClicked()
|
||||||
|
{
|
||||||
|
if (p_Widget->maximizeButton->isChecked()) {
|
||||||
|
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
|
||||||
|
} else {
|
||||||
|
SendMessage(m_ParentNativeWindowHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QWinWidget::onCloseButtonClicked()
|
||||||
|
{
|
||||||
|
if (true /* put your check for it if it safe to close your app here */) // eg, does the user need to save a document
|
||||||
|
{
|
||||||
|
// Safe to close, so hide the parent window
|
||||||
|
ShowWindow(m_ParentNativeWindowHandle, false);
|
||||||
|
|
||||||
|
// And then quit
|
||||||
|
QApplication::quit();
|
||||||
|
} else {
|
||||||
|
// Do nothing, and thus, don't actually close the window
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QWinWidget::nativeEvent(const QByteArray &, void *message, long *result)
|
||||||
|
{
|
||||||
|
MSG *msg = (MSG *)message;
|
||||||
|
|
||||||
|
if (msg->message == WM_SETFOCUS) {
|
||||||
|
Qt::FocusReason reason;
|
||||||
|
if (::GetKeyState(VK_LBUTTON) < 0 || ::GetKeyState(VK_RBUTTON) < 0)
|
||||||
|
reason = Qt::MouseFocusReason;
|
||||||
|
else if (::GetKeyState(VK_SHIFT) < 0)
|
||||||
|
reason = Qt::BacktabFocusReason;
|
||||||
|
else
|
||||||
|
reason = Qt::TabFocusReason;
|
||||||
|
QFocusEvent e(QEvent::FocusIn, reason);
|
||||||
|
QApplication::sendEvent(this, &e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only close if safeToClose clears()
|
||||||
|
if (msg->message == WM_CLOSE) {
|
||||||
|
if (true /* put your check for it if it safe to close your app here */) // eg, does the user need to save a document
|
||||||
|
{
|
||||||
|
// Safe to close, so hide the parent window
|
||||||
|
ShowWindow(m_ParentNativeWindowHandle, false);
|
||||||
|
|
||||||
|
// And then quit
|
||||||
|
QApplication::quit();
|
||||||
|
} else {
|
||||||
|
*result = 0; // Set the message to 0 to ignore it, and thus, don't
|
||||||
|
// actually close
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double check WM_SIZE messages to see if the parent native window is
|
||||||
|
// maximized
|
||||||
|
if (msg->message == WM_SIZE) {
|
||||||
|
if (p_Widget && p_Widget->maximizeButton) {
|
||||||
|
// Get the window state
|
||||||
|
WINDOWPLACEMENT wp;
|
||||||
|
GetWindowPlacement(m_ParentNativeWindowHandle, &wp);
|
||||||
|
|
||||||
|
// If we're maximized,
|
||||||
|
if (wp.showCmd == SW_MAXIMIZE) {
|
||||||
|
// Maximize button should show as Restore
|
||||||
|
p_Widget->maximizeButton->setChecked(true);
|
||||||
|
} else {
|
||||||
|
// Maximize button should show as Maximize
|
||||||
|
p_Widget->maximizeButton->setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass NCHITTESTS on the window edges as determined by BORDERWIDTH &
|
||||||
|
// TOOLBARHEIGHT through to the parent native window
|
||||||
|
if (msg->message == WM_NCHITTEST) {
|
||||||
|
RECT WindowRect;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
GetWindowRect(msg->hwnd, &WindowRect);
|
||||||
|
x = GET_X_LPARAM(msg->lParam) - WindowRect.left;
|
||||||
|
y = GET_Y_LPARAM(msg->lParam) - WindowRect.top;
|
||||||
|
|
||||||
|
if (x >= BORDERWIDTH &&
|
||||||
|
x <= WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||||
|
y >= BORDERWIDTH && y <= TOOLBARHEIGHT) {
|
||||||
|
if (false) { // if (p_Widget->toolBar) {
|
||||||
|
// If the mouse is over top of the toolbar area BUT is actually
|
||||||
|
// positioned over a child widget of the toolbar,
|
||||||
|
// Then we don't want to enable dragging. This allows for
|
||||||
|
// buttons in the toolbar, eg, a Maximize button, to keep the
|
||||||
|
// mouse interaction
|
||||||
|
if (QApplication::widgetAt(QCursor::pos()) != p_Widget->toolBar)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// The mouse is over the toolbar area & is NOT over a child of
|
||||||
|
// the toolbar, so pass this message
|
||||||
|
// through to the native window for HTCAPTION dragging
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (x < BORDERWIDTH && y < BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||||
|
y < BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH &&
|
||||||
|
y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (x < BORDERWIDTH &&
|
||||||
|
y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (x < BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (y < BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (x > WindowRect.right - WindowRect.left - BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
} else if (y > WindowRect.bottom - WindowRect.top - BORDERWIDTH) {
|
||||||
|
*result = HTTRANSPARENT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\reimp
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
QWinWidget::eventFilter(QObject *o, QEvent *e)
|
||||||
|
{
|
||||||
|
QWidget *w = (QWidget *)o;
|
||||||
|
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::WindowDeactivate:
|
||||||
|
if (w->isModal() && w->isHidden())
|
||||||
|
BringWindowToTop(m_ParentNativeWindowHandle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Hide:
|
||||||
|
if (_reenableParent) {
|
||||||
|
EnableWindow(m_ParentNativeWindowHandle, true);
|
||||||
|
_reenableParent = false;
|
||||||
|
}
|
||||||
|
resetFocus();
|
||||||
|
|
||||||
|
if (w->testAttribute(Qt::WA_DeleteOnClose) && w->isWindow())
|
||||||
|
deleteLater();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Show:
|
||||||
|
if (w->isWindow()) {
|
||||||
|
saveFocus();
|
||||||
|
hide();
|
||||||
|
if (w->isModal() && !_reenableParent) {
|
||||||
|
EnableWindow(m_ParentNativeWindowHandle, false);
|
||||||
|
_reenableParent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Close: {
|
||||||
|
::SetActiveWindow(m_ParentNativeWindowHandle);
|
||||||
|
if (w->testAttribute(Qt::WA_DeleteOnClose))
|
||||||
|
deleteLater();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(o, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \reimp
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QWinWidget::focusInEvent(QFocusEvent *e)
|
||||||
|
{
|
||||||
|
QWidget *candidate = this;
|
||||||
|
|
||||||
|
switch (e->reason()) {
|
||||||
|
case Qt::TabFocusReason:
|
||||||
|
case Qt::BacktabFocusReason:
|
||||||
|
while (!(candidate->focusPolicy() & Qt::TabFocus)) {
|
||||||
|
candidate = candidate->nextInFocusChain();
|
||||||
|
if (candidate == this) {
|
||||||
|
candidate = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidate) {
|
||||||
|
candidate->setFocus(e->reason());
|
||||||
|
if (e->reason() == Qt::BacktabFocusReason ||
|
||||||
|
e->reason() == Qt::TabFocusReason) {
|
||||||
|
candidate->setAttribute(Qt::WA_KeyboardFocusChange);
|
||||||
|
candidate->window()->setAttribute(
|
||||||
|
Qt::WA_KeyboardFocusChange);
|
||||||
|
}
|
||||||
|
if (e->reason() == Qt::BacktabFocusReason)
|
||||||
|
QWidget::focusNextPrevChild(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \reimp
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
QWinWidget::focusNextPrevChild(bool next)
|
||||||
|
{
|
||||||
|
QWidget *curFocus = focusWidget();
|
||||||
|
if (!next) {
|
||||||
|
if (!curFocus->isWindow()) {
|
||||||
|
QWidget *nextFocus = curFocus->nextInFocusChain();
|
||||||
|
QWidget *prevFocus = 0;
|
||||||
|
QWidget *topLevel = 0;
|
||||||
|
while (nextFocus != curFocus) {
|
||||||
|
if (nextFocus->focusPolicy() & Qt::TabFocus) {
|
||||||
|
prevFocus = nextFocus;
|
||||||
|
topLevel = 0;
|
||||||
|
}
|
||||||
|
nextFocus = nextFocus->nextInFocusChain();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topLevel) {
|
||||||
|
return QWidget::focusNextPrevChild(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QWidget *nextFocus = curFocus;
|
||||||
|
while (1 && nextFocus != 0) {
|
||||||
|
nextFocus = nextFocus->nextInFocusChain();
|
||||||
|
if (nextFocus->focusPolicy() & Qt::TabFocus) {
|
||||||
|
return QWidget::focusNextPrevChild(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::SetFocus(m_ParentNativeWindowHandle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
109
platform/borderless/qwinwidget.h
Normal file
109
platform/borderless/qwinwidget.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Solutions component.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:BSD$
|
||||||
|
** You may use this file under the terms of the BSD license as follows:
|
||||||
|
**
|
||||||
|
** "Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are
|
||||||
|
** met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in
|
||||||
|
** the documentation and/or other materials provided with the
|
||||||
|
** distribution.
|
||||||
|
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||||
|
** of its contributors may be used to endorse or promote products derived
|
||||||
|
** from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/* */
|
||||||
|
/* File is originally from
|
||||||
|
* https://github.com/qtproject/qt-solutions/tree/master/qtwinmigrate/src */
|
||||||
|
/* */
|
||||||
|
/* It has been modified to support borderless window (HTTTRANSPARENT) & to
|
||||||
|
* remove pre Qt5 cruft */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
|
||||||
|
// Declaration of the QWinWidget classes
|
||||||
|
|
||||||
|
#ifndef QWINWIDGET_H
|
||||||
|
#define QWINWIDGET_H
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "WinNativeWindow.h"
|
||||||
|
#include "widget.h"
|
||||||
|
|
||||||
|
class QWinWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QWinWidget();
|
||||||
|
~QWinWidget();
|
||||||
|
|
||||||
|
void show();
|
||||||
|
void center();
|
||||||
|
void showCentered();
|
||||||
|
void setGeometry(int x, int y, int w, int h);
|
||||||
|
|
||||||
|
HWND getParentWindow() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onMaximizeButtonClicked();
|
||||||
|
void onMinimizeButtonClicked();
|
||||||
|
void onCloseButtonClicked();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void childEvent(QChildEvent *e) override;
|
||||||
|
bool eventFilter(QObject *o, QEvent *e) override;
|
||||||
|
|
||||||
|
bool focusNextPrevChild(bool next) override;
|
||||||
|
void focusInEvent(QFocusEvent *e) override;
|
||||||
|
|
||||||
|
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout m_Layout;
|
||||||
|
|
||||||
|
Widget *p_Widget;
|
||||||
|
|
||||||
|
WinNativeWindow *p_ParentWinNativeWindow;
|
||||||
|
HWND m_ParentNativeWindowHandle;
|
||||||
|
|
||||||
|
HWND _prevFocus;
|
||||||
|
bool _reenableParent;
|
||||||
|
|
||||||
|
int BORDERWIDTH = 6; // Adjust this as you wish for # of pixels on the
|
||||||
|
// edges to show resize handles
|
||||||
|
int TOOLBARHEIGHT = 40; // Adjust this as you wish for # of pixels from the
|
||||||
|
// top to allow dragging the window
|
||||||
|
|
||||||
|
void saveFocus();
|
||||||
|
void resetFocus();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QWINWIDGET_H
|
11
platform/borderless/widget.cpp
Normal file
11
platform/borderless/widget.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include "widget.h"
|
||||||
|
|
||||||
|
Widget::Widget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
// Set a black background for funsies
|
||||||
|
QPalette Pal(palette());
|
||||||
|
Pal.setColor(QPalette::Background, Qt::blue);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
setPalette(Pal);
|
||||||
|
}
|
28
platform/borderless/widget.h
Normal file
28
platform/borderless/widget.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef WIDGET_H
|
||||||
|
#define WIDGET_H
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class Widget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Widget(QWidget *parent = 0);
|
||||||
|
|
||||||
|
// If you want to have Max/Min/Close buttons, look at how QWinWidget uses these
|
||||||
|
QPushButton *maximizeButton = nullptr;
|
||||||
|
QPushButton *minimizeButton = nullptr;
|
||||||
|
QPushButton *closeButton = nullptr;
|
||||||
|
|
||||||
|
// If you want to enable dragging the window when the mouse is over top of, say, a QToolBar,
|
||||||
|
// then look at how QWinWidget uses this
|
||||||
|
QToolBar *toolBar = nullptr;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WIDGET_H
|
235
platform/borderless/winnativewindow.cpp
Normal file
235
platform/borderless/winnativewindow.cpp
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
#include "WinNativeWindow.h"
|
||||||
|
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
HWND WinNativeWindow::childWindow = nullptr;
|
||||||
|
QWidget *WinNativeWindow::childWidget = nullptr;
|
||||||
|
|
||||||
|
WinNativeWindow::WinNativeWindow(const int x, const int y, const int width, const int height)
|
||||||
|
: hWnd(nullptr)
|
||||||
|
{
|
||||||
|
// The native window technically has a background color. You can set it here
|
||||||
|
HBRUSH windowBackground = CreateSolidBrush(RGB(255, 255, 255));
|
||||||
|
|
||||||
|
HINSTANCE hInstance = GetModuleHandle(nullptr);
|
||||||
|
WNDCLASSEX wcx = {0};
|
||||||
|
|
||||||
|
wcx.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wcx.style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;
|
||||||
|
wcx.hInstance = hInstance;
|
||||||
|
wcx.lpfnWndProc = WndProc;
|
||||||
|
wcx.cbClsExtra = 0;
|
||||||
|
wcx.cbWndExtra = 0;
|
||||||
|
wcx.lpszClassName = L"WindowClass";
|
||||||
|
wcx.hbrBackground = windowBackground;
|
||||||
|
wcx.hCursor = LoadCursor(hInstance, IDC_ARROW);
|
||||||
|
|
||||||
|
RegisterClassEx(&wcx);
|
||||||
|
if (FAILED(RegisterClassEx(&wcx))) {
|
||||||
|
throw std::runtime_error("Couldn't register window class");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a native window with the appropriate style
|
||||||
|
hWnd = CreateWindow(L"WindowClass", L"WindowTitle", aero_borderless, x, y, width, height, 0, 0,
|
||||||
|
hInstance, nullptr);
|
||||||
|
if (!hWnd) {
|
||||||
|
throw std::runtime_error("couldn't create window because of reasons");
|
||||||
|
}
|
||||||
|
|
||||||
|
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
||||||
|
|
||||||
|
// This code may be required for aero shadows on some versions of Windows
|
||||||
|
// const MARGINS aero_shadow_on = { 1, 1, 1, 1 };
|
||||||
|
// DwmExtendFrameIntoClientArea(hWnd, &aero_shadow_on);
|
||||||
|
|
||||||
|
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
WinNativeWindow::~WinNativeWindow()
|
||||||
|
{
|
||||||
|
// Hide the window & send the destroy message
|
||||||
|
ShowWindow(hWnd, SW_HIDE);
|
||||||
|
DestroyWindow(hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK WinNativeWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
WinNativeWindow *window =
|
||||||
|
reinterpret_cast<WinNativeWindow *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
|
||||||
|
|
||||||
|
if (!window) {
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message) {
|
||||||
|
// ALT + SPACE or F10 system menu
|
||||||
|
case WM_SYSCOMMAND: {
|
||||||
|
if (wParam == SC_KEYMENU) {
|
||||||
|
RECT winrect;
|
||||||
|
GetWindowRect(hWnd, &winrect);
|
||||||
|
TrackPopupMenu(GetSystemMenu(hWnd, false), TPM_TOPALIGN | TPM_LEFTALIGN,
|
||||||
|
winrect.left + 5, winrect.top + 5, 0, hWnd, NULL);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case WM_NCCALCSIZE: {
|
||||||
|
// this kills the window frame and title bar we added with
|
||||||
|
// WS_THICKFRAME and WS_CAPTION
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the parent window gets any close messages, send them over to
|
||||||
|
// QWinWidget and don't actually close here
|
||||||
|
case WM_CLOSE: {
|
||||||
|
if (childWindow) {
|
||||||
|
SendMessage(childWindow, WM_CLOSE, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_DESTROY: {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_NCHITTEST: {
|
||||||
|
const LONG borderWidth =
|
||||||
|
8 * childWidget->window()->devicePixelRatio(); // This value can be arbitrarily
|
||||||
|
// large as only
|
||||||
|
// intentionally-HTTRANSPARENT'd
|
||||||
|
// messages arrive here
|
||||||
|
RECT winrect;
|
||||||
|
GetWindowRect(hWnd, &winrect);
|
||||||
|
long x = GET_X_LPARAM(lParam);
|
||||||
|
long y = GET_Y_LPARAM(lParam);
|
||||||
|
|
||||||
|
// bottom left corner
|
||||||
|
if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom &&
|
||||||
|
y >= winrect.bottom - borderWidth) {
|
||||||
|
return HTBOTTOMLEFT;
|
||||||
|
}
|
||||||
|
// bottom right corner
|
||||||
|
if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom &&
|
||||||
|
y >= winrect.bottom - borderWidth) {
|
||||||
|
return HTBOTTOMRIGHT;
|
||||||
|
}
|
||||||
|
// top left corner
|
||||||
|
if (x >= winrect.left && x < winrect.left + borderWidth && y >= winrect.top &&
|
||||||
|
y < winrect.top + borderWidth) {
|
||||||
|
return HTTOPLEFT;
|
||||||
|
}
|
||||||
|
// top right corner
|
||||||
|
if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top &&
|
||||||
|
y < winrect.top + borderWidth) {
|
||||||
|
return HTTOPRIGHT;
|
||||||
|
}
|
||||||
|
// left border
|
||||||
|
if (x >= winrect.left && x < winrect.left + borderWidth) {
|
||||||
|
return HTLEFT;
|
||||||
|
}
|
||||||
|
// right border
|
||||||
|
if (x < winrect.right && x >= winrect.right - borderWidth) {
|
||||||
|
return HTRIGHT;
|
||||||
|
}
|
||||||
|
// bottom border
|
||||||
|
if (y < winrect.bottom && y >= winrect.bottom - borderWidth) {
|
||||||
|
return HTBOTTOM;
|
||||||
|
}
|
||||||
|
// top border
|
||||||
|
if (y >= winrect.top && y < winrect.top + borderWidth) {
|
||||||
|
return HTTOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it wasn't a border but we still got the message, return
|
||||||
|
// HTCAPTION to allow click-dragging the window
|
||||||
|
return HTCAPTION;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When this native window changes size, it needs to manually resize the
|
||||||
|
// QWinWidget child
|
||||||
|
case WM_SIZE: {
|
||||||
|
RECT winrect;
|
||||||
|
GetClientRect(hWnd, &winrect);
|
||||||
|
|
||||||
|
WINDOWPLACEMENT wp;
|
||||||
|
wp.length = sizeof(WINDOWPLACEMENT);
|
||||||
|
GetWindowPlacement(hWnd, &wp);
|
||||||
|
if (childWidget) {
|
||||||
|
if (wp.showCmd == SW_MAXIMIZE) {
|
||||||
|
childWidget->setGeometry(
|
||||||
|
8, 8 // Maximized window draw 8 pixels off screen
|
||||||
|
,
|
||||||
|
winrect.right / childWidget->window()->devicePixelRatio() - 16,
|
||||||
|
winrect.bottom / childWidget->window()->devicePixelRatio() - 16);
|
||||||
|
} else {
|
||||||
|
childWidget->setGeometry(
|
||||||
|
0, 0, winrect.right / childWidget->window()->devicePixelRatio(),
|
||||||
|
winrect.bottom / childWidget->window()->devicePixelRatio());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_GETMINMAXINFO: {
|
||||||
|
MINMAXINFO *minMaxInfo = (MINMAXINFO *)lParam;
|
||||||
|
if (window->minimumSize.required) {
|
||||||
|
minMaxInfo->ptMinTrackSize.x = window->getMinimumWidth();
|
||||||
|
;
|
||||||
|
minMaxInfo->ptMinTrackSize.y = window->getMinimumHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window->maximumSize.required) {
|
||||||
|
minMaxInfo->ptMaxTrackSize.x = window->getMaximumWidth();
|
||||||
|
minMaxInfo->ptMaxTrackSize.y = window->getMaximumHeight();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinNativeWindow::setGeometry(const int x, const int y, const int width, const int height)
|
||||||
|
{
|
||||||
|
MoveWindow(hWnd, x, y, width, height, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinNativeWindow::setMinimumSize(const int width, const int height)
|
||||||
|
{
|
||||||
|
this->minimumSize.required = true;
|
||||||
|
this->minimumSize.width = width;
|
||||||
|
this->minimumSize.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WinNativeWindow::getMinimumWidth()
|
||||||
|
{
|
||||||
|
return minimumSize.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WinNativeWindow::getMinimumHeight()
|
||||||
|
{
|
||||||
|
return minimumSize.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinNativeWindow::setMaximumSize(const int width, const int height)
|
||||||
|
{
|
||||||
|
this->maximumSize.required = true;
|
||||||
|
this->maximumSize.width = width;
|
||||||
|
this->maximumSize.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WinNativeWindow::getMaximumWidth()
|
||||||
|
{
|
||||||
|
return maximumSize.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WinNativeWindow::getMaximumHeight()
|
||||||
|
{
|
||||||
|
return maximumSize.height;
|
||||||
|
}
|
53
platform/borderless/winnativewindow.h
Normal file
53
platform/borderless/winnativewindow.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef WINNATIVEWINDOW_H
|
||||||
|
#define WINNATIVEWINDOW_H
|
||||||
|
|
||||||
|
#include "Windows.h"
|
||||||
|
#include "Windowsx.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class WinNativeWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WinNativeWindow(const int x, const int y, const int width, const int height);
|
||||||
|
~WinNativeWindow();
|
||||||
|
|
||||||
|
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
// These six functions exist to restrict native window resizing to whatever
|
||||||
|
// you want your app minimum/maximum size to be
|
||||||
|
void setMinimumSize(const int width, const int height);
|
||||||
|
int getMinimumHeight();
|
||||||
|
int getMinimumWidth();
|
||||||
|
|
||||||
|
void setMaximumSize(const int width, const int height);
|
||||||
|
int getMaximumHeight();
|
||||||
|
int getMaximumWidth();
|
||||||
|
void setGeometry(const int x, const int y, const int width, const int height);
|
||||||
|
|
||||||
|
HWND hWnd;
|
||||||
|
|
||||||
|
static HWND childWindow;
|
||||||
|
static QWidget *childWidget;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct sizeType {
|
||||||
|
sizeType()
|
||||||
|
: required(false)
|
||||||
|
, width(0)
|
||||||
|
, height(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
bool required;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
sizeType minimumSize;
|
||||||
|
sizeType maximumSize;
|
||||||
|
|
||||||
|
DWORD aero_borderless = WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
|
||||||
|
WS_THICKFRAME | WS_CLIPCHILDREN;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WINNATIVEWINDOW_H
|
|
@ -26,43 +26,34 @@ Resources::Resources()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Resources::load()
|
||||||
Resources::load()
|
|
||||||
{
|
{
|
||||||
// badges
|
// badges
|
||||||
Resources::badgeStaff =
|
Resources::badgeStaff = new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png"));
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png"));
|
Resources::badgeAdmin = new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
|
||||||
Resources::badgeAdmin =
|
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
|
|
||||||
Resources::badgeModerator =
|
Resources::badgeModerator =
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png"));
|
new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png"));
|
||||||
Resources::badgeGlobalmod =
|
Resources::badgeGlobalmod =
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png"));
|
new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png"));
|
||||||
Resources::badgeTurbo =
|
Resources::badgeTurbo = new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png"));
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png"));
|
Resources::badgeBroadcaster =
|
||||||
Resources::badgeBroadcaster = new messages::LazyLoadedImage(
|
new messages::LazyLoadedImage(new QPixmap(":/images/broadcaster_bg.png"));
|
||||||
new QPixmap(":/images/broadcaster_bg.png"));
|
Resources::badgePremium =
|
||||||
Resources::badgePremium = new messages::LazyLoadedImage(
|
new messages::LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png"));
|
||||||
new QPixmap(":/images/twitchprime_bg.png"));
|
|
||||||
|
|
||||||
// cheer badges
|
// cheer badges
|
||||||
Resources::cheerBadge100000 =
|
Resources::cheerBadge100000 =
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000"));
|
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000"));
|
||||||
Resources::cheerBadge10000 =
|
Resources::cheerBadge10000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000"));
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000"));
|
Resources::cheerBadge5000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000"));
|
||||||
Resources::cheerBadge5000 =
|
Resources::cheerBadge1000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000"));
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000"));
|
Resources::cheerBadge100 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer100"));
|
||||||
Resources::cheerBadge1000 =
|
Resources::cheerBadge1 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1"));
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000"));
|
|
||||||
Resources::cheerBadge100 =
|
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100"));
|
|
||||||
Resources::cheerBadge1 =
|
|
||||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer1"));
|
|
||||||
|
|
||||||
// button
|
// button
|
||||||
Resources::buttonBan = new messages::LazyLoadedImage(
|
Resources::buttonBan =
|
||||||
new QPixmap(":/images/button_ban.png"), 0.25);
|
new messages::LazyLoadedImage(new QPixmap(":/images/button_ban.png"), 0.25);
|
||||||
Resources::buttonTimeout = new messages::LazyLoadedImage(
|
Resources::buttonTimeout =
|
||||||
new QPixmap(":/images/button_timeout.png"), 0.25);
|
new messages::LazyLoadedImage(new QPixmap(":/images/button_timeout.png"), 0.25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
45
resources.h
45
resources.h
|
@ -11,93 +11,78 @@ public:
|
||||||
static void load();
|
static void load();
|
||||||
|
|
||||||
// badges
|
// badges
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeStaff()
|
||||||
getBadgeStaff()
|
|
||||||
{
|
{
|
||||||
return badgeStaff;
|
return badgeStaff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeAdmin()
|
||||||
getBadgeAdmin()
|
|
||||||
{
|
{
|
||||||
return badgeAdmin;
|
return badgeAdmin;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeGlobalmod()
|
||||||
getBadgeGlobalmod()
|
|
||||||
{
|
{
|
||||||
return badgeGlobalmod;
|
return badgeGlobalmod;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeModerator()
|
||||||
getBadgeModerator()
|
|
||||||
{
|
{
|
||||||
return badgeModerator;
|
return badgeModerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeTurbo()
|
||||||
getBadgeTurbo()
|
|
||||||
{
|
{
|
||||||
return badgeTurbo;
|
return badgeTurbo;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgeBroadcaster()
|
||||||
getBadgeBroadcaster()
|
|
||||||
{
|
{
|
||||||
return badgeBroadcaster;
|
return badgeBroadcaster;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getBadgePremium()
|
||||||
getBadgePremium()
|
|
||||||
{
|
{
|
||||||
return badgePremium;
|
return badgePremium;
|
||||||
}
|
}
|
||||||
|
|
||||||
// cheer badges
|
// cheer badges
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge100000()
|
||||||
getCheerBadge100000()
|
|
||||||
{
|
{
|
||||||
return cheerBadge100000;
|
return cheerBadge100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge10000()
|
||||||
getCheerBadge10000()
|
|
||||||
{
|
{
|
||||||
return cheerBadge10000;
|
return cheerBadge10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge5000()
|
||||||
getCheerBadge5000()
|
|
||||||
{
|
{
|
||||||
return cheerBadge5000;
|
return cheerBadge5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge1000()
|
||||||
getCheerBadge1000()
|
|
||||||
{
|
{
|
||||||
return cheerBadge1000;
|
return cheerBadge1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge100()
|
||||||
getCheerBadge100()
|
|
||||||
{
|
{
|
||||||
return cheerBadge100;
|
return cheerBadge100;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getCheerBadge1()
|
||||||
getCheerBadge1()
|
|
||||||
{
|
{
|
||||||
return cheerBadge1;
|
return cheerBadge1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getButtonBan()
|
||||||
getButtonBan()
|
|
||||||
{
|
{
|
||||||
return buttonBan;
|
return buttonBan;
|
||||||
}
|
}
|
||||||
|
|
||||||
static messages::LazyLoadedImage *
|
static messages::LazyLoadedImage *getButtonTimeout()
|
||||||
getButtonTimeout()
|
|
||||||
{
|
{
|
||||||
return buttonTimeout;
|
return buttonTimeout;
|
||||||
}
|
}
|
||||||
|
|
41
setting.h
41
setting.h
|
@ -11,70 +11,65 @@ class BaseSetting
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BaseSetting(const QString &_name)
|
BaseSetting(const QString &_name)
|
||||||
: name(_name)
|
: _name(_name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QVariant getVariant() = 0;
|
virtual QVariant getVariant() = 0;
|
||||||
virtual void setVariant(QVariant value) = 0;
|
virtual void setVariant(QVariant value) = 0;
|
||||||
|
|
||||||
const QString &
|
const QString &getName() const
|
||||||
getName() const
|
|
||||||
{
|
{
|
||||||
return this->name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString _name;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Setting : public BaseSetting
|
class Setting : public BaseSetting
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems,
|
Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems, const QString &_name,
|
||||||
const QString &_name, const T &defaultValue)
|
const T &defaultValue)
|
||||||
: BaseSetting(_name)
|
: BaseSetting(_name)
|
||||||
, value(defaultValue)
|
, _value(defaultValue)
|
||||||
{
|
{
|
||||||
settingItems.push_back(*this);
|
settingItems.push_back(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const T &
|
const T &get() const
|
||||||
get() const
|
|
||||||
{
|
{
|
||||||
return this->value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void set(const T &newValue)
|
||||||
set(const T &newValue)
|
|
||||||
{
|
{
|
||||||
if (this->value != newValue) {
|
if (_value != newValue) {
|
||||||
this->value = newValue;
|
_value = newValue;
|
||||||
|
|
||||||
this->valueChanged(newValue);
|
valueChanged(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QVariant
|
virtual QVariant getVariant() final
|
||||||
getVariant() final
|
|
||||||
{
|
{
|
||||||
return QVariant::fromValue(value);
|
return QVariant::fromValue(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
virtual void setVariant(QVariant value) final
|
||||||
setVariant(QVariant value) final
|
|
||||||
{
|
{
|
||||||
if (value.isValid()) {
|
if (value.isValid()) {
|
||||||
assert(value.canConvert<T>());
|
assert(value.canConvert<T>());
|
||||||
this->set(value.value<T>());
|
set(value.value<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::signals2::signal<void(const T &newValue)> valueChanged;
|
boost::signals2::signal<void(const T &newValue)> valueChanged;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T value;
|
T _value;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
127
settings.cpp
127
settings.cpp
|
@ -1,127 +0,0 @@
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
Settings Settings::instance;
|
|
||||||
|
|
||||||
Settings::Settings()
|
|
||||||
: settings(
|
|
||||||
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
|
||||||
"/Chatterino/newsettings.ini",
|
|
||||||
QSettings::IniFormat)
|
|
||||||
, portable(false)
|
|
||||||
, wordTypeMask(messages::Word::Default)
|
|
||||||
, theme(this->settingsItems, "theme", "dark")
|
|
||||||
, themeHue(this->settingsItems, "themeHue", 0)
|
|
||||||
, selectedUser(this->settingsItems, "selectedUser", "")
|
|
||||||
, emoteScale(this->settingsItems, "emoteScale", 1.0)
|
|
||||||
, mouseScrollMultiplier(this->settingsItems, "mouseScrollMultiplier", 1.0)
|
|
||||||
, scaleEmotesByLineHeight(this->settingsItems, "scaleEmotesByLineHeight",
|
|
||||||
false)
|
|
||||||
, showTimestamps(this->settingsItems, "showTimestamps", true)
|
|
||||||
, showTimestampSeconds(this->settingsItems, "showTimestampSeconds", false)
|
|
||||||
, showLastMessageIndicator(this->settingsItems, "showLastMessageIndicator",
|
|
||||||
false)
|
|
||||||
, allowDouplicateMessages(this->settingsItems, "allowDouplicateMessages",
|
|
||||||
true)
|
|
||||||
, linksDoubleClickOnly(this->settingsItems, "linksDoubleClickOnly", false)
|
|
||||||
, hideEmptyInput(this->settingsItems, "hideEmptyInput", false)
|
|
||||||
, showMessageLength(this->settingsItems, "showMessageLength", false)
|
|
||||||
, seperateMessages(this->settingsItems, "seperateMessages", false)
|
|
||||||
, mentionUsersWithAt(this->settingsItems, "mentionUsersWithAt", false)
|
|
||||||
, allowCommandsAtEnd(this->settingsItems, "allowCommandsAtEnd", false)
|
|
||||||
, enableHighlights(this->settingsItems, "enableHighlights", true)
|
|
||||||
, enableHighlightSound(this->settingsItems, "enableHighlightSound", true)
|
|
||||||
, enableHighlightTaskbar(this->settingsItems, "enableHighlightTaskbar",
|
|
||||||
true)
|
|
||||||
, customHighlightSound(this->settingsItems, "customHighlightSound", false)
|
|
||||||
, enableTwitchEmotes(this->settingsItems, "enableTwitchEmotes", true)
|
|
||||||
, enableBttvEmotes(this->settingsItems, "enableBttvEmotes", true)
|
|
||||||
, enableFfzEmotes(this->settingsItems, "enableFfzEmotes", true)
|
|
||||||
, enableEmojis(this->settingsItems, "enableEmojis", true)
|
|
||||||
, enableGifAnimations(this->settingsItems, "enableGifAnimations", true)
|
|
||||||
, enableGifs(this->settingsItems, "enableGifs", true)
|
|
||||||
, inlineWhispers(this->settingsItems, "inlineWhispers", true)
|
|
||||||
, windowTopMost(this->settingsItems, "windowTopMost", false)
|
|
||||||
, hideTabX(this->settingsItems, "hideTabX", false)
|
|
||||||
, hidePreferencesButton(this->settingsItems, "hidePreferencesButton", false)
|
|
||||||
, hideUserButton(this->settingsItems, "hideUserButton", false)
|
|
||||||
{
|
|
||||||
this->showTimestamps.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
this->showTimestampSeconds.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
this->enableBttvEmotes.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
this->enableEmojis.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
this->enableFfzEmotes.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
this->enableTwitchEmotes.valueChanged.connect(
|
|
||||||
[this](const auto &) { this->updateWordTypeMask(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Settings::save()
|
|
||||||
{
|
|
||||||
for (auto &item : settingsItems) {
|
|
||||||
this->settings.setValue(item.get().getName(), item.get().getVariant());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Settings::load()
|
|
||||||
{
|
|
||||||
for (auto &item : settingsItems) {
|
|
||||||
qDebug() << "Loading settings for " << item.get().getName();
|
|
||||||
|
|
||||||
item.get().setVariant(this->settings.value(item.get().getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Settings::isIgnoredEmote(const QString &)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Settings::updateWordTypeMask()
|
|
||||||
{
|
|
||||||
using namespace messages;
|
|
||||||
|
|
||||||
uint32_t mask = Word::Text;
|
|
||||||
|
|
||||||
if (showTimestamps.get()) {
|
|
||||||
mask |= showTimestampSeconds.get() ? Word::TimestampWithSeconds
|
|
||||||
: Word::TimestampNoSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
mask |= enableTwitchEmotes.get() ? Word::TwitchEmoteImage
|
|
||||||
: Word::TwitchEmoteText;
|
|
||||||
mask |= enableFfzEmotes.get() ? Word::FfzEmoteImage : Word::FfzEmoteText;
|
|
||||||
mask |= enableBttvEmotes.get() ? Word::BttvEmoteImage : Word::BttvEmoteText;
|
|
||||||
mask |= (enableBttvEmotes.get() && enableGifs.get()) ? Word::BttvEmoteImage
|
|
||||||
: Word::BttvEmoteText;
|
|
||||||
mask |= enableEmojis.get() ? Word::EmojiImage : Word::EmojiText;
|
|
||||||
|
|
||||||
mask |= Word::BitsAmount;
|
|
||||||
mask |= enableGifs.get() ? Word::BitsAnimated : Word::BitsStatic;
|
|
||||||
|
|
||||||
mask |= Word::Badges;
|
|
||||||
mask |= Word::Username;
|
|
||||||
|
|
||||||
Word::Type _mask = (Word::Type)mask;
|
|
||||||
|
|
||||||
// if (mask != _mask) {
|
|
||||||
wordTypeMask = _mask;
|
|
||||||
|
|
||||||
emit wordTypeMaskChanged();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace chatterino
|
|
146
settingsmanager.cpp
Normal file
146
settingsmanager.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#include "settingsmanager.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
SettingsManager SettingsManager::instance;
|
||||||
|
|
||||||
|
SettingsManager::SettingsManager()
|
||||||
|
: _settings(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
||||||
|
"/Chatterino/newsettings.ini",
|
||||||
|
QSettings::IniFormat)
|
||||||
|
, _portable(false)
|
||||||
|
, _wordTypeMask(Word::Default)
|
||||||
|
, theme(_settingsItems, "theme", "dark")
|
||||||
|
, themeHue(_settingsItems, "themeHue", 0)
|
||||||
|
, selectedUser(_settingsItems, "selectedUser", "")
|
||||||
|
, emoteScale(_settingsItems, "emoteScale", 1.0)
|
||||||
|
, mouseScrollMultiplier(_settingsItems, "mouseScrollMultiplier", 1.0)
|
||||||
|
, scaleEmotesByLineHeight(_settingsItems, "scaleEmotesByLineHeight", false)
|
||||||
|
, showTimestamps(_settingsItems, "showTimestamps", true)
|
||||||
|
, showTimestampSeconds(_settingsItems, "showTimestampSeconds", false)
|
||||||
|
, showLastMessageIndicator(_settingsItems, "showLastMessageIndicator", false)
|
||||||
|
, allowDouplicateMessages(_settingsItems, "allowDouplicateMessages", true)
|
||||||
|
, linksDoubleClickOnly(_settingsItems, "linksDoubleClickOnly", false)
|
||||||
|
, hideEmptyInput(_settingsItems, "hideEmptyInput", false)
|
||||||
|
, showMessageLength(_settingsItems, "showMessageLength", false)
|
||||||
|
, seperateMessages(_settingsItems, "seperateMessages", false)
|
||||||
|
, mentionUsersWithAt(_settingsItems, "mentionUsersWithAt", false)
|
||||||
|
, allowCommandsAtEnd(_settingsItems, "allowCommandsAtEnd", false)
|
||||||
|
, enableHighlights(_settingsItems, "enableHighlights", true)
|
||||||
|
, enableHighlightSound(_settingsItems, "enableHighlightSound", true)
|
||||||
|
, enableHighlightTaskbar(_settingsItems, "enableHighlightTaskbar", true)
|
||||||
|
, customHighlightSound(_settingsItems, "customHighlightSound", false)
|
||||||
|
, enableTwitchEmotes(_settingsItems, "enableTwitchEmotes", true)
|
||||||
|
, enableBttvEmotes(_settingsItems, "enableBttvEmotes", true)
|
||||||
|
, enableFfzEmotes(_settingsItems, "enableFfzEmotes", true)
|
||||||
|
, enableEmojis(_settingsItems, "enableEmojis", true)
|
||||||
|
, enableGifAnimations(_settingsItems, "enableGifAnimations", true)
|
||||||
|
, enableGifs(_settingsItems, "enableGifs", true)
|
||||||
|
, inlineWhispers(_settingsItems, "inlineWhispers", true)
|
||||||
|
, windowTopMost(_settingsItems, "windowTopMost", false)
|
||||||
|
, hideTabX(_settingsItems, "hideTabX", false)
|
||||||
|
, hidePreferencesButton(_settingsItems, "hidePreferencesButton", false)
|
||||||
|
, hideUserButton(_settingsItems, "hideUserButton", false)
|
||||||
|
, useCustomWindowFrame(_settingsItems, "useCustomWindowFrame", true)
|
||||||
|
{
|
||||||
|
this->showTimestamps.valueChanged.connect([this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
this->showTimestampSeconds.valueChanged.connect(
|
||||||
|
[this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
this->enableBttvEmotes.valueChanged.connect(
|
||||||
|
[this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
this->enableEmojis.valueChanged.connect([this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
this->enableFfzEmotes.valueChanged.connect(
|
||||||
|
[this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
this->enableTwitchEmotes.valueChanged.connect(
|
||||||
|
[this](const auto &) { this->updateWordTypeMask(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsManager::save()
|
||||||
|
{
|
||||||
|
for (auto &item : _settingsItems) {
|
||||||
|
_settings.setValue(item.get().getName(), item.get().getVariant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsManager::load()
|
||||||
|
{
|
||||||
|
for (auto &item : _settingsItems) {
|
||||||
|
qDebug() << "Loading settings for " << item.get().getName();
|
||||||
|
|
||||||
|
item.get().setVariant(_settings.value(item.get().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Word::Type SettingsManager::getWordTypeMask()
|
||||||
|
{
|
||||||
|
return _wordTypeMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsManager::isIgnoredEmote(const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingsManager::getPortable()
|
||||||
|
{
|
||||||
|
return _portable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsManager::setPortable(bool value)
|
||||||
|
{
|
||||||
|
_portable = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSettings &SettingsManager::getQSettings()
|
||||||
|
{
|
||||||
|
return _settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsManager::updateWordTypeMask()
|
||||||
|
{
|
||||||
|
uint32_t mask = Word::Text;
|
||||||
|
|
||||||
|
if (showTimestamps.get()) {
|
||||||
|
mask |= showTimestampSeconds.get() ? Word::TimestampWithSeconds : Word::TimestampNoSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask |= enableTwitchEmotes.get() ? Word::TwitchEmoteImage : Word::TwitchEmoteText;
|
||||||
|
mask |= enableFfzEmotes.get() ? Word::FfzEmoteImage : Word::FfzEmoteText;
|
||||||
|
mask |= enableBttvEmotes.get() ? Word::BttvEmoteImage : Word::BttvEmoteText;
|
||||||
|
mask |=
|
||||||
|
(enableBttvEmotes.get() && enableGifs.get()) ? Word::BttvEmoteImage : Word::BttvEmoteText;
|
||||||
|
mask |= enableEmojis.get() ? Word::EmojiImage : Word::EmojiText;
|
||||||
|
|
||||||
|
mask |= Word::BitsAmount;
|
||||||
|
mask |= enableGifs.get() ? Word::BitsAnimated : Word::BitsStatic;
|
||||||
|
|
||||||
|
mask |= Word::Badges;
|
||||||
|
mask |= Word::Username;
|
||||||
|
|
||||||
|
Word::Type _mask = (Word::Type)mask;
|
||||||
|
|
||||||
|
if (mask != _mask) {
|
||||||
|
_wordTypeMask = _mask;
|
||||||
|
|
||||||
|
emit wordTypeMaskChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSnapshot SettingsManager::createSnapshot()
|
||||||
|
{
|
||||||
|
SettingsSnapshot snapshot;
|
||||||
|
|
||||||
|
for (auto &item : this->_settingsItems) {
|
||||||
|
snapshot.addItem(item, item.get().getVariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace chatterino
|
|
@ -9,75 +9,10 @@
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
class Settings : public QObject
|
class SettingsManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
|
||||||
static Settings &
|
|
||||||
getInstance()
|
|
||||||
{
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void load();
|
|
||||||
void save();
|
|
||||||
|
|
||||||
messages::Word::Type
|
|
||||||
getWordTypeMask()
|
|
||||||
{
|
|
||||||
return wordTypeMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isIgnoredEmote(const QString &emote);
|
|
||||||
|
|
||||||
bool
|
|
||||||
getPortable()
|
|
||||||
{
|
|
||||||
return portable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setPortable(bool value)
|
|
||||||
{
|
|
||||||
portable = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSettings &
|
|
||||||
getQSettings()
|
|
||||||
{
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsSnapshot
|
|
||||||
createSnapshot()
|
|
||||||
{
|
|
||||||
SettingsSnapshot snapshot;
|
|
||||||
|
|
||||||
for (auto &item : this->settingsItems) {
|
|
||||||
snapshot.addItem(item, item.get().getVariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void wordTypeMaskChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Settings();
|
|
||||||
|
|
||||||
static Settings instance;
|
|
||||||
|
|
||||||
void updateWordTypeMask();
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
std::vector<std::reference_wrapper<BaseSetting>> settingsItems;
|
|
||||||
|
|
||||||
bool portable;
|
|
||||||
|
|
||||||
messages::Word::Type wordTypeMask;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Setting<QString> theme;
|
Setting<QString> theme;
|
||||||
Setting<float> themeHue;
|
Setting<float> themeHue;
|
||||||
|
@ -110,6 +45,41 @@ public:
|
||||||
Setting<bool> hideTabX;
|
Setting<bool> hideTabX;
|
||||||
Setting<bool> hidePreferencesButton;
|
Setting<bool> hidePreferencesButton;
|
||||||
Setting<bool> hideUserButton;
|
Setting<bool> hideUserButton;
|
||||||
|
Setting<bool> useCustomWindowFrame;
|
||||||
|
|
||||||
|
void load();
|
||||||
|
void save();
|
||||||
|
|
||||||
|
messages::Word::Type getWordTypeMask();
|
||||||
|
bool isIgnoredEmote(const QString &emote);
|
||||||
|
bool getPortable();
|
||||||
|
void setPortable(bool value);
|
||||||
|
QSettings &getQSettings();
|
||||||
|
SettingsSnapshot createSnapshot();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void wordTypeMaskChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
SettingsManager();
|
||||||
|
|
||||||
|
// variables
|
||||||
|
QSettings _settings;
|
||||||
|
std::vector<std::reference_wrapper<BaseSetting>> _settingsItems;
|
||||||
|
bool _portable;
|
||||||
|
messages::Word::Type _wordTypeMask;
|
||||||
|
|
||||||
|
// methods
|
||||||
|
void updateWordTypeMask();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static SettingsManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static SettingsManager instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
|
@ -3,34 +3,31 @@
|
||||||
|
|
||||||
#include "setting.h"
|
#include "setting.h"
|
||||||
|
|
||||||
struct SettingsSnapshot {
|
namespace chatterino {
|
||||||
private:
|
|
||||||
std::vector<
|
|
||||||
std::pair<std::reference_wrapper<chatterino::BaseSetting>, QVariant>>
|
|
||||||
items;
|
|
||||||
|
|
||||||
|
struct SettingsSnapshot {
|
||||||
public:
|
public:
|
||||||
SettingsSnapshot()
|
SettingsSnapshot()
|
||||||
: items()
|
: _items()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void addItem(std::reference_wrapper<BaseSetting> setting, const QVariant &value)
|
||||||
addItem(std::reference_wrapper<chatterino::BaseSetting> setting,
|
|
||||||
const QVariant &value)
|
|
||||||
{
|
{
|
||||||
items.push_back(
|
_items.push_back(
|
||||||
std::pair<std::reference_wrapper<chatterino::BaseSetting>,
|
std::pair<std::reference_wrapper<BaseSetting>, QVariant>(setting.get(), value));
|
||||||
QVariant>(setting.get(), value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void apply()
|
||||||
apply()
|
|
||||||
{
|
{
|
||||||
for (auto &item : this->items) {
|
for (auto &item : _items) {
|
||||||
item.first.get().setVariant(item.second);
|
item.first.get().setVariant(item.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<std::reference_wrapper<BaseSetting>, QVariant>> _items;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#endif // SETTINGSSNAPSHOT_H
|
#endif // SETTINGSSNAPSHOT_H
|
||||||
|
|
34
twitch/emotevalue.h
Normal file
34
twitch/emotevalue.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef TWITCHEMOTEVALUE_H
|
||||||
|
#define TWITCHEMOTEVALUE_H
|
||||||
|
|
||||||
|
#include "QString"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace twitch {
|
||||||
|
|
||||||
|
struct EmoteValue {
|
||||||
|
public:
|
||||||
|
int getSet()
|
||||||
|
{
|
||||||
|
return _set;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getId()
|
||||||
|
{
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &getChannelName()
|
||||||
|
{
|
||||||
|
return _channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _set;
|
||||||
|
int _id;
|
||||||
|
QString _channelName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TWITCHEMOTEVALUE_H
|
347
twitch/twitchmessagebuilder.cpp
Normal file
347
twitch/twitchmessagebuilder.cpp
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
#include "twitch/twitchmessagebuilder.h"
|
||||||
|
#include "colorscheme.h"
|
||||||
|
#include "emojis.h"
|
||||||
|
#include "emotemanager.h"
|
||||||
|
#include "ircmanager.h"
|
||||||
|
#include "resources.h"
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace twitch {
|
||||||
|
TwitchMessageBuilder::TwitchMessageBuilder()
|
||||||
|
: MessageBuilder()
|
||||||
|
, messageId()
|
||||||
|
, userName()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwitchMessageBuilder::appendTwitchBadges(const QStringList &badges)
|
||||||
|
{
|
||||||
|
for (QString badge : badges) {
|
||||||
|
if (badge.startsWith("bits/")) {
|
||||||
|
long long int cheer = std::strtoll(badge.mid(5).toStdString().c_str(), NULL, 10);
|
||||||
|
appendWord(Word(EmoteManager::getInstance().getCheerBadge(cheer), Word::BadgeCheer,
|
||||||
|
QString(), QString("Twitch Cheer" + QString::number(cheer))));
|
||||||
|
} else if (badge == "staff/1") {
|
||||||
|
appendWord(Word(Resources::getBadgeStaff(), Word::BadgeStaff, QString(),
|
||||||
|
QString("Twitch Staff")));
|
||||||
|
} else if (badge == "admin/1") {
|
||||||
|
appendWord(Word(Resources::getBadgeAdmin(), Word::BadgeAdmin, QString(),
|
||||||
|
QString("Twitch Admin")));
|
||||||
|
} else if (badge == "global_mod/1") {
|
||||||
|
appendWord(Word(Resources::getBadgeGlobalmod(), Word::BadgeGlobalMod, QString(),
|
||||||
|
QString("Global Moderator")));
|
||||||
|
} else if (badge == "moderator/1") {
|
||||||
|
// TODO: implement this xD
|
||||||
|
appendWord(Word(Resources::getBadgeTurbo(), Word::BadgeModerator, QString(),
|
||||||
|
QString("Channel Moderator"))); // custom badge
|
||||||
|
} else if (badge == "turbo/1") {
|
||||||
|
appendWord(Word(Resources::getBadgeStaff(), Word::BadgeTurbo, QString(),
|
||||||
|
QString("Turbo Subscriber")));
|
||||||
|
} else if (badge == "broadcaster/1") {
|
||||||
|
appendWord(Word(Resources::getBadgeBroadcaster(), Word::BadgeBroadcaster, QString(),
|
||||||
|
QString("Channel Broadcaster")));
|
||||||
|
} else if (badge == "premium/1") {
|
||||||
|
appendWord(Word(Resources::getBadgePremium(), Word::BadgePremium, QString(),
|
||||||
|
QString("Twitch Prime")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircMessage,
|
||||||
|
Channel *channel, const MessageParseArgs &args)
|
||||||
|
{
|
||||||
|
TwitchMessageBuilder b;
|
||||||
|
|
||||||
|
// timestamp
|
||||||
|
b.appendTimestamp();
|
||||||
|
|
||||||
|
auto tags = ircMessage->tags();
|
||||||
|
|
||||||
|
auto iterator = tags.find("id");
|
||||||
|
|
||||||
|
if (iterator != tags.end()) {
|
||||||
|
b.messageId = iterator.value().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// timestamps
|
||||||
|
iterator = tags.find("tmi-sent-ts");
|
||||||
|
|
||||||
|
// mod buttons
|
||||||
|
static QString buttonBanTooltip("Ban user");
|
||||||
|
static QString buttonTimeoutTooltip("Timeout user");
|
||||||
|
|
||||||
|
b.appendWord(Word(Resources::getButtonBan(), Word::ButtonBan, QString(), buttonBanTooltip,
|
||||||
|
Link(Link::UserBan, ircMessage->account())));
|
||||||
|
b.appendWord(Word(Resources::getButtonTimeout(), Word::ButtonTimeout, QString(),
|
||||||
|
buttonTimeoutTooltip, Link(Link::UserTimeout, ircMessage->account())));
|
||||||
|
|
||||||
|
// badges
|
||||||
|
iterator = tags.find("badges");
|
||||||
|
|
||||||
|
if (iterator != tags.end()) {
|
||||||
|
auto badges = iterator.value().toString().split(',');
|
||||||
|
|
||||||
|
b.appendTwitchBadges(badges);
|
||||||
|
}
|
||||||
|
|
||||||
|
// color
|
||||||
|
QColor usernameColor = ColorScheme::getInstance().SystemMessageColor;
|
||||||
|
|
||||||
|
iterator = tags.find("color");
|
||||||
|
if (iterator != tags.end()) {
|
||||||
|
usernameColor = QColor(iterator.value().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// channel name
|
||||||
|
if (args.includeChannelName) {
|
||||||
|
QString channelName("#" + channel->getName());
|
||||||
|
b.appendWord(Word(channelName, Word::Misc, ColorScheme::getInstance().SystemMessageColor,
|
||||||
|
QString(channelName), QString(),
|
||||||
|
Link(Link::Url, channel->getName() + "\n" + b.messageId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// username
|
||||||
|
b.userName = ircMessage->nick();
|
||||||
|
|
||||||
|
if (b.userName.isEmpty()) {
|
||||||
|
b.userName = tags.value(QLatin1String("login")).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString displayName;
|
||||||
|
|
||||||
|
iterator = tags.find("display-name");
|
||||||
|
if (iterator == tags.end()) {
|
||||||
|
displayName = ircMessage->account();
|
||||||
|
} else {
|
||||||
|
displayName = iterator.value().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasLocalizedName = QString::compare(displayName, ircMessage->account()) == 0;
|
||||||
|
QString userDisplayString =
|
||||||
|
displayName + (hasLocalizedName ? (" (" + ircMessage->account() + ")") : QString());
|
||||||
|
|
||||||
|
if (args.isSentWhisper) {
|
||||||
|
userDisplayString += IrcManager::getInstance().getUser().getUserName() + " -> ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.isReceivedWhisper) {
|
||||||
|
userDisplayString += " -> " + IrcManager::getInstance().getUser().getUserName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ircMessage->isAction()) {
|
||||||
|
userDisplayString += ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
b.appendWord(
|
||||||
|
Word(userDisplayString, Word::Username, usernameColor, userDisplayString, QString()));
|
||||||
|
|
||||||
|
// highlights
|
||||||
|
// TODO: implement this xD
|
||||||
|
|
||||||
|
// bits
|
||||||
|
QString bits = "";
|
||||||
|
|
||||||
|
iterator = tags.find("bits");
|
||||||
|
if (iterator != tags.end()) {
|
||||||
|
bits = iterator.value().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// twitch emotes
|
||||||
|
std::vector<std::pair<long int, LazyLoadedImage *>> twitchEmotes;
|
||||||
|
|
||||||
|
iterator = tags.find("emotes");
|
||||||
|
|
||||||
|
if (iterator != tags.end()) {
|
||||||
|
auto emotes = iterator.value().toString().split('/');
|
||||||
|
|
||||||
|
for (QString emote : emotes) {
|
||||||
|
if (!emote.contains(':'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QStringList parameters = emote.split(':');
|
||||||
|
|
||||||
|
if (parameters.length() < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
long int id = std::stol(parameters.at(0).toStdString(), NULL, 10);
|
||||||
|
|
||||||
|
QStringList occurences = parameters.at(1).split(',');
|
||||||
|
|
||||||
|
for (QString occurence : occurences) {
|
||||||
|
QStringList coords = occurence.split('-');
|
||||||
|
|
||||||
|
if (coords.length() < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
long int start = std::stol(coords.at(0).toStdString(), NULL, 10);
|
||||||
|
long int end = std::stol(coords.at(1).toStdString(), NULL, 10);
|
||||||
|
|
||||||
|
if (start >= end || start < 0 || end > ircMessage->content().length())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QString name = ircMessage->content().mid(start, end - start + 1);
|
||||||
|
|
||||||
|
twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
|
||||||
|
start, EmoteManager::getInstance().getTwitchEmoteById(name, id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool operator()(const std::pair<long int, messages::LazyLoadedImage *> &a,
|
||||||
|
const std::pair<long int, messages::LazyLoadedImage *> &b)
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
}
|
||||||
|
} customLess;
|
||||||
|
|
||||||
|
std::sort(twitchEmotes.begin(), twitchEmotes.end(), customLess);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentTwitchEmote = twitchEmotes.begin();
|
||||||
|
|
||||||
|
// words
|
||||||
|
QColor textColor = ircMessage->isAction() ? usernameColor : ColorScheme::getInstance().Text;
|
||||||
|
|
||||||
|
QStringList splits = ircMessage->content().split(' ');
|
||||||
|
|
||||||
|
long int i = 0;
|
||||||
|
|
||||||
|
for (QString split : splits) {
|
||||||
|
// twitch emote
|
||||||
|
if (currentTwitchEmote != twitchEmotes.end() && currentTwitchEmote->first == i) {
|
||||||
|
b.appendWord(Word(currentTwitchEmote->second, Word::TwitchEmoteImage,
|
||||||
|
currentTwitchEmote->second->getName(),
|
||||||
|
currentTwitchEmote->second->getName() + QString("\nTwitch Emote")));
|
||||||
|
b.appendWord(Word(currentTwitchEmote->second->getName(), Word::TwitchEmoteText,
|
||||||
|
textColor, currentTwitchEmote->second->getName(),
|
||||||
|
currentTwitchEmote->second->getName() + QString("\nTwitch Emote")));
|
||||||
|
|
||||||
|
i += split.length() + 1;
|
||||||
|
currentTwitchEmote = std::next(currentTwitchEmote);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split words
|
||||||
|
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
|
||||||
|
|
||||||
|
Emojis::parseEmojis(parsed, split);
|
||||||
|
|
||||||
|
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
|
||||||
|
LazyLoadedImage *image = std::get<0>(tuple);
|
||||||
|
|
||||||
|
if (image == NULL) { // is text
|
||||||
|
QString string = std::get<1>(tuple);
|
||||||
|
|
||||||
|
static QRegularExpression cheerRegex("cheer[1-9][0-9]*");
|
||||||
|
|
||||||
|
// cheers
|
||||||
|
if (!bits.isEmpty() && string.length() >= 6 && cheerRegex.match(string).isValid()) {
|
||||||
|
auto cheer = string.mid(5).toInt();
|
||||||
|
|
||||||
|
QString color;
|
||||||
|
|
||||||
|
QColor bitsColor;
|
||||||
|
|
||||||
|
if (cheer >= 10000) {
|
||||||
|
color = "red";
|
||||||
|
bitsColor = QColor::fromHslF(0, 1, 0.5);
|
||||||
|
} else if (cheer >= 5000) {
|
||||||
|
color = "blue";
|
||||||
|
bitsColor = QColor::fromHslF(0.61, 1, 0.4);
|
||||||
|
} else if (cheer >= 1000) {
|
||||||
|
color = "green";
|
||||||
|
bitsColor = QColor::fromHslF(0.5, 1, 0.5);
|
||||||
|
} else if (cheer >= 100) {
|
||||||
|
color = "purple";
|
||||||
|
bitsColor = QColor::fromHslF(0.8, 1, 0.5);
|
||||||
|
} else {
|
||||||
|
color = "gray";
|
||||||
|
bitsColor = QColor::fromHslF(0.5f, 0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString bitsLinkAnimated =
|
||||||
|
QString("http://static-cdn.jtvnw.net/bits/dark/animated/" + color + "/1");
|
||||||
|
QString bitsLink =
|
||||||
|
QString("http://static-cdn.jtvnw.net/bits/dark/static/" + color + "/1");
|
||||||
|
|
||||||
|
LazyLoadedImage *imageAnimated =
|
||||||
|
EmoteManager::getInstance().getMiscImageFromCache().getOrAdd(
|
||||||
|
bitsLinkAnimated,
|
||||||
|
[&bitsLinkAnimated] { return new LazyLoadedImage(bitsLinkAnimated); });
|
||||||
|
LazyLoadedImage *image =
|
||||||
|
EmoteManager::getInstance().getMiscImageFromCache().getOrAdd(
|
||||||
|
bitsLink, [&bitsLink] { return new LazyLoadedImage(bitsLink); });
|
||||||
|
|
||||||
|
b.appendWord(Word(imageAnimated, Word::BitsAnimated, QString("cheer"),
|
||||||
|
QString("Twitch Cheer"),
|
||||||
|
Link(Link::Url, QString("https://blog.twitch.tv/"
|
||||||
|
"introducing-cheering-celebrate-"
|
||||||
|
"together-da62af41fac6"))));
|
||||||
|
b.appendWord(Word(image, Word::BitsStatic, QString("cheer"),
|
||||||
|
QString("Twitch Cheer"),
|
||||||
|
Link(Link::Url, QString("https://blog.twitch.tv/"
|
||||||
|
"introducing-cheering-celebrate-"
|
||||||
|
"together-da62af41fac6"))));
|
||||||
|
|
||||||
|
b.appendWord(Word(QString("x" + string.mid(5)), Word::BitsAmount, bitsColor,
|
||||||
|
QString(string.mid(5)), QString("Twitch Cheer"),
|
||||||
|
Link(Link::Url, QString("https://blog.twitch.tv/"
|
||||||
|
"introducing-cheering-celebrate-"
|
||||||
|
"together-da62af41fac6"))));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bttv / ffz emotes
|
||||||
|
LazyLoadedImage *bttvEmote;
|
||||||
|
|
||||||
|
// TODO: Implement this (ignored emotes)
|
||||||
|
if (EmoteManager::getInstance().getBttvEmotes().tryGet(string, bttvEmote) ||
|
||||||
|
channel->getBttvChannelEmotes().tryGet(string, bttvEmote) ||
|
||||||
|
EmoteManager::getInstance().getFfzEmotes().tryGet(string, bttvEmote) ||
|
||||||
|
channel->getFfzChannelEmotes().tryGet(string, bttvEmote) ||
|
||||||
|
EmoteManager::getInstance().getChatterinoEmotes().tryGet(string, bttvEmote)) {
|
||||||
|
b.appendWord(Word(bttvEmote, Word::BttvEmoteImage, bttvEmote->getName(),
|
||||||
|
bttvEmote->getTooltip(),
|
||||||
|
Link(Link::Url, bttvEmote->getUrl())));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually just a word
|
||||||
|
QString link = b.matchLink(string);
|
||||||
|
|
||||||
|
b.appendWord(Word(string, Word::Text, textColor, string, QString(),
|
||||||
|
link.isEmpty() ? Link() : Link(Link::Url, link)));
|
||||||
|
} else { // is emoji
|
||||||
|
static QString emojiTooltip("Emoji");
|
||||||
|
|
||||||
|
b.appendWord(Word(image, Word::EmojiImage, image->getName(), emojiTooltip));
|
||||||
|
Word(image->getName(), Word::EmojiText, textColor, image->getName(), emojiTooltip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += split.length() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this xD
|
||||||
|
// if (!isReceivedWhisper &&
|
||||||
|
// AppSettings.HighlightIgnoredUsers.ContainsKey(Username))
|
||||||
|
// {
|
||||||
|
// HighlightTab = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return b.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// bool
|
||||||
|
// sortTwitchEmotes(const std::pair<long int, LazyLoadedImage *> &a,
|
||||||
|
// const std::pair<long int, LazyLoadedImage *> &b)
|
||||||
|
//{
|
||||||
|
// return a.first < b.first;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
31
twitch/twitchmessagebuilder.h
Normal file
31
twitch/twitchmessagebuilder.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef TWITCHMESSAGEBUILDER_H
|
||||||
|
#define TWITCHMESSAGEBUILDER_H
|
||||||
|
|
||||||
|
#include "channel.h"
|
||||||
|
#include "messages/messagebuilder.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace twitch {
|
||||||
|
|
||||||
|
class TwitchMessageBuilder : public messages::MessageBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TwitchMessageBuilder();
|
||||||
|
|
||||||
|
void appendTwitchBadges(const QStringList &badges);
|
||||||
|
|
||||||
|
QString messageId;
|
||||||
|
QString userName;
|
||||||
|
|
||||||
|
static messages::SharedMessage parse(const Communi::IrcPrivateMessage *ircMessage,
|
||||||
|
Channel *channel, const messages::MessageParseArgs &args);
|
||||||
|
|
||||||
|
// static bool sortTwitchEmotes(
|
||||||
|
// const std::pair<long int, messages::LazyLoadedImage *> &a,
|
||||||
|
// const std::pair<long int, messages::LazyLoadedImage *> &b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // TWITCHMESSAGEBUILDER_H
|
349
twitch/twitchparsemessage.cpp
Normal file
349
twitch/twitchparsemessage.cpp
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
//#include "twitchparsemessage.h"
|
||||||
|
//#include "colorscheme.h"
|
||||||
|
//#include "emojis.h"
|
||||||
|
//#include "emotemanager.h"
|
||||||
|
//#include "ircmanager.h"
|
||||||
|
//#include "resources.h"
|
||||||
|
//#include "twitch/twitchmessagebuilder.h"
|
||||||
|
//
|
||||||
|
//#include <QRegularExpression>
|
||||||
|
//
|
||||||
|
// using namespace chatterino::messages;
|
||||||
|
//
|
||||||
|
// namespace chatterino {
|
||||||
|
// namespace twitch {
|
||||||
|
// SharedMessage
|
||||||
|
// twitchParseMessage(const Communi::IrcPrivateMessage *ircMessage,
|
||||||
|
// Channel *channel, const MessageParseArgs &args)
|
||||||
|
//{
|
||||||
|
// TwitchMessageBuilder b;
|
||||||
|
//
|
||||||
|
// // timestamp
|
||||||
|
// b.appendTimestamp();
|
||||||
|
//
|
||||||
|
// auto tags = ircMessage->tags();
|
||||||
|
//
|
||||||
|
// auto iterator = tags.find("id");
|
||||||
|
//
|
||||||
|
// if (iterator != tags.end()) {
|
||||||
|
// b.messageId = iterator.value().toString();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // timestamps
|
||||||
|
// iterator = tags.find("tmi-sent-ts");
|
||||||
|
//
|
||||||
|
// // mod buttons
|
||||||
|
// static QString buttonBanTooltip("Ban user");
|
||||||
|
// static QString buttonTimeoutTooltip("Timeout user");
|
||||||
|
//
|
||||||
|
// b.appendWord(Word(Resources::getButtonBan(), Word::ButtonBan, QString(),
|
||||||
|
// buttonBanTooltip,
|
||||||
|
// Link(Link::UserBan, ircMessage->account())));
|
||||||
|
// b.appendWord(Word(Resources::getButtonTimeout(), Word::ButtonTimeout,
|
||||||
|
// QString(), buttonTimeoutTooltip,
|
||||||
|
// Link(Link::UserTimeout, ircMessage->account())));
|
||||||
|
//
|
||||||
|
// // badges
|
||||||
|
// iterator = tags.find("badges");
|
||||||
|
//
|
||||||
|
// if (iterator != tags.end()) {
|
||||||
|
// auto badges = iterator.value().toString().split(',');
|
||||||
|
//
|
||||||
|
// b.appendTwitchBadges(badges);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // color
|
||||||
|
// QColor usernameColor = ColorScheme::getInstance().SystemMessageColor;
|
||||||
|
//
|
||||||
|
// iterator = tags.find("color");
|
||||||
|
// if (iterator != tags.end()) {
|
||||||
|
// usernameColor = QColor(iterator.value().toString());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // channel name
|
||||||
|
// if (args.includeChannelName) {
|
||||||
|
// QString channelName("#" + channel->getName());
|
||||||
|
// b.appendWord(
|
||||||
|
// Word(channelName, Word::Misc,
|
||||||
|
// ColorScheme::getInstance().SystemMessageColor,
|
||||||
|
// QString(channelName), QString(),
|
||||||
|
// Link(Link::Url, channel->getName() + "\n" + b.messageId)));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // username
|
||||||
|
// b.userName = ircMessage->nick();
|
||||||
|
//
|
||||||
|
// if (b.userName.isEmpty()) {
|
||||||
|
// b.userName = tags.value(QLatin1String("login")).toString();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// QString displayName;
|
||||||
|
//
|
||||||
|
// iterator = tags.find("display-name");
|
||||||
|
// if (iterator == tags.end()) {
|
||||||
|
// displayName = ircMessage->account();
|
||||||
|
// } else {
|
||||||
|
// displayName = iterator.value().toString();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// bool hasLocalizedName =
|
||||||
|
// QString::compare(displayName, ircMessage->account()) == 0;
|
||||||
|
// QString userDisplayString =
|
||||||
|
// displayName +
|
||||||
|
// (hasLocalizedName ? (" (" + ircMessage->account() + ")") : QString());
|
||||||
|
//
|
||||||
|
// if (args.isSentWhisper) {
|
||||||
|
// userDisplayString +=
|
||||||
|
// IrcManager::getInstance().getUser().getUserName() + " -> ";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (args.isReceivedWhisper) {
|
||||||
|
// userDisplayString +=
|
||||||
|
// " -> " + IrcManager::getInstance().getUser().getUserName();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (!ircMessage->isAction()) {
|
||||||
|
// userDisplayString += ": ";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// b.appendWord(Word(userDisplayString, Word::Username, usernameColor,
|
||||||
|
// userDisplayString, QString()));
|
||||||
|
//
|
||||||
|
// // highlights
|
||||||
|
// // TODO: implement this xD
|
||||||
|
//
|
||||||
|
// // bits
|
||||||
|
// QString bits = "";
|
||||||
|
//
|
||||||
|
// iterator = tags.find("bits");
|
||||||
|
// if (iterator != tags.end()) {
|
||||||
|
// bits = iterator.value().toString();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // twitch emotes
|
||||||
|
// std::vector<std::pair<long int, LazyLoadedImage *>> twitchEmotes;
|
||||||
|
//
|
||||||
|
// iterator = tags.find("emotes");
|
||||||
|
//
|
||||||
|
// if (iterator != tags.end()) {
|
||||||
|
// auto emotes = iterator.value().toString().split('/');
|
||||||
|
//
|
||||||
|
// for (QString emote : emotes) {
|
||||||
|
// if (!emote.contains(':'))
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// QStringList parameters = emote.split(':');
|
||||||
|
//
|
||||||
|
// if (parameters.length() < 2)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// long int id = std::stol(parameters.at(0).toStdString(), NULL, 10);
|
||||||
|
//
|
||||||
|
// QStringList occurences = parameters.at(1).split(',');
|
||||||
|
//
|
||||||
|
// for (QString occurence : occurences) {
|
||||||
|
// QStringList coords = occurence.split('-');
|
||||||
|
//
|
||||||
|
// if (coords.length() < 2)
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// long int start =
|
||||||
|
// std::stol(coords.at(0).toStdString(), NULL, 10);
|
||||||
|
// long int end = std::stol(coords.at(1).toStdString(), NULL,
|
||||||
|
// 10);
|
||||||
|
//
|
||||||
|
// if (start >= end || start < 0 ||
|
||||||
|
// end > ircMessage->content().length())
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// QString name =
|
||||||
|
// ircMessage->content().mid(start, end - start + 1);
|
||||||
|
//
|
||||||
|
// twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
|
||||||
|
// start,
|
||||||
|
// EmoteManager::getInstance().getTwitchEmoteById(name,
|
||||||
|
// id)));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::sort(twitchEmotes.begin(), twitchEmotes.end(), sortTwitchEmotes);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// auto currentTwitchEmote = twitchEmotes.begin();
|
||||||
|
//
|
||||||
|
// // words
|
||||||
|
// QColor textColor = ircMessage->isAction() ? usernameColor
|
||||||
|
// :
|
||||||
|
// ColorScheme::getInstance().Text;
|
||||||
|
//
|
||||||
|
// QStringList splits = ircMessage->content().split(' ');
|
||||||
|
//
|
||||||
|
// long int i = 0;
|
||||||
|
//
|
||||||
|
// for (QString split : splits) {
|
||||||
|
// // twitch emote
|
||||||
|
// if (currentTwitchEmote != twitchEmotes.end() &&
|
||||||
|
// currentTwitchEmote->first == i) {
|
||||||
|
// b.appendWord(Word(currentTwitchEmote->second,
|
||||||
|
// Word::TwitchEmoteImage,
|
||||||
|
// currentTwitchEmote->second->getName(),
|
||||||
|
// currentTwitchEmote->second->getName() +
|
||||||
|
// QString("\nTwitch Emote")));
|
||||||
|
// b.appendWord(Word(currentTwitchEmote->second->getName(),
|
||||||
|
// Word::TwitchEmoteText, textColor,
|
||||||
|
// currentTwitchEmote->second->getName(),
|
||||||
|
// currentTwitchEmote->second->getName() +
|
||||||
|
// QString("\nTwitch Emote")));
|
||||||
|
//
|
||||||
|
// i += split.length() + 1;
|
||||||
|
// currentTwitchEmote = std::next(currentTwitchEmote);
|
||||||
|
//
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // split words
|
||||||
|
// std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
|
||||||
|
//
|
||||||
|
// Emojis::parseEmojis(parsed, split);
|
||||||
|
//
|
||||||
|
// for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
|
||||||
|
// LazyLoadedImage *image = std::get<0>(tuple);
|
||||||
|
//
|
||||||
|
// if (image == NULL) { // is text
|
||||||
|
// QString string = std::get<1>(tuple);
|
||||||
|
//
|
||||||
|
// static QRegularExpression cheerRegex("cheer[1-9][0-9]*");
|
||||||
|
//
|
||||||
|
// // cheers
|
||||||
|
// if (!bits.isEmpty() && string.length() >= 6 &&
|
||||||
|
// cheerRegex.match(string).isValid()) {
|
||||||
|
// auto cheer = string.mid(5).toInt();
|
||||||
|
//
|
||||||
|
// QString color;
|
||||||
|
//
|
||||||
|
// QColor bitsColor;
|
||||||
|
//
|
||||||
|
// if (cheer >= 10000) {
|
||||||
|
// color = "red";
|
||||||
|
// bitsColor = QColor::fromHslF(0, 1, 0.5);
|
||||||
|
// } else if (cheer >= 5000) {
|
||||||
|
// color = "blue";
|
||||||
|
// bitsColor = QColor::fromHslF(0.61, 1, 0.4);
|
||||||
|
// } else if (cheer >= 1000) {
|
||||||
|
// color = "green";
|
||||||
|
// bitsColor = QColor::fromHslF(0.5, 1, 0.5);
|
||||||
|
// } else if (cheer >= 100) {
|
||||||
|
// color = "purple";
|
||||||
|
// bitsColor = QColor::fromHslF(0.8, 1, 0.5);
|
||||||
|
// } else {
|
||||||
|
// color = "gray";
|
||||||
|
// bitsColor = QColor::fromHslF(0.5f, 0.5f, 0.5f);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// QString bitsLinkAnimated = QString(
|
||||||
|
// "http://static-cdn.jtvnw.net/bits/dark/animated/" +
|
||||||
|
// color + "/1");
|
||||||
|
// QString bitsLink = QString(
|
||||||
|
// "http://static-cdn.jtvnw.net/bits/dark/static/" +
|
||||||
|
// color + "/1");
|
||||||
|
//
|
||||||
|
// LazyLoadedImage *imageAnimated =
|
||||||
|
// EmoteManager::getInstance()
|
||||||
|
// .getMiscImageFromCache()
|
||||||
|
// .getOrAdd(bitsLinkAnimated, [&bitsLinkAnimated] {
|
||||||
|
// return new LazyLoadedImage(bitsLinkAnimated);
|
||||||
|
// });
|
||||||
|
// LazyLoadedImage *image =
|
||||||
|
// EmoteManager::getInstance()
|
||||||
|
// .getMiscImageFromCache()
|
||||||
|
// .getOrAdd(bitsLink, [&bitsLink] {
|
||||||
|
// return new LazyLoadedImage(bitsLink);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// b.appendWord(
|
||||||
|
// Word(imageAnimated, Word::BitsAnimated,
|
||||||
|
// QString("cheer"), QString("Twitch Cheer"),
|
||||||
|
// Link(Link::Url,
|
||||||
|
// QString("https://blog.twitch.tv/"
|
||||||
|
// "introducing-cheering-celebrate-"
|
||||||
|
// "together-da62af41fac6"))));
|
||||||
|
// b.appendWord(
|
||||||
|
// Word(image, Word::BitsStatic, QString("cheer"),
|
||||||
|
// QString("Twitch Cheer"),
|
||||||
|
// Link(Link::Url,
|
||||||
|
// QString("https://blog.twitch.tv/"
|
||||||
|
// "introducing-cheering-celebrate-"
|
||||||
|
// "together-da62af41fac6"))));
|
||||||
|
//
|
||||||
|
// b.appendWord(
|
||||||
|
// Word(QString("x" + string.mid(5)), Word::BitsAmount,
|
||||||
|
// bitsColor, QString(string.mid(5)),
|
||||||
|
// QString("Twitch Cheer"),
|
||||||
|
// Link(Link::Url,
|
||||||
|
// QString("https://blog.twitch.tv/"
|
||||||
|
// "introducing-cheering-celebrate-"
|
||||||
|
// "together-da62af41fac6"))));
|
||||||
|
//
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // bttv / ffz emotes
|
||||||
|
// LazyLoadedImage *bttvEmote;
|
||||||
|
//
|
||||||
|
// // TODO: Implement this (ignored emotes)
|
||||||
|
// if (EmoteManager::getInstance().getBttvEmotes().tryGet(
|
||||||
|
// string, bttvEmote) ||
|
||||||
|
// channel->getBttvChannelEmotes().tryGet(string, bttvEmote)
|
||||||
|
// ||
|
||||||
|
// EmoteManager::getInstance().getFfzEmotes().tryGet(
|
||||||
|
// string, bttvEmote) ||
|
||||||
|
// channel->getFfzChannelEmotes().tryGet(string, bttvEmote)
|
||||||
|
// ||
|
||||||
|
// EmoteManager::getInstance().getChatterinoEmotes().tryGet(
|
||||||
|
// string, bttvEmote)) {
|
||||||
|
// b.appendWord(Word(bttvEmote, Word::BttvEmoteImage,
|
||||||
|
// bttvEmote->getName(),
|
||||||
|
// bttvEmote->getTooltip(),
|
||||||
|
// Link(Link::Url, bttvEmote->getUrl())));
|
||||||
|
//
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // actually just a word
|
||||||
|
// QString link = b.matchLink(string);
|
||||||
|
//
|
||||||
|
// b.appendWord(
|
||||||
|
// Word(string, Word::Text, textColor, string, QString(),
|
||||||
|
// link.isEmpty() ? Link() : Link(Link::Url, link)));
|
||||||
|
// } else { // is emoji
|
||||||
|
// static QString emojiTooltip("Emoji");
|
||||||
|
//
|
||||||
|
// b.appendWord(Word(image, Word::EmojiImage, image->getName(),
|
||||||
|
// emojiTooltip));
|
||||||
|
// Word(image->getName(), Word::EmojiText, textColor,
|
||||||
|
// image->getName(), emojiTooltip);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// i += split.length() + 1;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: Implement this xD
|
||||||
|
// // if (!isReceivedWhisper &&
|
||||||
|
// // AppSettings.HighlightIgnoredUsers.ContainsKey(Username))
|
||||||
|
// // {
|
||||||
|
// // HighlightTab = false;
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// return b.build();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
// bool
|
||||||
|
// sortTwitchEmotes(const std::pair<long int, LazyLoadedImage *> &a,
|
||||||
|
// const std::pair<long int, LazyLoadedImage *> &b)
|
||||||
|
//{
|
||||||
|
// return a.first < b.first;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//
|
13
twitch/twitchparsemessage.h
Normal file
13
twitch/twitchparsemessage.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//#ifndef MESSAGEPARSER_H
|
||||||
|
//#define MESSAGEPARSER_H
|
||||||
|
//
|
||||||
|
//#include "messages/lazyloadedimage.h"
|
||||||
|
//#include "messages/messagebuilder.h"
|
||||||
|
//#include "messages/messageparseargs.h"
|
||||||
|
//
|
||||||
|
// namespace chatterino {
|
||||||
|
// namespace twitch {
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//#endif // MESSAGEPARSER_H
|
28
twitch/twitchuser.cpp
Normal file
28
twitch/twitchuser.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "twitchuser.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace twitch {
|
||||||
|
TwitchUser::TwitchUser(const QString &username, const QString &oauthToken,
|
||||||
|
const QString &oauthClient)
|
||||||
|
: IrcUser2(username, username, username, "oauth:" + oauthToken)
|
||||||
|
{
|
||||||
|
_oauthClient = oauthClient;
|
||||||
|
_oauthToken = oauthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &TwitchUser::getOAuthClient() const
|
||||||
|
{
|
||||||
|
return _oauthClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &TwitchUser::getOAuthToken() const
|
||||||
|
{
|
||||||
|
return _oauthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TwitchUser::isAnon() const
|
||||||
|
{
|
||||||
|
return IrcUser2::getNickName().startsWith("justinfan");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
twitch/twitchuser.h
Normal file
28
twitch/twitchuser.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef ACCOUNT_H
|
||||||
|
#define ACCOUNT_H
|
||||||
|
|
||||||
|
#include "ircuser2.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace twitch {
|
||||||
|
|
||||||
|
class TwitchUser : public IrcUser2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TwitchUser(const QString &username, const QString &oauthToken, const QString &oauthClient);
|
||||||
|
|
||||||
|
const QString &getOAuthToken() const;
|
||||||
|
const QString &getOAuthClient() const;
|
||||||
|
|
||||||
|
bool isAnon() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _oauthClient;
|
||||||
|
QString _oauthToken;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ACCOUNT_H
|
|
@ -1,35 +0,0 @@
|
||||||
#ifndef TWITCHEMOTEVALUE_H
|
|
||||||
#define TWITCHEMOTEVALUE_H
|
|
||||||
|
|
||||||
#include "QString"
|
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
|
|
||||||
struct TwitchEmoteValue {
|
|
||||||
public:
|
|
||||||
int
|
|
||||||
getSet()
|
|
||||||
{
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getId()
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getChannelName()
|
|
||||||
{
|
|
||||||
return channelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int set;
|
|
||||||
int id;
|
|
||||||
QString channelName;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // TWITCHEMOTEVALUE_H
|
|
16
usermanager.cpp
Normal file
16
usermanager.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "usermanager.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
AccountManager AccountManager::instance;
|
||||||
|
|
||||||
|
AccountManager::AccountManager()
|
||||||
|
: _anon("justinfan64537", "", "")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
twitch::TwitchUser &AccountManager::getAnon()
|
||||||
|
{
|
||||||
|
return _anon;
|
||||||
|
}
|
||||||
|
}
|
27
usermanager.h
Normal file
27
usermanager.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef ACCOUNTMANAGER_H
|
||||||
|
#define ACCOUNTMANAGER_H
|
||||||
|
|
||||||
|
#include "twitch/twitchuser.h"
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
|
class AccountManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static AccountManager &getInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
twitch::TwitchUser &getAnon();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static AccountManager instance;
|
||||||
|
|
||||||
|
AccountManager();
|
||||||
|
|
||||||
|
twitch::TwitchUser _anon;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ACCOUNTMANAGER_H
|
55
util/urlfetch.h
Normal file
55
util/urlfetch.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef URLFETCH_H
|
||||||
|
#define URLFETCH_H
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
static void urlFetch(const QString &url, std::function<void(QNetworkReply &)> successCallback)
|
||||||
|
{
|
||||||
|
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||||
|
|
||||||
|
QUrl requestUrl(url);
|
||||||
|
QNetworkRequest request(requestUrl);
|
||||||
|
|
||||||
|
QNetworkReply *reply = manager->get(request);
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
if (reply->error() == QNetworkReply::NetworkError::NoError) {
|
||||||
|
successCallback(*reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
manager->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void urlJsonFetch(const QString &url, std::function<void(QJsonObject &)> successCallback)
|
||||||
|
{
|
||||||
|
urlFetch(url, [=](QNetworkReply &reply) {
|
||||||
|
QByteArray data = reply.readAll();
|
||||||
|
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||||
|
|
||||||
|
if (jsonDoc.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject rootNode = jsonDoc.object();
|
||||||
|
|
||||||
|
successCallback(rootNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // URLFETCH_H
|
|
@ -1,7 +1,7 @@
|
||||||
#include "widgets/chatwidget.h"
|
#include "widgets/chatwidget.h"
|
||||||
#include "channels.h"
|
#include "channelmanager.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
#include "widgets/textinputdialog.h"
|
#include "widgets/textinputdialog.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -11,110 +11,167 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <boost/signals2.hpp>
|
#include <boost/signals2.hpp>
|
||||||
|
|
||||||
|
using namespace chatterino::messages;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
ChatWidget::ChatWidget(QWidget *parent)
|
ChatWidget::ChatWidget(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, messages()
|
, _messages()
|
||||||
, channel(Channels::getEmpty())
|
, _channel(ChannelManager::getInstance().getEmpty())
|
||||||
, channelName(QString())
|
, _channelName(QString())
|
||||||
, vbox(this)
|
, _vbox(this)
|
||||||
, header(this)
|
, _header(this)
|
||||||
, view(this)
|
, _view(this)
|
||||||
, input(this)
|
, _input(this)
|
||||||
{
|
{
|
||||||
this->vbox.setSpacing(0);
|
this->_vbox.setSpacing(0);
|
||||||
this->vbox.setMargin(1);
|
this->_vbox.setMargin(1);
|
||||||
|
|
||||||
this->vbox.addWidget(&header);
|
this->_vbox.addWidget(&_header);
|
||||||
this->vbox.addWidget(&view, 1);
|
this->_vbox.addWidget(&_view, 1);
|
||||||
this->vbox.addWidget(&input);
|
this->_vbox.addWidget(&_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatWidget::~ChatWidget()
|
ChatWidget::~ChatWidget()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Channel>
|
||||||
|
ChatWidget::getChannel() const
|
||||||
|
{
|
||||||
|
return _channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &
|
||||||
|
ChatWidget::getChannelName() const
|
||||||
|
{
|
||||||
|
return _channelName;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatWidget::setChannelName(const QString &name)
|
ChatWidget::setChannelName(const QString &name)
|
||||||
{
|
{
|
||||||
QString channel = name.trimmed();
|
QString channel = name.trimmed();
|
||||||
|
|
||||||
if (QString::compare(channel, this->channelName, Qt::CaseInsensitive) ==
|
// return if channel name is the same
|
||||||
0) {
|
if (QString::compare(channel, _channelName, Qt::CaseInsensitive) == 0) {
|
||||||
this->channelName = channel;
|
_channelName = channel;
|
||||||
this->header.updateChannelText();
|
_header.updateChannelText();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->channelName.isEmpty()) {
|
// remove current channel
|
||||||
Channels::removeChannel(this->channelName);
|
if (!_channelName.isEmpty()) {
|
||||||
|
ChannelManager::getInstance().removeChannel(_channelName);
|
||||||
|
|
||||||
this->messageAppendedConnection.disconnect();
|
detachChannel(_channel);
|
||||||
this->messageRemovedConnection.disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->channelName = channel;
|
// update members
|
||||||
this->header.updateChannelText();
|
_channelName = channel;
|
||||||
|
|
||||||
this->view.layoutMessages();
|
// update messages
|
||||||
|
_messages.clear();
|
||||||
messages.clear();
|
|
||||||
|
|
||||||
if (channel.isEmpty()) {
|
if (channel.isEmpty()) {
|
||||||
this->channel = NULL;
|
_channel = NULL;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this->channel = Channels::addChannel(channel);
|
_channel = ChannelManager::getInstance().addChannel(channel);
|
||||||
|
|
||||||
this->messageAppendedConnection =
|
attachChannel(_channel);
|
||||||
this->channel.get()->messageAppended.connect([this](
|
|
||||||
std::shared_ptr<messages::Message> &message) {
|
|
||||||
|
|
||||||
std::shared_ptr<messages::MessageRef> deleted;
|
|
||||||
|
|
||||||
auto messageRef = new messages::MessageRef(message);
|
|
||||||
|
|
||||||
this->messages.appendItem(
|
|
||||||
std::shared_ptr<messages::MessageRef>(messageRef), deleted);
|
|
||||||
});
|
|
||||||
|
|
||||||
this->messageRemovedConnection =
|
|
||||||
this->channel.get()->messageRemovedFromStart.connect(
|
|
||||||
[this](std::shared_ptr<messages::Message> &) {});
|
|
||||||
|
|
||||||
auto snapshot = this->channel.get()->getMessageSnapshot();
|
|
||||||
|
|
||||||
for (int i = 0; i < snapshot.getLength(); i++) {
|
|
||||||
std::shared_ptr<messages::MessageRef> deleted;
|
|
||||||
|
|
||||||
auto messageRef = new messages::MessageRef(snapshot[i]);
|
|
||||||
|
|
||||||
this->messages.appendItem(
|
|
||||||
std::shared_ptr<messages::MessageRef>(messageRef), deleted);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->view.layoutMessages();
|
// update header
|
||||||
this->view.update();
|
_header.updateChannelText();
|
||||||
|
|
||||||
|
// update view
|
||||||
|
_view.layoutMessages();
|
||||||
|
_view.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatWidget::attachChannel(SharedChannel channel)
|
||||||
|
{
|
||||||
|
// on new message
|
||||||
|
_messageAppendedConnection =
|
||||||
|
channel->messageAppended.connect([this](SharedMessage &message) {
|
||||||
|
SharedMessageRef deleted;
|
||||||
|
|
||||||
|
auto messageRef = new MessageRef(message);
|
||||||
|
|
||||||
|
if (_messages.appendItem(SharedMessageRef(messageRef), deleted)) {
|
||||||
|
qreal value =
|
||||||
|
std::max(0.0, _view.getScrollbar()->getDesiredValue() - 1);
|
||||||
|
|
||||||
|
_view.getScrollbar()->setDesiredValue(value, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// on message removed
|
||||||
|
_messageRemovedConnection =
|
||||||
|
_channel->messageRemovedFromStart.connect([this](SharedMessage &) {});
|
||||||
|
|
||||||
|
auto snapshot = _channel.get()->getMessageSnapshot();
|
||||||
|
|
||||||
|
for (int i = 0; i < snapshot.getLength(); i++) {
|
||||||
|
SharedMessageRef deleted;
|
||||||
|
|
||||||
|
auto messageRef = new MessageRef(snapshot[i]);
|
||||||
|
|
||||||
|
_messages.appendItem(SharedMessageRef(messageRef), deleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatWidget::detachChannel(std::shared_ptr<Channel> channel)
|
||||||
|
{
|
||||||
|
// on message added
|
||||||
|
_messageAppendedConnection.disconnect();
|
||||||
|
|
||||||
|
// on message removed
|
||||||
|
_messageRemovedConnection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
LimitedQueueSnapshot<SharedMessageRef>
|
||||||
|
ChatWidget::getMessagesSnapshot()
|
||||||
|
{
|
||||||
|
return _messages.getSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatWidget::showChangeChannelPopup()
|
ChatWidget::showChangeChannelPopup()
|
||||||
{
|
{
|
||||||
|
// create new input dialog and execute it
|
||||||
TextInputDialog dialog(this);
|
TextInputDialog dialog(this);
|
||||||
|
|
||||||
dialog.setText(this->channelName);
|
dialog.setText(_channelName);
|
||||||
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
setChannelName(dialog.getText());
|
setChannelName(dialog.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatWidget::layoutMessages()
|
||||||
|
{
|
||||||
|
if (_view.layoutMessages()) {
|
||||||
|
_view.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatWidget::updateGifEmotes()
|
||||||
|
{
|
||||||
|
_view.updateGifEmotes();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatWidget::paintEvent(QPaintEvent *)
|
ChatWidget::paintEvent(QPaintEvent *)
|
||||||
{
|
{
|
||||||
|
// color the background of the chat
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground);
|
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground);
|
||||||
|
@ -123,7 +180,7 @@ ChatWidget::paintEvent(QPaintEvent *)
|
||||||
void
|
void
|
||||||
ChatWidget::load(const boost::property_tree::ptree &tree)
|
ChatWidget::load(const boost::property_tree::ptree &tree)
|
||||||
{
|
{
|
||||||
// Load tab text
|
// load tab text
|
||||||
try {
|
try {
|
||||||
this->setChannelName(
|
this->setChannelName(
|
||||||
QString::fromStdString(tree.get<std::string>("channelName")));
|
QString::fromStdString(tree.get<std::string>("channelName")));
|
||||||
|
|
|
@ -27,51 +27,35 @@ public:
|
||||||
ChatWidget(QWidget *parent = 0);
|
ChatWidget(QWidget *parent = 0);
|
||||||
~ChatWidget();
|
~ChatWidget();
|
||||||
|
|
||||||
ChatWidgetView &
|
SharedChannel getChannel() const;
|
||||||
getView()
|
const QString &getChannelName() const;
|
||||||
{
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Channel>
|
|
||||||
getChannel() const
|
|
||||||
{
|
|
||||||
return channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString &
|
|
||||||
getChannelName() const
|
|
||||||
{
|
|
||||||
return channelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChannelName(const QString &name);
|
void setChannelName(const QString &name);
|
||||||
|
|
||||||
void showChangeChannelPopup();
|
void showChangeChannelPopup();
|
||||||
|
messages::LimitedQueueSnapshot<messages::SharedMessageRef> getMessagesSnapshot();
|
||||||
messages::LimitedQueueSnapshot<std::shared_ptr<messages::MessageRef>>
|
void layoutMessages();
|
||||||
getMessagesSnapshot()
|
void updateGifEmotes();
|
||||||
{
|
|
||||||
return messages.getSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *) override;
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
messages::LimitedQueue<std::shared_ptr<messages::MessageRef>> messages;
|
void attachChannel(std::shared_ptr<Channel> _channel);
|
||||||
|
void detachChannel(std::shared_ptr<Channel> _channel);
|
||||||
|
|
||||||
std::shared_ptr<Channel> channel;
|
messages::LimitedQueue<messages::SharedMessageRef> _messages;
|
||||||
QString channelName;
|
|
||||||
|
|
||||||
QFont font;
|
SharedChannel _channel;
|
||||||
QVBoxLayout vbox;
|
QString _channelName;
|
||||||
ChatWidgetHeader header;
|
|
||||||
ChatWidgetView view;
|
|
||||||
ChatWidgetInput input;
|
|
||||||
|
|
||||||
boost::signals2::connection messageAppendedConnection;
|
QFont _font;
|
||||||
boost::signals2::connection messageRemovedConnection;
|
QVBoxLayout _vbox;
|
||||||
|
ChatWidgetHeader _header;
|
||||||
|
ChatWidgetView _view;
|
||||||
|
ChatWidgetInput _input;
|
||||||
|
|
||||||
|
boost::signals2::connection _messageAppendedConnection;
|
||||||
|
boost::signals2::connection _messageRemovedConnection;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void load(const boost::property_tree::ptree &tree);
|
void load(const boost::property_tree::ptree &tree);
|
||||||
|
|
|
@ -13,90 +13,81 @@ namespace widgets {
|
||||||
|
|
||||||
ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent)
|
ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent)
|
||||||
: QWidget()
|
: QWidget()
|
||||||
, chatWidget(parent)
|
, _chatWidget(parent)
|
||||||
, dragStart()
|
, _dragStart()
|
||||||
, dragging(false)
|
, _dragging(false)
|
||||||
, leftLabel()
|
, _leftLabel()
|
||||||
, middleLabel()
|
, _middleLabel()
|
||||||
, rightLabel()
|
, _rightLabel()
|
||||||
, leftMenu(this)
|
, _leftMenu(this)
|
||||||
, rightMenu(this)
|
, _rightMenu(this)
|
||||||
{
|
{
|
||||||
setFixedHeight(32);
|
setFixedHeight(32);
|
||||||
|
|
||||||
updateColors();
|
updateColors();
|
||||||
updateChannelText();
|
updateChannelText();
|
||||||
|
|
||||||
setLayout(&this->hbox);
|
setLayout(&_hbox);
|
||||||
this->hbox.setMargin(0);
|
_hbox.setMargin(0);
|
||||||
this->hbox.addWidget(&this->leftLabel);
|
_hbox.addWidget(&_leftLabel);
|
||||||
this->hbox.addWidget(&this->middleLabel, 1);
|
_hbox.addWidget(&_middleLabel, 1);
|
||||||
this->hbox.addWidget(&this->rightLabel);
|
_hbox.addWidget(&_rightLabel);
|
||||||
|
|
||||||
// left
|
// left
|
||||||
this->leftLabel.getLabel().setTextFormat(Qt::RichText);
|
_leftLabel.getLabel().setTextFormat(Qt::RichText);
|
||||||
this->leftLabel.getLabel().setText(
|
_leftLabel.getLabel().setText("<img src=':/images/tool_moreCollapser_off16.png' />");
|
||||||
"<img src=':/images/tool_moreCollapser_off16.png' />");
|
|
||||||
|
|
||||||
QObject::connect(&this->leftLabel, &ChatWidgetHeaderButton::clicked, this,
|
QObject::connect(&_leftLabel, &ChatWidgetHeaderButton::clicked, this,
|
||||||
&ChatWidgetHeader::leftButtonClicked);
|
&ChatWidgetHeader::leftButtonClicked);
|
||||||
|
|
||||||
this->leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()),
|
_leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), QKeySequence(tr("Ctrl+T")));
|
||||||
QKeySequence(tr("Ctrl+T")));
|
_leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), QKeySequence(tr("Ctrl+W")));
|
||||||
this->leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()),
|
_leftMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
|
||||||
QKeySequence(tr("Ctrl+W")));
|
_leftMenu.addAction("Popup", this, SLOT(menuPopup()));
|
||||||
this->leftMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
|
_leftMenu.addSeparator();
|
||||||
this->leftMenu.addAction("Popup", this, SLOT(menuPopup()));
|
_leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()),
|
||||||
this->leftMenu.addSeparator();
|
QKeySequence(tr("Ctrl+R")));
|
||||||
this->leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()),
|
_leftMenu.addAction("Clear chat", this, SLOT(menuClearChat()));
|
||||||
QKeySequence(tr("Ctrl+R")));
|
_leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel()));
|
||||||
this->leftMenu.addAction("Clear chat", this, SLOT(menuClearChat()));
|
_leftMenu.addAction("Open pop-out player", this, SLOT(menuPopupPlayer()));
|
||||||
this->leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel()));
|
_leftMenu.addSeparator();
|
||||||
this->leftMenu.addAction("Open pop-out player", this,
|
_leftMenu.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes()));
|
||||||
SLOT(menuPopupPlayer()));
|
_leftMenu.addAction("Manual reconnect", this, SLOT(menuManualReconnect()));
|
||||||
this->leftMenu.addSeparator();
|
_leftMenu.addSeparator();
|
||||||
this->leftMenu.addAction("Reload channel emotes", this,
|
_leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
|
||||||
SLOT(menuReloadChannelEmotes()));
|
|
||||||
this->leftMenu.addAction("Manual reconnect", this,
|
|
||||||
SLOT(menuManualReconnect()));
|
|
||||||
this->leftMenu.addSeparator();
|
|
||||||
this->leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
|
|
||||||
|
|
||||||
// middle
|
// middle
|
||||||
this->middleLabel.setAlignment(Qt::AlignCenter);
|
_middleLabel.setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
connect(&this->middleLabel, &SignalLabel::mouseDoubleClick, this,
|
connect(&_middleLabel, &SignalLabel::mouseDoubleClick, this,
|
||||||
&ChatWidgetHeader::mouseDoubleClickEvent);
|
&ChatWidgetHeader::mouseDoubleClickEvent);
|
||||||
// connect(&this->middleLabel, &SignalLabel::mouseDown, this,
|
// connect(&this->middleLabel, &SignalLabel::mouseDown, this,
|
||||||
// &ChatWidgetHeader::mouseDoubleClickEvent);
|
// &ChatWidgetHeader::mouseDoubleClickEvent);
|
||||||
|
|
||||||
// right
|
// right
|
||||||
this->rightLabel.setMinimumWidth(height());
|
_rightLabel.setMinimumWidth(height());
|
||||||
this->rightLabel.getLabel().setTextFormat(Qt::RichText);
|
_rightLabel.getLabel().setTextFormat(Qt::RichText);
|
||||||
this->rightLabel.getLabel().setText("ayy");
|
_rightLabel.getLabel().setText("ayy");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::updateColors()
|
||||||
ChatWidgetHeader::updateColors()
|
|
||||||
{
|
{
|
||||||
QPalette palette;
|
QPalette palette;
|
||||||
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
|
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
|
||||||
|
|
||||||
this->leftLabel.setPalette(palette);
|
_leftLabel.setPalette(palette);
|
||||||
this->middleLabel.setPalette(palette);
|
_middleLabel.setPalette(palette);
|
||||||
this->rightLabel.setPalette(palette);
|
_rightLabel.setPalette(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::updateChannelText()
|
||||||
ChatWidgetHeader::updateChannelText()
|
|
||||||
{
|
{
|
||||||
const QString &c = this->chatWidget->getChannelName();
|
const QString &c = _chatWidget->getChannelName();
|
||||||
|
|
||||||
this->middleLabel.setText(c.isEmpty() ? "<no channel>" : c);
|
_middleLabel.setText(c.isEmpty() ? "<no channel>" : c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::paintEvent(QPaintEvent *)
|
||||||
ChatWidgetHeader::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
@ -105,21 +96,19 @@ ChatWidgetHeader::paintEvent(QPaintEvent *)
|
||||||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
|
||||||
ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
this->dragging = true;
|
_dragging = true;
|
||||||
|
|
||||||
this->dragStart = event->pos();
|
_dragStart = event->pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
||||||
ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (this->dragging) {
|
if (_dragging) {
|
||||||
if (std::abs(this->dragStart.x() - event->pos().x()) > 12 ||
|
if (std::abs(_dragStart.x() - event->pos().x()) > 12 ||
|
||||||
std::abs(this->dragStart.y() - event->pos().y()) > 12) {
|
std::abs(_dragStart.y() - event->pos().y()) > 12) {
|
||||||
auto chatWidget = this->chatWidget;
|
auto chatWidget = _chatWidget;
|
||||||
auto page = static_cast<NotebookPage *>(chatWidget->parentWidget());
|
auto page = static_cast<NotebookPage *>(chatWidget->parentWidget());
|
||||||
|
|
||||||
if (page != NULL) {
|
if (page != NULL) {
|
||||||
|
@ -149,73 +138,58 @@ ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
this->chatWidget->showChangeChannelPopup();
|
_chatWidget->showChangeChannelPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::leftButtonClicked()
|
||||||
ChatWidgetHeader::leftButtonClicked()
|
|
||||||
{
|
{
|
||||||
this->leftMenu.move(
|
_leftMenu.move(_leftLabel.mapToGlobal(QPoint(0, _leftLabel.height())));
|
||||||
this->leftLabel.mapToGlobal(QPoint(0, this->leftLabel.height())));
|
_leftMenu.show();
|
||||||
this->leftMenu.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::rightButtonClicked()
|
||||||
ChatWidgetHeader::rightButtonClicked()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeader::menuAddSplit()
|
||||||
ChatWidgetHeader::menuAddSplit()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuCloseSplit()
|
||||||
ChatWidgetHeader::menuCloseSplit()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuMoveSplit()
|
||||||
ChatWidgetHeader::menuMoveSplit()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuPopup()
|
||||||
ChatWidgetHeader::menuPopup()
|
|
||||||
{
|
{
|
||||||
auto widget = new ChatWidget();
|
auto widget = new ChatWidget();
|
||||||
widget->setChannelName(this->chatWidget->getChannelName());
|
widget->setChannelName(_chatWidget->getChannelName());
|
||||||
widget->show();
|
widget->show();
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuChangeChannel()
|
||||||
ChatWidgetHeader::menuChangeChannel()
|
|
||||||
{
|
{
|
||||||
this->chatWidget->showChangeChannelPopup();
|
_chatWidget->showChangeChannelPopup();
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuClearChat()
|
||||||
ChatWidgetHeader::menuClearChat()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuOpenChannel()
|
||||||
ChatWidgetHeader::menuOpenChannel()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuPopupPlayer()
|
||||||
ChatWidgetHeader::menuPopupPlayer()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuReloadChannelEmotes()
|
||||||
ChatWidgetHeader::menuReloadChannelEmotes()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuManualReconnect()
|
||||||
ChatWidgetHeader::menuManualReconnect()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void
|
void ChatWidgetHeader::menuShowChangelog()
|
||||||
ChatWidgetHeader::menuShowChangelog()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,9 @@ class ChatWidgetHeader : public QWidget
|
||||||
public:
|
public:
|
||||||
explicit ChatWidgetHeader(ChatWidget *parent);
|
explicit ChatWidgetHeader(ChatWidget *parent);
|
||||||
|
|
||||||
ChatWidget *
|
ChatWidget *getChatWidget()
|
||||||
getChatWidget()
|
|
||||||
{
|
{
|
||||||
return chatWidget;
|
return _chatWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateColors();
|
void updateColors();
|
||||||
|
@ -40,19 +39,19 @@ protected:
|
||||||
void mouseDoubleClickEvent(QMouseEvent *event);
|
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChatWidget *chatWidget;
|
ChatWidget *_chatWidget;
|
||||||
|
|
||||||
QPoint dragStart;
|
QPoint _dragStart;
|
||||||
bool dragging;
|
bool _dragging;
|
||||||
|
|
||||||
QHBoxLayout hbox;
|
QHBoxLayout _hbox;
|
||||||
|
|
||||||
ChatWidgetHeaderButton leftLabel;
|
ChatWidgetHeaderButton _leftLabel;
|
||||||
SignalLabel middleLabel;
|
SignalLabel _middleLabel;
|
||||||
ChatWidgetHeaderButton rightLabel;
|
ChatWidgetHeaderButton _rightLabel;
|
||||||
|
|
||||||
QMenu leftMenu;
|
QMenu _leftMenu;
|
||||||
QMenu rightMenu;
|
QMenu _rightMenu;
|
||||||
|
|
||||||
void leftButtonClicked();
|
void leftButtonClicked();
|
||||||
void rightButtonClicked();
|
void rightButtonClicked();
|
||||||
|
|
|
@ -9,59 +9,55 @@ namespace widgets {
|
||||||
|
|
||||||
ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing)
|
ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing)
|
||||||
: QWidget()
|
: QWidget()
|
||||||
, hbox()
|
, _hbox()
|
||||||
, label()
|
, _label()
|
||||||
, mouseOver(false)
|
, _mouseOver(false)
|
||||||
, mouseDown(false)
|
, _mouseDown(false)
|
||||||
{
|
{
|
||||||
setLayout(&hbox);
|
setLayout(&_hbox);
|
||||||
|
|
||||||
label.setAlignment(Qt::AlignCenter);
|
_label.setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
hbox.setMargin(0);
|
_hbox.setMargin(0);
|
||||||
hbox.addSpacing(spacing);
|
_hbox.addSpacing(spacing);
|
||||||
hbox.addWidget(&this->label);
|
_hbox.addWidget(&_label);
|
||||||
hbox.addSpacing(spacing);
|
_hbox.addSpacing(spacing);
|
||||||
|
|
||||||
QObject::connect(&this->label, &SignalLabel::mouseUp, this,
|
QObject::connect(&_label, &SignalLabel::mouseUp, this,
|
||||||
&ChatWidgetHeaderButton::labelMouseUp);
|
&ChatWidgetHeaderButton::labelMouseUp);
|
||||||
QObject::connect(&this->label, &SignalLabel::mouseDown, this,
|
QObject::connect(&_label, &SignalLabel::mouseDown, this,
|
||||||
&ChatWidgetHeaderButton::labelMouseDown);
|
&ChatWidgetHeaderButton::labelMouseDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
|
||||||
ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
QBrush brush(ColorScheme::getInstance().IsLightTheme
|
QBrush brush(ColorScheme::getInstance().IsLightTheme ? QColor(0, 0, 0, 32)
|
||||||
? QColor(0, 0, 0, 32)
|
: QColor(255, 255, 255, 32));
|
||||||
: QColor(255, 255, 255, 32));
|
|
||||||
|
|
||||||
if (this->mouseDown) {
|
if (_mouseDown) {
|
||||||
painter.fillRect(rect(), brush);
|
painter.fillRect(rect(), brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->mouseOver) {
|
if (_mouseOver) {
|
||||||
painter.fillRect(rect(), brush);
|
painter.fillRect(rect(), brush);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
|
||||||
ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
this->mouseDown = true;
|
_mouseDown = true;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
||||||
ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
this->mouseDown = false;
|
_mouseDown = false;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -69,36 +65,32 @@ ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::enterEvent(QEvent *)
|
||||||
ChatWidgetHeaderButton::enterEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
this->mouseOver = true;
|
_mouseOver = true;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::leaveEvent(QEvent *)
|
||||||
ChatWidgetHeaderButton::leaveEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
this->mouseOver = false;
|
_mouseOver = false;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::labelMouseUp()
|
||||||
ChatWidgetHeaderButton::labelMouseUp()
|
|
||||||
{
|
{
|
||||||
this->mouseDown = false;
|
_mouseDown = false;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
emit clicked();
|
emit clicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetHeaderButton::labelMouseDown()
|
||||||
ChatWidgetHeaderButton::labelMouseDown()
|
|
||||||
{
|
{
|
||||||
this->mouseDown = true;
|
_mouseDown = true;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,9 @@ class ChatWidgetHeaderButton : public QWidget
|
||||||
public:
|
public:
|
||||||
explicit ChatWidgetHeaderButton(int spacing = 6);
|
explicit ChatWidgetHeaderButton(int spacing = 6);
|
||||||
|
|
||||||
SignalLabel &
|
SignalLabel &getLabel()
|
||||||
getLabel()
|
|
||||||
{
|
{
|
||||||
return label;
|
return _label;
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -37,11 +36,11 @@ protected:
|
||||||
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHBoxLayout hbox;
|
QHBoxLayout _hbox;
|
||||||
SignalLabel label;
|
SignalLabel _label;
|
||||||
|
|
||||||
bool mouseOver;
|
bool _mouseOver;
|
||||||
bool mouseDown;
|
bool _mouseDown;
|
||||||
|
|
||||||
void labelMouseUp();
|
void labelMouseUp();
|
||||||
void labelMouseDown();
|
void labelMouseDown();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "chatwidget.h"
|
#include "chatwidget.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "ircmanager.h"
|
#include "ircmanager.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
|
|
||||||
#include <QCompleter>
|
#include <QCompleter>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -12,44 +12,43 @@ namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
|
ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
|
||||||
: chatWidget(widget)
|
: _chatWidget(widget)
|
||||||
, hbox()
|
, _hbox()
|
||||||
, vbox()
|
, _vbox()
|
||||||
, editContainer()
|
, _editContainer()
|
||||||
, edit()
|
, _edit()
|
||||||
, textLengthLabel()
|
, _textLengthLabel()
|
||||||
, emotesLabel(0)
|
, _emotesLabel(0)
|
||||||
{
|
{
|
||||||
this->setLayout(&this->hbox);
|
setLayout(&_hbox);
|
||||||
this->setMaximumHeight(150);
|
setMaximumHeight(150);
|
||||||
this->hbox.setMargin(4);
|
_hbox.setMargin(4);
|
||||||
|
|
||||||
this->hbox.addLayout(&this->editContainer);
|
_hbox.addLayout(&_editContainer);
|
||||||
this->hbox.addLayout(&this->vbox);
|
_hbox.addLayout(&_vbox);
|
||||||
|
|
||||||
this->editContainer.addWidget(&this->edit);
|
_editContainer.addWidget(&_edit);
|
||||||
this->editContainer.setMargin(4);
|
_editContainer.setMargin(4);
|
||||||
|
|
||||||
this->vbox.addWidget(&this->textLengthLabel);
|
_vbox.addWidget(&_textLengthLabel);
|
||||||
this->vbox.addStretch(1);
|
_vbox.addStretch(1);
|
||||||
this->vbox.addWidget(&this->emotesLabel);
|
_vbox.addWidget(&_emotesLabel);
|
||||||
|
|
||||||
this->textLengthLabel.setText("100");
|
_textLengthLabel.setText("100");
|
||||||
this->textLengthLabel.setAlignment(Qt::AlignRight);
|
_textLengthLabel.setAlignment(Qt::AlignRight);
|
||||||
this->emotesLabel.getLabel().setTextFormat(Qt::RichText);
|
_emotesLabel.getLabel().setTextFormat(Qt::RichText);
|
||||||
this->emotesLabel.getLabel().setText(
|
_emotesLabel.getLabel().setText(
|
||||||
"<img src=':/images/Emoji_Color_1F60A_19.png' width='12' height='12' "
|
"<img src=':/images/Emoji_Color_1F60A_19.png' width='12' height='12' "
|
||||||
"/>");
|
"/>");
|
||||||
|
|
||||||
QObject::connect(&edit, &ResizingTextEdit::textChanged, this,
|
QObject::connect(&_edit, &ResizingTextEdit::textChanged, this,
|
||||||
&ChatWidgetInput::editTextChanged);
|
&ChatWidgetInput::editTextChanged);
|
||||||
|
|
||||||
// QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this,
|
// QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this,
|
||||||
// &ChatWidgetInput::editKeyPressed);
|
// &ChatWidgetInput::editKeyPressed);
|
||||||
|
|
||||||
this->refreshTheme();
|
refreshTheme();
|
||||||
this->setMessageLengthVisisble(
|
setMessageLengthVisisble(SettingsManager::getInstance().showMessageLength.get());
|
||||||
Settings::getInstance().showMessageLength.get());
|
|
||||||
|
|
||||||
QStringList list;
|
QStringList list;
|
||||||
list.append("asd");
|
list.append("asd");
|
||||||
|
@ -57,20 +56,20 @@ ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
|
||||||
list.append("asdg");
|
list.append("asdg");
|
||||||
list.append("asdh");
|
list.append("asdh");
|
||||||
|
|
||||||
QCompleter *completer = new QCompleter(list, &edit);
|
QCompleter *completer = new QCompleter(list, &_edit);
|
||||||
|
|
||||||
completer->setWidget(&edit);
|
completer->setWidget(&_edit);
|
||||||
|
|
||||||
this->edit.keyPressed.connect([this, completer](QKeyEvent *event) {
|
_edit.keyPressed.connect([this, completer](QKeyEvent *event) {
|
||||||
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
|
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
|
||||||
auto ptr = this->chatWidget->getChannel();
|
auto ptr = _chatWidget->getChannel();
|
||||||
Channel *c = ptr.get();
|
Channel *c = ptr.get();
|
||||||
|
|
||||||
if (c != nullptr) {
|
if (c != nullptr) {
|
||||||
IrcManager::send("PRIVMSG #" + c->getName() + ": " +
|
IrcManager::getInstance().send("PRIVMSG #" + c->getName() + ": " +
|
||||||
this->edit.toPlainText());
|
_edit.toPlainText());
|
||||||
event->accept();
|
event->accept();
|
||||||
this->edit.setText(QString());
|
_edit.setText(QString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
|
@ -97,20 +96,18 @@ ChatWidgetInput::~ChatWidgetInput()
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetInput::refreshTheme()
|
||||||
ChatWidgetInput::refreshTheme()
|
|
||||||
{
|
{
|
||||||
QPalette palette;
|
QPalette palette;
|
||||||
|
|
||||||
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
|
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
|
||||||
|
|
||||||
this->textLengthLabel.setPalette(palette);
|
_textLengthLabel.setPalette(palette);
|
||||||
|
|
||||||
edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet);
|
_edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetInput::editTextChanged()
|
||||||
ChatWidgetInput::editTextChanged()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,8 +121,7 @@ ChatWidgetInput::editTextChanged()
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
void
|
void ChatWidgetInput::paintEvent(QPaintEvent *)
|
||||||
ChatWidgetInput::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
@ -134,13 +130,12 @@ ChatWidgetInput::paintEvent(QPaintEvent *)
|
||||||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetInput::resizeEvent(QResizeEvent *)
|
||||||
ChatWidgetInput::resizeEvent(QResizeEvent *)
|
|
||||||
{
|
{
|
||||||
if (height() == this->maximumHeight()) {
|
if (height() == maximumHeight()) {
|
||||||
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
_edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
} else {
|
} else {
|
||||||
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
_edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,21 +31,20 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *);
|
void resizeEvent(QResizeEvent *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ChatWidget *chatWidget;
|
ChatWidget *_chatWidget;
|
||||||
|
|
||||||
QHBoxLayout hbox;
|
QHBoxLayout _hbox;
|
||||||
QVBoxLayout vbox;
|
QVBoxLayout _vbox;
|
||||||
QHBoxLayout editContainer;
|
QHBoxLayout _editContainer;
|
||||||
ResizingTextEdit edit;
|
ResizingTextEdit _edit;
|
||||||
QLabel textLengthLabel;
|
QLabel _textLengthLabel;
|
||||||
ChatWidgetHeaderButton emotesLabel;
|
ChatWidgetHeaderButton _emotesLabel;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void refreshTheme();
|
void refreshTheme();
|
||||||
void
|
void setMessageLengthVisisble(bool value)
|
||||||
setMessageLengthVisisble(bool value)
|
|
||||||
{
|
{
|
||||||
this->textLengthLabel.setHidden(!value);
|
_textLengthLabel.setHidden(!value);
|
||||||
}
|
}
|
||||||
void editTextChanged();
|
void editTextChanged();
|
||||||
// void editKeyPressed(QKeyEvent *event);
|
// void editKeyPressed(QKeyEvent *event);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#include "widgets/chatwidgetview.h"
|
#include "widgets/chatwidgetview.h"
|
||||||
#include "channels.h"
|
#include "channelmanager.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "messages/message.h"
|
#include "messages/message.h"
|
||||||
#include "messages/wordpart.h"
|
#include "messages/wordpart.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
|
#include "ui_userpopup.h"
|
||||||
#include "widgets/chatwidget.h"
|
#include "widgets/chatwidget.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -18,46 +19,46 @@ namespace widgets {
|
||||||
|
|
||||||
ChatWidgetView::ChatWidgetView(ChatWidget *parent)
|
ChatWidgetView::ChatWidgetView(ChatWidget *parent)
|
||||||
: QWidget()
|
: QWidget()
|
||||||
, chatWidget(parent)
|
, _chatWidget(parent)
|
||||||
, scrollbar(this)
|
, _scrollbar(this)
|
||||||
, onlyUpdateEmotes(false)
|
, _userPopupWidget(_chatWidget->getChannel())
|
||||||
|
, _onlyUpdateEmotes(false)
|
||||||
|
, _mouseDown(false)
|
||||||
|
, _lastPressPosition()
|
||||||
{
|
{
|
||||||
this->setAttribute(Qt::WA_OpaquePaintEvent);
|
this->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
this->scrollbar.setSmallChange(5);
|
_scrollbar.setSmallChange(5);
|
||||||
this->setMouseTracking(true);
|
this->setMouseTracking(true);
|
||||||
|
|
||||||
QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged,
|
QObject::connect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged, this,
|
||||||
this, &ChatWidgetView::wordTypeMaskChanged);
|
&ChatWidgetView::wordTypeMaskChanged);
|
||||||
|
|
||||||
this->scrollbar.getCurrentValueChanged().connect([this] { update(); });
|
_scrollbar.getCurrentValueChanged().connect([this] { update(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatWidgetView::~ChatWidgetView()
|
ChatWidgetView::~ChatWidgetView()
|
||||||
{
|
{
|
||||||
QObject::disconnect(&Settings::getInstance(),
|
QObject::disconnect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged,
|
||||||
&Settings::wordTypeMaskChanged, this,
|
this, &ChatWidgetView::wordTypeMaskChanged);
|
||||||
&ChatWidgetView::wordTypeMaskChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool ChatWidgetView::layoutMessages()
|
||||||
ChatWidgetView::layoutMessages()
|
|
||||||
{
|
{
|
||||||
auto messages = chatWidget->getMessagesSnapshot();
|
auto messages = _chatWidget->getMessagesSnapshot();
|
||||||
|
|
||||||
if (messages.getLength() == 0) {
|
if (messages.getLength() == 0) {
|
||||||
this->scrollbar.setVisible(false);
|
_scrollbar.setVisible(false);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool showScrollbar = false, redraw = false;
|
bool showScrollbar = false, redraw = false;
|
||||||
|
|
||||||
int start = this->scrollbar.getCurrentValue();
|
int start = _scrollbar.getCurrentValue();
|
||||||
|
|
||||||
// layout the visible messages in the view
|
// layout the visible messages in the view
|
||||||
if (messages.getLength() > start) {
|
if (messages.getLength() > start) {
|
||||||
int y = -(messages[start].get()->getHeight() *
|
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
|
||||||
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
|
||||||
|
|
||||||
for (int i = start; i < messages.getLength(); ++i) {
|
for (int i = start; i < messages.getLength(); ++i) {
|
||||||
auto messagePtr = messages[i];
|
auto messagePtr = messages[i];
|
||||||
|
@ -84,39 +85,47 @@ ChatWidgetView::layoutMessages()
|
||||||
h -= message->getHeight();
|
h -= message->getHeight();
|
||||||
|
|
||||||
if (h < 0) {
|
if (h < 0) {
|
||||||
this->scrollbar.setLargeChange((messages.getLength() - i) +
|
_scrollbar.setLargeChange((messages.getLength() - i) + (qreal)h / message->getHeight());
|
||||||
(qreal)h / message->getHeight());
|
_scrollbar.setDesiredValue(_scrollbar.getDesiredValue());
|
||||||
this->scrollbar.setDesiredValue(this->scrollbar.getDesiredValue());
|
|
||||||
|
|
||||||
showScrollbar = true;
|
showScrollbar = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->scrollbar.setVisible(showScrollbar);
|
_scrollbar.setVisible(showScrollbar);
|
||||||
|
|
||||||
if (!showScrollbar) {
|
if (!showScrollbar) {
|
||||||
this->scrollbar.setDesiredValue(0);
|
_scrollbar.setDesiredValue(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->scrollbar.setMaximum(messages.getLength());
|
_scrollbar.setMaximum(messages.getLength());
|
||||||
|
|
||||||
return redraw;
|
return redraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetView::updateGifEmotes()
|
||||||
ChatWidgetView::resizeEvent(QResizeEvent *)
|
|
||||||
{
|
{
|
||||||
this->scrollbar.resize(this->scrollbar.width(), height());
|
_onlyUpdateEmotes = true;
|
||||||
this->scrollbar.move(width() - this->scrollbar.width(), 0);
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar *ChatWidgetView::getScrollbar()
|
||||||
|
{
|
||||||
|
return &_scrollbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidgetView::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
_scrollbar.resize(_scrollbar.width(), height());
|
||||||
|
_scrollbar.move(width() - _scrollbar.width(), 0);
|
||||||
|
|
||||||
layoutMessages();
|
layoutMessages();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
ChatWidgetView::paintEvent(QPaintEvent *event)
|
|
||||||
{
|
{
|
||||||
QPainter _painter(this);
|
QPainter _painter(this);
|
||||||
|
|
||||||
|
@ -125,10 +134,10 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
ColorScheme &scheme = ColorScheme::getInstance();
|
ColorScheme &scheme = ColorScheme::getInstance();
|
||||||
|
|
||||||
// only update gif emotes
|
// only update gif emotes
|
||||||
if (onlyUpdateEmotes) {
|
if (_onlyUpdateEmotes) {
|
||||||
onlyUpdateEmotes = false;
|
_onlyUpdateEmotes = false;
|
||||||
|
|
||||||
for (GifEmoteData &item : this->gifEmotes) {
|
for (GifEmoteData &item : _gifEmotes) {
|
||||||
_painter.fillRect(item.rect, scheme.ChatBackground);
|
_painter.fillRect(item.rect, scheme.ChatBackground);
|
||||||
|
|
||||||
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
||||||
|
@ -138,7 +147,7 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update all messages
|
// update all messages
|
||||||
gifEmotes.clear();
|
_gifEmotes.clear();
|
||||||
|
|
||||||
_painter.fillRect(rect(), scheme.ChatBackground);
|
_painter.fillRect(rect(), scheme.ChatBackground);
|
||||||
|
|
||||||
|
@ -175,16 +184,15 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
|
|
||||||
painter.fillRect(QRect(0, 9, 500, 2), QColor(0, 0, 0));*/
|
painter.fillRect(QRect(0, 9, 500, 2), QColor(0, 0, 0));*/
|
||||||
|
|
||||||
auto messages = chatWidget->getMessagesSnapshot();
|
auto messages = _chatWidget->getMessagesSnapshot();
|
||||||
|
|
||||||
int start = this->scrollbar.getCurrentValue();
|
int start = _scrollbar.getCurrentValue();
|
||||||
|
|
||||||
if (start >= messages.getLength()) {
|
if (start >= messages.getLength()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int y = -(messages[start].get()->getHeight() *
|
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
|
||||||
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
|
||||||
|
|
||||||
for (int i = start; i < messages.getLength(); ++i) {
|
for (int i = start; i < messages.getLength(); ++i) {
|
||||||
messages::MessageRef *messageRef = messages[i].get();
|
messages::MessageRef *messageRef = messages[i].get();
|
||||||
|
@ -205,20 +213,17 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
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()) {
|
|
||||||
// image
|
// image
|
||||||
if (wordPart.getWord().isImage()) {
|
if (wordPart.getWord().isImage()) {
|
||||||
messages::LazyLoadedImage &lli =
|
messages::LazyLoadedImage &lli = wordPart.getWord().getImage();
|
||||||
wordPart.getWord().getImage();
|
|
||||||
|
|
||||||
const QPixmap *image = lli.getPixmap();
|
const QPixmap *image = lli.getPixmap();
|
||||||
|
|
||||||
if (image != NULL) {
|
if (image != NULL) {
|
||||||
painter.drawPixmap(
|
painter.drawPixmap(QRect(wordPart.getX(), wordPart.getY(),
|
||||||
QRect(wordPart.getX(), wordPart.getY(),
|
wordPart.getWidth(), wordPart.getHeight()),
|
||||||
wordPart.getWidth(), wordPart.getHeight()),
|
*image);
|
||||||
*image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// text
|
// text
|
||||||
|
@ -230,10 +235,8 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
painter.setPen(color);
|
painter.setPen(color);
|
||||||
painter.setFont(wordPart.getWord().getFont());
|
painter.setFont(wordPart.getWord().getFont());
|
||||||
|
|
||||||
painter.drawText(
|
painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
|
||||||
QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
|
wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop));
|
||||||
wordPart.getText(),
|
|
||||||
QTextOption(Qt::AlignLeft | Qt::AlignTop));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,12 +251,12 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
if (lli.getAnimated()) {
|
if (lli.getAnimated()) {
|
||||||
GifEmoteData data;
|
GifEmoteData data;
|
||||||
data.image = &lli;
|
data.image = &lli;
|
||||||
QRect rect(wordPart.getX(), wordPart.getY() + y,
|
QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(),
|
||||||
wordPart.getWidth(), wordPart.getHeight());
|
wordPart.getHeight());
|
||||||
|
|
||||||
data.rect = rect;
|
data.rect = rect;
|
||||||
|
|
||||||
gifEmotes.push_back(data);
|
_gifEmotes.push_back(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,27 +272,24 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (GifEmoteData &item : this->gifEmotes) {
|
for (GifEmoteData &item : _gifEmotes) {
|
||||||
_painter.fillRect(item.rect, scheme.ChatBackground);
|
_painter.fillRect(item.rect, scheme.ChatBackground);
|
||||||
|
|
||||||
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetView::wheelEvent(QWheelEvent *event)
|
||||||
ChatWidgetView::wheelEvent(QWheelEvent *event)
|
|
||||||
{
|
{
|
||||||
if (this->scrollbar.isVisible()) {
|
if (_scrollbar.isVisible()) {
|
||||||
this->scrollbar.setDesiredValue(
|
_scrollbar.setDesiredValue(
|
||||||
this->scrollbar.getDesiredValue() -
|
_scrollbar.getDesiredValue() -
|
||||||
event->delta() / 10.0 *
|
event->delta() / 10.0 * SettingsManager::getInstance().mouseScrollMultiplier.get(),
|
||||||
Settings::getInstance().mouseScrollMultiplier.get(),
|
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
||||||
ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
std::shared_ptr<messages::MessageRef> message;
|
std::shared_ptr<messages::MessageRef> message;
|
||||||
QPoint relativePos;
|
QPoint relativePos;
|
||||||
|
@ -311,32 +311,86 @@ ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
|
||||||
int index = message->getSelectionIndex(relativePos);
|
int index = message->getSelectionIndex(relativePos);
|
||||||
|
|
||||||
qDebug() << index;
|
|
||||||
|
|
||||||
if (hoverWord.getLink().getIsValid()) {
|
if (hoverWord.getLink().getIsValid()) {
|
||||||
this->setCursor(Qt::PointingHandCursor);
|
this->setCursor(Qt::PointingHandCursor);
|
||||||
qDebug() << hoverWord.getLink().getValue();
|
|
||||||
} else {
|
} else {
|
||||||
this->setCursor(Qt::ArrowCursor);
|
this->setCursor(Qt::ArrowCursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void ChatWidgetView::mousePressEvent(QMouseEvent *event)
|
||||||
ChatWidgetView::tryGetMessageAt(QPoint p,
|
|
||||||
std::shared_ptr<messages::MessageRef> &_message,
|
|
||||||
QPoint &relativePos)
|
|
||||||
{
|
{
|
||||||
auto messages = chatWidget->getMessagesSnapshot();
|
_mouseDown = true;
|
||||||
|
_lastPressPosition = event->screenPos();
|
||||||
|
}
|
||||||
|
|
||||||
int start = this->scrollbar.getCurrentValue();
|
static float distanceBetweenPoints(const QPointF &p1, const QPointF &p2)
|
||||||
|
{
|
||||||
|
QPointF tmp = p1 - p2;
|
||||||
|
|
||||||
|
float distance = 0.f;
|
||||||
|
distance += tmp.x() * tmp.x();
|
||||||
|
distance += tmp.y() * tmp.y();
|
||||||
|
|
||||||
|
return std::sqrt(distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatWidgetView::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (!_mouseDown) {
|
||||||
|
// We didn't grab the mouse press, so we shouldn't be handling the mouse
|
||||||
|
// release
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mouseDown = false;
|
||||||
|
|
||||||
|
float distance = distanceBetweenPoints(_lastPressPosition, event->screenPos());
|
||||||
|
|
||||||
|
qDebug() << "Distance: " << distance;
|
||||||
|
|
||||||
|
if (fabsf(distance) > 15.f) {
|
||||||
|
// It wasn't a proper click, so we don't care about that here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you clicked and released less than X pixels away, it counts
|
||||||
|
// as a click!
|
||||||
|
|
||||||
|
// show user thing pajaW
|
||||||
|
|
||||||
|
std::shared_ptr<messages::MessageRef> message;
|
||||||
|
QPoint relativePos;
|
||||||
|
|
||||||
|
if (!tryGetMessageAt(event->pos(), message, relativePos)) {
|
||||||
|
// No message at clicked position
|
||||||
|
_userPopupWidget.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _message = message->getMessage();
|
||||||
|
auto user = _message->getUserName();
|
||||||
|
|
||||||
|
qDebug() << "Clicked " << user << "s message";
|
||||||
|
|
||||||
|
_userPopupWidget.setName(user);
|
||||||
|
_userPopupWidget.move(event->screenPos().toPoint());
|
||||||
|
_userPopupWidget.show();
|
||||||
|
_userPopupWidget.setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChatWidgetView::tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageRef> &_message,
|
||||||
|
QPoint &relativePos)
|
||||||
|
{
|
||||||
|
auto messages = _chatWidget->getMessagesSnapshot();
|
||||||
|
|
||||||
|
int start = _scrollbar.getCurrentValue();
|
||||||
|
|
||||||
if (start >= messages.getLength()) {
|
if (start >= messages.getLength()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int y = -(messages[start].get()->getHeight() *
|
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))) + 12;
|
||||||
(fmod(this->scrollbar.getCurrentValue(), 1))) +
|
|
||||||
12;
|
|
||||||
|
|
||||||
for (int i = start; i < messages.getLength(); ++i) {
|
for (int i = start; i < messages.getLength(); ++i) {
|
||||||
auto message = messages[i];
|
auto message = messages[i];
|
||||||
|
@ -352,5 +406,5 @@ ChatWidgetView::tryGetMessageAt(QPoint p,
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} // namespace widgets
|
||||||
}
|
} // namespace chatterino
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "messages/messageref.h"
|
#include "messages/messageref.h"
|
||||||
#include "messages/word.h"
|
#include "messages/word.h"
|
||||||
#include "widgets/scrollbar.h"
|
#include "widgets/scrollbar.h"
|
||||||
|
#include "widgets/userpopupwidget.h"
|
||||||
|
|
||||||
#include <QPaintEvent>
|
#include <QPaintEvent>
|
||||||
#include <QScroller>
|
#include <QScroller>
|
||||||
|
@ -18,20 +19,14 @@ class ChatWidget;
|
||||||
|
|
||||||
class ChatWidgetView : public QWidget
|
class ChatWidgetView : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ChatWidgetView(ChatWidget *parent);
|
explicit ChatWidgetView(ChatWidget *parent);
|
||||||
~ChatWidgetView();
|
~ChatWidgetView();
|
||||||
|
|
||||||
bool layoutMessages();
|
bool layoutMessages();
|
||||||
|
|
||||||
void
|
void updateGifEmotes();
|
||||||
updateGifEmotes()
|
ScrollBar *getScrollbar();
|
||||||
{
|
|
||||||
this->onlyUpdateEmotes = true;
|
|
||||||
this->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *);
|
void resizeEvent(QResizeEvent *);
|
||||||
|
@ -40,9 +35,10 @@ protected:
|
||||||
void wheelEvent(QWheelEvent *event);
|
void wheelEvent(QWheelEvent *event);
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent *event);
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
|
||||||
bool tryGetMessageAt(QPoint p,
|
bool tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageRef> &message,
|
||||||
std::shared_ptr<messages::MessageRef> &message,
|
|
||||||
QPoint &relativePos);
|
QPoint &relativePos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -51,22 +47,27 @@ private:
|
||||||
QRect rect;
|
QRect rect;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<GifEmoteData> gifEmotes;
|
std::vector<GifEmoteData> _gifEmotes;
|
||||||
|
|
||||||
ChatWidget *chatWidget;
|
ChatWidget *_chatWidget;
|
||||||
|
|
||||||
ScrollBar scrollbar;
|
ScrollBar _scrollbar;
|
||||||
bool onlyUpdateEmotes;
|
|
||||||
|
UserPopupWidget _userPopupWidget;
|
||||||
|
bool _onlyUpdateEmotes;
|
||||||
|
|
||||||
|
// Mouse event variables
|
||||||
|
bool _mouseDown;
|
||||||
|
QPointF _lastPressPosition;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void
|
void wordTypeMaskChanged()
|
||||||
wordTypeMaskChanged()
|
|
||||||
{
|
{
|
||||||
layoutMessages();
|
layoutMessages();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace widgets
|
||||||
}
|
} // namespace chatterino
|
||||||
|
|
||||||
#endif // CHATVIEW_H
|
#endif // CHATVIEW_H
|
||||||
|
|
141
widgets/fancybutton.cpp
Normal file
141
widgets/fancybutton.cpp
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
#include "fancybutton.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
FancyButton::FancyButton(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, _selected()
|
||||||
|
, _mouseOver()
|
||||||
|
, _mouseDown()
|
||||||
|
, _mousePos()
|
||||||
|
, _hoverMultiplier()
|
||||||
|
, _effectTimer()
|
||||||
|
, _mouseEffectColor(QColor(255, 255, 255))
|
||||||
|
|
||||||
|
{
|
||||||
|
connect(&_effectTimer, &QTimer::timeout, this, &FancyButton::onMouseEffectTimeout);
|
||||||
|
|
||||||
|
_effectTimer.setInterval(20);
|
||||||
|
_effectTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::setMouseEffectColor(QColor color)
|
||||||
|
{
|
||||||
|
_mouseEffectColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter painter;
|
||||||
|
|
||||||
|
fancyPaint(painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::fancyPaint(QPainter &painter)
|
||||||
|
{
|
||||||
|
QColor &c = _mouseEffectColor;
|
||||||
|
|
||||||
|
if (_hoverMultiplier > 0) {
|
||||||
|
QRadialGradient gradient(_mousePos.x(), _mousePos.y(), 50, _mousePos.x(), _mousePos.y());
|
||||||
|
|
||||||
|
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(), (int)(24 * _hoverMultiplier)));
|
||||||
|
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), (int)(12 * _hoverMultiplier)));
|
||||||
|
|
||||||
|
painter.fillRect(this->rect(), gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto effect : _clickEffects) {
|
||||||
|
QRadialGradient gradient(effect.position.x(), effect.position.y(),
|
||||||
|
effect.progress * (float)width() * 2, effect.position.x(),
|
||||||
|
effect.position.y());
|
||||||
|
|
||||||
|
gradient.setColorAt(
|
||||||
|
0, QColor(c.red(), c.green(), c.blue(), (int)((1 - effect.progress) * 95)));
|
||||||
|
gradient.setColorAt(
|
||||||
|
0.9999, QColor(c.red(), c.green(), c.blue(), (int)((1 - effect.progress) * 95)));
|
||||||
|
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), (int)(0)));
|
||||||
|
|
||||||
|
painter.fillRect(this->rect(), gradient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::enterEvent(QEvent *)
|
||||||
|
{
|
||||||
|
_mouseOver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::leaveEvent(QEvent *)
|
||||||
|
{
|
||||||
|
_mouseOver = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_clickEffects.push_back(ClickEffect(event->pos()));
|
||||||
|
|
||||||
|
_mouseDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mouseDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
_mousePos = event->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FancyButton::onMouseEffectTimeout()
|
||||||
|
{
|
||||||
|
bool performUpdate = false;
|
||||||
|
|
||||||
|
if (_selected) {
|
||||||
|
if (_hoverMultiplier != 0) {
|
||||||
|
_hoverMultiplier = std::max(0.0, _hoverMultiplier - 0.1);
|
||||||
|
performUpdate = true;
|
||||||
|
}
|
||||||
|
} else if (_mouseOver) {
|
||||||
|
if (_hoverMultiplier != 1) {
|
||||||
|
_hoverMultiplier = std::min(1.0, _hoverMultiplier + 0.5);
|
||||||
|
performUpdate = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_hoverMultiplier != 0) {
|
||||||
|
_hoverMultiplier = std::max(0.0, _hoverMultiplier - 0.3);
|
||||||
|
performUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clickEffects.size() != 0) {
|
||||||
|
performUpdate = true;
|
||||||
|
|
||||||
|
for (auto it = _clickEffects.begin(); it != _clickEffects.end();) {
|
||||||
|
(*it).progress += _mouseDown ? 0.02 : 0.07;
|
||||||
|
|
||||||
|
if ((*it).progress >= 1.0) {
|
||||||
|
it = _clickEffects.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (performUpdate) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
widgets/fancybutton.h
Normal file
55
widgets/fancybutton.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef FANCYBUTTON_H
|
||||||
|
#define FANCYBUTTON_H
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
namespace widgets {
|
||||||
|
class FancyButton : public QWidget
|
||||||
|
{
|
||||||
|
struct ClickEffect {
|
||||||
|
float progress;
|
||||||
|
QPoint position;
|
||||||
|
|
||||||
|
ClickEffect(QPoint position)
|
||||||
|
: progress()
|
||||||
|
, position(position)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
FancyButton(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setMouseEffectColor(QColor color);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
void enterEvent(QEvent *) override;
|
||||||
|
void leaveEvent(QEvent *) override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
void fancyPaint(QPainter &painter);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _selected;
|
||||||
|
bool _mouseOver;
|
||||||
|
bool _mouseDown;
|
||||||
|
QPoint _mousePos;
|
||||||
|
float _hoverMultiplier;
|
||||||
|
QTimer _effectTimer;
|
||||||
|
std::vector<ClickEffect> _clickEffects;
|
||||||
|
QColor _mouseEffectColor;
|
||||||
|
|
||||||
|
void onMouseEffectTimeout();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FANCYBUTTON_H
|
|
@ -1,24 +1,46 @@
|
||||||
#include "widgets/mainwindow.h"
|
#include "widgets/mainwindow.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
|
#include "settingsmanager.h"
|
||||||
#include "widgets/chatwidget.h"
|
#include "widgets/chatwidget.h"
|
||||||
#include "widgets/notebook.h"
|
#include "widgets/notebook.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#include <QVBoxLayout>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
#ifdef USEWINSDK
|
||||||
|
#include "Windows.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
: QMainWindow(parent)
|
: QWidget(parent)
|
||||||
, notebook(this)
|
, _notebook(this)
|
||||||
|
, _loaded(false)
|
||||||
|
, _titleBar()
|
||||||
{
|
{
|
||||||
setCentralWidget(&this->notebook);
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
// add titlebar
|
||||||
|
// if (SettingsManager::getInstance().useCustomWindowFrame.get()) {
|
||||||
|
// layout->addWidget(&_titleBar);
|
||||||
|
// }
|
||||||
|
|
||||||
|
layout->addWidget(&_notebook);
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
// set margin
|
||||||
|
// if (SettingsManager::getInstance().useCustomWindowFrame.get()) {
|
||||||
|
// layout->setMargin(1);
|
||||||
|
// } else {
|
||||||
|
layout->setMargin(0);
|
||||||
|
// }
|
||||||
|
|
||||||
QPalette palette;
|
QPalette palette;
|
||||||
palette.setColor(QPalette::Background,
|
palette.setColor(QPalette::Background, ColorScheme::getInstance().TabPanelBackground);
|
||||||
ColorScheme::getInstance().TabPanelBackground);
|
|
||||||
setPalette(palette);
|
setPalette(palette);
|
||||||
|
|
||||||
resize(1280, 800);
|
resize(1280, 800);
|
||||||
|
@ -28,10 +50,9 @@ MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
||||||
MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
|
||||||
{
|
{
|
||||||
auto *page = notebook.getSelectedPage();
|
auto *page = _notebook.getSelectedPage();
|
||||||
|
|
||||||
if (page == NULL) {
|
if (page == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -43,17 +64,14 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
||||||
ChatWidget *widget = *it;
|
ChatWidget *widget = *it;
|
||||||
|
|
||||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||||
if (widget->getView().layoutMessages()) {
|
widget->layoutMessages();
|
||||||
widget->getView().update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
||||||
MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
|
||||||
{
|
{
|
||||||
auto *page = notebook.getSelectedPage();
|
auto *page = _notebook.getSelectedPage();
|
||||||
|
|
||||||
if (page == NULL) {
|
if (page == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -65,16 +83,14 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
||||||
ChatWidget *widget = *it;
|
ChatWidget *widget = *it;
|
||||||
|
|
||||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||||
widget->getView().layoutMessages();
|
widget->layoutMessages();
|
||||||
widget->getView().update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void MainWindow::repaintGifEmotes()
|
||||||
MainWindow::repaintGifEmotes()
|
|
||||||
{
|
{
|
||||||
auto *page = notebook.getSelectedPage();
|
auto *page = _notebook.getSelectedPage();
|
||||||
|
|
||||||
if (page == NULL) {
|
if (page == NULL) {
|
||||||
return;
|
return;
|
||||||
|
@ -85,37 +101,43 @@ MainWindow::repaintGifEmotes()
|
||||||
for (auto it = widgets.begin(); it != widgets.end(); ++it) {
|
for (auto it = widgets.begin(); it != widgets.end(); ++it) {
|
||||||
ChatWidget *widget = *it;
|
ChatWidget *widget = *it;
|
||||||
|
|
||||||
widget->getView().updateGifEmotes();
|
widget->updateGifEmotes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void MainWindow::load(const boost::property_tree::ptree &tree)
|
||||||
MainWindow::load(const boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
this->notebook.load(tree);
|
this->_notebook.load(tree);
|
||||||
|
|
||||||
this->loaded = true;
|
_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::property_tree::ptree
|
boost::property_tree::ptree MainWindow::save()
|
||||||
MainWindow::save()
|
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree child;
|
boost::property_tree::ptree child;
|
||||||
|
|
||||||
child.put("type", "main");
|
child.put("type", "main");
|
||||||
|
|
||||||
this->notebook.save(child);
|
_notebook.save(child);
|
||||||
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void MainWindow::loadDefaults()
|
||||||
MainWindow::loadDefaults()
|
|
||||||
{
|
{
|
||||||
this->notebook.loadDefaults();
|
_notebook.loadDefaults();
|
||||||
|
|
||||||
this->loaded = true;
|
_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainWindow::isLoaded() const
|
||||||
|
{
|
||||||
|
return _loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notebook &MainWindow::getNotebook()
|
||||||
|
{
|
||||||
|
return _notebook;
|
||||||
|
}
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -2,38 +2,39 @@
|
||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include "widgets/notebook.h"
|
#include "widgets/notebook.h"
|
||||||
|
#include "widgets/titlebar.h"
|
||||||
|
|
||||||
|
#include <platform/borderless/qwinwidget.h>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = 0);
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
Notebook notebook;
|
|
||||||
|
|
||||||
void layoutVisibleChatWidgets(Channel *channel = NULL);
|
void layoutVisibleChatWidgets(Channel *channel = nullptr);
|
||||||
void repaintVisibleChatWidgets(Channel *channel = NULL);
|
void repaintVisibleChatWidgets(Channel *channel = nullptr);
|
||||||
void repaintGifEmotes();
|
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();
|
||||||
void loadDefaults();
|
void loadDefaults();
|
||||||
|
|
||||||
bool
|
bool isLoaded() const;
|
||||||
isLoaded() const
|
|
||||||
{
|
Notebook &getNotebook();
|
||||||
return this->loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool loaded = false;
|
Notebook _notebook;
|
||||||
|
bool _loaded;
|
||||||
|
TitleBar _titleBar;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
|
@ -19,106 +19,99 @@ namespace widgets {
|
||||||
|
|
||||||
Notebook::Notebook(QWidget *parent)
|
Notebook::Notebook(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, addButton(this)
|
, _addButton(this)
|
||||||
, settingsButton(this)
|
, _settingsButton(this)
|
||||||
, userButton(this)
|
, _userButton(this)
|
||||||
, selectedPage(nullptr)
|
, _selectedPage(nullptr)
|
||||||
{
|
{
|
||||||
connect(&this->settingsButton, SIGNAL(clicked()), this,
|
connect(&_settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
|
||||||
SLOT(settingsButtonClicked()));
|
connect(&_userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked()));
|
||||||
connect(&this->userButton, SIGNAL(clicked()), this,
|
connect(&_addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked()));
|
||||||
SLOT(usersButtonClicked()));
|
|
||||||
connect(&this->addButton, SIGNAL(clicked()), this,
|
|
||||||
SLOT(addPageButtonClicked()));
|
|
||||||
|
|
||||||
this->settingsButton.resize(24, 24);
|
_settingsButton.resize(24, 24);
|
||||||
this->settingsButton.icon = NotebookButton::IconSettings;
|
_settingsButton.icon = NotebookButton::IconSettings;
|
||||||
|
|
||||||
this->userButton.resize(24, 24);
|
_userButton.resize(24, 24);
|
||||||
this->userButton.move(24, 0);
|
_userButton.move(24, 0);
|
||||||
this->userButton.icon = NotebookButton::IconUser;
|
_userButton.icon = NotebookButton::IconUser;
|
||||||
|
|
||||||
this->addButton.resize(24, 24);
|
_addButton.resize(24, 24);
|
||||||
|
|
||||||
Settings::getInstance().hidePreferencesButton.valueChanged.connect(
|
SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect(
|
||||||
[this](const bool &) { this->performLayout(); });
|
[this](const bool &) { performLayout(); });
|
||||||
Settings::getInstance().hideUserButton.valueChanged.connect(
|
SettingsManager::getInstance().hideUserButton.valueChanged.connect(
|
||||||
[this](const bool &) { this->performLayout(); });
|
[this](const bool &) { performLayout(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
NotebookPage *
|
NotebookPage *Notebook::addPage(bool select)
|
||||||
Notebook::addPage(bool select)
|
|
||||||
{
|
{
|
||||||
auto tab = new NotebookTab(this);
|
auto tab = new NotebookTab(this);
|
||||||
auto page = new NotebookPage(this, tab);
|
auto page = new NotebookPage(this, tab);
|
||||||
|
|
||||||
tab->show();
|
tab->show();
|
||||||
|
|
||||||
if (select || this->pages.count() == 0) {
|
if (select || _pages.count() == 0) {
|
||||||
this->select(page);
|
this->select(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->pages.append(page);
|
_pages.append(page);
|
||||||
|
|
||||||
this->performLayout();
|
performLayout();
|
||||||
|
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::removePage(NotebookPage *page)
|
||||||
Notebook::removePage(NotebookPage *page)
|
|
||||||
{
|
{
|
||||||
int index = this->pages.indexOf(page);
|
int index = _pages.indexOf(page);
|
||||||
|
|
||||||
if (pages.size() == 1) {
|
if (_pages.size() == 1) {
|
||||||
this->select(NULL);
|
select(NULL);
|
||||||
} else if (index == pages.count() - 1) {
|
} else if (index == _pages.count() - 1) {
|
||||||
this->select(pages[index - 1]);
|
select(_pages[index - 1]);
|
||||||
} else {
|
} else {
|
||||||
this->select(pages[index + 1]);
|
select(_pages[index + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete page->tab;
|
delete page->getTab();
|
||||||
delete page;
|
delete page;
|
||||||
|
|
||||||
this->pages.removeOne(page);
|
_pages.removeOne(page);
|
||||||
|
|
||||||
if (this->pages.size() == 0) {
|
if (_pages.size() == 0) {
|
||||||
addPage();
|
addPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
performLayout();
|
performLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::select(NotebookPage *page)
|
||||||
Notebook::select(NotebookPage *page)
|
|
||||||
{
|
{
|
||||||
if (page == this->selectedPage)
|
if (page == _selectedPage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (page != nullptr) {
|
if (page != nullptr) {
|
||||||
page->setHidden(false);
|
page->setHidden(false);
|
||||||
page->tab->setSelected(true);
|
page->getTab()->setSelected(true);
|
||||||
page->tab->raise();
|
page->getTab()->raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->selectedPage != nullptr) {
|
if (_selectedPage != nullptr) {
|
||||||
this->selectedPage->setHidden(true);
|
_selectedPage->setHidden(true);
|
||||||
this->selectedPage->tab->setSelected(false);
|
_selectedPage->getTab()->setSelected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->selectedPage = page;
|
_selectedPage = page;
|
||||||
|
|
||||||
performLayout();
|
performLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
NotebookPage *
|
NotebookPage *Notebook::tabAt(QPoint point, int &index)
|
||||||
Notebook::tabAt(QPoint point, int &index)
|
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (auto *page : pages) {
|
for (auto *page : _pages) {
|
||||||
if (page->tab->getDesiredRect().contains(point)) {
|
if (page->getTab()->getDesiredRect().contains(point)) {
|
||||||
index = i;
|
index = i;
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
@ -130,97 +123,87 @@ Notebook::tabAt(QPoint point, int &index)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::rearrangePage(NotebookPage *page, int index)
|
||||||
Notebook::rearrangePage(NotebookPage *page, int index)
|
|
||||||
{
|
{
|
||||||
pages.move(pages.indexOf(page), index);
|
_pages.move(_pages.indexOf(page), index);
|
||||||
|
|
||||||
performLayout();
|
performLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::performLayout(bool animated)
|
||||||
Notebook::performLayout(bool animated)
|
|
||||||
{
|
{
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
|
|
||||||
if (Settings::getInstance().hidePreferencesButton.get()) {
|
if (SettingsManager::getInstance().hidePreferencesButton.get()) {
|
||||||
settingsButton.hide();
|
_settingsButton.hide();
|
||||||
} else {
|
} else {
|
||||||
settingsButton.show();
|
_settingsButton.show();
|
||||||
x += 24;
|
x += 24;
|
||||||
}
|
}
|
||||||
if (Settings::getInstance().hideUserButton.get()) {
|
if (SettingsManager::getInstance().hideUserButton.get()) {
|
||||||
userButton.hide();
|
_userButton.hide();
|
||||||
} else {
|
} else {
|
||||||
userButton.move(x, 0);
|
_userButton.move(x, 0);
|
||||||
userButton.show();
|
_userButton.show();
|
||||||
x += 24;
|
x += 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tabHeight = 16;
|
int tabHeight = 16;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (auto &i : this->pages) {
|
for (auto &i : _pages) {
|
||||||
tabHeight = i->tab->height();
|
tabHeight = i->getTab()->height();
|
||||||
|
|
||||||
if (!first &&
|
if (!first && (i == _pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
|
||||||
(i == this->pages.last() ? tabHeight : 0) + x + i->tab->width() >
|
y += i->getTab()->height();
|
||||||
width()) {
|
i->getTab()->moveAnimated(QPoint(0, y), animated);
|
||||||
y += i->tab->height();
|
x = i->getTab()->width();
|
||||||
i->tab->moveAnimated(QPoint(0, y), animated);
|
|
||||||
x = i->tab->width();
|
|
||||||
} else {
|
} else {
|
||||||
i->tab->moveAnimated(QPoint(x, y), animated);
|
i->getTab()->moveAnimated(QPoint(x, y), animated);
|
||||||
x += i->tab->width();
|
x += i->getTab()->width();
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->addButton.move(x, y);
|
_addButton.move(x, y);
|
||||||
|
|
||||||
if (this->selectedPage != nullptr) {
|
if (_selectedPage != nullptr) {
|
||||||
this->selectedPage->move(0, y + tabHeight);
|
_selectedPage->move(0, y + tabHeight);
|
||||||
this->selectedPage->resize(width(), height() - y - tabHeight);
|
_selectedPage->resize(width(), height() - y - tabHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::resizeEvent(QResizeEvent *)
|
||||||
Notebook::resizeEvent(QResizeEvent *)
|
|
||||||
{
|
{
|
||||||
performLayout(false);
|
performLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::settingsButtonClicked()
|
||||||
Notebook::settingsButtonClicked()
|
|
||||||
{
|
{
|
||||||
SettingsDialog *a = new SettingsDialog();
|
SettingsDialog *a = new SettingsDialog();
|
||||||
|
|
||||||
a->show();
|
a->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::usersButtonClicked()
|
||||||
Notebook::usersButtonClicked()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::addPageButtonClicked()
|
||||||
Notebook::addPageButtonClicked()
|
|
||||||
{
|
{
|
||||||
addPage(true);
|
addPage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::load(const boost::property_tree::ptree &tree)
|
||||||
Notebook::load(const boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
// Read a list of tabs
|
// Read a list of tabs
|
||||||
try {
|
try {
|
||||||
BOOST_FOREACH (const boost::property_tree::ptree::value_type &v,
|
BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, tree.get_child("tabs.")) {
|
||||||
tree.get_child("tabs.")) {
|
|
||||||
bool select = v.second.get<bool>("selected", false);
|
bool select = v.second.get<bool>("selected", false);
|
||||||
|
|
||||||
auto page = this->addPage(select);
|
auto page = addPage(select);
|
||||||
auto tab = page->tab;
|
auto tab = page->getTab();
|
||||||
tab->load(v.second);
|
tab->load(v.second);
|
||||||
page->load(v.second);
|
page->load(v.second);
|
||||||
}
|
}
|
||||||
|
@ -228,20 +211,19 @@ Notebook::load(const boost::property_tree::ptree &tree)
|
||||||
// can't read tabs
|
// can't read tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->pages.size() == 0) {
|
if (_pages.size() == 0) {
|
||||||
// No pages saved, show default stuff
|
// No pages saved, show default stuff
|
||||||
this->loadDefaults();
|
loadDefaults();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::save(boost::property_tree::ptree &tree)
|
||||||
Notebook::save(boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree tabs;
|
boost::property_tree::ptree tabs;
|
||||||
|
|
||||||
// Iterate through all tabs and add them to our tabs property thing
|
// Iterate through all tabs and add them to our tabs property thing
|
||||||
for (const auto &page : this->pages) {
|
for (const auto &page : _pages) {
|
||||||
boost::property_tree::ptree pTab = page->tab->save();
|
boost::property_tree::ptree pTab = page->getTab()->save();
|
||||||
|
|
||||||
boost::property_tree::ptree pChats = page->save();
|
boost::property_tree::ptree pChats = page->save();
|
||||||
|
|
||||||
|
@ -255,10 +237,9 @@ Notebook::save(boost::property_tree::ptree &tree)
|
||||||
tree.add_child("tabs", tabs);
|
tree.add_child("tabs", tabs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Notebook::loadDefaults()
|
||||||
Notebook::loadDefaults()
|
|
||||||
{
|
{
|
||||||
this->addPage();
|
addPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
|
@ -26,10 +26,9 @@ public:
|
||||||
void removePage(NotebookPage *page);
|
void removePage(NotebookPage *page);
|
||||||
void select(NotebookPage *page);
|
void select(NotebookPage *page);
|
||||||
|
|
||||||
NotebookPage *
|
NotebookPage *getSelectedPage()
|
||||||
getSelectedPage()
|
|
||||||
{
|
{
|
||||||
return selectedPage;
|
return _selectedPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void performLayout(bool animate = true);
|
void performLayout(bool animate = true);
|
||||||
|
@ -48,13 +47,13 @@ public slots:
|
||||||
void addPageButtonClicked();
|
void addPageButtonClicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<NotebookPage *> pages;
|
QList<NotebookPage *> _pages;
|
||||||
|
|
||||||
NotebookButton addButton;
|
NotebookButton _addButton;
|
||||||
NotebookButton settingsButton;
|
NotebookButton _settingsButton;
|
||||||
NotebookButton userButton;
|
NotebookButton _userButton;
|
||||||
|
|
||||||
NotebookPage *selectedPage;
|
NotebookPage *_selectedPage;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void load(const boost::property_tree::ptree &tree);
|
void load(const boost::property_tree::ptree &tree);
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
#include "widgets/notebookbutton.h"
|
#include "widgets/notebookbutton.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
|
#include "widgets/fancybutton.h"
|
||||||
|
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPainterPath>
|
#include <QPainterPath>
|
||||||
|
#include <QRadialGradient>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
NotebookButton::NotebookButton(QWidget *parent)
|
NotebookButton::NotebookButton(QWidget *parent)
|
||||||
: QWidget(parent)
|
: FancyButton(parent)
|
||||||
{
|
{
|
||||||
|
setMouseEffectColor(QColor(0, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookButton::paintEvent(QPaintEvent *)
|
||||||
NotebookButton::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
@ -23,29 +25,30 @@ NotebookButton::paintEvent(QPaintEvent *)
|
||||||
|
|
||||||
auto &colorScheme = ColorScheme::getInstance();
|
auto &colorScheme = ColorScheme::getInstance();
|
||||||
|
|
||||||
if (mouseDown) {
|
if (_mouseDown) {
|
||||||
background = colorScheme.TabSelectedBackground;
|
background = colorScheme.TabSelectedBackground;
|
||||||
foreground = colorScheme.TabSelectedText;
|
foreground = colorScheme.TabSelectedText;
|
||||||
} else if (mouseOver) {
|
} else if (_mouseOver) {
|
||||||
background = colorScheme.TabHoverBackground;
|
background = colorScheme.TabHoverBackground;
|
||||||
foreground = colorScheme.TabSelectedBackground;
|
foreground = colorScheme.TabSelectedBackground;
|
||||||
} else {
|
} else {
|
||||||
background = colorScheme.TabPanelBackground;
|
background = colorScheme.TabPanelBackground;
|
||||||
foreground = colorScheme.TabSelectedBackground;
|
// foreground = colorScheme.TabSelectedBackground;
|
||||||
|
foreground = QColor(230, 230, 230);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.setPen(Qt::NoPen);
|
painter.setPen(Qt::NoPen);
|
||||||
painter.fillRect(this->rect(), background);
|
painter.fillRect(this->rect(), background);
|
||||||
|
|
||||||
float h = this->height(), w = this->width();
|
float h = height(), w = width();
|
||||||
|
|
||||||
if (icon == IconPlus) {
|
if (icon == IconPlus) {
|
||||||
painter.fillRect(QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1,
|
painter.fillRect(
|
||||||
w - ((h / 12) * 5), (h / 12) * 1),
|
QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, w - ((h / 12) * 5), (h / 12) * 1),
|
||||||
foreground);
|
foreground);
|
||||||
painter.fillRect(QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1,
|
painter.fillRect(
|
||||||
(h / 12) * 1, w - ((h / 12) * 5)),
|
QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, (h / 12) * 1, w - ((h / 12) * 5)),
|
||||||
foreground);
|
foreground);
|
||||||
} else if (icon == IconUser) {
|
} else if (icon == IconUser) {
|
||||||
painter.setRenderHint(QPainter::Antialiasing);
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||||
|
@ -74,10 +77,8 @@ NotebookButton::paintEvent(QPaintEvent *)
|
||||||
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
|
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0),
|
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), (360 / 32.0));
|
||||||
(360 / 32.0));
|
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a, i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
|
||||||
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a,
|
|
||||||
i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.fillPath(path, foreground);
|
painter.fillPath(path, foreground);
|
||||||
|
@ -85,44 +86,21 @@ NotebookButton::paintEvent(QPaintEvent *)
|
||||||
painter.setBrush(background);
|
painter.setBrush(background);
|
||||||
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
|
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fancyPaint(painter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
|
||||||
NotebookButton::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
mouseDown = true;
|
_mouseDown = false;
|
||||||
|
|
||||||
this->update();
|
update();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
NotebookButton::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->button() == Qt::LeftButton) {
|
|
||||||
mouseDown = false;
|
|
||||||
|
|
||||||
this->update();
|
|
||||||
|
|
||||||
emit clicked();
|
emit clicked();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
FancyButton::mouseReleaseEvent(event);
|
||||||
NotebookButton::enterEvent(QEvent *)
|
|
||||||
{
|
|
||||||
mouseOver = true;
|
|
||||||
|
|
||||||
this->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
NotebookButton::leaveEvent(QEvent *)
|
|
||||||
{
|
|
||||||
mouseOver = false;
|
|
||||||
|
|
||||||
this->update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
#ifndef NOTEBOOKBUTTON_H
|
#ifndef NOTEBOOKBUTTON_H
|
||||||
#define NOTEBOOKBUTTON_H
|
#define NOTEBOOKBUTTON_H
|
||||||
|
|
||||||
|
#include "fancybutton.h"
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
class NotebookButton : public QWidget
|
class NotebookButton : public FancyButton
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const int IconPlus = 0;
|
static const int IconPlus = 0;
|
||||||
static const int IconUser = 1;
|
static const int IconUser = 1;
|
||||||
|
@ -18,18 +21,17 @@ public:
|
||||||
|
|
||||||
NotebookButton(QWidget *parent);
|
NotebookButton(QWidget *parent);
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
|
protected:
|
||||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void paintEvent(QPaintEvent *) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
void enterEvent(QEvent *) Q_DECL_OVERRIDE;
|
|
||||||
void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clicked();
|
void clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mouseOver = false;
|
bool _mouseOver = false;
|
||||||
bool mouseDown = false;
|
bool _mouseDown = false;
|
||||||
|
QPoint _mousePos;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,49 +21,60 @@ std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1);
|
||||||
|
|
||||||
NotebookPage::NotebookPage(QWidget *parent, NotebookTab *tab)
|
NotebookPage::NotebookPage(QWidget *parent, NotebookTab *tab)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, parentbox(this)
|
, _tab(tab)
|
||||||
, chatWidgets()
|
, _parentbox(this)
|
||||||
, preview(this)
|
, _chatWidgets()
|
||||||
|
, _preview(this)
|
||||||
{
|
{
|
||||||
this->tab = tab;
|
|
||||||
tab->page = this;
|
tab->page = this;
|
||||||
|
|
||||||
setHidden(true);
|
setHidden(true);
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
|
|
||||||
this->parentbox.addSpacing(2);
|
_parentbox.addSpacing(2);
|
||||||
this->parentbox.addLayout(&this->hbox);
|
_parentbox.addLayout(&_hbox);
|
||||||
this->parentbox.setMargin(0);
|
_parentbox.setMargin(0);
|
||||||
|
|
||||||
this->hbox.setSpacing(1);
|
_hbox.setSpacing(1);
|
||||||
this->hbox.setMargin(0);
|
_hbox.setMargin(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int>
|
const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
|
||||||
NotebookPage::removeFromLayout(ChatWidget *widget)
|
|
||||||
{
|
{
|
||||||
for (auto it = this->chatWidgets.begin(); it != this->chatWidgets.end();
|
return _chatWidgets;
|
||||||
++it) {
|
}
|
||||||
|
|
||||||
|
NotebookTab *NotebookPage::getTab() const
|
||||||
|
{
|
||||||
|
return _tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> NotebookPage::removeFromLayout(ChatWidget *widget)
|
||||||
|
{
|
||||||
|
// remove from chatWidgets vector
|
||||||
|
for (auto it = _chatWidgets.begin(); it != _chatWidgets.end(); ++it) {
|
||||||
if (*it == widget) {
|
if (*it == widget) {
|
||||||
this->chatWidgets.erase(it);
|
_chatWidgets.erase(it);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < this->hbox.count(); ++i) {
|
// remove from box and return location
|
||||||
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i));
|
for (int i = 0; i < _hbox.count(); ++i) {
|
||||||
|
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(i));
|
||||||
|
|
||||||
for (int j = 0; j < vbox->count(); ++j) {
|
for (int j = 0; j < vbox->count(); ++j) {
|
||||||
if (vbox->itemAt(j)->widget() != widget)
|
if (vbox->itemAt(j)->widget() != widget) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
widget->setParent(NULL);
|
widget->setParent(NULL);
|
||||||
|
|
||||||
bool isLastItem = vbox->count() == 0;
|
bool isLastItem = vbox->count() == 0;
|
||||||
|
|
||||||
if (isLastItem) {
|
if (isLastItem) {
|
||||||
this->hbox.removeItem(vbox);
|
_hbox.removeItem(vbox);
|
||||||
|
|
||||||
delete vbox;
|
delete vbox;
|
||||||
}
|
}
|
||||||
|
@ -75,19 +86,17 @@ NotebookPage::removeFromLayout(ChatWidget *widget)
|
||||||
return std::pair<int, int>(-1, -1);
|
return std::pair<int, int>(-1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::addToLayout(ChatWidget *widget,
|
||||||
NotebookPage::addToLayout(
|
std::pair<int, int> position = std::pair<int, int>(-1, -1))
|
||||||
ChatWidget *widget,
|
|
||||||
std::pair<int, int> position = std::pair<int, int>(-1, -1))
|
|
||||||
{
|
{
|
||||||
this->chatWidgets.push_back(widget);
|
_chatWidgets.push_back(widget);
|
||||||
|
|
||||||
// add vbox at the end
|
// add vbox at the end
|
||||||
if (position.first < 0 || position.first >= this->hbox.count()) {
|
if (position.first < 0 || position.first >= _hbox.count()) {
|
||||||
auto vbox = new QVBoxLayout();
|
auto vbox = new QVBoxLayout();
|
||||||
vbox->addWidget(widget);
|
vbox->addWidget(widget);
|
||||||
|
|
||||||
this->hbox.addLayout(vbox, 1);
|
_hbox.addLayout(vbox, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,101 +105,91 @@ NotebookPage::addToLayout(
|
||||||
auto vbox = new QVBoxLayout();
|
auto vbox = new QVBoxLayout();
|
||||||
vbox->addWidget(widget);
|
vbox->addWidget(widget);
|
||||||
|
|
||||||
this->hbox.insertLayout(position.first, vbox, 1);
|
_hbox.insertLayout(position.first, vbox, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to existing vbox
|
// add to existing vbox
|
||||||
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(position.first));
|
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(position.first));
|
||||||
|
|
||||||
vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)),
|
vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
|
||||||
widget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::enterEvent(QEvent *)
|
||||||
NotebookPage::enterEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
if (this->hbox.count() == 0) {
|
if (_hbox.count() == 0) {
|
||||||
setCursor(QCursor(Qt::PointingHandCursor));
|
setCursor(QCursor(Qt::PointingHandCursor));
|
||||||
} else {
|
} else {
|
||||||
setCursor(QCursor(Qt::ArrowCursor));
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::leaveEvent(QEvent *)
|
||||||
NotebookPage::leaveEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::mouseReleaseEvent(QMouseEvent *event)
|
||||||
NotebookPage::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (this->hbox.count() == 0 && event->button() == Qt::LeftButton) {
|
if (_hbox.count() == 0 && event->button() == Qt::LeftButton) {
|
||||||
// "Add Chat" was clicked
|
// "Add Chat" was clicked
|
||||||
this->addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1));
|
addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1));
|
||||||
|
|
||||||
setCursor(QCursor(Qt::ArrowCursor));
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::dragEnterEvent(QDragEnterEvent *event)
|
||||||
NotebookPage::dragEnterEvent(QDragEnterEvent *event)
|
|
||||||
{
|
{
|
||||||
if (!event->mimeData()->hasFormat("chatterino/split"))
|
if (!event->mimeData()->hasFormat("chatterino/split"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isDraggingSplit) {
|
if (isDraggingSplit) {
|
||||||
this->dropRegions.clear();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->hbox.count() == 0) {
|
_dropRegions.clear();
|
||||||
this->dropRegions.push_back(
|
|
||||||
DropRegion(rect(), std::pair<int, int>(-1, -1)));
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < this->hbox.count() + 1; ++i) {
|
|
||||||
this->dropRegions.push_back(DropRegion(
|
|
||||||
QRect(((i * 4 - 1) * width() / this->hbox.count()) / 4, 0,
|
|
||||||
width() / this->hbox.count() / 2 + 1, height() + 1),
|
|
||||||
|
|
||||||
std::pair<int, int>(i, -1)));
|
if (_hbox.count() == 0) {
|
||||||
}
|
_dropRegions.push_back(DropRegion(rect(), std::pair<int, int>(-1, -1)));
|
||||||
|
} else {
|
||||||
for (int i = 0; i < this->hbox.count(); ++i) {
|
for (int i = 0; i < _hbox.count() + 1; ++i) {
|
||||||
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i));
|
_dropRegions.push_back(DropRegion(QRect(((i * 4 - 1) * width() / _hbox.count()) / 4, 0,
|
||||||
|
width() / _hbox.count() / 2 + 1, height() + 1),
|
||||||
for (int j = 0; j < vbox->count() + 1; ++j) {
|
std::pair<int, int>(i, -1)));
|
||||||
this->dropRegions.push_back(DropRegion(
|
|
||||||
QRect(i * width() / this->hbox.count(),
|
|
||||||
((j * 2 - 1) * height() / vbox->count()) / 2,
|
|
||||||
width() / this->hbox.count() + 1,
|
|
||||||
height() / vbox->count() + 1),
|
|
||||||
|
|
||||||
std::pair<int, int>(i, j)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreviewRect(event->pos());
|
for (int i = 0; i < _hbox.count(); ++i) {
|
||||||
|
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(i));
|
||||||
|
|
||||||
event->acceptProposedAction();
|
for (int j = 0; j < vbox->count() + 1; ++j) {
|
||||||
|
_dropRegions.push_back(DropRegion(
|
||||||
|
QRect(i * width() / _hbox.count(), ((j * 2 - 1) * height() / vbox->count()) / 2,
|
||||||
|
width() / _hbox.count() + 1, height() / vbox->count() + 1),
|
||||||
|
|
||||||
|
std::pair<int, int>(i, j)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPreviewRect(event->pos());
|
||||||
|
|
||||||
|
event->acceptProposedAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::dragMoveEvent(QDragMoveEvent *event)
|
||||||
NotebookPage::dragMoveEvent(QDragMoveEvent *event)
|
|
||||||
{
|
{
|
||||||
setPreviewRect(event->pos());
|
setPreviewRect(event->pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::setPreviewRect(QPoint mousePos)
|
||||||
NotebookPage::setPreviewRect(QPoint mousePos)
|
|
||||||
{
|
{
|
||||||
for (DropRegion region : this->dropRegions) {
|
for (DropRegion region : _dropRegions) {
|
||||||
if (region.rect.contains(mousePos)) {
|
if (region.rect.contains(mousePos)) {
|
||||||
this->preview.setBounds(region.rect);
|
_preview.setBounds(region.rect);
|
||||||
|
|
||||||
if (!this->preview.isVisible()) {
|
if (!_preview.isVisible()) {
|
||||||
this->preview.show();
|
_preview.show();
|
||||||
this->preview.raise();
|
_preview.raise();
|
||||||
}
|
}
|
||||||
|
|
||||||
dropPosition = region.position;
|
dropPosition = region.position;
|
||||||
|
@ -199,17 +198,15 @@ NotebookPage::setPreviewRect(QPoint mousePos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->preview.hide();
|
_preview.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
|
||||||
NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
|
|
||||||
{
|
{
|
||||||
this->preview.hide();
|
_preview.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::dropEvent(QDropEvent *event)
|
||||||
NotebookPage::dropEvent(QDropEvent *event)
|
|
||||||
{
|
{
|
||||||
if (isDraggingSplit) {
|
if (isDraggingSplit) {
|
||||||
event->acceptProposedAction();
|
event->acceptProposedAction();
|
||||||
|
@ -219,33 +216,28 @@ NotebookPage::dropEvent(QDropEvent *event)
|
||||||
addToLayout(NotebookPage::draggingSplit, dropPosition);
|
addToLayout(NotebookPage::draggingSplit, dropPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->preview.hide();
|
_preview.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::paintEvent(QPaintEvent *)
|
||||||
NotebookPage::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
if (this->hbox.count() == 0) {
|
if (_hbox.count() == 0) {
|
||||||
painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground);
|
painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground);
|
||||||
|
|
||||||
painter.fillRect(0, 0, width(), 2,
|
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
|
||||||
ColorScheme::getInstance().TabSelectedBackground);
|
|
||||||
|
|
||||||
painter.setPen(ColorScheme::getInstance().Text);
|
painter.setPen(ColorScheme::getInstance().Text);
|
||||||
painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
|
painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
|
||||||
} else {
|
} else {
|
||||||
painter.fillRect(rect(),
|
painter.fillRect(rect(), ColorScheme::getInstance().TabSelectedBackground);
|
||||||
ColorScheme::getInstance().TabSelectedBackground);
|
|
||||||
|
|
||||||
painter.fillRect(0, 0, width(), 2,
|
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
|
||||||
ColorScheme::getInstance().TabSelectedBackground);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<int, int>
|
static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
||||||
getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < layout->count(); ++i) {
|
for (int i = 0; i < layout->count(); ++i) {
|
||||||
printf("xD\n");
|
printf("xD\n");
|
||||||
|
@ -254,10 +246,9 @@ getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
||||||
return std::make_pair(-1, -1);
|
return std::make_pair(-1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int>
|
std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
||||||
NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
|
||||||
{
|
{
|
||||||
auto layout = this->hbox.layout();
|
auto layout = _hbox.layout();
|
||||||
|
|
||||||
if (layout == nullptr) {
|
if (layout == nullptr) {
|
||||||
return std::make_pair(-1, -1);
|
return std::make_pair(-1, -1);
|
||||||
|
@ -266,8 +257,7 @@ NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
||||||
return getWidgetPositionInLayout(layout, chatWidget);
|
return getWidgetPositionInLayout(layout, chatWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPage::load(const boost::property_tree::ptree &tree)
|
||||||
NotebookPage::load(const boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
int column = 0;
|
int column = 0;
|
||||||
|
@ -276,7 +266,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
|
||||||
for (const auto &innerV : v.second.get_child("")) {
|
for (const auto &innerV : v.second.get_child("")) {
|
||||||
auto widget = new ChatWidget();
|
auto widget = new ChatWidget();
|
||||||
widget->load(innerV.second);
|
widget->load(innerV.second);
|
||||||
this->addToLayout(widget, std::pair<int, int>(column, row));
|
addToLayout(widget, std::pair<int, int>(column, row));
|
||||||
++row;
|
++row;
|
||||||
}
|
}
|
||||||
++column;
|
++column;
|
||||||
|
@ -286,8 +276,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
||||||
saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < layout->count(); ++i) {
|
for (int i = 0; i < layout->count(); ++i) {
|
||||||
auto item = layout->itemAt(i);
|
auto item = layout->itemAt(i);
|
||||||
|
@ -323,17 +312,16 @@ saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::property_tree::ptree
|
boost::property_tree::ptree NotebookPage::save()
|
||||||
NotebookPage::save()
|
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree tree;
|
boost::property_tree::ptree tree;
|
||||||
|
|
||||||
auto layout = this->hbox.layout();
|
auto layout = _hbox.layout();
|
||||||
|
|
||||||
saveFromLayout(layout, tree);
|
saveFromLayout(layout, tree);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for (const auto &chat : this->chatWidgets) {
|
for (const auto &chat : chatWidgets) {
|
||||||
boost::property_tree::ptree child = chat->save();
|
boost::property_tree::ptree child = chat->save();
|
||||||
|
|
||||||
// Set child position
|
// Set child position
|
||||||
|
|
|
@ -23,34 +23,31 @@ class NotebookPage : public QWidget
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NotebookPage(QWidget *parent, NotebookTab *tab);
|
NotebookPage(QWidget *parent, NotebookTab *_tab);
|
||||||
NotebookTab *tab;
|
|
||||||
|
|
||||||
std::pair<int, int> removeFromLayout(ChatWidget *widget);
|
std::pair<int, int> removeFromLayout(ChatWidget *widget);
|
||||||
void addToLayout(ChatWidget *widget, std::pair<int, int> position);
|
void addToLayout(ChatWidget *widget, std::pair<int, int> position);
|
||||||
|
|
||||||
const std::vector<ChatWidget *> &
|
const std::vector<ChatWidget *> &getChatWidgets() const;
|
||||||
getChatWidgets() const
|
NotebookTab *getTab() const;
|
||||||
{
|
|
||||||
return chatWidgets;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isDraggingSplit;
|
static bool isDraggingSplit;
|
||||||
static ChatWidget *draggingSplit;
|
static ChatWidget *draggingSplit;
|
||||||
static std::pair<int, int> dropPosition;
|
static std::pair<int, int> dropPosition;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
|
||||||
void enterEvent(QEvent *) override;
|
void enterEvent(QEvent *) override;
|
||||||
void leaveEvent(QEvent *) override;
|
void leaveEvent(QEvent *) override;
|
||||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
|
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||||
void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
|
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||||
void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE;
|
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||||
void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
|
void dropEvent(QDropEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
struct DropRegion {
|
struct DropRegion {
|
||||||
QRect rect;
|
QRect rect;
|
||||||
std::pair<int, int> position;
|
std::pair<int, int> position;
|
||||||
|
@ -62,15 +59,16 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
QVBoxLayout parentbox;
|
NotebookTab *_tab;
|
||||||
QHBoxLayout hbox;
|
|
||||||
|
|
||||||
std::vector<ChatWidget *> chatWidgets;
|
QVBoxLayout _parentbox;
|
||||||
std::vector<DropRegion> dropRegions;
|
QHBoxLayout _hbox;
|
||||||
|
|
||||||
NotebookPageDropPreview preview;
|
std::vector<ChatWidget *> _chatWidgets;
|
||||||
|
std::vector<DropRegion> _dropRegions;
|
||||||
|
|
||||||
|
NotebookPageDropPreview _preview;
|
||||||
|
|
||||||
private:
|
|
||||||
void setPreviewRect(QPoint mousePos);
|
void setPreviewRect(QPoint mousePos);
|
||||||
|
|
||||||
std::pair<int, int> getChatPosition(const ChatWidget *chatWidget);
|
std::pair<int, int> getChatPosition(const ChatWidget *chatWidget);
|
||||||
|
|
|
@ -17,8 +17,7 @@ NotebookPageDropPreview::NotebookPageDropPreview(QWidget *parent)
|
||||||
this->setHidden(true);
|
this->setHidden(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
||||||
NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
@ -26,14 +25,12 @@ NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
||||||
ColorScheme::getInstance().DropPreviewBackground);
|
ColorScheme::getInstance().DropPreviewBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPageDropPreview::hideEvent(QHideEvent *)
|
||||||
NotebookPageDropPreview::hideEvent(QHideEvent *)
|
|
||||||
{
|
{
|
||||||
animate = false;
|
animate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookPageDropPreview::setBounds(const QRect &rect)
|
||||||
NotebookPageDropPreview::setBounds(const QRect &rect)
|
|
||||||
{
|
{
|
||||||
if (rect == this->desiredGeometry) {
|
if (rect == this->desiredGeometry) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "widgets/notebooktab.h"
|
#include "widgets/notebooktab.h"
|
||||||
#include "colorscheme.h"
|
#include "colorscheme.h"
|
||||||
#include "settings.h"
|
#include "settingsmanager.h"
|
||||||
#include "widgets/notebook.h"
|
#include "widgets/notebook.h"
|
||||||
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -10,75 +10,113 @@ namespace widgets {
|
||||||
|
|
||||||
NotebookTab::NotebookTab(Notebook *notebook)
|
NotebookTab::NotebookTab(Notebook *notebook)
|
||||||
: QWidget(notebook)
|
: QWidget(notebook)
|
||||||
, posAnimation(this, "pos")
|
, _posAnimation(this, "pos")
|
||||||
, posAnimated(false)
|
, _posAnimated(false)
|
||||||
, posAnimationDesired()
|
, _posAnimationDesired()
|
||||||
, notebook(notebook)
|
, _notebook(notebook)
|
||||||
, title("<no title>")
|
, _title("<no title>")
|
||||||
, selected(false)
|
, _selected(false)
|
||||||
, mouseOver(false)
|
, _mouseOver(false)
|
||||||
, mouseDown(false)
|
, _mouseDown(false)
|
||||||
, mouseOverX(false)
|
, _mouseOverX(false)
|
||||||
, mouseDownX(false)
|
, _mouseDownX(false)
|
||||||
, highlightStyle(HighlightNone)
|
, _highlightStyle(HighlightNone)
|
||||||
{
|
{
|
||||||
this->calcSize();
|
this->calcSize();
|
||||||
this->setAcceptDrops(true);
|
this->setAcceptDrops(true);
|
||||||
|
|
||||||
posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
|
_posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
|
||||||
|
|
||||||
this->hideXConnection =
|
this->_hideXConnection = SettingsManager::getInstance().hideTabX.valueChanged.connect(
|
||||||
Settings::getInstance().hideTabX.valueChanged.connect(
|
boost::bind(&NotebookTab::hideTabXChanged, this, _1));
|
||||||
boost::bind(&NotebookTab::hideTabXChanged, this, _1));
|
|
||||||
|
|
||||||
this->setMouseTracking(true);
|
this->setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotebookTab::~NotebookTab()
|
NotebookTab::~NotebookTab()
|
||||||
{
|
{
|
||||||
this->hideXConnection.disconnect();
|
this->_hideXConnection.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::calcSize()
|
||||||
NotebookTab::calcSize()
|
|
||||||
{
|
{
|
||||||
if (Settings::getInstance().hideTabX.get()) {
|
if (SettingsManager::getInstance().hideTabX.get()) {
|
||||||
this->resize(this->fontMetrics().width(this->title) + 8, 24);
|
resize(fontMetrics().width(_title) + 8, 24);
|
||||||
} else {
|
} else {
|
||||||
this->resize(this->fontMetrics().width(this->title) + 8 + 24, 24);
|
resize(fontMetrics().width(_title) + 8 + 24, 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->parent() != nullptr) {
|
if (parent() != nullptr) {
|
||||||
((Notebook *)this->parent())->performLayout(true);
|
((Notebook *)parent())->performLayout(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
const QString &NotebookTab::getTitle() const
|
||||||
NotebookTab::moveAnimated(QPoint pos, bool animated)
|
|
||||||
{
|
{
|
||||||
posAnimationDesired = pos;
|
return _title;
|
||||||
|
}
|
||||||
|
|
||||||
if ((this->window() != NULL && !this->window()->isVisible()) || !animated ||
|
void NotebookTab::setTitle(const QString &title)
|
||||||
posAnimated == false) {
|
{
|
||||||
|
_title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotebookTab::getSelected()
|
||||||
|
{
|
||||||
|
return _selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotebookTab::setSelected(bool value)
|
||||||
|
{
|
||||||
|
_selected = value;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotebookTab::HighlightStyle NotebookTab::getHighlightStyle() const
|
||||||
|
{
|
||||||
|
return _highlightStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotebookTab::setHighlightStyle(HighlightStyle style)
|
||||||
|
{
|
||||||
|
_highlightStyle = style;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect NotebookTab::getDesiredRect() const
|
||||||
|
{
|
||||||
|
return QRect(_posAnimationDesired, size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotebookTab::hideTabXChanged(bool)
|
||||||
|
{
|
||||||
|
calcSize();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotebookTab::moveAnimated(QPoint pos, bool animated)
|
||||||
|
{
|
||||||
|
_posAnimationDesired = pos;
|
||||||
|
|
||||||
|
if ((window() != NULL && !window()->isVisible()) || !animated || _posAnimated == false) {
|
||||||
move(pos);
|
move(pos);
|
||||||
|
|
||||||
posAnimated = true;
|
_posAnimated = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->posAnimation.endValue() == pos) {
|
if (_posAnimation.endValue() == pos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->posAnimation.stop();
|
_posAnimation.stop();
|
||||||
this->posAnimation.setDuration(75);
|
_posAnimation.setDuration(75);
|
||||||
this->posAnimation.setStartValue(this->pos());
|
_posAnimation.setStartValue(this->pos());
|
||||||
this->posAnimation.setEndValue(pos);
|
_posAnimation.setEndValue(pos);
|
||||||
this->posAnimation.start();
|
_posAnimation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::paintEvent(QPaintEvent *)
|
||||||
NotebookTab::paintEvent(QPaintEvent *)
|
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
|
||||||
|
@ -86,16 +124,16 @@ NotebookTab::paintEvent(QPaintEvent *)
|
||||||
|
|
||||||
auto &colorScheme = ColorScheme::getInstance();
|
auto &colorScheme = ColorScheme::getInstance();
|
||||||
|
|
||||||
if (this->selected) {
|
if (_selected) {
|
||||||
painter.fillRect(rect(), colorScheme.TabSelectedBackground);
|
painter.fillRect(rect(), colorScheme.TabSelectedBackground);
|
||||||
fg = colorScheme.TabSelectedText;
|
fg = colorScheme.TabSelectedText;
|
||||||
} else if (this->mouseOver) {
|
} else if (_mouseOver) {
|
||||||
painter.fillRect(rect(), colorScheme.TabHoverBackground);
|
painter.fillRect(rect(), colorScheme.TabHoverBackground);
|
||||||
fg = colorScheme.TabHoverText;
|
fg = colorScheme.TabHoverText;
|
||||||
} else if (this->highlightStyle == HighlightHighlighted) {
|
} else if (_highlightStyle == HighlightHighlighted) {
|
||||||
painter.fillRect(rect(), colorScheme.TabHighlightedBackground);
|
painter.fillRect(rect(), colorScheme.TabHighlightedBackground);
|
||||||
fg = colorScheme.TabHighlightedText;
|
fg = colorScheme.TabHighlightedText;
|
||||||
} else if (this->highlightStyle == HighlightNewMessage) {
|
} else if (_highlightStyle == HighlightNewMessage) {
|
||||||
painter.fillRect(rect(), colorScheme.TabNewMessageBackground);
|
painter.fillRect(rect(), colorScheme.TabNewMessageBackground);
|
||||||
fg = colorScheme.TabHighlightedText;
|
fg = colorScheme.TabHighlightedText;
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,116 +143,105 @@ NotebookTab::paintEvent(QPaintEvent *)
|
||||||
|
|
||||||
painter.setPen(fg);
|
painter.setPen(fg);
|
||||||
|
|
||||||
QRect rect(0, 0,
|
QRect rect(0, 0, width() - (SettingsManager::getInstance().hideTabX.get() ? 0 : 16), height());
|
||||||
width() - (Settings::getInstance().hideTabX.get() ? 0 : 16),
|
|
||||||
height());
|
|
||||||
|
|
||||||
painter.drawText(rect, this->title, QTextOption(Qt::AlignCenter));
|
painter.drawText(rect, _title, QTextOption(Qt::AlignCenter));
|
||||||
|
|
||||||
if (!Settings::getInstance().hideTabX.get() &&
|
if (!SettingsManager::getInstance().hideTabX.get() && (_mouseOver || _selected)) {
|
||||||
(this->mouseOver || this->selected)) {
|
if (_mouseOverX) {
|
||||||
if (this->mouseOverX) {
|
painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
|
||||||
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64));
|
|
||||||
|
|
||||||
if (this->mouseDownX) {
|
if (_mouseDownX) {
|
||||||
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64));
|
painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.drawLine(this->getXRect().topLeft() + QPoint(4, 4),
|
painter.drawLine(getXRect().topLeft() + QPoint(4, 4),
|
||||||
this->getXRect().bottomRight() + QPoint(-4, -4));
|
getXRect().bottomRight() + QPoint(-4, -4));
|
||||||
painter.drawLine(this->getXRect().topRight() + QPoint(-4, 4),
|
painter.drawLine(getXRect().topRight() + QPoint(-4, 4),
|
||||||
this->getXRect().bottomLeft() + QPoint(4, -4));
|
getXRect().bottomLeft() + QPoint(4, -4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::mousePressEvent(QMouseEvent *event)
|
||||||
NotebookTab::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
this->mouseDown = true;
|
_mouseDown = true;
|
||||||
this->mouseDownX = this->getXRect().contains(event->pos());
|
_mouseDownX = getXRect().contains(event->pos());
|
||||||
|
|
||||||
this->update();
|
update();
|
||||||
|
|
||||||
this->notebook->select(page);
|
_notebook->select(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
|
||||||
NotebookTab::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
this->mouseDown = false;
|
_mouseDown = false;
|
||||||
|
|
||||||
if (!Settings::getInstance().hideTabX.get() && this->mouseDownX &&
|
if (!SettingsManager::getInstance().hideTabX.get() && _mouseDownX &&
|
||||||
this->getXRect().contains(event->pos())) {
|
getXRect().contains(event->pos())) {
|
||||||
this->mouseDownX = false;
|
_mouseDownX = false;
|
||||||
|
|
||||||
this->notebook->removePage(this->page);
|
_notebook->removePage(page);
|
||||||
} else {
|
} else {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::enterEvent(QEvent *)
|
||||||
NotebookTab::enterEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
this->mouseOver = true;
|
_mouseOver = true;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::leaveEvent(QEvent *)
|
||||||
NotebookTab::leaveEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
this->mouseOverX = this->mouseOver = false;
|
_mouseOverX = _mouseOver = false;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::dragEnterEvent(QDragEnterEvent *)
|
||||||
NotebookTab::dragEnterEvent(QDragEnterEvent *)
|
|
||||||
{
|
{
|
||||||
this->notebook->select(page);
|
_notebook->select(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::mouseMoveEvent(QMouseEvent *event)
|
||||||
NotebookTab::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
bool overX = this->getXRect().contains(event->pos());
|
bool overX = getXRect().contains(event->pos());
|
||||||
|
|
||||||
if (overX != this->mouseOverX) {
|
if (overX != _mouseOverX) {
|
||||||
this->mouseOverX = overX && !Settings::getInstance().hideTabX.get();
|
_mouseOverX = overX && !SettingsManager::getInstance().hideTabX.get();
|
||||||
|
|
||||||
this->update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) {
|
if (_mouseDown && !getDesiredRect().contains(event->pos())) {
|
||||||
QPoint relPoint = this->mapToParent(event->pos());
|
QPoint relPoint = mapToParent(event->pos());
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
NotebookPage *page = notebook->tabAt(relPoint, index);
|
NotebookPage *page = _notebook->tabAt(relPoint, index);
|
||||||
|
|
||||||
if (page != nullptr && page != this->page) {
|
if (page != nullptr && page != page) {
|
||||||
notebook->rearrangePage(this->page, index);
|
_notebook->rearrangePage(page, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void NotebookTab::load(const boost::property_tree::ptree &tree)
|
||||||
NotebookTab::load(const boost::property_tree::ptree &tree)
|
|
||||||
{
|
{
|
||||||
// Load tab title
|
// Load tab title
|
||||||
try {
|
try {
|
||||||
this->setTitle(QString::fromStdString(tree.get<std::string>("title")));
|
setTitle(QString::fromStdString(tree.get<std::string>("title")));
|
||||||
} catch (boost::property_tree::ptree_error) {
|
} catch (boost::property_tree::ptree_error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::property_tree::ptree
|
boost::property_tree::ptree NotebookTab::save()
|
||||||
NotebookTab::save()
|
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree tree;
|
boost::property_tree::ptree tree;
|
||||||
|
|
||||||
tree.put("title", this->getTitle().toStdString());
|
tree.put("title", getTitle().toStdString());
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,72 +18,27 @@ class NotebookTab : public QWidget
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum HighlightStyle {
|
enum HighlightStyle { HighlightNone, HighlightHighlighted, HighlightNewMessage };
|
||||||
HighlightNone,
|
|
||||||
HighlightHighlighted,
|
|
||||||
HighlightNewMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit NotebookTab(Notebook *notebook);
|
explicit NotebookTab(Notebook *_notebook);
|
||||||
~NotebookTab();
|
~NotebookTab();
|
||||||
|
|
||||||
void calcSize();
|
void calcSize();
|
||||||
|
|
||||||
NotebookPage *page;
|
NotebookPage *page;
|
||||||
|
|
||||||
const QString &
|
const QString &getTitle() const;
|
||||||
getTitle() const
|
void setTitle(const QString &title);
|
||||||
{
|
bool getSelected();
|
||||||
return this->title;
|
void setSelected(bool value);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
HighlightStyle getHighlightStyle() const;
|
||||||
setTitle(const QString &title)
|
void setHighlightStyle(HighlightStyle style);
|
||||||
{
|
|
||||||
this->title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
getSelected()
|
|
||||||
{
|
|
||||||
return this->selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setSelected(bool value)
|
|
||||||
{
|
|
||||||
this->selected = value;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
HighlightStyle
|
|
||||||
getHighlightStyle() const
|
|
||||||
{
|
|
||||||
return this->highlightStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setHighlightStyle(HighlightStyle style)
|
|
||||||
{
|
|
||||||
this->highlightStyle = style;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveAnimated(QPoint pos, bool animated = true);
|
void moveAnimated(QPoint pos, bool animated = true);
|
||||||
|
|
||||||
public:
|
QRect getDesiredRect() const;
|
||||||
QRect
|
void hideTabXChanged(bool);
|
||||||
getDesiredRect() const
|
|
||||||
{
|
|
||||||
return QRect(posAnimationDesired, this->size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
hideTabXChanged(bool)
|
|
||||||
{
|
|
||||||
calcSize();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *) override;
|
void paintEvent(QPaintEvent *) override;
|
||||||
|
@ -98,26 +53,25 @@ protected:
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::signals2::connection hideXConnection;
|
boost::signals2::connection _hideXConnection;
|
||||||
|
|
||||||
QPropertyAnimation posAnimation;
|
QPropertyAnimation _posAnimation;
|
||||||
bool posAnimated;
|
bool _posAnimated;
|
||||||
QPoint posAnimationDesired;
|
QPoint _posAnimationDesired;
|
||||||
|
|
||||||
Notebook *notebook;
|
Notebook *_notebook;
|
||||||
|
|
||||||
QString title;
|
QString _title;
|
||||||
|
|
||||||
bool selected;
|
bool _selected;
|
||||||
bool mouseOver;
|
bool _mouseOver;
|
||||||
bool mouseDown;
|
bool _mouseDown;
|
||||||
bool mouseOverX;
|
bool _mouseOverX;
|
||||||
bool mouseDownX;
|
bool _mouseDownX;
|
||||||
|
|
||||||
HighlightStyle highlightStyle;
|
HighlightStyle _highlightStyle;
|
||||||
|
|
||||||
QRect
|
QRect getXRect()
|
||||||
getXRect()
|
|
||||||
{
|
{
|
||||||
return QRect(this->width() - 20, 4, 16, 16);
|
return QRect(this->width() - 20, 4, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,15 @@ public:
|
||||||
sizePolicy.setVerticalPolicy(QSizePolicy::Preferred);
|
sizePolicy.setVerticalPolicy(QSizePolicy::Preferred);
|
||||||
this->setSizePolicy(sizePolicy);
|
this->setSizePolicy(sizePolicy);
|
||||||
|
|
||||||
QObject::connect(this, &QTextEdit::textChanged, this,
|
QObject::connect(this, &QTextEdit::textChanged, this, &QWidget::updateGeometry);
|
||||||
&QWidget::updateGeometry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize
|
QSize sizeHint() const override
|
||||||
sizeHint() const override
|
|
||||||
{
|
{
|
||||||
return QSize(this->width(), this->heightForWidth(this->width()));
|
return QSize(this->width(), this->heightForWidth(this->width()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool hasHeightForWidth() const override
|
||||||
hasHeightForWidth() const override
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -35,17 +32,14 @@ public:
|
||||||
boost::signals2::signal<void(QKeyEvent *)> keyPressed;
|
boost::signals2::signal<void(QKeyEvent *)> keyPressed;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int
|
int heightForWidth(int) const override
|
||||||
heightForWidth(int) const override
|
|
||||||
{
|
{
|
||||||
auto margins = this->contentsMargins();
|
auto margins = this->contentsMargins();
|
||||||
|
|
||||||
return margins.top() + document()->size().height() + margins.bottom() +
|
return margins.top() + document()->size().height() + margins.bottom() + 5;
|
||||||
5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void keyPressEvent(QKeyEvent *event)
|
||||||
keyPressEvent(QKeyEvent *event)
|
|
||||||
{
|
{
|
||||||
event->ignore();
|
event->ignore();
|
||||||
|
|
||||||
|
|
|
@ -11,35 +11,34 @@ namespace widgets {
|
||||||
|
|
||||||
ScrollBar::ScrollBar(QWidget *widget)
|
ScrollBar::ScrollBar(QWidget *widget)
|
||||||
: QWidget(widget)
|
: QWidget(widget)
|
||||||
, mutex()
|
, _mutex()
|
||||||
, currentValueAnimation(this, "currentValue")
|
, _currentValueAnimation(this, "currentValue")
|
||||||
, highlights(NULL)
|
, _highlights(NULL)
|
||||||
, mouseOverIndex(-1)
|
, _mouseOverIndex(-1)
|
||||||
, mouseDownIndex(-1)
|
, _mouseDownIndex(-1)
|
||||||
, lastMousePosition()
|
, _lastMousePosition()
|
||||||
, buttonHeight(16)
|
, _buttonHeight(16)
|
||||||
, trackHeight(100)
|
, _trackHeight(100)
|
||||||
, thumbRect()
|
, _thumbRect()
|
||||||
, maximum()
|
, _maximum()
|
||||||
, minimum()
|
, _minimum()
|
||||||
, largeChange()
|
, _largeChange()
|
||||||
, smallChange()
|
, _smallChange()
|
||||||
, desiredValue()
|
, _desiredValue()
|
||||||
, currentValueChanged()
|
, _currentValueChanged()
|
||||||
, currentValue()
|
, _currentValue()
|
||||||
{
|
{
|
||||||
this->resize(16, 100);
|
resize(16, 100);
|
||||||
|
|
||||||
this->currentValueAnimation.setDuration(300);
|
_currentValueAnimation.setDuration(250);
|
||||||
this->currentValueAnimation.setEasingCurve(
|
_currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic));
|
||||||
QEasingCurve(QEasingCurve::OutCubic));
|
|
||||||
|
|
||||||
this->setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar::~ScrollBar()
|
ScrollBar::~ScrollBar()
|
||||||
{
|
{
|
||||||
auto highlight = this->highlights;
|
auto highlight = _highlights;
|
||||||
|
|
||||||
while (highlight != NULL) {
|
while (highlight != NULL) {
|
||||||
auto tmp = highlight->next;
|
auto tmp = highlight->next;
|
||||||
|
@ -48,18 +47,17 @@ ScrollBar::~ScrollBar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
||||||
ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
|
||||||
{
|
{
|
||||||
this->mutex.lock();
|
_mutex.lock();
|
||||||
|
|
||||||
ScrollBarHighlight *last = NULL;
|
ScrollBarHighlight *last = NULL;
|
||||||
ScrollBarHighlight *current = this->highlights;
|
ScrollBarHighlight *current = _highlights;
|
||||||
|
|
||||||
while (current != NULL) {
|
while (current != NULL) {
|
||||||
if (func(*current)) {
|
if (func(*current)) {
|
||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
this->highlights = current->next;
|
_highlights = current->next;
|
||||||
} else {
|
} else {
|
||||||
last->next = current->next;
|
last->next = current->next;
|
||||||
}
|
}
|
||||||
|
@ -73,150 +71,233 @@ ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mutex.unlock();
|
_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::addHighlight(ScrollBarHighlight *highlight)
|
||||||
ScrollBar::addHighlight(ScrollBarHighlight *highlight)
|
|
||||||
{
|
{
|
||||||
this->mutex.lock();
|
_mutex.lock();
|
||||||
|
|
||||||
if (this->highlights == NULL) {
|
if (_highlights == NULL) {
|
||||||
this->highlights = highlight;
|
_highlights = highlight;
|
||||||
} else {
|
} else {
|
||||||
highlight->next = this->highlights->next;
|
highlight->next = _highlights->next;
|
||||||
this->highlights->next = highlight;
|
_highlights->next = highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mutex.unlock();
|
_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::setMaximum(qreal value)
|
||||||
ScrollBar::paintEvent(QPaintEvent *)
|
{
|
||||||
|
_maximum = value;
|
||||||
|
|
||||||
|
updateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::setMinimum(qreal value)
|
||||||
|
{
|
||||||
|
_minimum = value;
|
||||||
|
|
||||||
|
updateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::setLargeChange(qreal value)
|
||||||
|
{
|
||||||
|
_largeChange = value;
|
||||||
|
|
||||||
|
updateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::setSmallChange(qreal value)
|
||||||
|
{
|
||||||
|
_smallChange = value;
|
||||||
|
|
||||||
|
updateScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::setDesiredValue(qreal value, bool animated)
|
||||||
|
{
|
||||||
|
value = std::max(_minimum, std::min(_maximum - _largeChange, value));
|
||||||
|
|
||||||
|
if (_desiredValue != value) {
|
||||||
|
if (animated) {
|
||||||
|
_currentValueAnimation.stop();
|
||||||
|
_currentValueAnimation.setStartValue(_currentValue);
|
||||||
|
|
||||||
|
_currentValueAnimation.setEndValue(value);
|
||||||
|
_currentValueAnimation.start();
|
||||||
|
} else {
|
||||||
|
if (_currentValueAnimation.state() != QPropertyAnimation::Running) {
|
||||||
|
// currentValueAnimation.stop();
|
||||||
|
|
||||||
|
setCurrentValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_desiredValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getMaximum() const
|
||||||
|
{
|
||||||
|
return _maximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getMinimum() const
|
||||||
|
{
|
||||||
|
return _minimum;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getLargeChange() const
|
||||||
|
{
|
||||||
|
return _largeChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getSmallChange() const
|
||||||
|
{
|
||||||
|
return _smallChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getDesiredValue() const
|
||||||
|
{
|
||||||
|
return _desiredValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal ScrollBar::getCurrentValue() const
|
||||||
|
{
|
||||||
|
return _currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::signals2::signal<void()> &ScrollBar::getCurrentValueChanged()
|
||||||
|
{
|
||||||
|
return _currentValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::setCurrentValue(qreal value)
|
||||||
|
{
|
||||||
|
value = std::max(_minimum, std::min(_maximum - _largeChange, value));
|
||||||
|
|
||||||
|
if (_currentValue != value) {
|
||||||
|
_currentValue = value;
|
||||||
|
|
||||||
|
updateScroll();
|
||||||
|
_currentValueChanged();
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollBar::paintEvent(QPaintEvent *)
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG);
|
painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG);
|
||||||
|
|
||||||
painter.fillRect(QRect(0, 0, width(), this->buttonHeight),
|
painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0));
|
||||||
QColor(255, 0, 0));
|
painter.fillRect(QRect(0, height() - _buttonHeight, width(), _buttonHeight), QColor(255, 0, 0));
|
||||||
painter.fillRect(
|
|
||||||
QRect(0, height() - this->buttonHeight, width(), this->buttonHeight),
|
|
||||||
QColor(255, 0, 0));
|
|
||||||
|
|
||||||
painter.fillRect(this->thumbRect, QColor(0, 255, 255));
|
painter.fillRect(_thumbRect, QColor(0, 255, 255));
|
||||||
|
|
||||||
ScrollBarHighlight *highlight = this->highlights;
|
// ScrollBarHighlight *highlight = highlights;
|
||||||
|
|
||||||
this->mutex.lock();
|
_mutex.lock();
|
||||||
|
|
||||||
// do {
|
// do {
|
||||||
// painter.fillRect();
|
// painter.fillRect();
|
||||||
// } while ((highlight = highlight->next()) != NULL);
|
// } while ((highlight = highlight->next()) != NULL);
|
||||||
|
|
||||||
this->mutex.unlock();
|
_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::mouseMoveEvent(QMouseEvent *event)
|
||||||
ScrollBar::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
if (this->mouseDownIndex == -1) {
|
if (_mouseDownIndex == -1) {
|
||||||
int y = event->pos().y();
|
int y = event->pos().y();
|
||||||
|
|
||||||
auto oldIndex = this->mouseOverIndex;
|
auto oldIndex = _mouseOverIndex;
|
||||||
|
|
||||||
if (y < this->buttonHeight) {
|
if (y < _buttonHeight) {
|
||||||
this->mouseOverIndex = 0;
|
_mouseOverIndex = 0;
|
||||||
} else if (y < this->thumbRect.y()) {
|
} else if (y < _thumbRect.y()) {
|
||||||
this->mouseOverIndex = 1;
|
_mouseOverIndex = 1;
|
||||||
} else if (this->thumbRect.contains(2, y)) {
|
} else if (_thumbRect.contains(2, y)) {
|
||||||
this->mouseOverIndex = 2;
|
_mouseOverIndex = 2;
|
||||||
} else if (y < height() - this->buttonHeight) {
|
} else if (y < height() - _buttonHeight) {
|
||||||
this->mouseOverIndex = 3;
|
_mouseOverIndex = 3;
|
||||||
} else {
|
} else {
|
||||||
this->mouseOverIndex = 4;
|
_mouseOverIndex = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldIndex != this->mouseOverIndex) {
|
if (oldIndex != _mouseOverIndex) {
|
||||||
this->update();
|
update();
|
||||||
}
|
}
|
||||||
} else if (this->mouseDownIndex == 2) {
|
} else if (_mouseDownIndex == 2) {
|
||||||
int delta = event->pos().y() - lastMousePosition.y();
|
int delta = event->pos().y() - _lastMousePosition.y();
|
||||||
|
|
||||||
this->setDesiredValue(this->desiredValue +
|
setDesiredValue(_desiredValue + (qreal)delta / _trackHeight * _maximum);
|
||||||
(qreal)delta / this->trackHeight * maximum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->lastMousePosition = event->pos();
|
_lastMousePosition = event->pos();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::mousePressEvent(QMouseEvent *event)
|
||||||
ScrollBar::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
int y = event->pos().y();
|
int y = event->pos().y();
|
||||||
|
|
||||||
if (y < this->buttonHeight) {
|
if (y < _buttonHeight) {
|
||||||
this->mouseDownIndex = 0;
|
_mouseDownIndex = 0;
|
||||||
} else if (y < this->thumbRect.y()) {
|
} else if (y < _thumbRect.y()) {
|
||||||
this->mouseDownIndex = 1;
|
_mouseDownIndex = 1;
|
||||||
} else if (this->thumbRect.contains(2, y)) {
|
} else if (_thumbRect.contains(2, y)) {
|
||||||
this->mouseDownIndex = 2;
|
_mouseDownIndex = 2;
|
||||||
} else if (y < height() - this->buttonHeight) {
|
} else if (y < height() - _buttonHeight) {
|
||||||
this->mouseDownIndex = 3;
|
_mouseDownIndex = 3;
|
||||||
} else {
|
} else {
|
||||||
this->mouseDownIndex = 4;
|
_mouseDownIndex = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::mouseReleaseEvent(QMouseEvent *event)
|
||||||
ScrollBar::mouseReleaseEvent(QMouseEvent *event)
|
|
||||||
{
|
{
|
||||||
int y = event->pos().y();
|
int y = event->pos().y();
|
||||||
|
|
||||||
if (y < this->buttonHeight) {
|
if (y < _buttonHeight) {
|
||||||
if (this->mouseDownIndex == 0) {
|
if (_mouseDownIndex == 0) {
|
||||||
this->setDesiredValue(this->desiredValue - this->smallChange, true);
|
setDesiredValue(_desiredValue - _smallChange, true);
|
||||||
}
|
}
|
||||||
} else if (y < this->thumbRect.y()) {
|
} else if (y < _thumbRect.y()) {
|
||||||
if (this->mouseDownIndex == 1) {
|
if (_mouseDownIndex == 1) {
|
||||||
this->setDesiredValue(this->desiredValue - this->smallChange, true);
|
setDesiredValue(_desiredValue - _smallChange, true);
|
||||||
}
|
}
|
||||||
} else if (this->thumbRect.contains(2, y)) {
|
} else if (_thumbRect.contains(2, y)) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} else if (y < height() - this->buttonHeight) {
|
} else if (y < height() - _buttonHeight) {
|
||||||
if (this->mouseDownIndex == 3) {
|
if (_mouseDownIndex == 3) {
|
||||||
this->setDesiredValue(this->desiredValue + this->smallChange, true);
|
setDesiredValue(_desiredValue + _smallChange, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this->mouseDownIndex == 4) {
|
if (_mouseDownIndex == 4) {
|
||||||
this->setDesiredValue(this->desiredValue + this->smallChange, true);
|
setDesiredValue(_desiredValue + _smallChange, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->mouseDownIndex = -1;
|
_mouseDownIndex = -1;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::leaveEvent(QEvent *)
|
||||||
ScrollBar::leaveEvent(QEvent *)
|
|
||||||
{
|
{
|
||||||
this->mouseOverIndex = -1;
|
_mouseOverIndex = -1;
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ScrollBar::updateScroll()
|
||||||
ScrollBar::updateScroll()
|
|
||||||
{
|
{
|
||||||
this->trackHeight = height() - this->buttonHeight - this->buttonHeight -
|
_trackHeight = height() - _buttonHeight - _buttonHeight - MIN_THUMB_HEIGHT - 1;
|
||||||
MIN_THUMB_HEIGHT - 1;
|
|
||||||
|
|
||||||
this->thumbRect =
|
_thumbRect = QRect(0, (int)(_currentValue / _maximum * _trackHeight) + 1 + _buttonHeight,
|
||||||
QRect(0,
|
width(), (int)(_largeChange / _maximum * _trackHeight) + MIN_THUMB_HEIGHT);
|
||||||
(int)(this->currentValue / this->maximum * this->trackHeight) +
|
|
||||||
1 + this->buttonHeight,
|
|
||||||
width(),
|
|
||||||
(int)(this->largeChange / this->maximum * this->trackHeight) +
|
|
||||||
MIN_THUMB_HEIGHT);
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,128 +23,29 @@ public:
|
||||||
void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func);
|
void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func);
|
||||||
void addHighlight(ScrollBarHighlight *highlight);
|
void addHighlight(ScrollBarHighlight *highlight);
|
||||||
|
|
||||||
Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue)
|
Q_PROPERTY(qreal _desiredValue READ getDesiredValue WRITE setDesiredValue)
|
||||||
|
|
||||||
void
|
void setMaximum(qreal value);
|
||||||
setMaximum(qreal value)
|
void setMinimum(qreal value);
|
||||||
{
|
void setLargeChange(qreal value);
|
||||||
this->maximum = value;
|
void setSmallChange(qreal value);
|
||||||
|
void setDesiredValue(qreal value, bool animated = false);
|
||||||
this->updateScroll();
|
qreal getMaximum() const;
|
||||||
}
|
qreal getMinimum() const;
|
||||||
|
qreal getLargeChange() const;
|
||||||
void
|
qreal getSmallChange() const;
|
||||||
setMinimum(qreal value)
|
qreal getDesiredValue() const;
|
||||||
{
|
qreal getCurrentValue() const;
|
||||||
this->minimum = value;
|
boost::signals2::signal<void()> &getCurrentValueChanged();
|
||||||
|
void setCurrentValue(qreal value);
|
||||||
this->updateScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setLargeChange(qreal value)
|
|
||||||
{
|
|
||||||
this->largeChange = value;
|
|
||||||
|
|
||||||
this->updateScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setSmallChange(qreal value)
|
|
||||||
{
|
|
||||||
this->smallChange = value;
|
|
||||||
|
|
||||||
this->updateScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setDesiredValue(qreal value, bool animated = false)
|
|
||||||
{
|
|
||||||
value = std::max(this->minimum,
|
|
||||||
std::min(this->maximum - this->largeChange, value));
|
|
||||||
|
|
||||||
if (this->desiredValue != value) {
|
|
||||||
if (animated) {
|
|
||||||
this->currentValueAnimation.stop();
|
|
||||||
this->currentValueAnimation.setStartValue(this->currentValue);
|
|
||||||
|
|
||||||
this->currentValueAnimation.setEndValue(value);
|
|
||||||
this->currentValueAnimation.start();
|
|
||||||
} else {
|
|
||||||
this->currentValueAnimation.stop();
|
|
||||||
|
|
||||||
this->setCurrentValue(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->desiredValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getMaximum() const
|
|
||||||
{
|
|
||||||
return this->maximum;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getMinimum() const
|
|
||||||
{
|
|
||||||
return this->minimum;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getLargeChange() const
|
|
||||||
{
|
|
||||||
return this->largeChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getSmallChange() const
|
|
||||||
{
|
|
||||||
return this->smallChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getDesiredValue() const
|
|
||||||
{
|
|
||||||
return this->desiredValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal
|
|
||||||
getCurrentValue() const
|
|
||||||
{
|
|
||||||
return this->currentValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::signals2::signal<void()> &
|
|
||||||
getCurrentValueChanged()
|
|
||||||
{
|
|
||||||
return currentValueChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setCurrentValue(qreal value)
|
|
||||||
{
|
|
||||||
value = std::max(this->minimum,
|
|
||||||
std::min(this->maximum - this->largeChange, value));
|
|
||||||
|
|
||||||
if (this->currentValue != value) {
|
|
||||||
this->currentValue = value;
|
|
||||||
|
|
||||||
this->updateScroll();
|
|
||||||
this->currentValueChanged();
|
|
||||||
|
|
||||||
this->update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_PROPERTY(qreal currentValue READ getCurrentValue WRITE setCurrentValue)
|
Q_PROPERTY(qreal _currentValue READ getCurrentValue WRITE setCurrentValue)
|
||||||
|
|
||||||
QMutex mutex;
|
QMutex _mutex;
|
||||||
ScrollBarHighlight *highlights;
|
ScrollBarHighlight *_highlights;
|
||||||
|
|
||||||
QPropertyAnimation currentValueAnimation;
|
QPropertyAnimation _currentValueAnimation;
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *);
|
void paintEvent(QPaintEvent *);
|
||||||
void mouseMoveEvent(QMouseEvent *event);
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
@ -152,23 +53,23 @@ private:
|
||||||
void mouseReleaseEvent(QMouseEvent *event);
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
void leaveEvent(QEvent *);
|
void leaveEvent(QEvent *);
|
||||||
|
|
||||||
int mouseOverIndex;
|
int _mouseOverIndex;
|
||||||
int mouseDownIndex;
|
int _mouseDownIndex;
|
||||||
QPoint lastMousePosition;
|
QPoint _lastMousePosition;
|
||||||
|
|
||||||
int buttonHeight;
|
int _buttonHeight;
|
||||||
int trackHeight;
|
int _trackHeight;
|
||||||
|
|
||||||
QRect thumbRect;
|
QRect _thumbRect;
|
||||||
|
|
||||||
qreal maximum;
|
qreal _maximum;
|
||||||
qreal minimum;
|
qreal _minimum;
|
||||||
qreal largeChange;
|
qreal _largeChange;
|
||||||
qreal smallChange;
|
qreal _smallChange;
|
||||||
qreal desiredValue;
|
qreal _desiredValue;
|
||||||
qreal currentValue;
|
qreal _currentValue;
|
||||||
|
|
||||||
boost::signals2::signal<void()> currentValueChanged;
|
boost::signals2::signal<void()> _currentValueChanged;
|
||||||
|
|
||||||
void updateScroll();
|
void updateScroll();
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,11 @@
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex,
|
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag)
|
||||||
Style style, QString tag)
|
: _position(position)
|
||||||
: position(position)
|
, _colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex)))
|
||||||
, colorIndex(std::max(
|
, _style(style)
|
||||||
0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex)))
|
, _tag(tag)
|
||||||
, style(style)
|
|
||||||
, tag(tag)
|
|
||||||
, next(NULL)
|
, next(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue