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 <functional>
|
||||
|
||||
#define async_exec(a) \
|
||||
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
||||
#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a));
|
||||
|
||||
class LambdaRunnable : public QRunnable
|
||||
{
|
||||
class LambdaRunnable : public QRunnable {
|
||||
public:
|
||||
LambdaRunnable(std::function<void()> action)
|
||||
{
|
||||
this->action = action;
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
void run()
|
||||
{
|
||||
this->action();
|
||||
}
|
||||
|
|
263
channel.cpp
263
channel.cpp
|
@ -1,8 +1,9 @@
|
|||
#include "channel.h"
|
||||
#include "emotes.h"
|
||||
#include "emotemanager.h"
|
||||
#include "logging/loggingmanager.h"
|
||||
#include "messages/message.h"
|
||||
#include "windows.h"
|
||||
#include "util/urlfetch.h"
|
||||
#include "windowmanager.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
@ -14,133 +15,177 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Channel::Channel(const QString &channel)
|
||||
: messages()
|
||||
, name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1)
|
||||
: channel)
|
||||
, bttvChannelEmotes()
|
||||
, ffzChannelEmotes()
|
||||
, subLink("https://www.twitch.tv/" + name +
|
||||
"/subscribe?ref=in_chat_subscriber_link")
|
||||
, channelLink("https://twitch.tv/" + name)
|
||||
, popoutPlayerLink("https://player.twitch.tv/?channel=" + name)
|
||||
, loggingChannel(logging::get(name))
|
||||
: _messages()
|
||||
, _name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) : channel)
|
||||
, _bttvChannelEmotes()
|
||||
, _ffzChannelEmotes()
|
||||
, _subLink("https://www.twitch.tv/" + _name + "/subscribe?ref=in_chat_subscriber_link")
|
||||
, _channelLink("https://twitch.tv/" + _name)
|
||||
, _popoutPlayerLink("https://player.twitch.tv/?channel=" + _name)
|
||||
//, _loggingChannel(logging::get(_name))
|
||||
{
|
||||
reloadChannelEmotes();
|
||||
}
|
||||
|
||||
void
|
||||
Channel::reloadBttvEmotes()
|
||||
//
|
||||
// properties
|
||||
//
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getBttvChannelEmotes()
|
||||
{
|
||||
// bttv
|
||||
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();
|
||||
});
|
||||
return _bttvChannelEmotes;
|
||||
}
|
||||
|
||||
void
|
||||
Channel::reloadFfzEmotes()
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getFfzChannelEmotes()
|
||||
{
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
|
||||
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();
|
||||
});
|
||||
return _ffzChannelEmotes;
|
||||
}
|
||||
|
||||
void
|
||||
Channel::addMessage(std::shared_ptr<messages::Message> message)
|
||||
bool Channel::isEmpty() const
|
||||
{
|
||||
std::shared_ptr<messages::Message> deleted;
|
||||
return _name.isEmpty();
|
||||
}
|
||||
|
||||
if (this->loggingChannel.get() != nullptr) {
|
||||
this->loggingChannel->append(message);
|
||||
}
|
||||
const QString &Channel::getName() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
if (this->messages.appendItem(message, deleted)) {
|
||||
this->messageRemovedFromStart(deleted);
|
||||
int Channel::getRoomID() const
|
||||
{
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
132
channel.h
132
channel.h
|
@ -18,119 +18,59 @@ namespace messages {
|
|||
class Message;
|
||||
}
|
||||
|
||||
class ChannelManager;
|
||||
|
||||
typedef std::shared_ptr<Channel> SharedChannel;
|
||||
|
||||
class Channel
|
||||
{
|
||||
public:
|
||||
Channel(const QString &channel);
|
||||
|
||||
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)>
|
||||
messageRemovedFromStart;
|
||||
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)>
|
||||
messageAppended;
|
||||
boost::signals2::signal<void(messages::SharedMessage &)> messageRemovedFromStart;
|
||||
boost::signals2::signal<void(messages::SharedMessage &)> messageAppended;
|
||||
|
||||
// properties
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
||||
getBttvChannelEmotes()
|
||||
{
|
||||
return bttvChannelEmotes;
|
||||
}
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &getBttvChannelEmotes();
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &getFfzChannelEmotes();
|
||||
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> &
|
||||
getFfzChannelEmotes()
|
||||
{
|
||||
return ffzChannelEmotes;
|
||||
}
|
||||
|
||||
bool
|
||||
isEmpty() const
|
||||
{
|
||||
return name.isEmpty();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
bool isEmpty() const;
|
||||
const QString &getName() const;
|
||||
int getRoomID() const;
|
||||
const QString &getSubLink() const;
|
||||
const QString &getChannelLink() const;
|
||||
const QString &getPopoutPlayerLink() const;
|
||||
bool getIsLive() const;
|
||||
int getStreamViewerCount() const;
|
||||
const QString &getStreamStatus() const;
|
||||
const QString &getStreamGame() const;
|
||||
messages::LimitedQueueSnapshot<messages::SharedMessage> getMessageSnapshot();
|
||||
|
||||
// methods
|
||||
messages::LimitedQueueSnapshot<std::shared_ptr<messages::Message>>
|
||||
getMessageSnapshot()
|
||||
{
|
||||
return messages.getSnapshot();
|
||||
}
|
||||
|
||||
void addMessage(std::shared_ptr<messages::Message> message);
|
||||
|
||||
void
|
||||
reloadChannelEmotes()
|
||||
{
|
||||
this->reloadBttvEmotes();
|
||||
this->reloadFfzEmotes();
|
||||
}
|
||||
void addMessage(messages::SharedMessage message);
|
||||
void reloadChannelEmotes();
|
||||
|
||||
private:
|
||||
messages::LimitedQueue<std::shared_ptr<messages::Message>> messages;
|
||||
// variabeles
|
||||
messages::LimitedQueue<messages::SharedMessage> _messages;
|
||||
|
||||
QString name;
|
||||
int roomID;
|
||||
QString _name;
|
||||
int _roomID;
|
||||
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzChannelEmotes;
|
||||
|
||||
QString subLink;
|
||||
QString channelLink;
|
||||
QString popoutPlayerLink;
|
||||
QString _subLink;
|
||||
QString _channelLink;
|
||||
QString _popoutPlayerLink;
|
||||
|
||||
bool isLive;
|
||||
int streamViewerCount;
|
||||
QString streamStatus;
|
||||
QString streamGame;
|
||||
std::shared_ptr<logging::Channel> loggingChannel;
|
||||
bool _isLive;
|
||||
int _streamViewerCount;
|
||||
QString _streamStatus;
|
||||
QString _streamGame;
|
||||
// std::shared_ptr<logging::Channel> _loggingChannel;
|
||||
|
||||
// methods
|
||||
void reloadBttvEmotes();
|
||||
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
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
DEFINES += IRC_NAMESPACE=Communi
|
||||
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += main.cpp\
|
||||
account.cpp \
|
||||
channel.cpp \
|
||||
channels.cpp \
|
||||
colorscheme.cpp \
|
||||
emojis.cpp \
|
||||
emotes.cpp \
|
||||
fonts.cpp \
|
||||
ircmanager.cpp \
|
||||
messages/lazyloadedimage.cpp \
|
||||
messages/link.cpp \
|
||||
|
@ -35,7 +32,6 @@ SOURCES += main.cpp\
|
|||
messages/word.cpp \
|
||||
messages/wordpart.cpp \
|
||||
resources.cpp \
|
||||
settings.cpp \
|
||||
widgets/chatwidget.cpp \
|
||||
widgets/chatwidgetheader.cpp \
|
||||
widgets/chatwidgetheaderbutton.cpp \
|
||||
|
@ -52,21 +48,31 @@ SOURCES += main.cpp\
|
|||
widgets/settingsdialog.cpp \
|
||||
widgets/settingsdialogtab.cpp \
|
||||
widgets/textinputdialog.cpp \
|
||||
windows.cpp \
|
||||
messages/messageref.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 \
|
||||
channel.h \
|
||||
channels.h \
|
||||
colorscheme.h \
|
||||
common.h \
|
||||
concurrentmap.h \
|
||||
emojis.h \
|
||||
emotes.h \
|
||||
fonts.h \
|
||||
ircmanager.h \
|
||||
messages/lazyloadedimage.h \
|
||||
messages/link.h \
|
||||
|
@ -75,8 +81,7 @@ HEADERS += account.h \
|
|||
messages/wordpart.h \
|
||||
resources.h \
|
||||
setting.h \
|
||||
settings.h \
|
||||
twitchemotevalue.h \
|
||||
twitch/emotevalue.h \
|
||||
widgets/chatwidget.h \
|
||||
widgets/chatwidgetheader.h \
|
||||
widgets/chatwidgetheaderbutton.h \
|
||||
|
@ -94,14 +99,29 @@ HEADERS += account.h \
|
|||
widgets/settingsdialogtab.h \
|
||||
widgets/signallabel.h \
|
||||
widgets/textinputdialog.h \
|
||||
windows.h \
|
||||
widgets/resizingtextedit.h \
|
||||
settingssnapshot.h \
|
||||
messages/limitedqueue.h \
|
||||
messages/limitedqueuesnapshot.h \
|
||||
messages/messageref.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 =
|
||||
|
||||
|
@ -112,9 +132,29 @@ DISTFILES +=
|
|||
|
||||
# Include boost
|
||||
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 {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
|
||||
FORMS += \
|
||||
forms/userpopup.ui
|
||||
|
|
|
@ -7,7 +7,7 @@ Language: Cpp
|
|||
SpacesBeforeTrailingComments: 2
|
||||
AccessModifierOffset: -1
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlwaysBreakAfterDefinitionReturnType: true
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
# BreakBeforeBraces: Linux
|
||||
|
@ -25,4 +25,5 @@ BraceWrapping: {
|
|||
AfterControlStatement: 'false'
|
||||
AfterFunction: 'true'
|
||||
BeforeCatch: 'false'
|
||||
}
|
||||
}
|
||||
ColumnLimit: 100
|
|
@ -1,38 +1,36 @@
|
|||
#define LOOKUP_COLOR_COUNT 360
|
||||
|
||||
#include "colorscheme.h"
|
||||
#include "settings.h"
|
||||
#include "windows.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "windowmanager.h"
|
||||
|
||||
#include <QColor>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
void
|
||||
ColorScheme::init()
|
||||
void ColorScheme::init()
|
||||
{
|
||||
static bool initiated = false;
|
||||
|
||||
if (!initiated) {
|
||||
initiated = true;
|
||||
ColorScheme::getInstance().update();
|
||||
Settings::getInstance().theme.valueChanged.connect(
|
||||
SettingsManager::getInstance().theme.valueChanged.connect(
|
||||
[](const QString &) { ColorScheme::getInstance().update(); });
|
||||
Settings::getInstance().themeHue.valueChanged.connect(
|
||||
SettingsManager::getInstance().themeHue.valueChanged.connect(
|
||||
[](const float &) { ColorScheme::getInstance().update(); });
|
||||
|
||||
ColorScheme::getInstance().updated.connect(
|
||||
[] { Windows::repaintVisibleChatWidgets(); });
|
||||
[] { WindowManager::repaintVisibleChatWidgets(); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ColorScheme::update()
|
||||
void ColorScheme::update()
|
||||
{
|
||||
QString theme = Settings::getInstance().theme.get();
|
||||
QString theme = SettingsManager::getInstance().theme.get();
|
||||
theme = theme.toLower();
|
||||
|
||||
qreal hue = Settings::getInstance().themeHue.get();
|
||||
qreal hue = SettingsManager::getInstance().themeHue.get();
|
||||
|
||||
if (theme == "light") {
|
||||
setColors(hue, 0.8);
|
||||
|
@ -47,33 +45,40 @@ ColorScheme::update()
|
|||
|
||||
// hue: theme color (0 - 1)
|
||||
// multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black
|
||||
void
|
||||
ColorScheme::setColors(float hue, float multiplyer)
|
||||
void ColorScheme::setColors(float hue, float multiplyer)
|
||||
{
|
||||
IsLightTheme = multiplyer > 0;
|
||||
bool hasDarkBorder = false;
|
||||
|
||||
SystemMessageColor = QColor(140, 127, 127);
|
||||
|
||||
auto isLightTheme = IsLightTheme;
|
||||
|
||||
auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l,
|
||||
qreal a = 1.0) {
|
||||
auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, qreal a = 1.0) {
|
||||
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);
|
||||
|
||||
// tab
|
||||
TabPanelBackground = QColor(255, 255, 255);
|
||||
TabBackground = QColor(255, 255, 255);
|
||||
TabHoverBackground = getColor(hue, 0, 0.05);
|
||||
if (hasDarkBorder) {
|
||||
// TabPanelBackground = getColor(hue, 0, 0.8);
|
||||
// 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);
|
||||
TabHighlightedBackground = getColor(hue, 0.5, 0.2);
|
||||
TabNewMessageBackground =
|
||||
QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern);
|
||||
TabText = QColor(0, 0, 0);
|
||||
TabNewMessageBackground = QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern);
|
||||
if (hasDarkBorder)
|
||||
// TabText = QColor(210, 210, 210);
|
||||
// TabHoverText = QColor(210, 210, 210);
|
||||
TabText = QColor(0, 0, 0);
|
||||
TabHoverText = QColor(0, 0, 0);
|
||||
TabSelectedText = QColor(255, 255, 255);
|
||||
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);
|
||||
|
||||
// stylesheet
|
||||
InputStyleSheet =
|
||||
"background:" + ChatInputBackground.name() + ";" +
|
||||
"border:" + TabSelectedBackground.name() + ";" +
|
||||
"color:" + Text.name() + ";" +
|
||||
"selection-background-color:" + TabSelectedBackground.name();
|
||||
InputStyleSheet = "background:" + ChatInputBackground.name() + ";" +
|
||||
"border:" + TabSelectedBackground.name() + ";" + "color:" + Text.name() +
|
||||
";" + "selection-background-color:" + TabSelectedBackground.name();
|
||||
|
||||
updated();
|
||||
}
|
||||
|
||||
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from,
|
||||
qreal to, qreal fromValue,
|
||||
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
|
||||
qreal toValue)
|
||||
{
|
||||
qreal diff = toValue - fromValue;
|
||||
|
@ -127,8 +129,7 @@ void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ColorScheme::normalizeColor(QColor &color)
|
||||
void ColorScheme::normalizeColor(QColor &color)
|
||||
{
|
||||
// qreal l = color.lightnessF();
|
||||
// qreal s = color.saturationF();
|
||||
|
|
|
@ -61,8 +61,7 @@ public:
|
|||
const int HighlightColorCount = 3;
|
||||
QColor HighlightColors[3];
|
||||
|
||||
static ColorScheme &
|
||||
getInstance()
|
||||
static ColorScheme &getInstance()
|
||||
{
|
||||
static ColorScheme instance;
|
||||
|
||||
|
@ -87,8 +86,8 @@ private:
|
|||
qreal middleLookupTable[360] = {};
|
||||
qreal minLookupTable[360] = {};
|
||||
|
||||
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to,
|
||||
qreal fromValue, qreal toValue);
|
||||
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
|
||||
qreal toValue);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -14,17 +14,16 @@ class ConcurrentMap
|
|||
{
|
||||
public:
|
||||
ConcurrentMap()
|
||||
: map()
|
||||
: _map()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
tryGet(const TKey &name, TValue &value) const
|
||||
bool tryGet(const TKey &name, TValue &value) const
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
auto a = map.find(name);
|
||||
if (a == map.end()) {
|
||||
auto a = _map.find(name);
|
||||
if (a == _map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -33,40 +32,37 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
TValue
|
||||
getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
||||
TValue getOrAdd(const TKey &name, std::function<TValue()> addLambda)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
auto a = map.find(name);
|
||||
if (a == map.end()) {
|
||||
auto a = _map.find(name);
|
||||
if (a == _map.end()) {
|
||||
TValue value = addLambda();
|
||||
map.insert(name, value);
|
||||
_map.insert(name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
return a.value();
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
void clear()
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
map.clear();
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
void
|
||||
insert(const TKey &name, const TValue &value)
|
||||
void insert(const TKey &name, const TValue &value)
|
||||
{
|
||||
QMutexLocker lock(&this->mutex);
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
||||
map.insert(name, value);
|
||||
_map.insert(name, value);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable QMutex mutex;
|
||||
QMap<TKey, TValue> map;
|
||||
mutable QMutex _mutex;
|
||||
QMap<TKey, TValue> _map;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
45
emojis.cpp
45
emojis.cpp
|
@ -1,5 +1,5 @@
|
|||
#include "emojis.h"
|
||||
#include "emotes.h"
|
||||
#include "emotemanager.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStringBuilder>
|
||||
|
@ -15,17 +15,14 @@ QMap<QChar, QMap<QString, QString>> Emojis::firstEmojiChars;
|
|||
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache;
|
||||
|
||||
QString
|
||||
Emojis::replaceShortCodes(const QString &text)
|
||||
QString Emojis::replaceShortCodes(const QString &text)
|
||||
{
|
||||
// TODO: Implement this xD
|
||||
return text;
|
||||
}
|
||||
|
||||
void
|
||||
Emojis::parseEmojis(
|
||||
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||
const QString &text)
|
||||
void Emojis::parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||
const QString &text)
|
||||
{
|
||||
long lastSlice = 0;
|
||||
|
||||
|
@ -44,21 +41,14 @@ Emojis::parseEmojis(
|
|||
emojiIter.value() + ".png";
|
||||
|
||||
if (i - lastSlice != 0) {
|
||||
vector.push_back(
|
||||
std::tuple<messages::LazyLoadedImage *,
|
||||
QString>(
|
||||
NULL, text.mid(lastSlice, i - lastSlice)));
|
||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
NULL, text.mid(lastSlice, i - lastSlice)));
|
||||
}
|
||||
|
||||
vector.push_back(
|
||||
std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
imageCache.getOrAdd(
|
||||
url,
|
||||
[&url] {
|
||||
return new messages::LazyLoadedImage(
|
||||
url, 0.35);
|
||||
}),
|
||||
QString()));
|
||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
imageCache.getOrAdd(
|
||||
url, [&url] { return new messages::LazyLoadedImage(url, 0.35); }),
|
||||
QString()));
|
||||
|
||||
i += j - 1;
|
||||
|
||||
|
@ -72,13 +62,12 @@ Emojis::parseEmojis(
|
|||
}
|
||||
|
||||
if (lastSlice < text.length()) {
|
||||
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
|
||||
NULL, text.mid(lastSlice)));
|
||||
vector.push_back(
|
||||
std::tuple<messages::LazyLoadedImage *, QString>(NULL, text.mid(lastSlice)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Emojis::loadEmojis()
|
||||
void Emojis::loadEmojis()
|
||||
{
|
||||
QFile file(":/emojidata.txt");
|
||||
file.open(QFile::ReadOnly);
|
||||
|
@ -106,8 +95,7 @@ Emojis::loadEmojis()
|
|||
emotes[i++] = QString(item).toUInt(nullptr, 16);
|
||||
}
|
||||
|
||||
shortCodeToEmoji.insert(
|
||||
a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
|
||||
shortCodeToEmoji.insert(a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
|
||||
}
|
||||
|
||||
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
|
||||
|
@ -122,9 +110,8 @@ Emojis::loadEmojis()
|
|||
continue;
|
||||
}
|
||||
|
||||
firstEmojiChars.insert(
|
||||
emoji.first.at(0),
|
||||
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
|
||||
firstEmojiChars.insert(emoji.first.at(0),
|
||||
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
5
emojis.h
5
emojis.h
|
@ -14,9 +14,8 @@ namespace chatterino {
|
|||
class Emojis
|
||||
{
|
||||
public:
|
||||
static void parseEmojis(
|
||||
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||
const QString &text);
|
||||
static void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
|
||||
const QString &text);
|
||||
|
||||
static void loadEmojis();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "emotes.h"
|
||||
#include "emotemanager.h"
|
||||
#include "resources.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
@ -11,41 +11,77 @@
|
|||
#include <QNetworkRequest>
|
||||
#include <memory>
|
||||
|
||||
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0"
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
QString Emotes::twitchEmoteTemplate(
|
||||
"https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0");
|
||||
EmoteManager EmoteManager::instance;
|
||||
|
||||
ConcurrentMap<QString, TwitchEmoteValue *> Emotes::twitchEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::bttvEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::ffzEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::chatterinoEmotes;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *>
|
||||
Emotes::bttvChannelEmoteFromCaches;
|
||||
ConcurrentMap<int, messages::LazyLoadedImage *>
|
||||
Emotes::ffzChannelEmoteFromCaches;
|
||||
ConcurrentMap<long, messages::LazyLoadedImage *> Emotes::twitchEmoteFromCache;
|
||||
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::miscImageFromCache;
|
||||
boost::signals2::signal<void()> Emotes::gifUpdateTimerSignal;
|
||||
|
||||
QTimer Emotes::gifUpdateTimer;
|
||||
bool Emotes::gifUpdateTimerInitiated(false);
|
||||
|
||||
int Emotes::generation = 0;
|
||||
|
||||
Emotes::Emotes()
|
||||
EmoteManager::EmoteManager()
|
||||
: _twitchEmotes()
|
||||
, _bttvEmotes()
|
||||
, _ffzEmotes()
|
||||
, _chatterinoEmotes()
|
||||
, _bttvChannelEmoteFromCaches()
|
||||
, _ffzChannelEmoteFromCaches()
|
||||
, _twitchEmoteFromCache()
|
||||
, _miscImageFromCache()
|
||||
, _gifUpdateTimerSignal()
|
||||
, _gifUpdateTimer()
|
||||
, _gifUpdateTimerInitiated(false)
|
||||
, _generation(0)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Emotes::loadGlobalEmotes()
|
||||
ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
|
||||
{
|
||||
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();
|
||||
loadFfzEmotes();
|
||||
}
|
||||
|
||||
void
|
||||
Emotes::loadBttvEmotes()
|
||||
void EmoteManager::loadBttvEmotes()
|
||||
{
|
||||
// bttv
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
|
@ -63,21 +99,19 @@ Emotes::loadBttvEmotes()
|
|||
|
||||
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) {
|
||||
QString id = emote.toObject().value("id").toString();
|
||||
QString code = emote.toObject().value("code").toString();
|
||||
// emote.value("imageType").toString();
|
||||
|
||||
QString tmp = _template;
|
||||
QString tmp = linkTemplate;
|
||||
tmp.detach();
|
||||
QString url =
|
||||
tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||
QString url = tmp.replace("{{id}}", id).replace("{{image}}", "1x");
|
||||
|
||||
Emotes::getBttvEmotes().insert(
|
||||
code, new messages::LazyLoadedImage(
|
||||
url, 1, code, code + "\nGlobal Bttv Emote"));
|
||||
EmoteManager::getBttvEmotes().insert(
|
||||
code, new LazyLoadedImage(url, 1, code, code + "\nGlobal Bttv Emote"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,8 +120,7 @@ Emotes::loadBttvEmotes()
|
|||
});
|
||||
}
|
||||
|
||||
void
|
||||
Emotes::loadFfzEmotes()
|
||||
void EmoteManager::loadFfzEmotes()
|
||||
{
|
||||
// ffz
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
|
@ -113,15 +146,14 @@ Emotes::loadFfzEmotes()
|
|||
|
||||
// margins
|
||||
|
||||
int id = object.value("id").toInt();
|
||||
// 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();
|
||||
|
||||
Emotes::getBttvEmotes().insert(
|
||||
code, new messages::LazyLoadedImage(
|
||||
url1, 1, code, code + "\nGlobal Ffz Emote"));
|
||||
EmoteManager::getBttvEmotes().insert(
|
||||
code, new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,41 +163,34 @@ Emotes::loadFfzEmotes()
|
|||
});
|
||||
}
|
||||
|
||||
messages::LazyLoadedImage *
|
||||
Emotes::getTwitchEmoteById(const QString &name, long id)
|
||||
LazyLoadedImage *EmoteManager::getTwitchEmoteById(const QString &name, long id)
|
||||
{
|
||||
qDebug() << "loading twitch emote: " << id;
|
||||
|
||||
return Emotes::twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
|
||||
qDebug() << "loading twitch emote: " << id;
|
||||
return EmoteManager::_twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
|
||||
qDebug() << "added twitch emote: " << id;
|
||||
qreal scale;
|
||||
QString url = getTwitchEmoteLink(id, scale);
|
||||
return new messages::LazyLoadedImage(url, scale, name,
|
||||
name + "\nTwitch Emote");
|
||||
return new LazyLoadedImage(url, scale, name, name + "\nTwitch Emote");
|
||||
});
|
||||
}
|
||||
|
||||
QString
|
||||
Emotes::getTwitchEmoteLink(long id, qreal &scale)
|
||||
QString EmoteManager::getTwitchEmoteLink(long id, qreal &scale)
|
||||
{
|
||||
scale = .5;
|
||||
|
||||
QString value = Emotes::twitchEmoteTemplate;
|
||||
QString value = TWITCH_EMOTE_TEMPLATE;
|
||||
|
||||
value.detach();
|
||||
|
||||
return value.replace("{id}", QString::number(id)).replace("{scale}", "2");
|
||||
}
|
||||
|
||||
messages::LazyLoadedImage *
|
||||
Emotes::getCheerImage(long long amount, bool animated)
|
||||
LazyLoadedImage *EmoteManager::getCheerImage(long long amount, bool animated)
|
||||
{
|
||||
// TODO: fix this xD
|
||||
return getCheerBadge(amount);
|
||||
}
|
||||
|
||||
messages::LazyLoadedImage *
|
||||
Emotes::getCheerBadge(long long amount)
|
||||
LazyLoadedImage *EmoteManager::getCheerBadge(long long amount)
|
||||
{
|
||||
if (amount >= 100000) {
|
||||
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 "asyncexec.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 <ircconnection.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#include <future>
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
Account *IrcManager::account = nullptr;
|
||||
std::shared_ptr<IrcConnection> IrcManager::connection;
|
||||
QMutex IrcManager::connectionMutex;
|
||||
long IrcManager::connectionGeneration = 0;
|
||||
const QString IrcManager::defaultClientId = "7ue61iz46fz11y3cugd0l3tawb4taal";
|
||||
QNetworkAccessManager IrcManager::accessManager;
|
||||
IrcManager IrcManager::instance;
|
||||
|
||||
QMap<QString, bool> IrcManager::twitchBlockedUsers;
|
||||
QMutex IrcManager::twitchBlockedUsersMutex;
|
||||
const QString IrcManager::defaultClientId("7ue61iz46fz11y3cugd0l3tawb4taal");
|
||||
|
||||
IrcManager::IrcManager()
|
||||
: _account(AccountManager::getInstance().getAnon())
|
||||
, _connection()
|
||||
, _connectionMutex()
|
||||
, _connectionGeneration(0)
|
||||
, _twitchBlockedUsers()
|
||||
, _twitchBlockedUsersMutex()
|
||||
, _accessManager()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::connect()
|
||||
const twitch::TwitchUser &IrcManager::getUser() const
|
||||
{
|
||||
return _account;
|
||||
}
|
||||
|
||||
void IrcManager::setUser(const twitch::TwitchUser &account)
|
||||
{
|
||||
_account = account;
|
||||
}
|
||||
|
||||
void IrcManager::connect()
|
||||
{
|
||||
disconnect();
|
||||
|
||||
async_exec([] { beginConnecting(); });
|
||||
async_exec([this] { beginConnecting(); });
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::beginConnecting()
|
||||
void 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);
|
||||
QObject::connect(c, &IrcConnection::privateMessageReceived,
|
||||
&privateMessageReceived);
|
||||
|
||||
if (account->isAnon()) {
|
||||
if (_account.isAnon()) {
|
||||
// fetch ignored users
|
||||
QString username = account->getUsername();
|
||||
QString oauthClient = account->getOauthClient();
|
||||
QString oauthToken = account->getOauthToken();
|
||||
QString username = _account.getUserName();
|
||||
QString oauthClient = _account.getOAuthClient();
|
||||
QString oauthToken = _account.getOAuthToken();
|
||||
|
||||
{
|
||||
QString nextLink = "https://api.twitch.tv/kraken/users/" +
|
||||
username + "/blocks?limit=" + 100 +
|
||||
"&client_id=" + oauthClient;
|
||||
QString nextLink = "https://api.twitch.tv/kraken/users/" + username +
|
||||
"/blocks?limit=" + 100 + "&client_id=" + oauthClient;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
|
||||
QNetworkReply *reply = manager->get(req);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||
IrcManager::twitchBlockedUsersMutex.lock();
|
||||
IrcManager::twitchBlockedUsers.clear();
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
_twitchBlockedUsersMutex.lock();
|
||||
_twitchBlockedUsers.clear();
|
||||
_twitchBlockedUsersMutex.unlock();
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
|
||||
|
@ -78,15 +94,13 @@ IrcManager::beginConnecting()
|
|||
|
||||
auto blocks = root.value("blocks").toArray();
|
||||
|
||||
IrcManager::twitchBlockedUsersMutex.lock();
|
||||
_twitchBlockedUsersMutex.lock();
|
||||
for (QJsonValue block : blocks) {
|
||||
QJsonObject user =
|
||||
block.toObject().value("user").toObject();
|
||||
QJsonObject user = block.toObject().value("user").toObject();
|
||||
// display_name
|
||||
IrcManager::twitchBlockedUsers.insert(
|
||||
user.value("name").toString().toLower(), true);
|
||||
_twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
|
||||
}
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
_twitchBlockedUsersMutex.unlock();
|
||||
|
||||
manager->deleteLater();
|
||||
});
|
||||
|
@ -94,10 +108,10 @@ IrcManager::beginConnecting()
|
|||
|
||||
// fetch available twitch emtoes
|
||||
{
|
||||
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" +
|
||||
username + "/emotes?oauth_token=" +
|
||||
oauthToken + "&client_id=" + oauthClient));
|
||||
QNetworkReply *reply = IrcManager::accessManager.get(req);
|
||||
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username +
|
||||
"/emotes?oauth_token=" + oauthToken +
|
||||
"&client_id=" + oauthClient));
|
||||
QNetworkReply *reply = _accessManager.get(req);
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||
QByteArray data = reply->readAll();
|
||||
|
@ -109,15 +123,13 @@ IrcManager::beginConnecting()
|
|||
|
||||
auto blocks = root.value("blocks").toArray();
|
||||
|
||||
IrcManager::twitchBlockedUsersMutex.lock();
|
||||
_twitchBlockedUsersMutex.lock();
|
||||
for (QJsonValue block : blocks) {
|
||||
QJsonObject user =
|
||||
block.toObject().value("user").toObject();
|
||||
QJsonObject user = block.toObject().value("user").toObject();
|
||||
// display_name
|
||||
IrcManager::twitchBlockedUsers.insert(
|
||||
user.value("name").toString().toLower(), true);
|
||||
_twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
|
||||
}
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
_twitchBlockedUsersMutex.unlock();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -129,19 +141,17 @@ IrcManager::beginConnecting()
|
|||
c->setNickName("justinfan123");
|
||||
c->setRealName("justinfan123");
|
||||
|
||||
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
||||
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
||||
c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
||||
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());
|
||||
IrcManager::connection = std::shared_ptr<IrcConnection>(c);
|
||||
_connection = std::shared_ptr<Communi::IrcConnection>(c);
|
||||
|
||||
auto channels = Channels::getItems();
|
||||
|
||||
for (auto &channel : channels) {
|
||||
c->sendRaw("JOIN #" + channel.get()->getName());
|
||||
for (auto &channel : ChannelManager::getInstance().getItems()) {
|
||||
c->sendRaw("JOIN #" + channel->getName());
|
||||
}
|
||||
|
||||
c->open();
|
||||
|
@ -150,53 +160,50 @@ IrcManager::beginConnecting()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::disconnect()
|
||||
void IrcManager::disconnect()
|
||||
{
|
||||
IrcManager::connectionMutex.lock();
|
||||
_connectionMutex.lock();
|
||||
|
||||
auto c = IrcManager::connection;
|
||||
if (IrcManager::connection.get() != NULL) {
|
||||
IrcManager::connection = std::shared_ptr<IrcConnection>();
|
||||
auto c = _connection;
|
||||
if (_connection.get() != NULL) {
|
||||
_connection = std::shared_ptr<Communi::IrcConnection>();
|
||||
}
|
||||
|
||||
IrcManager::connectionMutex.unlock();
|
||||
_connectionMutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::send(QString raw)
|
||||
void IrcManager::send(QString raw)
|
||||
{
|
||||
IrcManager::connectionMutex.lock();
|
||||
IrcManager::connection->sendRaw(raw);
|
||||
IrcManager::connectionMutex.unlock();
|
||||
_connectionMutex.lock();
|
||||
|
||||
_connection->sendRaw(raw);
|
||||
|
||||
_connectionMutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::sendJoin(const QString &channel)
|
||||
void IrcManager::sendJoin(const QString &channel)
|
||||
{
|
||||
IrcManager::connectionMutex.lock();
|
||||
_connectionMutex.lock();
|
||||
|
||||
if (IrcManager::connection.get() != NULL) {
|
||||
IrcManager::connection.get()->sendRaw("JOIN #" + channel);
|
||||
if (_connection.get() != NULL) {
|
||||
_connection.get()->sendRaw("JOIN #" + channel);
|
||||
}
|
||||
|
||||
IrcManager::connectionMutex.unlock();
|
||||
_connectionMutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::partChannel(const QString &channel)
|
||||
void IrcManager::partChannel(const QString &channel)
|
||||
{
|
||||
IrcManager::connectionMutex.lock();
|
||||
_connectionMutex.lock();
|
||||
|
||||
if (IrcManager::connection.get() != NULL) {
|
||||
IrcManager::connection.get()->sendRaw("PART #" + channel);
|
||||
if (_connection.get() != NULL) {
|
||||
_connection.get()->sendRaw("PART #" + channel);
|
||||
}
|
||||
|
||||
IrcManager::connectionMutex.unlock();
|
||||
_connectionMutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::messageReceived(IrcMessage *message)
|
||||
void IrcManager::messageReceived(Communi::IrcMessage *message)
|
||||
{
|
||||
// qInfo(message->command().toStdString().c_str());
|
||||
|
||||
|
@ -211,63 +218,51 @@ IrcManager::messageReceived(IrcMessage *message)
|
|||
// }
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::privateMessageReceived(IrcPrivateMessage *message)
|
||||
void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
||||
{
|
||||
// qInfo(message->content().toStdString().c_str());
|
||||
|
||||
auto c = Channels::getChannel(message->target().mid(1));
|
||||
auto c = ChannelManager::getInstance().getChannel(message->target().mid(1));
|
||||
|
||||
if (c != NULL) {
|
||||
c->addMessage(std::shared_ptr<messages::Message>(
|
||||
new messages::Message(*message, *c)));
|
||||
messages::MessageParseArgs args;
|
||||
|
||||
c->addMessage(twitch::TwitchMessageBuilder::parse(message, c.get(), args));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IrcManager::isTwitchBlockedUser(QString const &username)
|
||||
bool 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()) {
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
return true;
|
||||
return iterator != _twitchBlockedUsers.end();
|
||||
}
|
||||
|
||||
bool
|
||||
IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
|
||||
bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
|
||||
{
|
||||
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() +
|
||||
"/blocks/" + username +
|
||||
"?oauth_token=" + account->getOauthToken() +
|
||||
"&client_id=" + account->getOauthClient());
|
||||
QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
|
||||
username + "?oauth_token=" + _account.getOAuthToken() +
|
||||
"&client_id=" + _account.getOAuthClient());
|
||||
|
||||
QNetworkRequest request(url);
|
||||
auto reply = IrcManager::accessManager.put(request, QByteArray());
|
||||
auto reply = _accessManager.put(request, QByteArray());
|
||||
reply->waitForReadyRead(10000);
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
IrcManager::twitchBlockedUsersMutex.lock();
|
||||
IrcManager::twitchBlockedUsers.insert(username, true);
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
_twitchBlockedUsersMutex.lock();
|
||||
_twitchBlockedUsers.insert(username, true);
|
||||
_twitchBlockedUsersMutex.unlock();
|
||||
|
||||
delete reply;
|
||||
return true;
|
||||
}
|
||||
|
||||
errorMessage = "Error while ignoring user \"" + username +
|
||||
"\": " + reply->errorString();
|
||||
reply->deleteLater();
|
||||
|
||||
errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::addIgnoredUser(QString const &username)
|
||||
void IrcManager::addIgnoredUser(QString const &username)
|
||||
{
|
||||
QString errorMessage;
|
||||
if (!tryAddIgnoredUser(username, errorMessage)) {
|
||||
|
@ -275,38 +270,40 @@ IrcManager::addIgnoredUser(QString const &username)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
|
||||
bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
|
||||
{
|
||||
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() +
|
||||
"/blocks/" + username +
|
||||
"?oauth_token=" + account->getOauthToken() +
|
||||
"&client_id=" + account->getOauthClient());
|
||||
QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
|
||||
username + "?oauth_token=" + _account.getOAuthToken() +
|
||||
"&client_id=" + _account.getOAuthClient());
|
||||
|
||||
QNetworkRequest request(url);
|
||||
auto reply = IrcManager::accessManager.deleteResource(request);
|
||||
auto reply = _accessManager.deleteResource(request);
|
||||
reply->waitForReadyRead(10000);
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
IrcManager::twitchBlockedUsersMutex.lock();
|
||||
IrcManager::twitchBlockedUsers.remove(username);
|
||||
IrcManager::twitchBlockedUsersMutex.unlock();
|
||||
_twitchBlockedUsersMutex.lock();
|
||||
_twitchBlockedUsers.remove(username);
|
||||
_twitchBlockedUsersMutex.unlock();
|
||||
|
||||
delete reply;
|
||||
return true;
|
||||
}
|
||||
|
||||
errorMessage = "Error while unignoring user \"" + username +
|
||||
"\": " + reply->errorString();
|
||||
reply->deleteLater();
|
||||
|
||||
errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
IrcManager::removeIgnoredUser(QString const &username)
|
||||
void IrcManager::removeIgnoredUser(QString const &username)
|
||||
{
|
||||
QString errorMessage;
|
||||
if (!tryRemoveIgnoredUser(username, errorMessage)) {
|
||||
// TODO: Implement IrcManager::removeIgnoredUser
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkAccessManager &IrcManager::getAccessManager()
|
||||
{
|
||||
return _accessManager;
|
||||
}
|
||||
}
|
||||
|
|
55
ircmanager.h
55
ircmanager.h
|
@ -3,8 +3,8 @@
|
|||
|
||||
#define TWITCH_MAX_MESSAGELENGTH 500
|
||||
|
||||
#include "account.h"
|
||||
#include "messages/message.h"
|
||||
#include "twitch/twitchuser.h"
|
||||
|
||||
#include <IrcMessage>
|
||||
#include <QMap>
|
||||
|
@ -16,50 +16,59 @@
|
|||
|
||||
namespace chatterino {
|
||||
|
||||
class IrcManager
|
||||
class IrcManager : public QObject
|
||||
{
|
||||
public:
|
||||
static void connect();
|
||||
static void disconnect();
|
||||
Q_OBJECT
|
||||
|
||||
static void send(QString raw);
|
||||
public:
|
||||
static IrcManager &getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
static const QString defaultClientId;
|
||||
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
void send(QString raw);
|
||||
|
||||
bool isTwitchBlockedUser(QString const &username);
|
||||
bool tryAddIgnoredUser(QString const &username, QString &errorMessage);
|
||||
void addIgnoredUser(QString const &username);
|
||||
bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage);
|
||||
void removeIgnoredUser(QString const &username);
|
||||
|
||||
static Account *account;
|
||||
QNetworkAccessManager &getAccessManager();
|
||||
|
||||
static QNetworkAccessManager &
|
||||
getAccessManager()
|
||||
{
|
||||
return accessManager;
|
||||
}
|
||||
void sendJoin(const QString &channel);
|
||||
|
||||
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:
|
||||
static IrcManager instance;
|
||||
IrcManager();
|
||||
|
||||
static QMap<QString, bool> twitchBlockedUsers;
|
||||
static QMutex twitchBlockedUsersMutex;
|
||||
// variables
|
||||
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;
|
||||
static QMutex connectionMutex;
|
||||
static long connectionGeneration;
|
||||
QNetworkAccessManager _accessManager;
|
||||
|
||||
static void messageReceived(IrcMessage *message);
|
||||
static void privateMessageReceived(IrcPrivateMessage *message);
|
||||
// methods
|
||||
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();
|
||||
|
||||
this->fileName =
|
||||
this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
|
||||
this->fileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
|
||||
|
||||
// Open file handle to log file of current date
|
||||
this->fileHandle.setFileName(this->baseDirectory + QDir::separator() +
|
||||
this->fileName);
|
||||
this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileName);
|
||||
|
||||
this->fileHandle.open(QIODevice::Append);
|
||||
this->appendLine(this->generateOpeningString(now));
|
||||
|
@ -31,8 +29,7 @@ Channel::~Channel()
|
|||
this->fileHandle.close();
|
||||
}
|
||||
|
||||
void
|
||||
Channel::append(std::shared_ptr<messages::Message> message)
|
||||
void Channel::append(std::shared_ptr<messages::Message> message)
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
|
@ -47,8 +44,7 @@ Channel::append(std::shared_ptr<messages::Message> message)
|
|||
this->appendLine(str);
|
||||
}
|
||||
|
||||
QString
|
||||
Channel::generateOpeningString(const QDateTime &now) const
|
||||
QString Channel::generateOpeningString(const QDateTime &now) const
|
||||
{
|
||||
QString ret = QLatin1Literal("# Start logging at ");
|
||||
|
||||
|
@ -59,8 +55,7 @@ Channel::generateOpeningString(const QDateTime &now) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
QString
|
||||
Channel::generateClosingString(const QDateTime &now) const
|
||||
QString Channel::generateClosingString(const QDateTime &now) const
|
||||
{
|
||||
QString ret = QLatin1Literal("# Stop logging at ");
|
||||
|
||||
|
@ -71,8 +66,7 @@ Channel::generateClosingString(const QDateTime &now) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
Channel::appendLine(const QString &line)
|
||||
void Channel::appendLine(const QString &line)
|
||||
{
|
||||
this->fileHandle.write(line.toUtf8());
|
||||
this->fileHandle.flush();
|
||||
|
|
|
@ -15,17 +15,14 @@ namespace logging {
|
|||
class Channel
|
||||
{
|
||||
public:
|
||||
explicit Channel(const QString &_channelName,
|
||||
const QString &_baseDirectory);
|
||||
explicit Channel(const QString &_channelName, const QString &_baseDirectory);
|
||||
~Channel();
|
||||
|
||||
void append(std::shared_ptr<messages::Message> message);
|
||||
|
||||
private:
|
||||
QString generateOpeningString(
|
||||
const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||
QString generateClosingString(
|
||||
const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||
QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||
QString generateClosingString(const QDateTime &now = QDateTime::currentDateTime()) const;
|
||||
|
||||
void appendLine(const QString &line);
|
||||
|
||||
|
|
|
@ -15,13 +15,11 @@ static QString mentionsBasePath;
|
|||
|
||||
std::unordered_map<std::string, std::weak_ptr<Channel>> channels;
|
||||
|
||||
void
|
||||
init()
|
||||
void init()
|
||||
{
|
||||
// Make sure all folders are properly created
|
||||
logBasePath =
|
||||
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
||||
QDir::separator() + "Logs";
|
||||
logBasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
|
||||
QDir::separator() + "Logs";
|
||||
channelBasePath = logBasePath + QDir::separator() + "Channels";
|
||||
whispersBasePath = logBasePath + QDir::separator() + "Whispers";
|
||||
mentionsBasePath = logBasePath + QDir::separator() + "Mentions";
|
||||
|
@ -55,8 +53,7 @@ init()
|
|||
}
|
||||
}
|
||||
|
||||
static const QString &
|
||||
getBaseDirectory(const QString &channelName)
|
||||
static const QString &getBaseDirectory(const QString &channelName)
|
||||
{
|
||||
if (channelName == "/whispers") {
|
||||
return whispersBasePath;
|
||||
|
@ -67,8 +64,7 @@ getBaseDirectory(const QString &channelName)
|
|||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel>
|
||||
get(const QString &channelName)
|
||||
std::shared_ptr<Channel> get(const QString &channelName)
|
||||
{
|
||||
if (channelName.isEmpty()) {
|
||||
return nullptr;
|
||||
|
|
23
main.cpp
23
main.cpp
|
@ -1,13 +1,13 @@
|
|||
#include "channels.h"
|
||||
#include "channelmanager.h"
|
||||
#include "colorscheme.h"
|
||||
#include "emojis.h"
|
||||
#include "emotes.h"
|
||||
#include "emotemanager.h"
|
||||
#include "ircmanager.h"
|
||||
#include "logging/loggingmanager.h"
|
||||
#include "resources.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "widgets/mainwindow.h"
|
||||
#include "windows.h"
|
||||
#include "windowmanager.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
|
@ -22,26 +22,25 @@ main(int argc, char *argv[])
|
|||
QApplication a(argc, argv);
|
||||
|
||||
chatterino::logging::init();
|
||||
chatterino::initChannels();
|
||||
Settings::getInstance().load();
|
||||
SettingsManager::getInstance().load();
|
||||
Resources::load();
|
||||
Emojis::loadEmojis();
|
||||
Emotes::loadGlobalEmotes();
|
||||
EmoteManager::getInstance().loadGlobalEmotes();
|
||||
|
||||
ColorScheme::getInstance().init();
|
||||
|
||||
Windows::load();
|
||||
WindowManager::load();
|
||||
|
||||
MainWindow &w = Windows::getMainWindow();
|
||||
MainWindow &w = WindowManager::getMainWindow();
|
||||
w.show();
|
||||
|
||||
IrcManager::connect();
|
||||
IrcManager::getInstance().connect();
|
||||
|
||||
int ret = a.exec();
|
||||
|
||||
Settings::getInstance().save();
|
||||
SettingsManager::getInstance().save();
|
||||
|
||||
Windows::save();
|
||||
WindowManager::save();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "messages/lazyloadedimage.h"
|
||||
|
||||
#include "asyncexec.h"
|
||||
#include "emotes.h"
|
||||
#include "emotemanager.h"
|
||||
#include "ircmanager.h"
|
||||
#include "windows.h"
|
||||
#include "windowmanager.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
|
@ -16,9 +16,8 @@
|
|||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
||||
const QString &name, const QString &tooltip,
|
||||
const QMargins &margin, bool isHat)
|
||||
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name,
|
||||
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||
: currentPixmap(NULL)
|
||||
, allFrames()
|
||||
, currentFrame(0)
|
||||
|
@ -34,9 +33,8 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
|
|||
{
|
||||
}
|
||||
|
||||
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
||||
const QString &name, const QString &tooltip,
|
||||
const QMargins &margin, bool isHat)
|
||||
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name,
|
||||
const QString &tooltip, const QMargins &margin, bool isHat)
|
||||
: currentPixmap(image)
|
||||
, allFrames()
|
||||
, currentFrame(0)
|
||||
|
@ -52,8 +50,7 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
LazyLoadedImage::loadImage()
|
||||
void LazyLoadedImage::loadImage()
|
||||
{
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
|
||||
|
@ -92,29 +89,25 @@ LazyLoadedImage::loadImage()
|
|||
if (allFrames.size() > 1) {
|
||||
this->animated = true;
|
||||
|
||||
Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
||||
EmoteManager::getInstance().getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
|
||||
}
|
||||
|
||||
Emotes::incGeneration();
|
||||
Windows::layoutVisibleChatWidgets();
|
||||
EmoteManager::getInstance().incGeneration();
|
||||
WindowManager::layoutVisibleChatWidgets();
|
||||
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
LazyLoadedImage::gifUpdateTimout()
|
||||
void LazyLoadedImage::gifUpdateTimout()
|
||||
{
|
||||
this->currentFrameOffset += GIF_FRAME_LENGTH;
|
||||
|
||||
while (true) {
|
||||
if (this->currentFrameOffset >
|
||||
this->allFrames.at(this->currentFrame).duration) {
|
||||
this->currentFrameOffset -=
|
||||
this->allFrames.at(this->currentFrame).duration;
|
||||
this->currentFrame =
|
||||
(this->currentFrame + 1) % this->allFrames.size();
|
||||
if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) {
|
||||
this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration;
|
||||
this->currentFrame = (this->currentFrame + 1) % this->allFrames.size();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,19 +10,14 @@ namespace messages {
|
|||
class LazyLoadedImage : QObject
|
||||
{
|
||||
public:
|
||||
explicit LazyLoadedImage(const QString &url, qreal scale = 1,
|
||||
const QString &name = "",
|
||||
const QString &tooltip = "",
|
||||
const QMargins &margin = QMargins(),
|
||||
explicit LazyLoadedImage(const QString &url, qreal scale = 1, const QString &name = "",
|
||||
const QString &tooltip = "", const QMargins &margin = QMargins(),
|
||||
bool isHat = false);
|
||||
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1,
|
||||
const QString &name = "",
|
||||
const QString &tooltip = "",
|
||||
const QMargins &margin = QMargins(),
|
||||
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, const QString &name = "",
|
||||
const QString &tooltip = "", const QMargins &margin = QMargins(),
|
||||
bool isHat = false);
|
||||
|
||||
const QPixmap *
|
||||
getPixmap()
|
||||
const QPixmap *getPixmap()
|
||||
{
|
||||
if (!isLoading) {
|
||||
isLoading = true;
|
||||
|
@ -32,50 +27,42 @@ public:
|
|||
return currentPixmap;
|
||||
}
|
||||
|
||||
qreal
|
||||
getScale() const
|
||||
qreal getScale() const
|
||||
{
|
||||
return scale;
|
||||
}
|
||||
|
||||
const QString &
|
||||
getUrl() const
|
||||
const QString &getUrl() const
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
const QString &
|
||||
getName() const
|
||||
const QString &getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
const QString &
|
||||
getTooltip() const
|
||||
const QString &getTooltip() const
|
||||
{
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
const QMargins &
|
||||
getMargin() const
|
||||
const QMargins &getMargin() const
|
||||
{
|
||||
return margin;
|
||||
}
|
||||
|
||||
bool
|
||||
getAnimated() const
|
||||
bool getAnimated() const
|
||||
{
|
||||
return animated;
|
||||
}
|
||||
|
||||
bool
|
||||
getIsHat() const
|
||||
bool getIsHat() const
|
||||
{
|
||||
return ishat;
|
||||
}
|
||||
|
||||
int
|
||||
getWidth() const
|
||||
int getWidth() const
|
||||
{
|
||||
if (currentPixmap == NULL) {
|
||||
return 16;
|
||||
|
@ -83,8 +70,7 @@ public:
|
|||
return currentPixmap->width();
|
||||
}
|
||||
|
||||
int
|
||||
getHeight() const
|
||||
int getHeight() const
|
||||
{
|
||||
if (currentPixmap == NULL) {
|
||||
return 16;
|
||||
|
|
|
@ -24,8 +24,7 @@ public:
|
|||
vector->reserve(this->limit + this->buffer);
|
||||
}
|
||||
|
||||
void
|
||||
clear()
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->mutex);
|
||||
|
||||
|
@ -37,8 +36,7 @@ public:
|
|||
|
||||
// return true if an item was deleted
|
||||
// deleted will be set if the item was deleted
|
||||
bool
|
||||
appendItem(const T &item, T &deleted)
|
||||
bool appendItem(const T &item, T &deleted)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(this->mutex);
|
||||
|
||||
|
@ -63,7 +61,7 @@ public:
|
|||
} else {
|
||||
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->offset++;
|
||||
|
||||
|
@ -77,12 +75,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
messages::LimitedQueueSnapshot<T>
|
||||
getSnapshot()
|
||||
messages::LimitedQueueSnapshot<T> getSnapshot()
|
||||
{
|
||||
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());
|
||||
} else {
|
||||
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit);
|
||||
|
|
|
@ -11,16 +11,14 @@ template <typename T>
|
|||
class LimitedQueueSnapshot
|
||||
{
|
||||
public:
|
||||
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset,
|
||||
int length)
|
||||
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset, int length)
|
||||
: vector(ptr)
|
||||
, offset(offset)
|
||||
, length(length)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
getLength()
|
||||
int getLength()
|
||||
{
|
||||
return this->length;
|
||||
}
|
||||
|
|
|
@ -23,20 +23,17 @@ public:
|
|||
Link();
|
||||
Link(Type getType, const QString &getValue);
|
||||
|
||||
bool
|
||||
getIsValid() const
|
||||
bool getIsValid() const
|
||||
{
|
||||
return type == None;
|
||||
}
|
||||
|
||||
Type
|
||||
getType() const
|
||||
Type getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
const QString &
|
||||
getValue() const
|
||||
const QString &getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
#include "channel.h"
|
||||
#include "colorscheme.h"
|
||||
#include "emojis.h"
|
||||
#include "emotes.h"
|
||||
#include "fonts.h"
|
||||
#include "emotemanager.h"
|
||||
#include "fontmanager.h"
|
||||
#include "ircmanager.h"
|
||||
#include "messages/link.h"
|
||||
#include "qcolor.h"
|
||||
#include "resources.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
|
||||
#include <QObjectUserData>
|
||||
#include <QStringList>
|
||||
|
@ -19,396 +19,66 @@
|
|||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
QRegularExpression *Message::cheerRegex =
|
||||
new QRegularExpression("cheer[1-9][0-9]*");
|
||||
|
||||
Message::Message(const QString &text)
|
||||
: _words()
|
||||
{
|
||||
words.push_back(Word(text, Word::Text,
|
||||
ColorScheme::getInstance().SystemMessageColor, text,
|
||||
QString()));
|
||||
_words.push_back(
|
||||
Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString()));
|
||||
}
|
||||
|
||||
Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel,
|
||||
bool enablePingSound, bool isReceivedWhisper,
|
||||
bool isSentWhisper, bool includeChannel)
|
||||
: content(ircMessage.content())
|
||||
Message::Message(const std::vector<Word> &words)
|
||||
: _words(words)
|
||||
{
|
||||
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
|
||||
Message::matchLink(const QString &string)
|
||||
bool Message::getCanHighlightTab() const
|
||||
{
|
||||
// TODO: Implement this xD
|
||||
return QString();
|
||||
return _highlightTab;
|
||||
}
|
||||
|
||||
bool
|
||||
Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage *> &a,
|
||||
const std::pair<long int, LazyLoadedImage *> &b)
|
||||
const QString &Message::getTimeoutUser() const
|
||||
{
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef MESSAGE_H
|
||||
#define MESSAGE_H
|
||||
|
||||
#include "messages/message.h"
|
||||
#include "messages/messageparseargs.h"
|
||||
#include "messages/word.h"
|
||||
#include "messages/wordpart.h"
|
||||
|
||||
|
@ -8,84 +10,33 @@
|
|||
#include <QVector>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
class Channel;
|
||||
|
||||
namespace messages {
|
||||
class Message;
|
||||
|
||||
typedef std::shared_ptr<Message> SharedMessage;
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
Message(const QString &text);
|
||||
Message(const IrcPrivateMessage &ircMessage, Channel &channel,
|
||||
bool enablePingSound = true, bool isReceivedWhisper = false,
|
||||
bool isSentWhisper = false, bool includeChannel = false);
|
||||
Message(const std::vector<messages::Word> &words);
|
||||
|
||||
~Message()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
getCanHighlightTab() const
|
||||
{
|
||||
return highlightTab;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
bool getCanHighlightTab() const;
|
||||
const QString &getTimeoutUser() const;
|
||||
int getTimeoutCount() const;
|
||||
const QString &getUserName() const;
|
||||
const QString &getDisplayName() const;
|
||||
const QString &getContent() const;
|
||||
const std::chrono::time_point<std::chrono::system_clock> &getParseTime() const;
|
||||
std::vector<Word> &getWords();
|
||||
bool isDisabled() const;
|
||||
const QString &getId() const;
|
||||
|
||||
private:
|
||||
static LazyLoadedImage *badgeStaff;
|
||||
|
@ -98,24 +49,18 @@ private:
|
|||
|
||||
static QRegularExpression *cheerRegex;
|
||||
|
||||
bool highlightTab = false;
|
||||
QString timeoutUser = "";
|
||||
int timeoutCount = 0;
|
||||
bool disabled = false;
|
||||
std::chrono::time_point<std::chrono::system_clock> parseTime;
|
||||
bool _highlightTab = false;
|
||||
QString _timeoutUser = "";
|
||||
int _timeoutCount = 0;
|
||||
bool _isDisabled = false;
|
||||
std::chrono::time_point<std::chrono::system_clock> _parseTime;
|
||||
|
||||
QString userName = "";
|
||||
QString displayName = "";
|
||||
QString content;
|
||||
QString id = "";
|
||||
QString _userName = "";
|
||||
QString _displayName = "";
|
||||
QString _content;
|
||||
QString _id = "";
|
||||
|
||||
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);
|
||||
std::vector<Word> _words;
|
||||
};
|
||||
|
||||
} // 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 "emotes.h"
|
||||
#include "settings.h"
|
||||
#include "emotemanager.h"
|
||||
#include "settingsmanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
@ -9,82 +9,87 @@
|
|||
#define MARGIN_TOP 8
|
||||
#define MARGIN_BOTTOM 8
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
MessageRef::MessageRef(std::shared_ptr<Message> message)
|
||||
: message(message.get())
|
||||
, messagePtr(message)
|
||||
, wordParts()
|
||||
MessageRef::MessageRef(SharedMessage message)
|
||||
: _message(message)
|
||||
, _wordParts()
|
||||
, buffer()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
MessageRef::layout(int width, bool enableEmoteMargins)
|
||||
Message *MessageRef::getMessage()
|
||||
{
|
||||
auto &settings = Settings::getInstance();
|
||||
return _message.get();
|
||||
}
|
||||
|
||||
bool sizeChanged = width != this->currentLayoutWidth;
|
||||
bool redraw = width != this->currentLayoutWidth;
|
||||
int MessageRef::getHeight() const
|
||||
{
|
||||
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 mediumTextLineHeight =
|
||||
Fonts::getFontMetrics(Fonts::Medium).height();
|
||||
int mediumTextLineHeight =
|
||||
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
||||
|
||||
bool recalculateImages =
|
||||
this->emoteGeneration != Emotes::getGeneration();
|
||||
bool recalculateText = this->fontGeneration != Fonts::getGeneration();
|
||||
bool newWordTypes =
|
||||
this->currentWordTypes != Settings::getInstance().getWordTypeMask();
|
||||
bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration();
|
||||
bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration();
|
||||
bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
||||
|
||||
qreal emoteScale = settings.emoteScale.get();
|
||||
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
||||
qreal emoteScale = settings.emoteScale.get();
|
||||
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
||||
|
||||
if (recalculateImages || recalculateText || newWordTypes) {
|
||||
this->emoteGeneration = Emotes::getGeneration();
|
||||
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) {
|
||||
// calculate word sizes
|
||||
if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
|
||||
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 y = MARGIN_TOP;
|
||||
|
@ -96,12 +101,11 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
int lineHeight = 0;
|
||||
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();
|
||||
it != this->message->getWords().end(); ++it) {
|
||||
for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) {
|
||||
Word &word = *it;
|
||||
|
||||
if ((word.getType() & flags) == Word::None) {
|
||||
|
@ -121,7 +125,7 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
|
||||
// word wrapping
|
||||
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
|
||||
this->alignWordParts(lineStart, lineHeight);
|
||||
alignWordParts(lineStart, lineHeight);
|
||||
|
||||
y += lineHeight;
|
||||
|
||||
|
@ -144,9 +148,8 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
|
||||
QString mid = text.mid(start, i - start - 1);
|
||||
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y,
|
||||
width, word.getHeight(),
|
||||
lineNumber, mid, mid));
|
||||
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(),
|
||||
lineNumber, mid, mid));
|
||||
|
||||
y += metrics.height();
|
||||
|
||||
|
@ -160,20 +163,19 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
QString mid(text.mid(start));
|
||||
width = metrics.width(mid);
|
||||
|
||||
this->wordParts.push_back(
|
||||
WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||
word.getHeight(), lineNumber, mid, mid));
|
||||
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
||||
word.getHeight(), lineNumber, mid, mid));
|
||||
x = width + MARGIN_LEFT + spaceWidth;
|
||||
|
||||
lineHeight = word.getHeight();
|
||||
|
||||
lineStart = this->wordParts.size() - 1;
|
||||
lineStart = _wordParts.size() - 1;
|
||||
|
||||
first = false;
|
||||
} else if (first || x + word.getWidth() + xOffset <= right) {
|
||||
// fits in the line
|
||||
this->wordParts.push_back(WordPart(word, x, y - word.getHeight(),
|
||||
lineNumber, word.getCopyText()));
|
||||
_wordParts.push_back(
|
||||
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||
|
||||
x += word.getWidth() + xOffset;
|
||||
x += spaceWidth;
|
||||
|
@ -183,15 +185,14 @@ MessageRef::layout(int width, bool enableEmoteMargins)
|
|||
first = false;
|
||||
} else {
|
||||
// doesn't fit in the line
|
||||
this->alignWordParts(lineStart, lineHeight);
|
||||
alignWordParts(lineStart, lineHeight);
|
||||
|
||||
y += lineHeight;
|
||||
|
||||
this->wordParts.push_back(WordPart(word, MARGIN_LEFT,
|
||||
y - word.getHeight(), lineNumber,
|
||||
word.getCopyText()));
|
||||
_wordParts.push_back(
|
||||
WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText()));
|
||||
|
||||
lineStart = this->wordParts.size() - 1;
|
||||
lineStart = _wordParts.size() - 1;
|
||||
|
||||
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;
|
||||
this->height = y + lineHeight;
|
||||
_height = y + lineHeight;
|
||||
}
|
||||
|
||||
if (sizeChanged) {
|
||||
this->buffer = nullptr;
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
this->updateBuffer = true;
|
||||
updateBuffer = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MessageRef::alignWordParts(int lineStart, int lineHeight)
|
||||
const std::vector<WordPart> &MessageRef::getWordParts() const
|
||||
{
|
||||
for (size_t i = lineStart; i < this->wordParts.size(); i++) {
|
||||
WordPart &wordPart2 = this->wordParts.at(i);
|
||||
return _wordParts;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
|
||||
bool MessageRef::tryGetWordPart(QPoint point, 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)) {
|
||||
word = wordPart.getWord();
|
||||
return true;
|
||||
|
@ -241,18 +246,17 @@ MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
|
|||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
MessageRef::getSelectionIndex(QPoint position)
|
||||
int MessageRef::getSelectionIndex(QPoint position)
|
||||
{
|
||||
if (this->wordParts.size() == 0) {
|
||||
if (_wordParts.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find out in which line the cursor is
|
||||
int lineNumber = 0, lineStart = 0, lineEnd = 0;
|
||||
|
||||
for (int i = 0; i < this->wordParts.size(); i++) {
|
||||
WordPart &part = this->wordParts[i];
|
||||
for (int i = 0; i < _wordParts.size(); i++) {
|
||||
WordPart &part = _wordParts[i];
|
||||
|
||||
// return if curser under the word
|
||||
if (position.y() >= part.getBottom()) {
|
||||
|
@ -271,13 +275,13 @@ MessageRef::getSelectionIndex(QPoint position)
|
|||
int index = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = lineStart; i < lineEnd; i++) {
|
||||
WordPart &part = this->wordParts[i];
|
||||
WordPart &part = _wordParts[i];
|
||||
|
||||
// curser is left of the word part
|
||||
if (position.x() < part.getX()) {
|
||||
|
@ -304,8 +308,7 @@ MessageRef::getSelectionIndex(QPoint position)
|
|||
}
|
||||
|
||||
index++;
|
||||
x = part.getX() +
|
||||
part.getWord().getFontMetrics().width(text, j + 1);
|
||||
x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,9 +318,9 @@ MessageRef::getSelectionIndex(QPoint position)
|
|||
return index;
|
||||
|
||||
// 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
|
||||
// if (position.y() >= part.getBottom()) {
|
||||
|
|
|
@ -9,30 +9,21 @@
|
|||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
class MessageRef;
|
||||
|
||||
typedef std::shared_ptr<MessageRef> SharedMessageRef;
|
||||
|
||||
class MessageRef
|
||||
{
|
||||
public:
|
||||
MessageRef(std::shared_ptr<Message> message);
|
||||
MessageRef(SharedMessage message);
|
||||
|
||||
Message *
|
||||
getMessage()
|
||||
{
|
||||
return this->message;
|
||||
}
|
||||
|
||||
int
|
||||
getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
Message *getMessage();
|
||||
int getHeight() const;
|
||||
|
||||
bool layout(int width, bool enableEmoteMargins = true);
|
||||
|
||||
const std::vector<WordPart> &
|
||||
getWordParts() const
|
||||
{
|
||||
return wordParts;
|
||||
}
|
||||
const std::vector<WordPart> &getWordParts() const;
|
||||
|
||||
std::shared_ptr<QPixmap> buffer = nullptr;
|
||||
bool updateBuffer = false;
|
||||
|
@ -42,18 +33,18 @@ public:
|
|||
int getSelectionIndex(QPoint position);
|
||||
|
||||
private:
|
||||
Message *message;
|
||||
std::shared_ptr<Message> messagePtr;
|
||||
// variables
|
||||
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 emoteGeneration = -1;
|
||||
Word::Type currentWordTypes = Word::None;
|
||||
int _currentLayoutWidth = -1;
|
||||
int _fontGeneration = -1;
|
||||
int _emoteGeneration = -1;
|
||||
Word::Type _currentWordTypes = Word::None;
|
||||
|
||||
// methods
|
||||
void alignWordParts(int lineStart, int lineHeight);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,35 +4,131 @@ namespace chatterino {
|
|||
namespace messages {
|
||||
|
||||
// Image word
|
||||
Word::Word(LazyLoadedImage *image, Type type, const QString ©text,
|
||||
const QString &tooltip, const Link &link)
|
||||
: image(image)
|
||||
, text()
|
||||
, color()
|
||||
Word::Word(LazyLoadedImage *image, Type type, const QString ©text, const QString &tooltip,
|
||||
const Link &link)
|
||||
: _image(image)
|
||||
, _text()
|
||||
, _color()
|
||||
, _isImage(true)
|
||||
, type(type)
|
||||
, copyText(copytext)
|
||||
, tooltip(tooltip)
|
||||
, link(link)
|
||||
, characterWidthCache()
|
||||
, _type(type)
|
||||
, _copyText(copytext)
|
||||
, _tooltip(tooltip)
|
||||
, _link(link)
|
||||
, _characterWidthCache()
|
||||
{
|
||||
image->getWidth(); // professional segfault test
|
||||
}
|
||||
|
||||
// Text word
|
||||
Word::Word(const QString &text, Type type, const QColor &color,
|
||||
const QString ©text, const QString &tooltip, const Link &link)
|
||||
: image(NULL)
|
||||
, text(text)
|
||||
, color(color)
|
||||
Word::Word(const QString &text, Type type, const QColor &color, const QString ©text,
|
||||
const QString &tooltip, const Link &link)
|
||||
: _image(NULL)
|
||||
, _text(text)
|
||||
, _color(color)
|
||||
, _isImage(false)
|
||||
, type(type)
|
||||
, copyText(copytext)
|
||||
, tooltip(tooltip)
|
||||
, link(link)
|
||||
, characterWidthCache()
|
||||
, _type(type)
|
||||
, _copyText(copytext)
|
||||
, _tooltip(tooltip)
|
||||
, _link(link)
|
||||
, _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 chatterino
|
||||
|
|
182
messages/word.h
182
messages/word.h
|
@ -1,7 +1,7 @@
|
|||
#ifndef WORD_H
|
||||
#define WORD_H
|
||||
|
||||
#include "fonts.h"
|
||||
#include "fontmanager.h"
|
||||
#include "messages/lazyloadedimage.h"
|
||||
#include "messages/link.h"
|
||||
|
||||
|
@ -31,8 +31,7 @@ public:
|
|||
BttvGifEmoteText = (1 << 9),
|
||||
FfzEmoteImage = (1 << 10),
|
||||
FfzEmoteText = (1 << 11),
|
||||
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage |
|
||||
FfzEmoteImage,
|
||||
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage,
|
||||
|
||||
BitsStatic = (1 << 12),
|
||||
BitsAnimated = (1 << 13),
|
||||
|
@ -46,9 +45,8 @@ public:
|
|||
BadgePremium = (1 << 20),
|
||||
BadgeChatterino = (1 << 21),
|
||||
BadgeCheer = (1 << 22),
|
||||
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator |
|
||||
BadgeTurbo | BadgeBroadcaster | BadgePremium |
|
||||
BadgeChatterino | BadgeCheer,
|
||||
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | BadgeTurbo |
|
||||
BadgeBroadcaster | BadgePremium | BadgeChatterino | BadgeCheer,
|
||||
|
||||
Username = (1 << 23),
|
||||
BitsAmount = (1 << 24),
|
||||
|
@ -59,157 +57,61 @@ public:
|
|||
EmojiImage = (1 << 27),
|
||||
EmojiText = (1 << 28),
|
||||
|
||||
Default = TimestampNoSeconds | Badges | Username | BitsStatic |
|
||||
FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage |
|
||||
TwitchEmoteImage | BitsAmount | Text | ButtonBan |
|
||||
ButtonTimeout
|
||||
Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
|
||||
BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
|
||||
ButtonBan | ButtonTimeout
|
||||
};
|
||||
|
||||
Word()
|
||||
{
|
||||
}
|
||||
explicit Word(LazyLoadedImage *image, Type getType, const QString ©text,
|
||||
explicit Word(LazyLoadedImage *_image, Type getType, const QString ©text,
|
||||
const QString &getTooltip, const Link &getLink = Link());
|
||||
explicit Word(const QString &text, Type getType, const QColor &getColor,
|
||||
const QString ©text, const QString &getTooltip,
|
||||
const Link &getLink = Link());
|
||||
explicit Word(const QString &_text, Type getType, const QColor &getColor,
|
||||
const QString ©text, const QString &getTooltip, const Link &getLink = Link());
|
||||
|
||||
LazyLoadedImage &
|
||||
getImage() const
|
||||
{
|
||||
return *image;
|
||||
}
|
||||
LazyLoadedImage &getImage() const;
|
||||
const QString &getText() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
void setSize(int _width, int _height);
|
||||
|
||||
const QString &
|
||||
getText() const
|
||||
{
|
||||
return this->text;
|
||||
}
|
||||
bool isImage() const;
|
||||
bool isText() const;
|
||||
const QString &getCopyText() const;
|
||||
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
|
||||
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;
|
||||
}
|
||||
std::vector<short> &getCharacterWidthCache() const;
|
||||
|
||||
private:
|
||||
LazyLoadedImage *image;
|
||||
QString text;
|
||||
QColor color;
|
||||
LazyLoadedImage *_image;
|
||||
QString _text;
|
||||
QColor _color;
|
||||
bool _isImage;
|
||||
|
||||
Type type;
|
||||
QString copyText;
|
||||
QString tooltip;
|
||||
Type _type;
|
||||
QString _copyText;
|
||||
QString _tooltip;
|
||||
|
||||
int width = 16;
|
||||
int height = 16;
|
||||
int xOffset = 0;
|
||||
int yOffset = 0;
|
||||
int _width = 16;
|
||||
int _height = 16;
|
||||
int _xOffset = 0;
|
||||
int _yOffset = 0;
|
||||
|
||||
bool _hasTrailingSpace;
|
||||
Fonts::Type font = Fonts::Medium;
|
||||
Link link;
|
||||
FontManager::Type _font = FontManager::Medium;
|
||||
Link _link;
|
||||
|
||||
mutable std::vector<short> characterWidthCache;
|
||||
mutable std::vector<short> _characterWidthCache;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -4,33 +4,103 @@
|
|||
namespace chatterino {
|
||||
namespace messages {
|
||||
|
||||
WordPart::WordPart(Word &word, int x, int y, int lineNumber,
|
||||
const QString ©Text, bool allowTrailingSpace)
|
||||
: m_word(word)
|
||||
, copyText(copyText)
|
||||
, text(word.isText() ? m_word.getText() : QString())
|
||||
, x(x)
|
||||
, y(y)
|
||||
, width(word.getWidth())
|
||||
, height(word.getHeight())
|
||||
, lineNumber(lineNumber)
|
||||
WordPart::WordPart(Word &word, int x, int y, int lineNumber, const QString ©Text,
|
||||
bool allowTrailingSpace)
|
||||
: _word(word)
|
||||
, _copyText(copyText)
|
||||
, _text(word.isText() ? _word.getText() : QString())
|
||||
, _x(x)
|
||||
, _y(y)
|
||||
, _width(word.getWidth())
|
||||
, _height(word.getHeight())
|
||||
, _lineNumber(lineNumber)
|
||||
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
|
||||
{
|
||||
}
|
||||
|
||||
WordPart::WordPart(Word &word, int x, int y, int width, int height,
|
||||
int lineNumber, const QString ©Text,
|
||||
const QString &customText, bool allowTrailingSpace)
|
||||
: m_word(word)
|
||||
, copyText(copyText)
|
||||
, text(customText)
|
||||
, x(x)
|
||||
, y(y)
|
||||
, width(width)
|
||||
, height(height)
|
||||
, lineNumber(lineNumber)
|
||||
WordPart::WordPart(Word &word, int x, int y, int width, int height, int lineNumber,
|
||||
const QString ©Text, const QString &customText, bool allowTrailingSpace)
|
||||
: _word(word)
|
||||
, _copyText(copyText)
|
||||
, _text(customText)
|
||||
, _x(x)
|
||||
, _y(y)
|
||||
, _width(width)
|
||||
, _height(height)
|
||||
, _lineNumber(lineNumber)
|
||||
, _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
|
||||
{
|
||||
public:
|
||||
WordPart(Word &getWord, int getX, int getY, int lineNumber,
|
||||
const QString &getCopyText, bool allowTrailingSpace = true);
|
||||
WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText,
|
||||
bool allowTrailingSpace = true);
|
||||
|
||||
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight,
|
||||
int lineNumber, const QString &getCopyText,
|
||||
const QString &customText, bool allowTrailingSpace = true);
|
||||
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber,
|
||||
const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true);
|
||||
|
||||
const Word &
|
||||
getWord() const
|
||||
{
|
||||
return this->m_word;
|
||||
}
|
||||
|
||||
int
|
||||
getWidth() const
|
||||
{
|
||||
return this->width;
|
||||
}
|
||||
|
||||
int
|
||||
getHeight() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
const Word &getWord() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
int getX() const;
|
||||
int getY() const;
|
||||
void setPosition(int _x, int _y);
|
||||
void setY(int _y);
|
||||
int getRight() const;
|
||||
int getBottom() const;
|
||||
QRect getRect() const;
|
||||
const QString getCopyText() const;
|
||||
int hasTrailingSpace() const;
|
||||
const QString &getText() const;
|
||||
int getLineNumber();
|
||||
|
||||
private:
|
||||
Word &m_word;
|
||||
Word &_word;
|
||||
|
||||
QString copyText;
|
||||
QString text;
|
||||
QString _copyText;
|
||||
QString _text;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
int _x;
|
||||
int _y;
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
int lineNumber;
|
||||
int _lineNumber;
|
||||
|
||||
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
|
||||
Resources::load()
|
||||
void Resources::load()
|
||||
{
|
||||
// badges
|
||||
Resources::badgeStaff =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png"));
|
||||
Resources::badgeAdmin =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
|
||||
Resources::badgeStaff = new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png"));
|
||||
Resources::badgeAdmin = new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
|
||||
Resources::badgeModerator =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png"));
|
||||
Resources::badgeGlobalmod =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png"));
|
||||
Resources::badgeTurbo =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png"));
|
||||
Resources::badgeBroadcaster = new messages::LazyLoadedImage(
|
||||
new QPixmap(":/images/broadcaster_bg.png"));
|
||||
Resources::badgePremium = new messages::LazyLoadedImage(
|
||||
new QPixmap(":/images/twitchprime_bg.png"));
|
||||
Resources::badgeTurbo = new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png"));
|
||||
Resources::badgeBroadcaster =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/broadcaster_bg.png"));
|
||||
Resources::badgePremium =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png"));
|
||||
|
||||
// cheer badges
|
||||
Resources::cheerBadge100000 =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000"));
|
||||
Resources::cheerBadge10000 =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000"));
|
||||
Resources::cheerBadge5000 =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000"));
|
||||
Resources::cheerBadge1000 =
|
||||
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"));
|
||||
Resources::cheerBadge10000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000"));
|
||||
Resources::cheerBadge5000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000"));
|
||||
Resources::cheerBadge1000 = 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
|
||||
Resources::buttonBan = new messages::LazyLoadedImage(
|
||||
new QPixmap(":/images/button_ban.png"), 0.25);
|
||||
Resources::buttonTimeout = new messages::LazyLoadedImage(
|
||||
new QPixmap(":/images/button_timeout.png"), 0.25);
|
||||
Resources::buttonBan =
|
||||
new messages::LazyLoadedImage(new QPixmap(":/images/button_ban.png"), 0.25);
|
||||
Resources::buttonTimeout =
|
||||
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();
|
||||
|
||||
// badges
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeStaff()
|
||||
static messages::LazyLoadedImage *getBadgeStaff()
|
||||
{
|
||||
return badgeStaff;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeAdmin()
|
||||
static messages::LazyLoadedImage *getBadgeAdmin()
|
||||
{
|
||||
return badgeAdmin;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeGlobalmod()
|
||||
static messages::LazyLoadedImage *getBadgeGlobalmod()
|
||||
{
|
||||
return badgeGlobalmod;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeModerator()
|
||||
static messages::LazyLoadedImage *getBadgeModerator()
|
||||
{
|
||||
return badgeModerator;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeTurbo()
|
||||
static messages::LazyLoadedImage *getBadgeTurbo()
|
||||
{
|
||||
return badgeTurbo;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgeBroadcaster()
|
||||
static messages::LazyLoadedImage *getBadgeBroadcaster()
|
||||
{
|
||||
return badgeBroadcaster;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getBadgePremium()
|
||||
static messages::LazyLoadedImage *getBadgePremium()
|
||||
{
|
||||
return badgePremium;
|
||||
}
|
||||
|
||||
// cheer badges
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge100000()
|
||||
static messages::LazyLoadedImage *getCheerBadge100000()
|
||||
{
|
||||
return cheerBadge100000;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge10000()
|
||||
static messages::LazyLoadedImage *getCheerBadge10000()
|
||||
{
|
||||
return cheerBadge10000;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge5000()
|
||||
static messages::LazyLoadedImage *getCheerBadge5000()
|
||||
{
|
||||
return cheerBadge5000;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge1000()
|
||||
static messages::LazyLoadedImage *getCheerBadge1000()
|
||||
{
|
||||
return cheerBadge1000;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge100()
|
||||
static messages::LazyLoadedImage *getCheerBadge100()
|
||||
{
|
||||
return cheerBadge100;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getCheerBadge1()
|
||||
static messages::LazyLoadedImage *getCheerBadge1()
|
||||
{
|
||||
return cheerBadge1;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getButtonBan()
|
||||
static messages::LazyLoadedImage *getButtonBan()
|
||||
{
|
||||
return buttonBan;
|
||||
}
|
||||
|
||||
static messages::LazyLoadedImage *
|
||||
getButtonTimeout()
|
||||
static messages::LazyLoadedImage *getButtonTimeout()
|
||||
{
|
||||
return buttonTimeout;
|
||||
}
|
||||
|
|
41
setting.h
41
setting.h
|
@ -11,70 +11,65 @@ class BaseSetting
|
|||
{
|
||||
public:
|
||||
BaseSetting(const QString &_name)
|
||||
: name(_name)
|
||||
: _name(_name)
|
||||
{
|
||||
}
|
||||
|
||||
virtual QVariant getVariant() = 0;
|
||||
virtual void setVariant(QVariant value) = 0;
|
||||
|
||||
const QString &
|
||||
getName() const
|
||||
const QString &getName() const
|
||||
{
|
||||
return this->name;
|
||||
return _name;
|
||||
}
|
||||
|
||||
private:
|
||||
QString name;
|
||||
QString _name;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class Setting : public BaseSetting
|
||||
{
|
||||
public:
|
||||
Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems,
|
||||
const QString &_name, const T &defaultValue)
|
||||
Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems, const QString &_name,
|
||||
const T &defaultValue)
|
||||
: BaseSetting(_name)
|
||||
, value(defaultValue)
|
||||
, _value(defaultValue)
|
||||
{
|
||||
settingItems.push_back(*this);
|
||||
}
|
||||
|
||||
const T &
|
||||
get() const
|
||||
const T &get() const
|
||||
{
|
||||
return this->value;
|
||||
return _value;
|
||||
}
|
||||
|
||||
void
|
||||
set(const T &newValue)
|
||||
void set(const T &newValue)
|
||||
{
|
||||
if (this->value != newValue) {
|
||||
this->value = newValue;
|
||||
if (_value != newValue) {
|
||||
_value = newValue;
|
||||
|
||||
this->valueChanged(newValue);
|
||||
valueChanged(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
virtual QVariant
|
||||
getVariant() final
|
||||
virtual QVariant getVariant() final
|
||||
{
|
||||
return QVariant::fromValue(value);
|
||||
return QVariant::fromValue(_value);
|
||||
}
|
||||
|
||||
virtual void
|
||||
setVariant(QVariant value) final
|
||||
virtual void setVariant(QVariant value) final
|
||||
{
|
||||
if (value.isValid()) {
|
||||
assert(value.canConvert<T>());
|
||||
this->set(value.value<T>());
|
||||
set(value.value<T>());
|
||||
}
|
||||
}
|
||||
|
||||
boost::signals2::signal<void(const T &newValue)> valueChanged;
|
||||
|
||||
private:
|
||||
T value;
|
||||
T _value;
|
||||
};
|
||||
|
||||
} // 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 {
|
||||
|
||||
class Settings : public QObject
|
||||
class SettingsManager : public QObject
|
||||
{
|
||||
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:
|
||||
Setting<QString> theme;
|
||||
Setting<float> themeHue;
|
||||
|
@ -110,6 +45,41 @@ public:
|
|||
Setting<bool> hideTabX;
|
||||
Setting<bool> hidePreferencesButton;
|
||||
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
|
|
@ -3,34 +3,31 @@
|
|||
|
||||
#include "setting.h"
|
||||
|
||||
struct SettingsSnapshot {
|
||||
private:
|
||||
std::vector<
|
||||
std::pair<std::reference_wrapper<chatterino::BaseSetting>, QVariant>>
|
||||
items;
|
||||
namespace chatterino {
|
||||
|
||||
struct SettingsSnapshot {
|
||||
public:
|
||||
SettingsSnapshot()
|
||||
: items()
|
||||
: _items()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
addItem(std::reference_wrapper<chatterino::BaseSetting> setting,
|
||||
const QVariant &value)
|
||||
void addItem(std::reference_wrapper<BaseSetting> setting, const QVariant &value)
|
||||
{
|
||||
items.push_back(
|
||||
std::pair<std::reference_wrapper<chatterino::BaseSetting>,
|
||||
QVariant>(setting.get(), value));
|
||||
_items.push_back(
|
||||
std::pair<std::reference_wrapper<BaseSetting>, QVariant>(setting.get(), value));
|
||||
}
|
||||
|
||||
void
|
||||
apply()
|
||||
void apply()
|
||||
{
|
||||
for (auto &item : this->items) {
|
||||
for (auto &item : _items) {
|
||||
item.first.get().setVariant(item.second);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::reference_wrapper<BaseSetting>, QVariant>> _items;
|
||||
};
|
||||
}
|
||||
|
||||
#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 "channels.h"
|
||||
#include "channelmanager.h"
|
||||
#include "colorscheme.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "widgets/textinputdialog.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
@ -11,110 +11,167 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
ChatWidget::ChatWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, messages()
|
||||
, channel(Channels::getEmpty())
|
||||
, channelName(QString())
|
||||
, vbox(this)
|
||||
, header(this)
|
||||
, view(this)
|
||||
, input(this)
|
||||
, _messages()
|
||||
, _channel(ChannelManager::getInstance().getEmpty())
|
||||
, _channelName(QString())
|
||||
, _vbox(this)
|
||||
, _header(this)
|
||||
, _view(this)
|
||||
, _input(this)
|
||||
{
|
||||
this->vbox.setSpacing(0);
|
||||
this->vbox.setMargin(1);
|
||||
this->_vbox.setSpacing(0);
|
||||
this->_vbox.setMargin(1);
|
||||
|
||||
this->vbox.addWidget(&header);
|
||||
this->vbox.addWidget(&view, 1);
|
||||
this->vbox.addWidget(&input);
|
||||
this->_vbox.addWidget(&_header);
|
||||
this->_vbox.addWidget(&_view, 1);
|
||||
this->_vbox.addWidget(&_input);
|
||||
}
|
||||
|
||||
ChatWidget::~ChatWidget()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel>
|
||||
ChatWidget::getChannel() const
|
||||
{
|
||||
return _channel;
|
||||
}
|
||||
|
||||
const QString &
|
||||
ChatWidget::getChannelName() const
|
||||
{
|
||||
return _channelName;
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidget::setChannelName(const QString &name)
|
||||
{
|
||||
QString channel = name.trimmed();
|
||||
|
||||
if (QString::compare(channel, this->channelName, Qt::CaseInsensitive) ==
|
||||
0) {
|
||||
this->channelName = channel;
|
||||
this->header.updateChannelText();
|
||||
// return if channel name is the same
|
||||
if (QString::compare(channel, _channelName, Qt::CaseInsensitive) == 0) {
|
||||
_channelName = channel;
|
||||
_header.updateChannelText();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->channelName.isEmpty()) {
|
||||
Channels::removeChannel(this->channelName);
|
||||
// remove current channel
|
||||
if (!_channelName.isEmpty()) {
|
||||
ChannelManager::getInstance().removeChannel(_channelName);
|
||||
|
||||
this->messageAppendedConnection.disconnect();
|
||||
this->messageRemovedConnection.disconnect();
|
||||
detachChannel(_channel);
|
||||
}
|
||||
|
||||
this->channelName = channel;
|
||||
this->header.updateChannelText();
|
||||
// update members
|
||||
_channelName = channel;
|
||||
|
||||
this->view.layoutMessages();
|
||||
|
||||
messages.clear();
|
||||
// update messages
|
||||
_messages.clear();
|
||||
|
||||
if (channel.isEmpty()) {
|
||||
this->channel = NULL;
|
||||
|
||||
_channel = NULL;
|
||||
} else {
|
||||
this->channel = Channels::addChannel(channel);
|
||||
_channel = ChannelManager::getInstance().addChannel(channel);
|
||||
|
||||
this->messageAppendedConnection =
|
||||
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);
|
||||
}
|
||||
attachChannel(_channel);
|
||||
}
|
||||
|
||||
this->view.layoutMessages();
|
||||
this->view.update();
|
||||
// update header
|
||||
_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
|
||||
ChatWidget::showChangeChannelPopup()
|
||||
{
|
||||
// create new input dialog and execute it
|
||||
TextInputDialog dialog(this);
|
||||
|
||||
dialog.setText(this->channelName);
|
||||
dialog.setText(_channelName);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
setChannelName(dialog.getText());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidget::layoutMessages()
|
||||
{
|
||||
if (_view.layoutMessages()) {
|
||||
_view.update();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidget::updateGifEmotes()
|
||||
{
|
||||
_view.updateGifEmotes();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidget::paintEvent(QPaintEvent *)
|
||||
{
|
||||
// color the background of the chat
|
||||
QPainter painter(this);
|
||||
|
||||
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground);
|
||||
|
@ -123,7 +180,7 @@ ChatWidget::paintEvent(QPaintEvent *)
|
|||
void
|
||||
ChatWidget::load(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
// Load tab text
|
||||
// load tab text
|
||||
try {
|
||||
this->setChannelName(
|
||||
QString::fromStdString(tree.get<std::string>("channelName")));
|
||||
|
|
|
@ -27,51 +27,35 @@ public:
|
|||
ChatWidget(QWidget *parent = 0);
|
||||
~ChatWidget();
|
||||
|
||||
ChatWidgetView &
|
||||
getView()
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
std::shared_ptr<Channel>
|
||||
getChannel() const
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
|
||||
const QString &
|
||||
getChannelName() const
|
||||
{
|
||||
return channelName;
|
||||
}
|
||||
|
||||
SharedChannel getChannel() const;
|
||||
const QString &getChannelName() const;
|
||||
void setChannelName(const QString &name);
|
||||
|
||||
void showChangeChannelPopup();
|
||||
|
||||
messages::LimitedQueueSnapshot<std::shared_ptr<messages::MessageRef>>
|
||||
getMessagesSnapshot()
|
||||
{
|
||||
return messages.getSnapshot();
|
||||
}
|
||||
messages::LimitedQueueSnapshot<messages::SharedMessageRef> getMessagesSnapshot();
|
||||
void layoutMessages();
|
||||
void updateGifEmotes();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
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;
|
||||
QString channelName;
|
||||
messages::LimitedQueue<messages::SharedMessageRef> _messages;
|
||||
|
||||
QFont font;
|
||||
QVBoxLayout vbox;
|
||||
ChatWidgetHeader header;
|
||||
ChatWidgetView view;
|
||||
ChatWidgetInput input;
|
||||
SharedChannel _channel;
|
||||
QString _channelName;
|
||||
|
||||
boost::signals2::connection messageAppendedConnection;
|
||||
boost::signals2::connection messageRemovedConnection;
|
||||
QFont _font;
|
||||
QVBoxLayout _vbox;
|
||||
ChatWidgetHeader _header;
|
||||
ChatWidgetView _view;
|
||||
ChatWidgetInput _input;
|
||||
|
||||
boost::signals2::connection _messageAppendedConnection;
|
||||
boost::signals2::connection _messageRemovedConnection;
|
||||
|
||||
public:
|
||||
void load(const boost::property_tree::ptree &tree);
|
||||
|
|
|
@ -13,90 +13,81 @@ namespace widgets {
|
|||
|
||||
ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent)
|
||||
: QWidget()
|
||||
, chatWidget(parent)
|
||||
, dragStart()
|
||||
, dragging(false)
|
||||
, leftLabel()
|
||||
, middleLabel()
|
||||
, rightLabel()
|
||||
, leftMenu(this)
|
||||
, rightMenu(this)
|
||||
, _chatWidget(parent)
|
||||
, _dragStart()
|
||||
, _dragging(false)
|
||||
, _leftLabel()
|
||||
, _middleLabel()
|
||||
, _rightLabel()
|
||||
, _leftMenu(this)
|
||||
, _rightMenu(this)
|
||||
{
|
||||
setFixedHeight(32);
|
||||
|
||||
updateColors();
|
||||
updateChannelText();
|
||||
|
||||
setLayout(&this->hbox);
|
||||
this->hbox.setMargin(0);
|
||||
this->hbox.addWidget(&this->leftLabel);
|
||||
this->hbox.addWidget(&this->middleLabel, 1);
|
||||
this->hbox.addWidget(&this->rightLabel);
|
||||
setLayout(&_hbox);
|
||||
_hbox.setMargin(0);
|
||||
_hbox.addWidget(&_leftLabel);
|
||||
_hbox.addWidget(&_middleLabel, 1);
|
||||
_hbox.addWidget(&_rightLabel);
|
||||
|
||||
// left
|
||||
this->leftLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
this->leftLabel.getLabel().setText(
|
||||
"<img src=':/images/tool_moreCollapser_off16.png' />");
|
||||
_leftLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
_leftLabel.getLabel().setText("<img src=':/images/tool_moreCollapser_off16.png' />");
|
||||
|
||||
QObject::connect(&this->leftLabel, &ChatWidgetHeaderButton::clicked, this,
|
||||
QObject::connect(&_leftLabel, &ChatWidgetHeaderButton::clicked, this,
|
||||
&ChatWidgetHeader::leftButtonClicked);
|
||||
|
||||
this->leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()),
|
||||
QKeySequence(tr("Ctrl+T")));
|
||||
this->leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()),
|
||||
QKeySequence(tr("Ctrl+W")));
|
||||
this->leftMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
|
||||
this->leftMenu.addAction("Popup", this, SLOT(menuPopup()));
|
||||
this->leftMenu.addSeparator();
|
||||
this->leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()),
|
||||
QKeySequence(tr("Ctrl+R")));
|
||||
this->leftMenu.addAction("Clear chat", this, SLOT(menuClearChat()));
|
||||
this->leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel()));
|
||||
this->leftMenu.addAction("Open pop-out player", this,
|
||||
SLOT(menuPopupPlayer()));
|
||||
this->leftMenu.addSeparator();
|
||||
this->leftMenu.addAction("Reload channel emotes", this,
|
||||
SLOT(menuReloadChannelEmotes()));
|
||||
this->leftMenu.addAction("Manual reconnect", this,
|
||||
SLOT(menuManualReconnect()));
|
||||
this->leftMenu.addSeparator();
|
||||
this->leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
|
||||
_leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), QKeySequence(tr("Ctrl+T")));
|
||||
_leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), QKeySequence(tr("Ctrl+W")));
|
||||
_leftMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
|
||||
_leftMenu.addAction("Popup", this, SLOT(menuPopup()));
|
||||
_leftMenu.addSeparator();
|
||||
_leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()),
|
||||
QKeySequence(tr("Ctrl+R")));
|
||||
_leftMenu.addAction("Clear chat", this, SLOT(menuClearChat()));
|
||||
_leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel()));
|
||||
_leftMenu.addAction("Open pop-out player", this, SLOT(menuPopupPlayer()));
|
||||
_leftMenu.addSeparator();
|
||||
_leftMenu.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes()));
|
||||
_leftMenu.addAction("Manual reconnect", this, SLOT(menuManualReconnect()));
|
||||
_leftMenu.addSeparator();
|
||||
_leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
|
||||
|
||||
// middle
|
||||
this->middleLabel.setAlignment(Qt::AlignCenter);
|
||||
_middleLabel.setAlignment(Qt::AlignCenter);
|
||||
|
||||
connect(&this->middleLabel, &SignalLabel::mouseDoubleClick, this,
|
||||
connect(&_middleLabel, &SignalLabel::mouseDoubleClick, this,
|
||||
&ChatWidgetHeader::mouseDoubleClickEvent);
|
||||
// connect(&this->middleLabel, &SignalLabel::mouseDown, this,
|
||||
// &ChatWidgetHeader::mouseDoubleClickEvent);
|
||||
|
||||
// right
|
||||
this->rightLabel.setMinimumWidth(height());
|
||||
this->rightLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
this->rightLabel.getLabel().setText("ayy");
|
||||
_rightLabel.setMinimumWidth(height());
|
||||
_rightLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
_rightLabel.getLabel().setText("ayy");
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::updateColors()
|
||||
void ChatWidgetHeader::updateColors()
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
|
||||
|
||||
this->leftLabel.setPalette(palette);
|
||||
this->middleLabel.setPalette(palette);
|
||||
this->rightLabel.setPalette(palette);
|
||||
_leftLabel.setPalette(palette);
|
||||
_middleLabel.setPalette(palette);
|
||||
_rightLabel.setPalette(palette);
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::updateChannelText()
|
||||
void 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
|
||||
ChatWidgetHeader::paintEvent(QPaintEvent *)
|
||||
void ChatWidgetHeader::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -105,21 +96,19 @@ ChatWidgetHeader::paintEvent(QPaintEvent *)
|
|||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
|
||||
void ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
this->dragging = true;
|
||||
_dragging = true;
|
||||
|
||||
this->dragStart = event->pos();
|
||||
_dragStart = event->pos();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
||||
void ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->dragging) {
|
||||
if (std::abs(this->dragStart.x() - event->pos().x()) > 12 ||
|
||||
std::abs(this->dragStart.y() - event->pos().y()) > 12) {
|
||||
auto chatWidget = this->chatWidget;
|
||||
if (_dragging) {
|
||||
if (std::abs(_dragStart.x() - event->pos().x()) > 12 ||
|
||||
std::abs(_dragStart.y() - event->pos().y()) > 12) {
|
||||
auto chatWidget = _chatWidget;
|
||||
auto page = static_cast<NotebookPage *>(chatWidget->parentWidget());
|
||||
|
||||
if (page != NULL) {
|
||||
|
@ -149,73 +138,58 @@ ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
void ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->chatWidget->showChangeChannelPopup();
|
||||
_chatWidget->showChangeChannelPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::leftButtonClicked()
|
||||
void ChatWidgetHeader::leftButtonClicked()
|
||||
{
|
||||
this->leftMenu.move(
|
||||
this->leftLabel.mapToGlobal(QPoint(0, this->leftLabel.height())));
|
||||
this->leftMenu.show();
|
||||
_leftMenu.move(_leftLabel.mapToGlobal(QPoint(0, _leftLabel.height())));
|
||||
_leftMenu.show();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::rightButtonClicked()
|
||||
void ChatWidgetHeader::rightButtonClicked()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeader::menuAddSplit()
|
||||
void ChatWidgetHeader::menuAddSplit()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuCloseSplit()
|
||||
void ChatWidgetHeader::menuCloseSplit()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuMoveSplit()
|
||||
void ChatWidgetHeader::menuMoveSplit()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuPopup()
|
||||
void ChatWidgetHeader::menuPopup()
|
||||
{
|
||||
auto widget = new ChatWidget();
|
||||
widget->setChannelName(this->chatWidget->getChannelName());
|
||||
widget->setChannelName(_chatWidget->getChannelName());
|
||||
widget->show();
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuChangeChannel()
|
||||
void ChatWidgetHeader::menuChangeChannel()
|
||||
{
|
||||
this->chatWidget->showChangeChannelPopup();
|
||||
_chatWidget->showChangeChannelPopup();
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuClearChat()
|
||||
void ChatWidgetHeader::menuClearChat()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuOpenChannel()
|
||||
void ChatWidgetHeader::menuOpenChannel()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuPopupPlayer()
|
||||
void ChatWidgetHeader::menuPopupPlayer()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuReloadChannelEmotes()
|
||||
void ChatWidgetHeader::menuReloadChannelEmotes()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuManualReconnect()
|
||||
void ChatWidgetHeader::menuManualReconnect()
|
||||
{
|
||||
}
|
||||
void
|
||||
ChatWidgetHeader::menuShowChangelog()
|
||||
void ChatWidgetHeader::menuShowChangelog()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,9 @@ class ChatWidgetHeader : public QWidget
|
|||
public:
|
||||
explicit ChatWidgetHeader(ChatWidget *parent);
|
||||
|
||||
ChatWidget *
|
||||
getChatWidget()
|
||||
ChatWidget *getChatWidget()
|
||||
{
|
||||
return chatWidget;
|
||||
return _chatWidget;
|
||||
}
|
||||
|
||||
void updateColors();
|
||||
|
@ -40,19 +39,19 @@ protected:
|
|||
void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
|
||||
private:
|
||||
ChatWidget *chatWidget;
|
||||
ChatWidget *_chatWidget;
|
||||
|
||||
QPoint dragStart;
|
||||
bool dragging;
|
||||
QPoint _dragStart;
|
||||
bool _dragging;
|
||||
|
||||
QHBoxLayout hbox;
|
||||
QHBoxLayout _hbox;
|
||||
|
||||
ChatWidgetHeaderButton leftLabel;
|
||||
SignalLabel middleLabel;
|
||||
ChatWidgetHeaderButton rightLabel;
|
||||
ChatWidgetHeaderButton _leftLabel;
|
||||
SignalLabel _middleLabel;
|
||||
ChatWidgetHeaderButton _rightLabel;
|
||||
|
||||
QMenu leftMenu;
|
||||
QMenu rightMenu;
|
||||
QMenu _leftMenu;
|
||||
QMenu _rightMenu;
|
||||
|
||||
void leftButtonClicked();
|
||||
void rightButtonClicked();
|
||||
|
|
|
@ -9,59 +9,55 @@ namespace widgets {
|
|||
|
||||
ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing)
|
||||
: QWidget()
|
||||
, hbox()
|
||||
, label()
|
||||
, mouseOver(false)
|
||||
, mouseDown(false)
|
||||
, _hbox()
|
||||
, _label()
|
||||
, _mouseOver(false)
|
||||
, _mouseDown(false)
|
||||
{
|
||||
setLayout(&hbox);
|
||||
setLayout(&_hbox);
|
||||
|
||||
label.setAlignment(Qt::AlignCenter);
|
||||
_label.setAlignment(Qt::AlignCenter);
|
||||
|
||||
hbox.setMargin(0);
|
||||
hbox.addSpacing(spacing);
|
||||
hbox.addWidget(&this->label);
|
||||
hbox.addSpacing(spacing);
|
||||
_hbox.setMargin(0);
|
||||
_hbox.addSpacing(spacing);
|
||||
_hbox.addWidget(&_label);
|
||||
_hbox.addSpacing(spacing);
|
||||
|
||||
QObject::connect(&this->label, &SignalLabel::mouseUp, this,
|
||||
QObject::connect(&_label, &SignalLabel::mouseUp, this,
|
||||
&ChatWidgetHeaderButton::labelMouseUp);
|
||||
QObject::connect(&this->label, &SignalLabel::mouseDown, this,
|
||||
QObject::connect(&_label, &SignalLabel::mouseDown, this,
|
||||
&ChatWidgetHeaderButton::labelMouseDown);
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
|
||||
void ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
QBrush brush(ColorScheme::getInstance().IsLightTheme
|
||||
? QColor(0, 0, 0, 32)
|
||||
: QColor(255, 255, 255, 32));
|
||||
QBrush brush(ColorScheme::getInstance().IsLightTheme ? QColor(0, 0, 0, 32)
|
||||
: QColor(255, 255, 255, 32));
|
||||
|
||||
if (this->mouseDown) {
|
||||
if (_mouseDown) {
|
||||
painter.fillRect(rect(), brush);
|
||||
}
|
||||
|
||||
if (this->mouseOver) {
|
||||
if (_mouseOver) {
|
||||
painter.fillRect(rect(), brush);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
|
||||
void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->mouseDown = true;
|
||||
_mouseDown = true;
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->mouseDown = false;
|
||||
_mouseDown = false;
|
||||
|
||||
update();
|
||||
|
||||
|
@ -69,36 +65,32 @@ ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::enterEvent(QEvent *)
|
||||
void ChatWidgetHeaderButton::enterEvent(QEvent *)
|
||||
{
|
||||
this->mouseOver = true;
|
||||
_mouseOver = true;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::leaveEvent(QEvent *)
|
||||
void ChatWidgetHeaderButton::leaveEvent(QEvent *)
|
||||
{
|
||||
this->mouseOver = false;
|
||||
_mouseOver = false;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::labelMouseUp()
|
||||
void ChatWidgetHeaderButton::labelMouseUp()
|
||||
{
|
||||
this->mouseDown = false;
|
||||
_mouseDown = false;
|
||||
|
||||
update();
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetHeaderButton::labelMouseDown()
|
||||
void ChatWidgetHeaderButton::labelMouseDown()
|
||||
{
|
||||
this->mouseDown = true;
|
||||
_mouseDown = true;
|
||||
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -18,10 +18,9 @@ class ChatWidgetHeaderButton : public QWidget
|
|||
public:
|
||||
explicit ChatWidgetHeaderButton(int spacing = 6);
|
||||
|
||||
SignalLabel &
|
||||
getLabel()
|
||||
SignalLabel &getLabel()
|
||||
{
|
||||
return label;
|
||||
return _label;
|
||||
}
|
||||
|
||||
signals:
|
||||
|
@ -37,11 +36,11 @@ protected:
|
|||
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QHBoxLayout hbox;
|
||||
SignalLabel label;
|
||||
QHBoxLayout _hbox;
|
||||
SignalLabel _label;
|
||||
|
||||
bool mouseOver;
|
||||
bool mouseDown;
|
||||
bool _mouseOver;
|
||||
bool _mouseDown;
|
||||
|
||||
void labelMouseUp();
|
||||
void labelMouseDown();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "chatwidget.h"
|
||||
#include "colorscheme.h"
|
||||
#include "ircmanager.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
|
||||
#include <QCompleter>
|
||||
#include <QPainter>
|
||||
|
@ -12,44 +12,43 @@ namespace chatterino {
|
|||
namespace widgets {
|
||||
|
||||
ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
|
||||
: chatWidget(widget)
|
||||
, hbox()
|
||||
, vbox()
|
||||
, editContainer()
|
||||
, edit()
|
||||
, textLengthLabel()
|
||||
, emotesLabel(0)
|
||||
: _chatWidget(widget)
|
||||
, _hbox()
|
||||
, _vbox()
|
||||
, _editContainer()
|
||||
, _edit()
|
||||
, _textLengthLabel()
|
||||
, _emotesLabel(0)
|
||||
{
|
||||
this->setLayout(&this->hbox);
|
||||
this->setMaximumHeight(150);
|
||||
this->hbox.setMargin(4);
|
||||
setLayout(&_hbox);
|
||||
setMaximumHeight(150);
|
||||
_hbox.setMargin(4);
|
||||
|
||||
this->hbox.addLayout(&this->editContainer);
|
||||
this->hbox.addLayout(&this->vbox);
|
||||
_hbox.addLayout(&_editContainer);
|
||||
_hbox.addLayout(&_vbox);
|
||||
|
||||
this->editContainer.addWidget(&this->edit);
|
||||
this->editContainer.setMargin(4);
|
||||
_editContainer.addWidget(&_edit);
|
||||
_editContainer.setMargin(4);
|
||||
|
||||
this->vbox.addWidget(&this->textLengthLabel);
|
||||
this->vbox.addStretch(1);
|
||||
this->vbox.addWidget(&this->emotesLabel);
|
||||
_vbox.addWidget(&_textLengthLabel);
|
||||
_vbox.addStretch(1);
|
||||
_vbox.addWidget(&_emotesLabel);
|
||||
|
||||
this->textLengthLabel.setText("100");
|
||||
this->textLengthLabel.setAlignment(Qt::AlignRight);
|
||||
this->emotesLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
this->emotesLabel.getLabel().setText(
|
||||
_textLengthLabel.setText("100");
|
||||
_textLengthLabel.setAlignment(Qt::AlignRight);
|
||||
_emotesLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
_emotesLabel.getLabel().setText(
|
||||
"<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);
|
||||
|
||||
// QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this,
|
||||
// &ChatWidgetInput::editKeyPressed);
|
||||
|
||||
this->refreshTheme();
|
||||
this->setMessageLengthVisisble(
|
||||
Settings::getInstance().showMessageLength.get());
|
||||
refreshTheme();
|
||||
setMessageLengthVisisble(SettingsManager::getInstance().showMessageLength.get());
|
||||
|
||||
QStringList list;
|
||||
list.append("asd");
|
||||
|
@ -57,20 +56,20 @@ ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
|
|||
list.append("asdg");
|
||||
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) {
|
||||
auto ptr = this->chatWidget->getChannel();
|
||||
auto ptr = _chatWidget->getChannel();
|
||||
Channel *c = ptr.get();
|
||||
|
||||
if (c != nullptr) {
|
||||
IrcManager::send("PRIVMSG #" + c->getName() + ": " +
|
||||
this->edit.toPlainText());
|
||||
IrcManager::getInstance().send("PRIVMSG #" + c->getName() + ": " +
|
||||
_edit.toPlainText());
|
||||
event->accept();
|
||||
this->edit.setText(QString());
|
||||
_edit.setText(QString());
|
||||
}
|
||||
}
|
||||
// else {
|
||||
|
@ -97,20 +96,18 @@ ChatWidgetInput::~ChatWidgetInput()
|
|||
*/
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetInput::refreshTheme()
|
||||
void ChatWidgetInput::refreshTheme()
|
||||
{
|
||||
QPalette palette;
|
||||
|
||||
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
|
||||
ChatWidgetInput::editTextChanged()
|
||||
void ChatWidgetInput::editTextChanged()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -124,8 +121,7 @@ ChatWidgetInput::editTextChanged()
|
|||
// }
|
||||
//}
|
||||
|
||||
void
|
||||
ChatWidgetInput::paintEvent(QPaintEvent *)
|
||||
void ChatWidgetInput::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -134,13 +130,12 @@ ChatWidgetInput::paintEvent(QPaintEvent *)
|
|||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetInput::resizeEvent(QResizeEvent *)
|
||||
void ChatWidgetInput::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
if (height() == this->maximumHeight()) {
|
||||
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
if (height() == maximumHeight()) {
|
||||
_edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
} else {
|
||||
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
_edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,21 +31,20 @@ protected:
|
|||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
private:
|
||||
ChatWidget *chatWidget;
|
||||
ChatWidget *_chatWidget;
|
||||
|
||||
QHBoxLayout hbox;
|
||||
QVBoxLayout vbox;
|
||||
QHBoxLayout editContainer;
|
||||
ResizingTextEdit edit;
|
||||
QLabel textLengthLabel;
|
||||
ChatWidgetHeaderButton emotesLabel;
|
||||
QHBoxLayout _hbox;
|
||||
QVBoxLayout _vbox;
|
||||
QHBoxLayout _editContainer;
|
||||
ResizingTextEdit _edit;
|
||||
QLabel _textLengthLabel;
|
||||
ChatWidgetHeaderButton _emotesLabel;
|
||||
|
||||
private slots:
|
||||
void refreshTheme();
|
||||
void
|
||||
setMessageLengthVisisble(bool value)
|
||||
void setMessageLengthVisisble(bool value)
|
||||
{
|
||||
this->textLengthLabel.setHidden(!value);
|
||||
_textLengthLabel.setHidden(!value);
|
||||
}
|
||||
void editTextChanged();
|
||||
// void editKeyPressed(QKeyEvent *event);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include "widgets/chatwidgetview.h"
|
||||
#include "channels.h"
|
||||
#include "channelmanager.h"
|
||||
#include "colorscheme.h"
|
||||
#include "messages/message.h"
|
||||
#include "messages/wordpart.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "ui_userpopup.h"
|
||||
#include "widgets/chatwidget.h"
|
||||
|
||||
#include <math.h>
|
||||
|
@ -18,46 +19,46 @@ namespace widgets {
|
|||
|
||||
ChatWidgetView::ChatWidgetView(ChatWidget *parent)
|
||||
: QWidget()
|
||||
, chatWidget(parent)
|
||||
, scrollbar(this)
|
||||
, onlyUpdateEmotes(false)
|
||||
, _chatWidget(parent)
|
||||
, _scrollbar(this)
|
||||
, _userPopupWidget(_chatWidget->getChannel())
|
||||
, _onlyUpdateEmotes(false)
|
||||
, _mouseDown(false)
|
||||
, _lastPressPosition()
|
||||
{
|
||||
this->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
this->scrollbar.setSmallChange(5);
|
||||
_scrollbar.setSmallChange(5);
|
||||
this->setMouseTracking(true);
|
||||
|
||||
QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged,
|
||||
this, &ChatWidgetView::wordTypeMaskChanged);
|
||||
QObject::connect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged, this,
|
||||
&ChatWidgetView::wordTypeMaskChanged);
|
||||
|
||||
this->scrollbar.getCurrentValueChanged().connect([this] { update(); });
|
||||
_scrollbar.getCurrentValueChanged().connect([this] { update(); });
|
||||
}
|
||||
|
||||
ChatWidgetView::~ChatWidgetView()
|
||||
{
|
||||
QObject::disconnect(&Settings::getInstance(),
|
||||
&Settings::wordTypeMaskChanged, this,
|
||||
&ChatWidgetView::wordTypeMaskChanged);
|
||||
QObject::disconnect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged,
|
||||
this, &ChatWidgetView::wordTypeMaskChanged);
|
||||
}
|
||||
|
||||
bool
|
||||
ChatWidgetView::layoutMessages()
|
||||
bool ChatWidgetView::layoutMessages()
|
||||
{
|
||||
auto messages = chatWidget->getMessagesSnapshot();
|
||||
auto messages = _chatWidget->getMessagesSnapshot();
|
||||
|
||||
if (messages.getLength() == 0) {
|
||||
this->scrollbar.setVisible(false);
|
||||
_scrollbar.setVisible(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showScrollbar = false, redraw = false;
|
||||
|
||||
int start = this->scrollbar.getCurrentValue();
|
||||
int start = _scrollbar.getCurrentValue();
|
||||
|
||||
// layout the visible messages in the view
|
||||
if (messages.getLength() > start) {
|
||||
int y = -(messages[start].get()->getHeight() *
|
||||
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
||||
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
|
||||
|
||||
for (int i = start; i < messages.getLength(); ++i) {
|
||||
auto messagePtr = messages[i];
|
||||
|
@ -84,39 +85,47 @@ ChatWidgetView::layoutMessages()
|
|||
h -= message->getHeight();
|
||||
|
||||
if (h < 0) {
|
||||
this->scrollbar.setLargeChange((messages.getLength() - i) +
|
||||
(qreal)h / message->getHeight());
|
||||
this->scrollbar.setDesiredValue(this->scrollbar.getDesiredValue());
|
||||
_scrollbar.setLargeChange((messages.getLength() - i) + (qreal)h / message->getHeight());
|
||||
_scrollbar.setDesiredValue(_scrollbar.getDesiredValue());
|
||||
|
||||
showScrollbar = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->scrollbar.setVisible(showScrollbar);
|
||||
_scrollbar.setVisible(showScrollbar);
|
||||
|
||||
if (!showScrollbar) {
|
||||
this->scrollbar.setDesiredValue(0);
|
||||
_scrollbar.setDesiredValue(0);
|
||||
}
|
||||
|
||||
this->scrollbar.setMaximum(messages.getLength());
|
||||
_scrollbar.setMaximum(messages.getLength());
|
||||
|
||||
return redraw;
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetView::resizeEvent(QResizeEvent *)
|
||||
void ChatWidgetView::updateGifEmotes()
|
||||
{
|
||||
this->scrollbar.resize(this->scrollbar.width(), height());
|
||||
this->scrollbar.move(width() - this->scrollbar.width(), 0);
|
||||
_onlyUpdateEmotes = true;
|
||||
this->update();
|
||||
}
|
||||
|
||||
ScrollBar *ChatWidgetView::getScrollbar()
|
||||
{
|
||||
return &_scrollbar;
|
||||
}
|
||||
|
||||
void ChatWidgetView::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
_scrollbar.resize(_scrollbar.width(), height());
|
||||
_scrollbar.move(width() - _scrollbar.width(), 0);
|
||||
|
||||
layoutMessages();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||
void ChatWidgetView::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter _painter(this);
|
||||
|
||||
|
@ -125,10 +134,10 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
ColorScheme &scheme = ColorScheme::getInstance();
|
||||
|
||||
// only update gif emotes
|
||||
if (onlyUpdateEmotes) {
|
||||
onlyUpdateEmotes = false;
|
||||
if (_onlyUpdateEmotes) {
|
||||
_onlyUpdateEmotes = false;
|
||||
|
||||
for (GifEmoteData &item : this->gifEmotes) {
|
||||
for (GifEmoteData &item : _gifEmotes) {
|
||||
_painter.fillRect(item.rect, scheme.ChatBackground);
|
||||
|
||||
_painter.drawPixmap(item.rect, *item.image->getPixmap());
|
||||
|
@ -138,7 +147,7 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
}
|
||||
|
||||
// update all messages
|
||||
gifEmotes.clear();
|
||||
_gifEmotes.clear();
|
||||
|
||||
_painter.fillRect(rect(), scheme.ChatBackground);
|
||||
|
||||
|
@ -175,16 +184,15 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int y = -(messages[start].get()->getHeight() *
|
||||
(fmod(this->scrollbar.getCurrentValue(), 1)));
|
||||
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
|
||||
|
||||
for (int i = start; i < messages.getLength(); ++i) {
|
||||
messages::MessageRef *messageRef = messages[i].get();
|
||||
|
@ -205,20 +213,17 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
QPainter painter(buffer);
|
||||
painter.fillRect(buffer->rect(), scheme.ChatBackground);
|
||||
|
||||
for (messages::WordPart const &wordPart :
|
||||
messageRef->getWordParts()) {
|
||||
for (messages::WordPart const &wordPart : messageRef->getWordParts()) {
|
||||
// image
|
||||
if (wordPart.getWord().isImage()) {
|
||||
messages::LazyLoadedImage &lli =
|
||||
wordPart.getWord().getImage();
|
||||
messages::LazyLoadedImage &lli = wordPart.getWord().getImage();
|
||||
|
||||
const QPixmap *image = lli.getPixmap();
|
||||
|
||||
if (image != NULL) {
|
||||
painter.drawPixmap(
|
||||
QRect(wordPart.getX(), wordPart.getY(),
|
||||
wordPart.getWidth(), wordPart.getHeight()),
|
||||
*image);
|
||||
painter.drawPixmap(QRect(wordPart.getX(), wordPart.getY(),
|
||||
wordPart.getWidth(), wordPart.getHeight()),
|
||||
*image);
|
||||
}
|
||||
}
|
||||
// text
|
||||
|
@ -230,10 +235,8 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
painter.setPen(color);
|
||||
painter.setFont(wordPart.getWord().getFont());
|
||||
|
||||
painter.drawText(
|
||||
QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
|
||||
wordPart.getText(),
|
||||
QTextOption(Qt::AlignLeft | Qt::AlignTop));
|
||||
painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
|
||||
wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,12 +251,12 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
|
|||
if (lli.getAnimated()) {
|
||||
GifEmoteData data;
|
||||
data.image = &lli;
|
||||
QRect rect(wordPart.getX(), wordPart.getY() + y,
|
||||
wordPart.getWidth(), wordPart.getHeight());
|
||||
QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(),
|
||||
wordPart.getHeight());
|
||||
|
||||
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.drawPixmap(item.rect, *item.image->getPixmap());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetView::wheelEvent(QWheelEvent *event)
|
||||
void ChatWidgetView::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (this->scrollbar.isVisible()) {
|
||||
this->scrollbar.setDesiredValue(
|
||||
this->scrollbar.getDesiredValue() -
|
||||
event->delta() / 10.0 *
|
||||
Settings::getInstance().mouseScrollMultiplier.get(),
|
||||
if (_scrollbar.isVisible()) {
|
||||
_scrollbar.setDesiredValue(
|
||||
_scrollbar.getDesiredValue() -
|
||||
event->delta() / 10.0 * SettingsManager::getInstance().mouseScrollMultiplier.get(),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
||||
void ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
std::shared_ptr<messages::MessageRef> message;
|
||||
QPoint relativePos;
|
||||
|
@ -311,32 +311,86 @@ ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
|
|||
|
||||
int index = message->getSelectionIndex(relativePos);
|
||||
|
||||
qDebug() << index;
|
||||
|
||||
if (hoverWord.getLink().getIsValid()) {
|
||||
this->setCursor(Qt::PointingHandCursor);
|
||||
qDebug() << hoverWord.getLink().getValue();
|
||||
} else {
|
||||
this->setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ChatWidgetView::tryGetMessageAt(QPoint p,
|
||||
std::shared_ptr<messages::MessageRef> &_message,
|
||||
QPoint &relativePos)
|
||||
void ChatWidgetView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
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()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int y = -(messages[start].get()->getHeight() *
|
||||
(fmod(this->scrollbar.getCurrentValue(), 1))) +
|
||||
12;
|
||||
int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))) + 12;
|
||||
|
||||
for (int i = start; i < messages.getLength(); ++i) {
|
||||
auto message = messages[i];
|
||||
|
@ -352,5 +406,5 @@ ChatWidgetView::tryGetMessageAt(QPoint p,
|
|||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "messages/messageref.h"
|
||||
#include "messages/word.h"
|
||||
#include "widgets/scrollbar.h"
|
||||
#include "widgets/userpopupwidget.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QScroller>
|
||||
|
@ -18,20 +19,14 @@ class ChatWidget;
|
|||
|
||||
class ChatWidgetView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ChatWidgetView(ChatWidget *parent);
|
||||
~ChatWidgetView();
|
||||
|
||||
bool layoutMessages();
|
||||
|
||||
void
|
||||
updateGifEmotes()
|
||||
{
|
||||
this->onlyUpdateEmotes = true;
|
||||
this->update();
|
||||
}
|
||||
void updateGifEmotes();
|
||||
ScrollBar *getScrollbar();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
@ -40,9 +35,10 @@ protected:
|
|||
void wheelEvent(QWheelEvent *event);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
|
||||
bool tryGetMessageAt(QPoint p,
|
||||
std::shared_ptr<messages::MessageRef> &message,
|
||||
bool tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageRef> &message,
|
||||
QPoint &relativePos);
|
||||
|
||||
private:
|
||||
|
@ -51,22 +47,27 @@ private:
|
|||
QRect rect;
|
||||
};
|
||||
|
||||
std::vector<GifEmoteData> gifEmotes;
|
||||
std::vector<GifEmoteData> _gifEmotes;
|
||||
|
||||
ChatWidget *chatWidget;
|
||||
ChatWidget *_chatWidget;
|
||||
|
||||
ScrollBar scrollbar;
|
||||
bool onlyUpdateEmotes;
|
||||
ScrollBar _scrollbar;
|
||||
|
||||
UserPopupWidget _userPopupWidget;
|
||||
bool _onlyUpdateEmotes;
|
||||
|
||||
// Mouse event variables
|
||||
bool _mouseDown;
|
||||
QPointF _lastPressPosition;
|
||||
|
||||
private slots:
|
||||
void
|
||||
wordTypeMaskChanged()
|
||||
void wordTypeMaskChanged()
|
||||
{
|
||||
layoutMessages();
|
||||
update();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
||||
|
||||
#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 "colorscheme.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "widgets/chatwidget.h"
|
||||
#include "widgets/notebook.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPalette>
|
||||
#include <QVBoxLayout>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#ifdef USEWINSDK
|
||||
#include "Windows.h"
|
||||
#endif
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, notebook(this)
|
||||
: QWidget(parent)
|
||||
, _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;
|
||||
palette.setColor(QPalette::Background,
|
||||
ColorScheme::getInstance().TabPanelBackground);
|
||||
palette.setColor(QPalette::Background, ColorScheme::getInstance().TabPanelBackground);
|
||||
setPalette(palette);
|
||||
|
||||
resize(1280, 800);
|
||||
|
@ -28,10 +50,9 @@ MainWindow::~MainWindow()
|
|||
{
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
||||
void MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
||||
{
|
||||
auto *page = notebook.getSelectedPage();
|
||||
auto *page = _notebook.getSelectedPage();
|
||||
|
||||
if (page == NULL) {
|
||||
return;
|
||||
|
@ -43,17 +64,14 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel)
|
|||
ChatWidget *widget = *it;
|
||||
|
||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||
if (widget->getView().layoutMessages()) {
|
||||
widget->getView().update();
|
||||
}
|
||||
widget->layoutMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
||||
void MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
||||
{
|
||||
auto *page = notebook.getSelectedPage();
|
||||
auto *page = _notebook.getSelectedPage();
|
||||
|
||||
if (page == NULL) {
|
||||
return;
|
||||
|
@ -65,16 +83,14 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel)
|
|||
ChatWidget *widget = *it;
|
||||
|
||||
if (channel == NULL || channel == widget->getChannel().get()) {
|
||||
widget->getView().layoutMessages();
|
||||
widget->getView().update();
|
||||
widget->layoutMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::repaintGifEmotes()
|
||||
void MainWindow::repaintGifEmotes()
|
||||
{
|
||||
auto *page = notebook.getSelectedPage();
|
||||
auto *page = _notebook.getSelectedPage();
|
||||
|
||||
if (page == NULL) {
|
||||
return;
|
||||
|
@ -85,37 +101,43 @@ MainWindow::repaintGifEmotes()
|
|||
for (auto it = widgets.begin(); it != widgets.end(); ++it) {
|
||||
ChatWidget *widget = *it;
|
||||
|
||||
widget->getView().updateGifEmotes();
|
||||
widget->updateGifEmotes();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::load(const boost::property_tree::ptree &tree)
|
||||
void 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
|
||||
MainWindow::save()
|
||||
boost::property_tree::ptree MainWindow::save()
|
||||
{
|
||||
boost::property_tree::ptree child;
|
||||
|
||||
child.put("type", "main");
|
||||
|
||||
this->notebook.save(child);
|
||||
_notebook.save(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::loadDefaults()
|
||||
void 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 chatterino
|
||||
|
|
|
@ -2,38 +2,39 @@
|
|||
#define MAINWINDOW_H
|
||||
|
||||
#include "widgets/notebook.h"
|
||||
#include "widgets/titlebar.h"
|
||||
|
||||
#include <platform/borderless/qwinwidget.h>
|
||||
#include <QMainWindow>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
class MainWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
Notebook notebook;
|
||||
|
||||
void layoutVisibleChatWidgets(Channel *channel = NULL);
|
||||
void repaintVisibleChatWidgets(Channel *channel = NULL);
|
||||
void layoutVisibleChatWidgets(Channel *channel = nullptr);
|
||||
void repaintVisibleChatWidgets(Channel *channel = nullptr);
|
||||
void repaintGifEmotes();
|
||||
|
||||
void load(const boost::property_tree::ptree &tree);
|
||||
boost::property_tree::ptree save();
|
||||
void loadDefaults();
|
||||
|
||||
bool
|
||||
isLoaded() const
|
||||
{
|
||||
return this->loaded;
|
||||
}
|
||||
bool isLoaded() const;
|
||||
|
||||
Notebook &getNotebook();
|
||||
|
||||
private:
|
||||
bool loaded = false;
|
||||
Notebook _notebook;
|
||||
bool _loaded;
|
||||
TitleBar _titleBar;
|
||||
};
|
||||
|
||||
} // namespace widgets
|
||||
|
|
|
@ -19,106 +19,99 @@ namespace widgets {
|
|||
|
||||
Notebook::Notebook(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, addButton(this)
|
||||
, settingsButton(this)
|
||||
, userButton(this)
|
||||
, selectedPage(nullptr)
|
||||
, _addButton(this)
|
||||
, _settingsButton(this)
|
||||
, _userButton(this)
|
||||
, _selectedPage(nullptr)
|
||||
{
|
||||
connect(&this->settingsButton, SIGNAL(clicked()), this,
|
||||
SLOT(settingsButtonClicked()));
|
||||
connect(&this->userButton, SIGNAL(clicked()), this,
|
||||
SLOT(usersButtonClicked()));
|
||||
connect(&this->addButton, SIGNAL(clicked()), this,
|
||||
SLOT(addPageButtonClicked()));
|
||||
connect(&_settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
|
||||
connect(&_userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked()));
|
||||
connect(&_addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked()));
|
||||
|
||||
this->settingsButton.resize(24, 24);
|
||||
this->settingsButton.icon = NotebookButton::IconSettings;
|
||||
_settingsButton.resize(24, 24);
|
||||
_settingsButton.icon = NotebookButton::IconSettings;
|
||||
|
||||
this->userButton.resize(24, 24);
|
||||
this->userButton.move(24, 0);
|
||||
this->userButton.icon = NotebookButton::IconUser;
|
||||
_userButton.resize(24, 24);
|
||||
_userButton.move(24, 0);
|
||||
_userButton.icon = NotebookButton::IconUser;
|
||||
|
||||
this->addButton.resize(24, 24);
|
||||
_addButton.resize(24, 24);
|
||||
|
||||
Settings::getInstance().hidePreferencesButton.valueChanged.connect(
|
||||
[this](const bool &) { this->performLayout(); });
|
||||
Settings::getInstance().hideUserButton.valueChanged.connect(
|
||||
[this](const bool &) { this->performLayout(); });
|
||||
SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect(
|
||||
[this](const bool &) { performLayout(); });
|
||||
SettingsManager::getInstance().hideUserButton.valueChanged.connect(
|
||||
[this](const bool &) { performLayout(); });
|
||||
}
|
||||
|
||||
NotebookPage *
|
||||
Notebook::addPage(bool select)
|
||||
NotebookPage *Notebook::addPage(bool select)
|
||||
{
|
||||
auto tab = new NotebookTab(this);
|
||||
auto page = new NotebookPage(this, tab);
|
||||
|
||||
tab->show();
|
||||
|
||||
if (select || this->pages.count() == 0) {
|
||||
if (select || _pages.count() == 0) {
|
||||
this->select(page);
|
||||
}
|
||||
|
||||
this->pages.append(page);
|
||||
_pages.append(page);
|
||||
|
||||
this->performLayout();
|
||||
performLayout();
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::removePage(NotebookPage *page)
|
||||
void Notebook::removePage(NotebookPage *page)
|
||||
{
|
||||
int index = this->pages.indexOf(page);
|
||||
int index = _pages.indexOf(page);
|
||||
|
||||
if (pages.size() == 1) {
|
||||
this->select(NULL);
|
||||
} else if (index == pages.count() - 1) {
|
||||
this->select(pages[index - 1]);
|
||||
if (_pages.size() == 1) {
|
||||
select(NULL);
|
||||
} else if (index == _pages.count() - 1) {
|
||||
select(_pages[index - 1]);
|
||||
} else {
|
||||
this->select(pages[index + 1]);
|
||||
select(_pages[index + 1]);
|
||||
}
|
||||
|
||||
delete page->tab;
|
||||
delete page->getTab();
|
||||
delete page;
|
||||
|
||||
this->pages.removeOne(page);
|
||||
_pages.removeOne(page);
|
||||
|
||||
if (this->pages.size() == 0) {
|
||||
if (_pages.size() == 0) {
|
||||
addPage();
|
||||
}
|
||||
|
||||
performLayout();
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::select(NotebookPage *page)
|
||||
void Notebook::select(NotebookPage *page)
|
||||
{
|
||||
if (page == this->selectedPage)
|
||||
if (page == _selectedPage)
|
||||
return;
|
||||
|
||||
if (page != nullptr) {
|
||||
page->setHidden(false);
|
||||
page->tab->setSelected(true);
|
||||
page->tab->raise();
|
||||
page->getTab()->setSelected(true);
|
||||
page->getTab()->raise();
|
||||
}
|
||||
|
||||
if (this->selectedPage != nullptr) {
|
||||
this->selectedPage->setHidden(true);
|
||||
this->selectedPage->tab->setSelected(false);
|
||||
if (_selectedPage != nullptr) {
|
||||
_selectedPage->setHidden(true);
|
||||
_selectedPage->getTab()->setSelected(false);
|
||||
}
|
||||
|
||||
this->selectedPage = page;
|
||||
_selectedPage = page;
|
||||
|
||||
performLayout();
|
||||
}
|
||||
|
||||
NotebookPage *
|
||||
Notebook::tabAt(QPoint point, int &index)
|
||||
NotebookPage *Notebook::tabAt(QPoint point, int &index)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto *page : pages) {
|
||||
if (page->tab->getDesiredRect().contains(point)) {
|
||||
for (auto *page : _pages) {
|
||||
if (page->getTab()->getDesiredRect().contains(point)) {
|
||||
index = i;
|
||||
return page;
|
||||
}
|
||||
|
@ -130,97 +123,87 @@ Notebook::tabAt(QPoint point, int &index)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::rearrangePage(NotebookPage *page, int index)
|
||||
void Notebook::rearrangePage(NotebookPage *page, int index)
|
||||
{
|
||||
pages.move(pages.indexOf(page), index);
|
||||
_pages.move(_pages.indexOf(page), index);
|
||||
|
||||
performLayout();
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::performLayout(bool animated)
|
||||
void Notebook::performLayout(bool animated)
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
|
||||
if (Settings::getInstance().hidePreferencesButton.get()) {
|
||||
settingsButton.hide();
|
||||
if (SettingsManager::getInstance().hidePreferencesButton.get()) {
|
||||
_settingsButton.hide();
|
||||
} else {
|
||||
settingsButton.show();
|
||||
_settingsButton.show();
|
||||
x += 24;
|
||||
}
|
||||
if (Settings::getInstance().hideUserButton.get()) {
|
||||
userButton.hide();
|
||||
if (SettingsManager::getInstance().hideUserButton.get()) {
|
||||
_userButton.hide();
|
||||
} else {
|
||||
userButton.move(x, 0);
|
||||
userButton.show();
|
||||
_userButton.move(x, 0);
|
||||
_userButton.show();
|
||||
x += 24;
|
||||
}
|
||||
|
||||
int tabHeight = 16;
|
||||
bool first = true;
|
||||
|
||||
for (auto &i : this->pages) {
|
||||
tabHeight = i->tab->height();
|
||||
for (auto &i : _pages) {
|
||||
tabHeight = i->getTab()->height();
|
||||
|
||||
if (!first &&
|
||||
(i == this->pages.last() ? tabHeight : 0) + x + i->tab->width() >
|
||||
width()) {
|
||||
y += i->tab->height();
|
||||
i->tab->moveAnimated(QPoint(0, y), animated);
|
||||
x = i->tab->width();
|
||||
if (!first && (i == _pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
|
||||
y += i->getTab()->height();
|
||||
i->getTab()->moveAnimated(QPoint(0, y), animated);
|
||||
x = i->getTab()->width();
|
||||
} else {
|
||||
i->tab->moveAnimated(QPoint(x, y), animated);
|
||||
x += i->tab->width();
|
||||
i->getTab()->moveAnimated(QPoint(x, y), animated);
|
||||
x += i->getTab()->width();
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
this->addButton.move(x, y);
|
||||
_addButton.move(x, y);
|
||||
|
||||
if (this->selectedPage != nullptr) {
|
||||
this->selectedPage->move(0, y + tabHeight);
|
||||
this->selectedPage->resize(width(), height() - y - tabHeight);
|
||||
if (_selectedPage != nullptr) {
|
||||
_selectedPage->move(0, y + tabHeight);
|
||||
_selectedPage->resize(width(), height() - y - tabHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::resizeEvent(QResizeEvent *)
|
||||
void Notebook::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
performLayout(false);
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::settingsButtonClicked()
|
||||
void Notebook::settingsButtonClicked()
|
||||
{
|
||||
SettingsDialog *a = new SettingsDialog();
|
||||
|
||||
a->show();
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::usersButtonClicked()
|
||||
void Notebook::usersButtonClicked()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::addPageButtonClicked()
|
||||
void Notebook::addPageButtonClicked()
|
||||
{
|
||||
addPage(true);
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::load(const boost::property_tree::ptree &tree)
|
||||
void Notebook::load(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
// Read a list of tabs
|
||||
try {
|
||||
BOOST_FOREACH (const boost::property_tree::ptree::value_type &v,
|
||||
tree.get_child("tabs.")) {
|
||||
BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, tree.get_child("tabs.")) {
|
||||
bool select = v.second.get<bool>("selected", false);
|
||||
|
||||
auto page = this->addPage(select);
|
||||
auto tab = page->tab;
|
||||
auto page = addPage(select);
|
||||
auto tab = page->getTab();
|
||||
tab->load(v.second);
|
||||
page->load(v.second);
|
||||
}
|
||||
|
@ -228,20 +211,19 @@ Notebook::load(const boost::property_tree::ptree &tree)
|
|||
// can't read tabs
|
||||
}
|
||||
|
||||
if (this->pages.size() == 0) {
|
||||
if (_pages.size() == 0) {
|
||||
// No pages saved, show default stuff
|
||||
this->loadDefaults();
|
||||
loadDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::save(boost::property_tree::ptree &tree)
|
||||
void Notebook::save(boost::property_tree::ptree &tree)
|
||||
{
|
||||
boost::property_tree::ptree tabs;
|
||||
|
||||
// Iterate through all tabs and add them to our tabs property thing
|
||||
for (const auto &page : this->pages) {
|
||||
boost::property_tree::ptree pTab = page->tab->save();
|
||||
for (const auto &page : _pages) {
|
||||
boost::property_tree::ptree pTab = page->getTab()->save();
|
||||
|
||||
boost::property_tree::ptree pChats = page->save();
|
||||
|
||||
|
@ -255,10 +237,9 @@ Notebook::save(boost::property_tree::ptree &tree)
|
|||
tree.add_child("tabs", tabs);
|
||||
}
|
||||
|
||||
void
|
||||
Notebook::loadDefaults()
|
||||
void Notebook::loadDefaults()
|
||||
{
|
||||
this->addPage();
|
||||
addPage();
|
||||
}
|
||||
|
||||
} // namespace widgets
|
||||
|
|
|
@ -26,10 +26,9 @@ public:
|
|||
void removePage(NotebookPage *page);
|
||||
void select(NotebookPage *page);
|
||||
|
||||
NotebookPage *
|
||||
getSelectedPage()
|
||||
NotebookPage *getSelectedPage()
|
||||
{
|
||||
return selectedPage;
|
||||
return _selectedPage;
|
||||
}
|
||||
|
||||
void performLayout(bool animate = true);
|
||||
|
@ -48,13 +47,13 @@ public slots:
|
|||
void addPageButtonClicked();
|
||||
|
||||
private:
|
||||
QList<NotebookPage *> pages;
|
||||
QList<NotebookPage *> _pages;
|
||||
|
||||
NotebookButton addButton;
|
||||
NotebookButton settingsButton;
|
||||
NotebookButton userButton;
|
||||
NotebookButton _addButton;
|
||||
NotebookButton _settingsButton;
|
||||
NotebookButton _userButton;
|
||||
|
||||
NotebookPage *selectedPage;
|
||||
NotebookPage *_selectedPage;
|
||||
|
||||
public:
|
||||
void load(const boost::property_tree::ptree &tree);
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
#include "widgets/notebookbutton.h"
|
||||
#include "colorscheme.h"
|
||||
#include "widgets/fancybutton.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QRadialGradient>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
NotebookButton::NotebookButton(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
: FancyButton(parent)
|
||||
{
|
||||
setMouseEffectColor(QColor(0, 0, 0));
|
||||
}
|
||||
|
||||
void
|
||||
NotebookButton::paintEvent(QPaintEvent *)
|
||||
void NotebookButton::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -23,29 +25,30 @@ NotebookButton::paintEvent(QPaintEvent *)
|
|||
|
||||
auto &colorScheme = ColorScheme::getInstance();
|
||||
|
||||
if (mouseDown) {
|
||||
if (_mouseDown) {
|
||||
background = colorScheme.TabSelectedBackground;
|
||||
foreground = colorScheme.TabSelectedText;
|
||||
} else if (mouseOver) {
|
||||
} else if (_mouseOver) {
|
||||
background = colorScheme.TabHoverBackground;
|
||||
foreground = colorScheme.TabSelectedBackground;
|
||||
} else {
|
||||
background = colorScheme.TabPanelBackground;
|
||||
foreground = colorScheme.TabSelectedBackground;
|
||||
// foreground = colorScheme.TabSelectedBackground;
|
||||
foreground = QColor(230, 230, 230);
|
||||
}
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.fillRect(this->rect(), background);
|
||||
|
||||
float h = this->height(), w = this->width();
|
||||
float h = height(), w = width();
|
||||
|
||||
if (icon == IconPlus) {
|
||||
painter.fillRect(QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1,
|
||||
w - ((h / 12) * 5), (h / 12) * 1),
|
||||
foreground);
|
||||
painter.fillRect(QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1,
|
||||
(h / 12) * 1, w - ((h / 12) * 5)),
|
||||
foreground);
|
||||
painter.fillRect(
|
||||
QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, w - ((h / 12) * 5), (h / 12) * 1),
|
||||
foreground);
|
||||
painter.fillRect(
|
||||
QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, (h / 12) * 1, w - ((h / 12) * 5)),
|
||||
foreground);
|
||||
} else if (icon == IconUser) {
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
@ -74,10 +77,8 @@ NotebookButton::paintEvent(QPaintEvent *)
|
|||
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
path.arcTo(a, a, 6 * a, 6 * 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));
|
||||
path.arcTo(a, a, 6 * a, 6 * 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);
|
||||
|
@ -85,44 +86,21 @@ NotebookButton::paintEvent(QPaintEvent *)
|
|||
painter.setBrush(background);
|
||||
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
|
||||
}
|
||||
|
||||
fancyPaint(painter);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookButton::mousePressEvent(QMouseEvent *event)
|
||||
void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
mouseDown = true;
|
||||
_mouseDown = false;
|
||||
|
||||
this->update();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
mouseDown = false;
|
||||
|
||||
this->update();
|
||||
update();
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookButton::enterEvent(QEvent *)
|
||||
{
|
||||
mouseOver = true;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookButton::leaveEvent(QEvent *)
|
||||
{
|
||||
mouseOver = false;
|
||||
|
||||
this->update();
|
||||
FancyButton::mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#ifndef NOTEBOOKBUTTON_H
|
||||
#define NOTEBOOKBUTTON_H
|
||||
|
||||
#include "fancybutton.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
class NotebookButton : public QWidget
|
||||
class NotebookButton : public FancyButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const int IconPlus = 0;
|
||||
static const int IconUser = 1;
|
||||
|
@ -18,18 +21,17 @@ public:
|
|||
|
||||
NotebookButton(QWidget *parent);
|
||||
|
||||
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
|
||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
void enterEvent(QEvent *) Q_DECL_OVERRIDE;
|
||||
void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
private:
|
||||
bool mouseOver = false;
|
||||
bool mouseDown = false;
|
||||
bool _mouseOver = 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)
|
||||
: QWidget(parent)
|
||||
, parentbox(this)
|
||||
, chatWidgets()
|
||||
, preview(this)
|
||||
, _tab(tab)
|
||||
, _parentbox(this)
|
||||
, _chatWidgets()
|
||||
, _preview(this)
|
||||
{
|
||||
this->tab = tab;
|
||||
tab->page = this;
|
||||
|
||||
setHidden(true);
|
||||
setAcceptDrops(true);
|
||||
|
||||
this->parentbox.addSpacing(2);
|
||||
this->parentbox.addLayout(&this->hbox);
|
||||
this->parentbox.setMargin(0);
|
||||
_parentbox.addSpacing(2);
|
||||
_parentbox.addLayout(&_hbox);
|
||||
_parentbox.setMargin(0);
|
||||
|
||||
this->hbox.setSpacing(1);
|
||||
this->hbox.setMargin(0);
|
||||
_hbox.setSpacing(1);
|
||||
_hbox.setMargin(0);
|
||||
}
|
||||
|
||||
std::pair<int, int>
|
||||
NotebookPage::removeFromLayout(ChatWidget *widget)
|
||||
const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
|
||||
{
|
||||
for (auto it = this->chatWidgets.begin(); it != this->chatWidgets.end();
|
||||
++it) {
|
||||
return _chatWidgets;
|
||||
}
|
||||
|
||||
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) {
|
||||
this->chatWidgets.erase(it);
|
||||
_chatWidgets.erase(it);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this->hbox.count(); ++i) {
|
||||
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i));
|
||||
// remove from box and return location
|
||||
for (int i = 0; i < _hbox.count(); ++i) {
|
||||
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(i));
|
||||
|
||||
for (int j = 0; j < vbox->count(); ++j) {
|
||||
if (vbox->itemAt(j)->widget() != widget)
|
||||
if (vbox->itemAt(j)->widget() != widget) {
|
||||
continue;
|
||||
}
|
||||
|
||||
widget->setParent(NULL);
|
||||
|
||||
bool isLastItem = vbox->count() == 0;
|
||||
|
||||
if (isLastItem) {
|
||||
this->hbox.removeItem(vbox);
|
||||
_hbox.removeItem(vbox);
|
||||
|
||||
delete vbox;
|
||||
}
|
||||
|
@ -75,19 +86,17 @@ NotebookPage::removeFromLayout(ChatWidget *widget)
|
|||
return std::pair<int, int>(-1, -1);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::addToLayout(
|
||||
ChatWidget *widget,
|
||||
std::pair<int, int> position = std::pair<int, int>(-1, -1))
|
||||
void NotebookPage::addToLayout(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
|
||||
if (position.first < 0 || position.first >= this->hbox.count()) {
|
||||
if (position.first < 0 || position.first >= _hbox.count()) {
|
||||
auto vbox = new QVBoxLayout();
|
||||
vbox->addWidget(widget);
|
||||
|
||||
this->hbox.addLayout(vbox, 1);
|
||||
_hbox.addLayout(vbox, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -96,101 +105,91 @@ NotebookPage::addToLayout(
|
|||
auto vbox = new QVBoxLayout();
|
||||
vbox->addWidget(widget);
|
||||
|
||||
this->hbox.insertLayout(position.first, vbox, 1);
|
||||
_hbox.insertLayout(position.first, vbox, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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)),
|
||||
widget);
|
||||
vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::enterEvent(QEvent *)
|
||||
void NotebookPage::enterEvent(QEvent *)
|
||||
{
|
||||
if (this->hbox.count() == 0) {
|
||||
if (_hbox.count() == 0) {
|
||||
setCursor(QCursor(Qt::PointingHandCursor));
|
||||
} else {
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::leaveEvent(QEvent *)
|
||||
void NotebookPage::leaveEvent(QEvent *)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::mouseReleaseEvent(QMouseEvent *event)
|
||||
void 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
|
||||
this->addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1));
|
||||
addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1));
|
||||
|
||||
setCursor(QCursor(Qt::ArrowCursor));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::dragEnterEvent(QDragEnterEvent *event)
|
||||
void NotebookPage::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (!event->mimeData()->hasFormat("chatterino/split"))
|
||||
return;
|
||||
|
||||
if (isDraggingSplit) {
|
||||
this->dropRegions.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->hbox.count() == 0) {
|
||||
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),
|
||||
_dropRegions.clear();
|
||||
|
||||
std::pair<int, int>(i, -1)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < this->hbox.count(); ++i) {
|
||||
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i));
|
||||
|
||||
for (int j = 0; j < vbox->count() + 1; ++j) {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
if (_hbox.count() == 0) {
|
||||
_dropRegions.push_back(DropRegion(rect(), std::pair<int, int>(-1, -1)));
|
||||
} else {
|
||||
for (int i = 0; i < _hbox.count() + 1; ++i) {
|
||||
_dropRegions.push_back(DropRegion(QRect(((i * 4 - 1) * width() / _hbox.count()) / 4, 0,
|
||||
width() / _hbox.count() / 2 + 1, height() + 1),
|
||||
std::pair<int, int>(i, -1)));
|
||||
}
|
||||
|
||||
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
|
||||
NotebookPage::dragMoveEvent(QDragMoveEvent *event)
|
||||
void NotebookPage::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
setPreviewRect(event->pos());
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::setPreviewRect(QPoint mousePos)
|
||||
void NotebookPage::setPreviewRect(QPoint mousePos)
|
||||
{
|
||||
for (DropRegion region : this->dropRegions) {
|
||||
for (DropRegion region : _dropRegions) {
|
||||
if (region.rect.contains(mousePos)) {
|
||||
this->preview.setBounds(region.rect);
|
||||
_preview.setBounds(region.rect);
|
||||
|
||||
if (!this->preview.isVisible()) {
|
||||
this->preview.show();
|
||||
this->preview.raise();
|
||||
if (!_preview.isVisible()) {
|
||||
_preview.show();
|
||||
_preview.raise();
|
||||
}
|
||||
|
||||
dropPosition = region.position;
|
||||
|
@ -199,17 +198,15 @@ NotebookPage::setPreviewRect(QPoint mousePos)
|
|||
}
|
||||
}
|
||||
|
||||
this->preview.hide();
|
||||
_preview.hide();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
this->preview.hide();
|
||||
_preview.hide();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::dropEvent(QDropEvent *event)
|
||||
void NotebookPage::dropEvent(QDropEvent *event)
|
||||
{
|
||||
if (isDraggingSplit) {
|
||||
event->acceptProposedAction();
|
||||
|
@ -219,33 +216,28 @@ NotebookPage::dropEvent(QDropEvent *event)
|
|||
addToLayout(NotebookPage::draggingSplit, dropPosition);
|
||||
}
|
||||
|
||||
this->preview.hide();
|
||||
_preview.hide();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::paintEvent(QPaintEvent *)
|
||||
void NotebookPage::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
if (this->hbox.count() == 0) {
|
||||
if (_hbox.count() == 0) {
|
||||
painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground);
|
||||
|
||||
painter.fillRect(0, 0, width(), 2,
|
||||
ColorScheme::getInstance().TabSelectedBackground);
|
||||
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
|
||||
|
||||
painter.setPen(ColorScheme::getInstance().Text);
|
||||
painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
|
||||
} else {
|
||||
painter.fillRect(rect(),
|
||||
ColorScheme::getInstance().TabSelectedBackground);
|
||||
painter.fillRect(rect(), ColorScheme::getInstance().TabSelectedBackground);
|
||||
|
||||
painter.fillRect(0, 0, width(), 2,
|
||||
ColorScheme::getInstance().TabSelectedBackground);
|
||||
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int, int>
|
||||
getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
||||
static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
||||
{
|
||||
for (int i = 0; i < layout->count(); ++i) {
|
||||
printf("xD\n");
|
||||
|
@ -254,10 +246,9 @@ getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
|
|||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
std::pair<int, int>
|
||||
NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
||||
std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
||||
{
|
||||
auto layout = this->hbox.layout();
|
||||
auto layout = _hbox.layout();
|
||||
|
||||
if (layout == nullptr) {
|
||||
return std::make_pair(-1, -1);
|
||||
|
@ -266,8 +257,7 @@ NotebookPage::getChatPosition(const ChatWidget *chatWidget)
|
|||
return getWidgetPositionInLayout(layout, chatWidget);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPage::load(const boost::property_tree::ptree &tree)
|
||||
void NotebookPage::load(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
try {
|
||||
int column = 0;
|
||||
|
@ -276,7 +266,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
|
|||
for (const auto &innerV : v.second.get_child("")) {
|
||||
auto widget = new ChatWidget();
|
||||
widget->load(innerV.second);
|
||||
this->addToLayout(widget, std::pair<int, int>(column, row));
|
||||
addToLayout(widget, std::pair<int, int>(column, row));
|
||||
++row;
|
||||
}
|
||||
++column;
|
||||
|
@ -286,8 +276,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
||||
static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
||||
{
|
||||
for (int i = 0; i < layout->count(); ++i) {
|
||||
auto item = layout->itemAt(i);
|
||||
|
@ -323,17 +312,16 @@ saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
|
|||
}
|
||||
}
|
||||
|
||||
boost::property_tree::ptree
|
||||
NotebookPage::save()
|
||||
boost::property_tree::ptree NotebookPage::save()
|
||||
{
|
||||
boost::property_tree::ptree tree;
|
||||
|
||||
auto layout = this->hbox.layout();
|
||||
auto layout = _hbox.layout();
|
||||
|
||||
saveFromLayout(layout, tree);
|
||||
|
||||
/*
|
||||
for (const auto &chat : this->chatWidgets) {
|
||||
for (const auto &chat : chatWidgets) {
|
||||
boost::property_tree::ptree child = chat->save();
|
||||
|
||||
// Set child position
|
||||
|
|
|
@ -23,34 +23,31 @@ class NotebookPage : public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NotebookPage(QWidget *parent, NotebookTab *tab);
|
||||
NotebookTab *tab;
|
||||
NotebookPage(QWidget *parent, NotebookTab *_tab);
|
||||
|
||||
std::pair<int, int> removeFromLayout(ChatWidget *widget);
|
||||
void addToLayout(ChatWidget *widget, std::pair<int, int> position);
|
||||
|
||||
const std::vector<ChatWidget *> &
|
||||
getChatWidgets() const
|
||||
{
|
||||
return chatWidgets;
|
||||
}
|
||||
const std::vector<ChatWidget *> &getChatWidgets() const;
|
||||
NotebookTab *getTab() const;
|
||||
|
||||
static bool isDraggingSplit;
|
||||
static ChatWidget *draggingSplit;
|
||||
static std::pair<int, int> dropPosition;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
void enterEvent(QEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
|
||||
void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE;
|
||||
void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
|
||||
private:
|
||||
struct DropRegion {
|
||||
QRect rect;
|
||||
std::pair<int, int> position;
|
||||
|
@ -62,15 +59,16 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
QVBoxLayout parentbox;
|
||||
QHBoxLayout hbox;
|
||||
NotebookTab *_tab;
|
||||
|
||||
std::vector<ChatWidget *> chatWidgets;
|
||||
std::vector<DropRegion> dropRegions;
|
||||
QVBoxLayout _parentbox;
|
||||
QHBoxLayout _hbox;
|
||||
|
||||
NotebookPageDropPreview preview;
|
||||
std::vector<ChatWidget *> _chatWidgets;
|
||||
std::vector<DropRegion> _dropRegions;
|
||||
|
||||
NotebookPageDropPreview _preview;
|
||||
|
||||
private:
|
||||
void setPreviewRect(QPoint mousePos);
|
||||
|
||||
std::pair<int, int> getChatPosition(const ChatWidget *chatWidget);
|
||||
|
|
|
@ -17,8 +17,7 @@ NotebookPageDropPreview::NotebookPageDropPreview(QWidget *parent)
|
|||
this->setHidden(true);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
||||
void NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -26,14 +25,12 @@ NotebookPageDropPreview::paintEvent(QPaintEvent *)
|
|||
ColorScheme::getInstance().DropPreviewBackground);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPageDropPreview::hideEvent(QHideEvent *)
|
||||
void NotebookPageDropPreview::hideEvent(QHideEvent *)
|
||||
{
|
||||
animate = false;
|
||||
}
|
||||
|
||||
void
|
||||
NotebookPageDropPreview::setBounds(const QRect &rect)
|
||||
void NotebookPageDropPreview::setBounds(const QRect &rect)
|
||||
{
|
||||
if (rect == this->desiredGeometry) {
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "widgets/notebooktab.h"
|
||||
#include "colorscheme.h"
|
||||
#include "settings.h"
|
||||
#include "settingsmanager.h"
|
||||
#include "widgets/notebook.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
@ -10,75 +10,113 @@ namespace widgets {
|
|||
|
||||
NotebookTab::NotebookTab(Notebook *notebook)
|
||||
: QWidget(notebook)
|
||||
, posAnimation(this, "pos")
|
||||
, posAnimated(false)
|
||||
, posAnimationDesired()
|
||||
, notebook(notebook)
|
||||
, title("<no title>")
|
||||
, selected(false)
|
||||
, mouseOver(false)
|
||||
, mouseDown(false)
|
||||
, mouseOverX(false)
|
||||
, mouseDownX(false)
|
||||
, highlightStyle(HighlightNone)
|
||||
, _posAnimation(this, "pos")
|
||||
, _posAnimated(false)
|
||||
, _posAnimationDesired()
|
||||
, _notebook(notebook)
|
||||
, _title("<no title>")
|
||||
, _selected(false)
|
||||
, _mouseOver(false)
|
||||
, _mouseDown(false)
|
||||
, _mouseOverX(false)
|
||||
, _mouseDownX(false)
|
||||
, _highlightStyle(HighlightNone)
|
||||
{
|
||||
this->calcSize();
|
||||
this->setAcceptDrops(true);
|
||||
|
||||
posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
|
||||
_posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
|
||||
|
||||
this->hideXConnection =
|
||||
Settings::getInstance().hideTabX.valueChanged.connect(
|
||||
boost::bind(&NotebookTab::hideTabXChanged, this, _1));
|
||||
this->_hideXConnection = SettingsManager::getInstance().hideTabX.valueChanged.connect(
|
||||
boost::bind(&NotebookTab::hideTabXChanged, this, _1));
|
||||
|
||||
this->setMouseTracking(true);
|
||||
}
|
||||
|
||||
NotebookTab::~NotebookTab()
|
||||
{
|
||||
this->hideXConnection.disconnect();
|
||||
this->_hideXConnection.disconnect();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::calcSize()
|
||||
void NotebookTab::calcSize()
|
||||
{
|
||||
if (Settings::getInstance().hideTabX.get()) {
|
||||
this->resize(this->fontMetrics().width(this->title) + 8, 24);
|
||||
if (SettingsManager::getInstance().hideTabX.get()) {
|
||||
resize(fontMetrics().width(_title) + 8, 24);
|
||||
} else {
|
||||
this->resize(this->fontMetrics().width(this->title) + 8 + 24, 24);
|
||||
resize(fontMetrics().width(_title) + 8 + 24, 24);
|
||||
}
|
||||
|
||||
if (this->parent() != nullptr) {
|
||||
((Notebook *)this->parent())->performLayout(true);
|
||||
if (parent() != nullptr) {
|
||||
((Notebook *)parent())->performLayout(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::moveAnimated(QPoint pos, bool animated)
|
||||
const QString &NotebookTab::getTitle() const
|
||||
{
|
||||
posAnimationDesired = pos;
|
||||
return _title;
|
||||
}
|
||||
|
||||
if ((this->window() != NULL && !this->window()->isVisible()) || !animated ||
|
||||
posAnimated == false) {
|
||||
void NotebookTab::setTitle(const QString &title)
|
||||
{
|
||||
_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);
|
||||
|
||||
posAnimated = true;
|
||||
_posAnimated = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->posAnimation.endValue() == pos) {
|
||||
if (_posAnimation.endValue() == pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->posAnimation.stop();
|
||||
this->posAnimation.setDuration(75);
|
||||
this->posAnimation.setStartValue(this->pos());
|
||||
this->posAnimation.setEndValue(pos);
|
||||
this->posAnimation.start();
|
||||
_posAnimation.stop();
|
||||
_posAnimation.setDuration(75);
|
||||
_posAnimation.setStartValue(this->pos());
|
||||
_posAnimation.setEndValue(pos);
|
||||
_posAnimation.start();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::paintEvent(QPaintEvent *)
|
||||
void NotebookTab::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
|
@ -86,16 +124,16 @@ NotebookTab::paintEvent(QPaintEvent *)
|
|||
|
||||
auto &colorScheme = ColorScheme::getInstance();
|
||||
|
||||
if (this->selected) {
|
||||
if (_selected) {
|
||||
painter.fillRect(rect(), colorScheme.TabSelectedBackground);
|
||||
fg = colorScheme.TabSelectedText;
|
||||
} else if (this->mouseOver) {
|
||||
} else if (_mouseOver) {
|
||||
painter.fillRect(rect(), colorScheme.TabHoverBackground);
|
||||
fg = colorScheme.TabHoverText;
|
||||
} else if (this->highlightStyle == HighlightHighlighted) {
|
||||
} else if (_highlightStyle == HighlightHighlighted) {
|
||||
painter.fillRect(rect(), colorScheme.TabHighlightedBackground);
|
||||
fg = colorScheme.TabHighlightedText;
|
||||
} else if (this->highlightStyle == HighlightNewMessage) {
|
||||
} else if (_highlightStyle == HighlightNewMessage) {
|
||||
painter.fillRect(rect(), colorScheme.TabNewMessageBackground);
|
||||
fg = colorScheme.TabHighlightedText;
|
||||
} else {
|
||||
|
@ -105,116 +143,105 @@ NotebookTab::paintEvent(QPaintEvent *)
|
|||
|
||||
painter.setPen(fg);
|
||||
|
||||
QRect rect(0, 0,
|
||||
width() - (Settings::getInstance().hideTabX.get() ? 0 : 16),
|
||||
height());
|
||||
QRect rect(0, 0, width() - (SettingsManager::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() &&
|
||||
(this->mouseOver || this->selected)) {
|
||||
if (this->mouseOverX) {
|
||||
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64));
|
||||
if (!SettingsManager::getInstance().hideTabX.get() && (_mouseOver || _selected)) {
|
||||
if (_mouseOverX) {
|
||||
painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
|
||||
|
||||
if (this->mouseDownX) {
|
||||
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64));
|
||||
if (_mouseDownX) {
|
||||
painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
|
||||
}
|
||||
}
|
||||
|
||||
painter.drawLine(this->getXRect().topLeft() + QPoint(4, 4),
|
||||
this->getXRect().bottomRight() + QPoint(-4, -4));
|
||||
painter.drawLine(this->getXRect().topRight() + QPoint(-4, 4),
|
||||
this->getXRect().bottomLeft() + QPoint(4, -4));
|
||||
painter.drawLine(getXRect().topLeft() + QPoint(4, 4),
|
||||
getXRect().bottomRight() + QPoint(-4, -4));
|
||||
painter.drawLine(getXRect().topRight() + QPoint(-4, 4),
|
||||
getXRect().bottomLeft() + QPoint(4, -4));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::mousePressEvent(QMouseEvent *event)
|
||||
void NotebookTab::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
this->mouseDown = true;
|
||||
this->mouseDownX = this->getXRect().contains(event->pos());
|
||||
_mouseDown = true;
|
||||
_mouseDownX = getXRect().contains(event->pos());
|
||||
|
||||
this->update();
|
||||
update();
|
||||
|
||||
this->notebook->select(page);
|
||||
_notebook->select(page);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::mouseReleaseEvent(QMouseEvent *event)
|
||||
void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
this->mouseDown = false;
|
||||
_mouseDown = false;
|
||||
|
||||
if (!Settings::getInstance().hideTabX.get() && this->mouseDownX &&
|
||||
this->getXRect().contains(event->pos())) {
|
||||
this->mouseDownX = false;
|
||||
if (!SettingsManager::getInstance().hideTabX.get() && _mouseDownX &&
|
||||
getXRect().contains(event->pos())) {
|
||||
_mouseDownX = false;
|
||||
|
||||
this->notebook->removePage(this->page);
|
||||
_notebook->removePage(page);
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::enterEvent(QEvent *)
|
||||
void NotebookTab::enterEvent(QEvent *)
|
||||
{
|
||||
this->mouseOver = true;
|
||||
_mouseOver = true;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::leaveEvent(QEvent *)
|
||||
void NotebookTab::leaveEvent(QEvent *)
|
||||
{
|
||||
this->mouseOverX = this->mouseOver = false;
|
||||
_mouseOverX = _mouseOver = false;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::dragEnterEvent(QDragEnterEvent *)
|
||||
void NotebookTab::dragEnterEvent(QDragEnterEvent *)
|
||||
{
|
||||
this->notebook->select(page);
|
||||
_notebook->select(page);
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::mouseMoveEvent(QMouseEvent *event)
|
||||
void NotebookTab::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
bool overX = this->getXRect().contains(event->pos());
|
||||
bool overX = getXRect().contains(event->pos());
|
||||
|
||||
if (overX != this->mouseOverX) {
|
||||
this->mouseOverX = overX && !Settings::getInstance().hideTabX.get();
|
||||
if (overX != _mouseOverX) {
|
||||
_mouseOverX = overX && !SettingsManager::getInstance().hideTabX.get();
|
||||
|
||||
this->update();
|
||||
update();
|
||||
}
|
||||
|
||||
if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) {
|
||||
QPoint relPoint = this->mapToParent(event->pos());
|
||||
if (_mouseDown && !getDesiredRect().contains(event->pos())) {
|
||||
QPoint relPoint = mapToParent(event->pos());
|
||||
|
||||
int index;
|
||||
NotebookPage *page = notebook->tabAt(relPoint, index);
|
||||
NotebookPage *page = _notebook->tabAt(relPoint, index);
|
||||
|
||||
if (page != nullptr && page != this->page) {
|
||||
notebook->rearrangePage(this->page, index);
|
||||
if (page != nullptr && page != page) {
|
||||
_notebook->rearrangePage(page, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotebookTab::load(const boost::property_tree::ptree &tree)
|
||||
void NotebookTab::load(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
// Load tab title
|
||||
try {
|
||||
this->setTitle(QString::fromStdString(tree.get<std::string>("title")));
|
||||
setTitle(QString::fromStdString(tree.get<std::string>("title")));
|
||||
} catch (boost::property_tree::ptree_error) {
|
||||
}
|
||||
}
|
||||
|
||||
boost::property_tree::ptree
|
||||
NotebookTab::save()
|
||||
boost::property_tree::ptree NotebookTab::save()
|
||||
{
|
||||
boost::property_tree::ptree tree;
|
||||
|
||||
tree.put("title", this->getTitle().toStdString());
|
||||
tree.put("title", getTitle().toStdString());
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
|
@ -18,72 +18,27 @@ class NotebookTab : public QWidget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum HighlightStyle {
|
||||
HighlightNone,
|
||||
HighlightHighlighted,
|
||||
HighlightNewMessage
|
||||
};
|
||||
enum HighlightStyle { HighlightNone, HighlightHighlighted, HighlightNewMessage };
|
||||
|
||||
explicit NotebookTab(Notebook *notebook);
|
||||
explicit NotebookTab(Notebook *_notebook);
|
||||
~NotebookTab();
|
||||
|
||||
void calcSize();
|
||||
|
||||
NotebookPage *page;
|
||||
|
||||
const QString &
|
||||
getTitle() const
|
||||
{
|
||||
return this->title;
|
||||
}
|
||||
const QString &getTitle() const;
|
||||
void setTitle(const QString &title);
|
||||
bool getSelected();
|
||||
void setSelected(bool value);
|
||||
|
||||
void
|
||||
setTitle(const QString &title)
|
||||
{
|
||||
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();
|
||||
}
|
||||
HighlightStyle getHighlightStyle() const;
|
||||
void setHighlightStyle(HighlightStyle style);
|
||||
|
||||
void moveAnimated(QPoint pos, bool animated = true);
|
||||
|
||||
public:
|
||||
QRect
|
||||
getDesiredRect() const
|
||||
{
|
||||
return QRect(posAnimationDesired, this->size());
|
||||
}
|
||||
|
||||
void
|
||||
hideTabXChanged(bool)
|
||||
{
|
||||
calcSize();
|
||||
update();
|
||||
}
|
||||
QRect getDesiredRect() const;
|
||||
void hideTabXChanged(bool);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
@ -98,26 +53,25 @@ protected:
|
|||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
boost::signals2::connection hideXConnection;
|
||||
boost::signals2::connection _hideXConnection;
|
||||
|
||||
QPropertyAnimation posAnimation;
|
||||
bool posAnimated;
|
||||
QPoint posAnimationDesired;
|
||||
QPropertyAnimation _posAnimation;
|
||||
bool _posAnimated;
|
||||
QPoint _posAnimationDesired;
|
||||
|
||||
Notebook *notebook;
|
||||
Notebook *_notebook;
|
||||
|
||||
QString title;
|
||||
QString _title;
|
||||
|
||||
bool selected;
|
||||
bool mouseOver;
|
||||
bool mouseDown;
|
||||
bool mouseOverX;
|
||||
bool mouseDownX;
|
||||
bool _selected;
|
||||
bool _mouseOver;
|
||||
bool _mouseDown;
|
||||
bool _mouseOverX;
|
||||
bool _mouseDownX;
|
||||
|
||||
HighlightStyle highlightStyle;
|
||||
HighlightStyle _highlightStyle;
|
||||
|
||||
QRect
|
||||
getXRect()
|
||||
QRect getXRect()
|
||||
{
|
||||
return QRect(this->width() - 20, 4, 16, 16);
|
||||
}
|
||||
|
|
|
@ -16,18 +16,15 @@ public:
|
|||
sizePolicy.setVerticalPolicy(QSizePolicy::Preferred);
|
||||
this->setSizePolicy(sizePolicy);
|
||||
|
||||
QObject::connect(this, &QTextEdit::textChanged, this,
|
||||
&QWidget::updateGeometry);
|
||||
QObject::connect(this, &QTextEdit::textChanged, this, &QWidget::updateGeometry);
|
||||
}
|
||||
|
||||
QSize
|
||||
sizeHint() const override
|
||||
QSize sizeHint() const override
|
||||
{
|
||||
return QSize(this->width(), this->heightForWidth(this->width()));
|
||||
}
|
||||
|
||||
bool
|
||||
hasHeightForWidth() const override
|
||||
bool hasHeightForWidth() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -35,17 +32,14 @@ public:
|
|||
boost::signals2::signal<void(QKeyEvent *)> keyPressed;
|
||||
|
||||
protected:
|
||||
int
|
||||
heightForWidth(int) const override
|
||||
int heightForWidth(int) const override
|
||||
{
|
||||
auto margins = this->contentsMargins();
|
||||
|
||||
return margins.top() + document()->size().height() + margins.bottom() +
|
||||
5;
|
||||
return margins.top() + document()->size().height() + margins.bottom() + 5;
|
||||
}
|
||||
|
||||
void
|
||||
keyPressEvent(QKeyEvent *event)
|
||||
void keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
|
|
|
@ -11,35 +11,34 @@ namespace widgets {
|
|||
|
||||
ScrollBar::ScrollBar(QWidget *widget)
|
||||
: QWidget(widget)
|
||||
, mutex()
|
||||
, currentValueAnimation(this, "currentValue")
|
||||
, highlights(NULL)
|
||||
, mouseOverIndex(-1)
|
||||
, mouseDownIndex(-1)
|
||||
, lastMousePosition()
|
||||
, buttonHeight(16)
|
||||
, trackHeight(100)
|
||||
, thumbRect()
|
||||
, maximum()
|
||||
, minimum()
|
||||
, largeChange()
|
||||
, smallChange()
|
||||
, desiredValue()
|
||||
, currentValueChanged()
|
||||
, currentValue()
|
||||
, _mutex()
|
||||
, _currentValueAnimation(this, "currentValue")
|
||||
, _highlights(NULL)
|
||||
, _mouseOverIndex(-1)
|
||||
, _mouseDownIndex(-1)
|
||||
, _lastMousePosition()
|
||||
, _buttonHeight(16)
|
||||
, _trackHeight(100)
|
||||
, _thumbRect()
|
||||
, _maximum()
|
||||
, _minimum()
|
||||
, _largeChange()
|
||||
, _smallChange()
|
||||
, _desiredValue()
|
||||
, _currentValueChanged()
|
||||
, _currentValue()
|
||||
{
|
||||
this->resize(16, 100);
|
||||
resize(16, 100);
|
||||
|
||||
this->currentValueAnimation.setDuration(300);
|
||||
this->currentValueAnimation.setEasingCurve(
|
||||
QEasingCurve(QEasingCurve::OutCubic));
|
||||
_currentValueAnimation.setDuration(250);
|
||||
_currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic));
|
||||
|
||||
this->setMouseTracking(true);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
ScrollBar::~ScrollBar()
|
||||
{
|
||||
auto highlight = this->highlights;
|
||||
auto highlight = _highlights;
|
||||
|
||||
while (highlight != NULL) {
|
||||
auto tmp = highlight->next;
|
||||
|
@ -48,18 +47,17 @@ ScrollBar::~ScrollBar()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
||||
void ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
||||
{
|
||||
this->mutex.lock();
|
||||
_mutex.lock();
|
||||
|
||||
ScrollBarHighlight *last = NULL;
|
||||
ScrollBarHighlight *current = this->highlights;
|
||||
ScrollBarHighlight *current = _highlights;
|
||||
|
||||
while (current != NULL) {
|
||||
if (func(*current)) {
|
||||
if (last == NULL) {
|
||||
this->highlights = current->next;
|
||||
_highlights = current->next;
|
||||
} else {
|
||||
last->next = current->next;
|
||||
}
|
||||
|
@ -73,150 +71,233 @@ ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
|
|||
}
|
||||
}
|
||||
|
||||
this->mutex.unlock();
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::addHighlight(ScrollBarHighlight *highlight)
|
||||
void ScrollBar::addHighlight(ScrollBarHighlight *highlight)
|
||||
{
|
||||
this->mutex.lock();
|
||||
_mutex.lock();
|
||||
|
||||
if (this->highlights == NULL) {
|
||||
this->highlights = highlight;
|
||||
if (_highlights == NULL) {
|
||||
_highlights = highlight;
|
||||
} else {
|
||||
highlight->next = this->highlights->next;
|
||||
this->highlights->next = highlight;
|
||||
highlight->next = _highlights->next;
|
||||
_highlights->next = highlight;
|
||||
}
|
||||
|
||||
this->mutex.unlock();
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::paintEvent(QPaintEvent *)
|
||||
void ScrollBar::setMaximum(qreal value)
|
||||
{
|
||||
_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);
|
||||
painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG);
|
||||
|
||||
painter.fillRect(QRect(0, 0, width(), this->buttonHeight),
|
||||
QColor(255, 0, 0));
|
||||
painter.fillRect(
|
||||
QRect(0, height() - this->buttonHeight, width(), this->buttonHeight),
|
||||
QColor(255, 0, 0));
|
||||
painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0));
|
||||
painter.fillRect(QRect(0, height() - _buttonHeight, width(), _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 {
|
||||
// painter.fillRect();
|
||||
// } while ((highlight = highlight->next()) != NULL);
|
||||
|
||||
this->mutex.unlock();
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::mouseMoveEvent(QMouseEvent *event)
|
||||
void ScrollBar::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (this->mouseDownIndex == -1) {
|
||||
if (_mouseDownIndex == -1) {
|
||||
int y = event->pos().y();
|
||||
|
||||
auto oldIndex = this->mouseOverIndex;
|
||||
auto oldIndex = _mouseOverIndex;
|
||||
|
||||
if (y < this->buttonHeight) {
|
||||
this->mouseOverIndex = 0;
|
||||
} else if (y < this->thumbRect.y()) {
|
||||
this->mouseOverIndex = 1;
|
||||
} else if (this->thumbRect.contains(2, y)) {
|
||||
this->mouseOverIndex = 2;
|
||||
} else if (y < height() - this->buttonHeight) {
|
||||
this->mouseOverIndex = 3;
|
||||
if (y < _buttonHeight) {
|
||||
_mouseOverIndex = 0;
|
||||
} else if (y < _thumbRect.y()) {
|
||||
_mouseOverIndex = 1;
|
||||
} else if (_thumbRect.contains(2, y)) {
|
||||
_mouseOverIndex = 2;
|
||||
} else if (y < height() - _buttonHeight) {
|
||||
_mouseOverIndex = 3;
|
||||
} else {
|
||||
this->mouseOverIndex = 4;
|
||||
_mouseOverIndex = 4;
|
||||
}
|
||||
|
||||
if (oldIndex != this->mouseOverIndex) {
|
||||
this->update();
|
||||
if (oldIndex != _mouseOverIndex) {
|
||||
update();
|
||||
}
|
||||
} else if (this->mouseDownIndex == 2) {
|
||||
int delta = event->pos().y() - lastMousePosition.y();
|
||||
} else if (_mouseDownIndex == 2) {
|
||||
int delta = event->pos().y() - _lastMousePosition.y();
|
||||
|
||||
this->setDesiredValue(this->desiredValue +
|
||||
(qreal)delta / this->trackHeight * maximum);
|
||||
setDesiredValue(_desiredValue + (qreal)delta / _trackHeight * _maximum);
|
||||
}
|
||||
|
||||
this->lastMousePosition = event->pos();
|
||||
_lastMousePosition = event->pos();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::mousePressEvent(QMouseEvent *event)
|
||||
void ScrollBar::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
int y = event->pos().y();
|
||||
|
||||
if (y < this->buttonHeight) {
|
||||
this->mouseDownIndex = 0;
|
||||
} else if (y < this->thumbRect.y()) {
|
||||
this->mouseDownIndex = 1;
|
||||
} else if (this->thumbRect.contains(2, y)) {
|
||||
this->mouseDownIndex = 2;
|
||||
} else if (y < height() - this->buttonHeight) {
|
||||
this->mouseDownIndex = 3;
|
||||
if (y < _buttonHeight) {
|
||||
_mouseDownIndex = 0;
|
||||
} else if (y < _thumbRect.y()) {
|
||||
_mouseDownIndex = 1;
|
||||
} else if (_thumbRect.contains(2, y)) {
|
||||
_mouseDownIndex = 2;
|
||||
} else if (y < height() - _buttonHeight) {
|
||||
_mouseDownIndex = 3;
|
||||
} else {
|
||||
this->mouseDownIndex = 4;
|
||||
_mouseDownIndex = 4;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::mouseReleaseEvent(QMouseEvent *event)
|
||||
void ScrollBar::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
int y = event->pos().y();
|
||||
|
||||
if (y < this->buttonHeight) {
|
||||
if (this->mouseDownIndex == 0) {
|
||||
this->setDesiredValue(this->desiredValue - this->smallChange, true);
|
||||
if (y < _buttonHeight) {
|
||||
if (_mouseDownIndex == 0) {
|
||||
setDesiredValue(_desiredValue - _smallChange, true);
|
||||
}
|
||||
} else if (y < this->thumbRect.y()) {
|
||||
if (this->mouseDownIndex == 1) {
|
||||
this->setDesiredValue(this->desiredValue - this->smallChange, true);
|
||||
} else if (y < _thumbRect.y()) {
|
||||
if (_mouseDownIndex == 1) {
|
||||
setDesiredValue(_desiredValue - _smallChange, true);
|
||||
}
|
||||
} else if (this->thumbRect.contains(2, y)) {
|
||||
} else if (_thumbRect.contains(2, y)) {
|
||||
// do nothing
|
||||
} else if (y < height() - this->buttonHeight) {
|
||||
if (this->mouseDownIndex == 3) {
|
||||
this->setDesiredValue(this->desiredValue + this->smallChange, true);
|
||||
} else if (y < height() - _buttonHeight) {
|
||||
if (_mouseDownIndex == 3) {
|
||||
setDesiredValue(_desiredValue + _smallChange, true);
|
||||
}
|
||||
} else {
|
||||
if (this->mouseDownIndex == 4) {
|
||||
this->setDesiredValue(this->desiredValue + this->smallChange, true);
|
||||
if (_mouseDownIndex == 4) {
|
||||
setDesiredValue(_desiredValue + _smallChange, true);
|
||||
}
|
||||
}
|
||||
|
||||
this->mouseDownIndex = -1;
|
||||
_mouseDownIndex = -1;
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::leaveEvent(QEvent *)
|
||||
void ScrollBar::leaveEvent(QEvent *)
|
||||
{
|
||||
this->mouseOverIndex = -1;
|
||||
_mouseOverIndex = -1;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
ScrollBar::updateScroll()
|
||||
void ScrollBar::updateScroll()
|
||||
{
|
||||
this->trackHeight = height() - this->buttonHeight - this->buttonHeight -
|
||||
MIN_THUMB_HEIGHT - 1;
|
||||
_trackHeight = height() - _buttonHeight - _buttonHeight - MIN_THUMB_HEIGHT - 1;
|
||||
|
||||
this->thumbRect =
|
||||
QRect(0,
|
||||
(int)(this->currentValue / this->maximum * this->trackHeight) +
|
||||
1 + this->buttonHeight,
|
||||
width(),
|
||||
(int)(this->largeChange / this->maximum * this->trackHeight) +
|
||||
MIN_THUMB_HEIGHT);
|
||||
_thumbRect = QRect(0, (int)(_currentValue / _maximum * _trackHeight) + 1 + _buttonHeight,
|
||||
width(), (int)(_largeChange / _maximum * _trackHeight) + MIN_THUMB_HEIGHT);
|
||||
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -23,128 +23,29 @@ public:
|
|||
void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func);
|
||||
void addHighlight(ScrollBarHighlight *highlight);
|
||||
|
||||
Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue)
|
||||
Q_PROPERTY(qreal _desiredValue READ getDesiredValue WRITE setDesiredValue)
|
||||
|
||||
void
|
||||
setMaximum(qreal value)
|
||||
{
|
||||
this->maximum = value;
|
||||
|
||||
this->updateScroll();
|
||||
}
|
||||
|
||||
void
|
||||
setMinimum(qreal value)
|
||||
{
|
||||
this->minimum = 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();
|
||||
}
|
||||
}
|
||||
void setMaximum(qreal value);
|
||||
void setMinimum(qreal value);
|
||||
void setLargeChange(qreal value);
|
||||
void setSmallChange(qreal value);
|
||||
void setDesiredValue(qreal value, bool animated = false);
|
||||
qreal getMaximum() const;
|
||||
qreal getMinimum() const;
|
||||
qreal getLargeChange() const;
|
||||
qreal getSmallChange() const;
|
||||
qreal getDesiredValue() const;
|
||||
qreal getCurrentValue() const;
|
||||
boost::signals2::signal<void()> &getCurrentValueChanged();
|
||||
void setCurrentValue(qreal value);
|
||||
|
||||
private:
|
||||
Q_PROPERTY(qreal currentValue READ getCurrentValue WRITE setCurrentValue)
|
||||
Q_PROPERTY(qreal _currentValue READ getCurrentValue WRITE setCurrentValue)
|
||||
|
||||
QMutex mutex;
|
||||
ScrollBarHighlight *highlights;
|
||||
QMutex _mutex;
|
||||
ScrollBarHighlight *_highlights;
|
||||
|
||||
QPropertyAnimation currentValueAnimation;
|
||||
QPropertyAnimation _currentValueAnimation;
|
||||
|
||||
void paintEvent(QPaintEvent *);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
|
@ -152,23 +53,23 @@ private:
|
|||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void leaveEvent(QEvent *);
|
||||
|
||||
int mouseOverIndex;
|
||||
int mouseDownIndex;
|
||||
QPoint lastMousePosition;
|
||||
int _mouseOverIndex;
|
||||
int _mouseDownIndex;
|
||||
QPoint _lastMousePosition;
|
||||
|
||||
int buttonHeight;
|
||||
int trackHeight;
|
||||
int _buttonHeight;
|
||||
int _trackHeight;
|
||||
|
||||
QRect thumbRect;
|
||||
QRect _thumbRect;
|
||||
|
||||
qreal maximum;
|
||||
qreal minimum;
|
||||
qreal largeChange;
|
||||
qreal smallChange;
|
||||
qreal desiredValue;
|
||||
qreal currentValue;
|
||||
qreal _maximum;
|
||||
qreal _minimum;
|
||||
qreal _largeChange;
|
||||
qreal _smallChange;
|
||||
qreal _desiredValue;
|
||||
qreal _currentValue;
|
||||
|
||||
boost::signals2::signal<void()> currentValueChanged;
|
||||
boost::signals2::signal<void()> _currentValueChanged;
|
||||
|
||||
void updateScroll();
|
||||
};
|
||||
|
|
|
@ -4,13 +4,11 @@
|
|||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex,
|
||||
Style style, QString tag)
|
||||
: position(position)
|
||||
, colorIndex(std::max(
|
||||
0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex)))
|
||||
, style(style)
|
||||
, tag(tag)
|
||||
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag)
|
||||
: _position(position)
|
||||
, _colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex)))
|
||||
, _style(style)
|
||||
, _tag(tag)
|
||||
, next(NULL)
|
||||
{
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue