refactoring

This commit is contained in:
fourtf 2017-04-12 17:46:44 +02:00
parent 8ef492d7ae
commit 96db82e867
114 changed files with 5554 additions and 3703 deletions

View file

@ -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;
}
}

View file

@ -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

View file

@ -7,19 +7,16 @@
#include <QThreadPool> #include <QThreadPool>
#include <functional> #include <functional>
#define async_exec(a) \ #define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a));
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
class LambdaRunnable : public QRunnable class LambdaRunnable : public QRunnable {
{
public: public:
LambdaRunnable(std::function<void()> action) LambdaRunnable(std::function<void()> action)
{ {
this->action = action; this->action = action;
} }
void void run()
run()
{ {
this->action(); this->action();
} }

View file

@ -1,8 +1,9 @@
#include "channel.h" #include "channel.h"
#include "emotes.h" #include "emotemanager.h"
#include "logging/loggingmanager.h" #include "logging/loggingmanager.h"
#include "messages/message.h" #include "messages/message.h"
#include "windows.h" #include "util/urlfetch.h"
#include "windowmanager.h"
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
@ -14,133 +15,177 @@
#include <memory> #include <memory>
using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
Channel::Channel(const QString &channel) Channel::Channel(const QString &channel)
: messages() : _messages()
, name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) , _name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) : channel)
: channel) , _bttvChannelEmotes()
, bttvChannelEmotes() , _ffzChannelEmotes()
, ffzChannelEmotes() , _subLink("https://www.twitch.tv/" + _name + "/subscribe?ref=in_chat_subscriber_link")
, subLink("https://www.twitch.tv/" + name + , _channelLink("https://twitch.tv/" + _name)
"/subscribe?ref=in_chat_subscriber_link") , _popoutPlayerLink("https://player.twitch.tv/?channel=" + _name)
, channelLink("https://twitch.tv/" + name) //, _loggingChannel(logging::get(_name))
, popoutPlayerLink("https://player.twitch.tv/?channel=" + name)
, loggingChannel(logging::get(name))
{ {
reloadChannelEmotes(); reloadChannelEmotes();
} }
void //
Channel::reloadBttvEmotes() // properties
//
ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getBttvChannelEmotes()
{ {
// bttv return _bttvChannelEmotes;
QNetworkAccessManager *manager = new QNetworkAccessManager();
QUrl url("https://api.betterttv.net/2/channels/" + this->name);
QNetworkRequest request(url);
QNetworkReply *reply = manager->get(request);
QObject::connect(reply, &QNetworkReply::finished, [=] {
if (reply->error() == QNetworkReply::NetworkError::NoError) {
QByteArray data = reply->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
QJsonObject root = jsonDoc.object();
auto emotes = root.value("emotes").toArray();
QString _template = "https:" + root.value("urlTemplate").toString();
for (const QJsonValue &emote : emotes) {
QString id = emote.toObject().value("id").toString();
QString code = emote.toObject().value("code").toString();
// emote.value("imageType").toString();
QString tmp = _template;
tmp.detach();
QString url =
tmp.replace("{{id}}", id).replace("{{image}}", "1x");
this->getBttvChannelEmotes().insert(
code, Emotes::getBttvChannelEmoteFromCaches().getOrAdd(
id, [&code, &url] {
return new messages::LazyLoadedImage(
url, 1, code,
code + "\nChannel Bttv Emote");
}));
}
}
reply->deleteLater();
manager->deleteLater();
});
} }
void ConcurrentMap<QString, messages::LazyLoadedImage *> &Channel::getFfzChannelEmotes()
Channel::reloadFfzEmotes()
{ {
QNetworkAccessManager *manager = new QNetworkAccessManager(); return _ffzChannelEmotes;
QUrl url("http://api.frankerfacez.com/v1/room/" + this->name);
QNetworkRequest request(url);
QNetworkReply *reply = manager->get(request);
QObject::connect(reply, &QNetworkReply::finished, [=] {
if (reply->error() == QNetworkReply::NetworkError::NoError) {
QByteArray data = reply->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
QJsonObject root = jsonDoc.object();
auto sets = root.value("sets").toObject();
for (const QJsonValue &set : sets) {
auto emoticons = set.toObject().value("emoticons").toArray();
for (const QJsonValue &emote : emoticons) {
QJsonObject object = emote.toObject();
// margins
int id = object.value("id").toInt();
QString code = object.value("name").toString();
QJsonObject urls = object.value("urls").toObject();
QString url1 = "http:" + urls.value("1").toString();
this->getFfzChannelEmotes().insert(
code, Emotes::getFfzChannelEmoteFromCaches().getOrAdd(
id, [&code, &url1] {
return new messages::LazyLoadedImage(
url1, 1, code,
code + "\nGlobal Ffz Emote");
}));
}
}
}
reply->deleteLater();
manager->deleteLater();
});
} }
void bool Channel::isEmpty() const
Channel::addMessage(std::shared_ptr<messages::Message> message)
{ {
std::shared_ptr<messages::Message> deleted; return _name.isEmpty();
}
if (this->loggingChannel.get() != nullptr) { const QString &Channel::getName() const
this->loggingChannel->append(message); {
} return _name;
}
if (this->messages.appendItem(message, deleted)) { int Channel::getRoomID() const
this->messageRemovedFromStart(deleted); {
return _roomID;
}
const QString &Channel::getSubLink() const
{
return _subLink;
}
const QString &Channel::getChannelLink() const
{
return _channelLink;
}
const QString &Channel::getPopoutPlayerLink() const
{
return _popoutPlayerLink;
}
bool Channel::getIsLive() const
{
return _isLive;
}
int Channel::getStreamViewerCount() const
{
return _streamViewerCount;
}
const QString &Channel::getStreamStatus() const
{
return _streamStatus;
}
const QString &Channel::getStreamGame() const
{
return _streamGame;
}
messages::LimitedQueueSnapshot<messages::SharedMessage> Channel::getMessageSnapshot()
{
return _messages.getSnapshot();
}
//
// methods
//
void Channel::addMessage(std::shared_ptr<Message> message)
{
std::shared_ptr<Message> deleted;
// if (_loggingChannel.get() != nullptr) {
// _loggingChannel->append(message);
// }
if (_messages.appendItem(message, deleted)) {
messageRemovedFromStart(deleted);
} }
this->messageAppended(message); this->messageAppended(message);
Windows::repaintVisibleChatWidgets(this); WindowManager::repaintVisibleChatWidgets(this);
}
// private methods
void Channel::reloadChannelEmotes()
{
reloadBttvEmotes();
reloadFfzEmotes();
}
void Channel::reloadBttvEmotes()
{
util::urlJsonFetch(
"https://api.betterttv.net/2/channels/" + _name, [this](QJsonObject &rootNode) {
auto emotesNode = rootNode.value("emotes").toArray();
QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString();
for (const QJsonValue &emoteNode : emotesNode) {
QJsonObject emoteObject = emoteNode.toObject();
QString id = emoteObject.value("id").toString();
QString code = emoteObject.value("code").toString();
// emoteObject.value("imageType").toString();
QString link = linkTemplate;
link.detach();
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
auto emote = EmoteManager::getInstance().getBttvChannelEmoteFromCaches().getOrAdd(
id, [&code, &link] {
return new LazyLoadedImage(link, 1, code, code + "\nChannel Bttv Emote");
});
this->getBttvChannelEmotes().insert(code, emote);
}
});
}
void Channel::reloadFfzEmotes()
{
util::urlJsonFetch("http://api.frankerfacez.com/v1/room/" + _name, [this](
QJsonObject &rootNode) {
auto setsNode = rootNode.value("sets").toObject();
for (const QJsonValue &setNode : setsNode) {
auto emotesNode = setNode.toObject().value("emoticons").toArray();
for (const QJsonValue &emoteNode : emotesNode) {
QJsonObject emoteObject = emoteNode.toObject();
// margins
int id = emoteObject.value("id").toInt();
QString code = emoteObject.value("name").toString();
QJsonObject urls = emoteObject.value("urls").toObject();
QString url1 = "http:" + urls.value("1").toString();
auto emote = EmoteManager::getInstance().getFfzChannelEmoteFromCaches().getOrAdd(
id, [&code, &url1] {
return new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote");
});
getFfzChannelEmotes().insert(code, emote);
}
}
});
} }
} // namespace chatterino } // namespace chatterino

132
channel.h
View file

@ -18,119 +18,59 @@ namespace messages {
class Message; class Message;
} }
class ChannelManager;
typedef std::shared_ptr<Channel> SharedChannel;
class Channel class Channel
{ {
public: public:
Channel(const QString &channel); Channel(const QString &channel);
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)> boost::signals2::signal<void(messages::SharedMessage &)> messageRemovedFromStart;
messageRemovedFromStart; boost::signals2::signal<void(messages::SharedMessage &)> messageAppended;
boost::signals2::signal<void(std::shared_ptr<messages::Message> &)>
messageAppended;
// properties // properties
ConcurrentMap<QString, messages::LazyLoadedImage *> & ConcurrentMap<QString, messages::LazyLoadedImage *> &getBttvChannelEmotes();
getBttvChannelEmotes() ConcurrentMap<QString, messages::LazyLoadedImage *> &getFfzChannelEmotes();
{
return bttvChannelEmotes;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> & bool isEmpty() const;
getFfzChannelEmotes() const QString &getName() const;
{ int getRoomID() const;
return ffzChannelEmotes; const QString &getSubLink() const;
} const QString &getChannelLink() const;
const QString &getPopoutPlayerLink() const;
bool bool getIsLive() const;
isEmpty() const int getStreamViewerCount() const;
{ const QString &getStreamStatus() const;
return name.isEmpty(); const QString &getStreamGame() const;
} messages::LimitedQueueSnapshot<messages::SharedMessage> getMessageSnapshot();
const QString &
getName() const
{
return name;
}
int
getRoomID() const
{
return roomID;
}
const QString &
getSubLink() const
{
return subLink;
}
const QString &
getChannelLink() const
{
return channelLink;
}
const QString &
getPopoutPlayerLink() const
{
return popoutPlayerLink;
}
bool
getIsLive() const
{
return isLive;
}
int
getStreamViewerCount() const
{
return streamViewerCount;
}
const QString &
getStreamStatus() const
{
return streamStatus;
}
const QString &
getStreamGame() const
{
return streamGame;
}
// methods // methods
messages::LimitedQueueSnapshot<std::shared_ptr<messages::Message>> void addMessage(messages::SharedMessage message);
getMessageSnapshot() void reloadChannelEmotes();
{
return messages.getSnapshot();
}
void addMessage(std::shared_ptr<messages::Message> message);
void
reloadChannelEmotes()
{
this->reloadBttvEmotes();
this->reloadFfzEmotes();
}
private: private:
messages::LimitedQueue<std::shared_ptr<messages::Message>> messages; // variabeles
messages::LimitedQueue<messages::SharedMessage> _messages;
QString name; QString _name;
int roomID; int _roomID;
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes; ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes; ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzChannelEmotes;
QString subLink; QString _subLink;
QString channelLink; QString _channelLink;
QString popoutPlayerLink; QString _popoutPlayerLink;
bool isLive; bool _isLive;
int streamViewerCount; int _streamViewerCount;
QString streamStatus; QString _streamStatus;
QString streamGame; QString _streamGame;
std::shared_ptr<logging::Channel> loggingChannel; // std::shared_ptr<logging::Channel> _loggingChannel;
// methods
void reloadBttvEmotes(); void reloadBttvEmotes();
void reloadFfzEmotes(); void reloadFfzEmotes();
}; };

122
channelmanager.cpp Normal file
View 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
View 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

View file

@ -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

View file

@ -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

View file

@ -17,17 +17,14 @@ TARGET = chatterino
TEMPLATE = app TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += IRC_NAMESPACE=Communi
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += main.cpp\ SOURCES += main.cpp\
account.cpp \
channel.cpp \ channel.cpp \
channels.cpp \
colorscheme.cpp \ colorscheme.cpp \
emojis.cpp \ emojis.cpp \
emotes.cpp \
fonts.cpp \
ircmanager.cpp \ ircmanager.cpp \
messages/lazyloadedimage.cpp \ messages/lazyloadedimage.cpp \
messages/link.cpp \ messages/link.cpp \
@ -35,7 +32,6 @@ SOURCES += main.cpp\
messages/word.cpp \ messages/word.cpp \
messages/wordpart.cpp \ messages/wordpart.cpp \
resources.cpp \ resources.cpp \
settings.cpp \
widgets/chatwidget.cpp \ widgets/chatwidget.cpp \
widgets/chatwidgetheader.cpp \ widgets/chatwidgetheader.cpp \
widgets/chatwidgetheaderbutton.cpp \ widgets/chatwidgetheaderbutton.cpp \
@ -52,21 +48,31 @@ SOURCES += main.cpp\
widgets/settingsdialog.cpp \ widgets/settingsdialog.cpp \
widgets/settingsdialogtab.cpp \ widgets/settingsdialogtab.cpp \
widgets/textinputdialog.cpp \ widgets/textinputdialog.cpp \
windows.cpp \
messages/messageref.cpp \ messages/messageref.cpp \
logging/loggingmanager.cpp \ logging/loggingmanager.cpp \
logging/loggingchannel.cpp logging/loggingchannel.cpp \
windowmanager.cpp \
channelmanager.cpp \
fontmanager.cpp \
settingsmanager.cpp \
emotemanager.cpp \
usermanager.cpp \
twitch/twitchuser.cpp \
messages/messagebuilder.cpp \
twitch/twitchmessagebuilder.cpp \
ircuser2.cpp \
twitch/twitchparsemessage.cpp \
widgets/fancybutton.cpp \
widgets/titlebar.cpp \
widgets/userpopupwidget.cpp
HEADERS += account.h \ HEADERS += \
asyncexec.h \ asyncexec.h \
channel.h \ channel.h \
channels.h \
colorscheme.h \ colorscheme.h \
common.h \ common.h \
concurrentmap.h \ concurrentmap.h \
emojis.h \ emojis.h \
emotes.h \
fonts.h \
ircmanager.h \ ircmanager.h \
messages/lazyloadedimage.h \ messages/lazyloadedimage.h \
messages/link.h \ messages/link.h \
@ -75,8 +81,7 @@ HEADERS += account.h \
messages/wordpart.h \ messages/wordpart.h \
resources.h \ resources.h \
setting.h \ setting.h \
settings.h \ twitch/emotevalue.h \
twitchemotevalue.h \
widgets/chatwidget.h \ widgets/chatwidget.h \
widgets/chatwidgetheader.h \ widgets/chatwidgetheader.h \
widgets/chatwidgetheaderbutton.h \ widgets/chatwidgetheaderbutton.h \
@ -94,14 +99,29 @@ HEADERS += account.h \
widgets/settingsdialogtab.h \ widgets/settingsdialogtab.h \
widgets/signallabel.h \ widgets/signallabel.h \
widgets/textinputdialog.h \ widgets/textinputdialog.h \
windows.h \
widgets/resizingtextedit.h \ widgets/resizingtextedit.h \
settingssnapshot.h \ settingssnapshot.h \
messages/limitedqueue.h \ messages/limitedqueue.h \
messages/limitedqueuesnapshot.h \ messages/limitedqueuesnapshot.h \
messages/messageref.h \ messages/messageref.h \
logging/loggingmanager.h \ logging/loggingmanager.h \
logging/loggingchannel.h logging/loggingchannel.h \
channelmanager.h \
windowmanager.h \
settingsmanager.h \
fontmanager.h \
emotemanager.h \
util/urlfetch.h \
usermanager.h \
twitch/twitchuser.h \
messages/messageparseargs.h \
messages/messagebuilder.h \
twitch/twitchmessagebuilder.h \
ircuser2.h \
twitch/twitchparsemessage.h \
widgets/fancybutton.h \
widgets/titlebar.h \
widgets/userpopupwidget.h
PRECOMPILED_HEADER = PRECOMPILED_HEADER =
@ -112,9 +132,29 @@ DISTFILES +=
# Include boost # Include boost
win32 { win32 {
INCLUDEPATH += C:\local\boost\ INCLUDEPATH += C:\local\boost\
}
# Optional dependency on windows sdk 7.1
win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) {
LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \
-ldwmapi \
-lgdi32
SOURCES += platform/borderless/qwinwidget.cpp \
platform/borderless/winnativewindow.cpp \
platform/borderless/widget.cpp
HEADERS += platform/borderless/qwinwidget.h \
platform/borderless/winnativewindow.h \
platform/borderless/widget.h
DEFINES += "USEWINSDK"
} }
macx { macx {
INCLUDEPATH += /usr/local/include INCLUDEPATH += /usr/local/include
} }
FORMS += \
forms/userpopup.ui

View file

@ -7,7 +7,7 @@ Language: Cpp
SpacesBeforeTrailingComments: 2 SpacesBeforeTrailingComments: 2
AccessModifierOffset: -1 AccessModifierOffset: -1
AlignEscapedNewlinesLeft: true AlignEscapedNewlinesLeft: true
AlwaysBreakAfterDefinitionReturnType: true AlwaysBreakAfterDefinitionReturnType: false
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
BreakConstructorInitializersBeforeComma: true BreakConstructorInitializersBeforeComma: true
# BreakBeforeBraces: Linux # BreakBeforeBraces: Linux
@ -26,3 +26,4 @@ BraceWrapping: {
AfterFunction: 'true' AfterFunction: 'true'
BeforeCatch: 'false' BeforeCatch: 'false'
} }
ColumnLimit: 100

View file

@ -1,38 +1,36 @@
#define LOOKUP_COLOR_COUNT 360 #define LOOKUP_COLOR_COUNT 360
#include "colorscheme.h" #include "colorscheme.h"
#include "settings.h" #include "settingsmanager.h"
#include "windows.h" #include "windowmanager.h"
#include <QColor> #include <QColor>
namespace chatterino { namespace chatterino {
void void ColorScheme::init()
ColorScheme::init()
{ {
static bool initiated = false; static bool initiated = false;
if (!initiated) { if (!initiated) {
initiated = true; initiated = true;
ColorScheme::getInstance().update(); ColorScheme::getInstance().update();
Settings::getInstance().theme.valueChanged.connect( SettingsManager::getInstance().theme.valueChanged.connect(
[](const QString &) { ColorScheme::getInstance().update(); }); [](const QString &) { ColorScheme::getInstance().update(); });
Settings::getInstance().themeHue.valueChanged.connect( SettingsManager::getInstance().themeHue.valueChanged.connect(
[](const float &) { ColorScheme::getInstance().update(); }); [](const float &) { ColorScheme::getInstance().update(); });
ColorScheme::getInstance().updated.connect( ColorScheme::getInstance().updated.connect(
[] { Windows::repaintVisibleChatWidgets(); }); [] { WindowManager::repaintVisibleChatWidgets(); });
} }
} }
void void ColorScheme::update()
ColorScheme::update()
{ {
QString theme = Settings::getInstance().theme.get(); QString theme = SettingsManager::getInstance().theme.get();
theme = theme.toLower(); theme = theme.toLower();
qreal hue = Settings::getInstance().themeHue.get(); qreal hue = SettingsManager::getInstance().themeHue.get();
if (theme == "light") { if (theme == "light") {
setColors(hue, 0.8); setColors(hue, 0.8);
@ -47,33 +45,40 @@ ColorScheme::update()
// hue: theme color (0 - 1) // hue: theme color (0 - 1)
// multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black // multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black
void void ColorScheme::setColors(float hue, float multiplyer)
ColorScheme::setColors(float hue, float multiplyer)
{ {
IsLightTheme = multiplyer > 0; IsLightTheme = multiplyer > 0;
bool hasDarkBorder = false;
SystemMessageColor = QColor(140, 127, 127); SystemMessageColor = QColor(140, 127, 127);
auto isLightTheme = IsLightTheme; auto isLightTheme = IsLightTheme;
auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, qreal a = 1.0) {
qreal a = 1.0) {
return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a); return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a);
}; };
DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.3); DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.6);
Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255); Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255);
// tab // tab
TabPanelBackground = QColor(255, 255, 255); if (hasDarkBorder) {
TabBackground = QColor(255, 255, 255); // TabPanelBackground = getColor(hue, 0, 0.8);
TabHoverBackground = getColor(hue, 0, 0.05); // TabBackground = getColor(hue, 0, 0.8);
// TabHoverBackground = getColor(hue, 0, 0.8);
} else {
TabPanelBackground = QColor(255, 255, 255);
TabBackground = QColor(255, 255, 255);
TabHoverBackground = getColor(hue, 0, 0.05);
}
TabSelectedBackground = getColor(hue, 0.5, 0.5); TabSelectedBackground = getColor(hue, 0.5, 0.5);
TabHighlightedBackground = getColor(hue, 0.5, 0.2); TabHighlightedBackground = getColor(hue, 0.5, 0.2);
TabNewMessageBackground = TabNewMessageBackground = QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern);
QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern); if (hasDarkBorder)
TabText = QColor(0, 0, 0); // TabText = QColor(210, 210, 210);
// TabHoverText = QColor(210, 210, 210);
TabText = QColor(0, 0, 0);
TabHoverText = QColor(0, 0, 0); TabHoverText = QColor(0, 0, 0);
TabSelectedText = QColor(255, 255, 255); TabSelectedText = QColor(255, 255, 255);
TabHighlightedText = QColor(0, 0, 0); TabHighlightedText = QColor(0, 0, 0);
@ -103,17 +108,14 @@ ColorScheme::setColors(float hue, float multiplyer)
fillLookupTableValues(this->minLookupTable, 0.833, 1.000, 0.30, 0.33); fillLookupTableValues(this->minLookupTable, 0.833, 1.000, 0.30, 0.33);
// stylesheet // stylesheet
InputStyleSheet = InputStyleSheet = "background:" + ChatInputBackground.name() + ";" +
"background:" + ChatInputBackground.name() + ";" + "border:" + TabSelectedBackground.name() + ";" + "color:" + Text.name() +
"border:" + TabSelectedBackground.name() + ";" + ";" + "selection-background-color:" + TabSelectedBackground.name();
"color:" + Text.name() + ";" +
"selection-background-color:" + TabSelectedBackground.name();
updated(); updated();
} }
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
qreal to, qreal fromValue,
qreal toValue) qreal toValue)
{ {
qreal diff = toValue - fromValue; qreal diff = toValue - fromValue;
@ -127,8 +129,7 @@ void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from,
} }
} }
void void ColorScheme::normalizeColor(QColor &color)
ColorScheme::normalizeColor(QColor &color)
{ {
// qreal l = color.lightnessF(); // qreal l = color.lightnessF();
// qreal s = color.saturationF(); // qreal s = color.saturationF();

View file

@ -61,8 +61,7 @@ public:
const int HighlightColorCount = 3; const int HighlightColorCount = 3;
QColor HighlightColors[3]; QColor HighlightColors[3];
static ColorScheme & static ColorScheme &getInstance()
getInstance()
{ {
static ColorScheme instance; static ColorScheme instance;
@ -87,8 +86,8 @@ private:
qreal middleLookupTable[360] = {}; qreal middleLookupTable[360] = {};
qreal minLookupTable[360] = {}; qreal minLookupTable[360] = {};
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue,
qreal fromValue, qreal toValue); qreal toValue);
}; };
} }

View file

@ -14,17 +14,16 @@ class ConcurrentMap
{ {
public: public:
ConcurrentMap() ConcurrentMap()
: map() : _map()
{ {
} }
bool bool tryGet(const TKey &name, TValue &value) const
tryGet(const TKey &name, TValue &value) const
{ {
QMutexLocker lock(&this->mutex); QMutexLocker lock(&_mutex);
auto a = map.find(name); auto a = _map.find(name);
if (a == map.end()) { if (a == _map.end()) {
return false; return false;
} }
@ -33,40 +32,37 @@ public:
return true; return true;
} }
TValue TValue getOrAdd(const TKey &name, std::function<TValue()> addLambda)
getOrAdd(const TKey &name, std::function<TValue()> addLambda)
{ {
QMutexLocker lock(&this->mutex); QMutexLocker lock(&_mutex);
auto a = map.find(name); auto a = _map.find(name);
if (a == map.end()) { if (a == _map.end()) {
TValue value = addLambda(); TValue value = addLambda();
map.insert(name, value); _map.insert(name, value);
return value; return value;
} }
return a.value(); return a.value();
} }
void void clear()
clear()
{ {
QMutexLocker lock(&this->mutex); QMutexLocker lock(&_mutex);
map.clear(); _map.clear();
} }
void void insert(const TKey &name, const TValue &value)
insert(const TKey &name, const TValue &value)
{ {
QMutexLocker lock(&this->mutex); QMutexLocker lock(&_mutex);
map.insert(name, value); _map.insert(name, value);
} }
private: private:
mutable QMutex mutex; mutable QMutex _mutex;
QMap<TKey, TValue> map; QMap<TKey, TValue> _map;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -1,5 +1,5 @@
#include "emojis.h" #include "emojis.h"
#include "emotes.h" #include "emotemanager.h"
#include <QFile> #include <QFile>
#include <QStringBuilder> #include <QStringBuilder>
@ -15,17 +15,14 @@ QMap<QChar, QMap<QString, QString>> Emojis::firstEmojiChars;
ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache; ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache;
QString QString Emojis::replaceShortCodes(const QString &text)
Emojis::replaceShortCodes(const QString &text)
{ {
// TODO: Implement this xD // TODO: Implement this xD
return text; return text;
} }
void void Emojis::parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
Emojis::parseEmojis( const QString &text)
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
const QString &text)
{ {
long lastSlice = 0; long lastSlice = 0;
@ -44,21 +41,14 @@ Emojis::parseEmojis(
emojiIter.value() + ".png"; emojiIter.value() + ".png";
if (i - lastSlice != 0) { if (i - lastSlice != 0) {
vector.push_back( vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
std::tuple<messages::LazyLoadedImage *, NULL, text.mid(lastSlice, i - lastSlice)));
QString>(
NULL, text.mid(lastSlice, i - lastSlice)));
} }
vector.push_back( vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
std::tuple<messages::LazyLoadedImage *, QString>( imageCache.getOrAdd(
imageCache.getOrAdd( url, [&url] { return new messages::LazyLoadedImage(url, 0.35); }),
url, QString()));
[&url] {
return new messages::LazyLoadedImage(
url, 0.35);
}),
QString()));
i += j - 1; i += j - 1;
@ -72,13 +62,12 @@ Emojis::parseEmojis(
} }
if (lastSlice < text.length()) { if (lastSlice < text.length()) {
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>( vector.push_back(
NULL, text.mid(lastSlice))); std::tuple<messages::LazyLoadedImage *, QString>(NULL, text.mid(lastSlice)));
} }
} }
void void Emojis::loadEmojis()
Emojis::loadEmojis()
{ {
QFile file(":/emojidata.txt"); QFile file(":/emojidata.txt");
file.open(QFile::ReadOnly); file.open(QFile::ReadOnly);
@ -106,8 +95,7 @@ Emojis::loadEmojis()
emotes[i++] = QString(item).toUInt(nullptr, 16); emotes[i++] = QString(item).toUInt(nullptr, 16);
} }
shortCodeToEmoji.insert( shortCodeToEmoji.insert(a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
} }
for (auto const &emoji : shortCodeToEmoji.toStdMap()) { for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
@ -122,9 +110,8 @@ Emojis::loadEmojis()
continue; continue;
} }
firstEmojiChars.insert( firstEmojiChars.insert(emoji.first.at(0),
emoji.first.at(0), QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
} }
} }
} }

View file

@ -14,9 +14,8 @@ namespace chatterino {
class Emojis class Emojis
{ {
public: public:
static void parseEmojis( static void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector, const QString &text);
const QString &text);
static void loadEmojis(); static void loadEmojis();

View file

@ -1,4 +1,4 @@
#include "emotes.h" #include "emotemanager.h"
#include "resources.h" #include "resources.h"
#include <QDebug> #include <QDebug>
@ -11,41 +11,77 @@
#include <QNetworkRequest> #include <QNetworkRequest>
#include <memory> #include <memory>
#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0"
using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
QString Emotes::twitchEmoteTemplate( EmoteManager EmoteManager::instance;
"https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0");
ConcurrentMap<QString, TwitchEmoteValue *> Emotes::twitchEmotes; EmoteManager::EmoteManager()
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::bttvEmotes; : _twitchEmotes()
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::ffzEmotes; , _bttvEmotes()
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::chatterinoEmotes; , _ffzEmotes()
ConcurrentMap<QString, messages::LazyLoadedImage *> , _chatterinoEmotes()
Emotes::bttvChannelEmoteFromCaches; , _bttvChannelEmoteFromCaches()
ConcurrentMap<int, messages::LazyLoadedImage *> , _ffzChannelEmoteFromCaches()
Emotes::ffzChannelEmoteFromCaches; , _twitchEmoteFromCache()
ConcurrentMap<long, messages::LazyLoadedImage *> Emotes::twitchEmoteFromCache; , _miscImageFromCache()
ConcurrentMap<QString, messages::LazyLoadedImage *> Emotes::miscImageFromCache; , _gifUpdateTimerSignal()
boost::signals2::signal<void()> Emotes::gifUpdateTimerSignal; , _gifUpdateTimer()
, _gifUpdateTimerInitiated(false)
QTimer Emotes::gifUpdateTimer; , _generation(0)
bool Emotes::gifUpdateTimerInitiated(false);
int Emotes::generation = 0;
Emotes::Emotes()
{ {
} }
void ConcurrentMap<QString, twitch::EmoteValue *> &EmoteManager::getTwitchEmotes()
Emotes::loadGlobalEmotes() {
return _twitchEmotes;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getBttvEmotes()
{
return _bttvEmotes;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getFfzEmotes()
{
return _ffzEmotes;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getChatterinoEmotes()
{
return _chatterinoEmotes;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getBttvChannelEmoteFromCaches()
{
return _bttvChannelEmoteFromCaches;
}
ConcurrentMap<int, messages::LazyLoadedImage *> &EmoteManager::getFfzChannelEmoteFromCaches()
{
return _ffzChannelEmoteFromCaches;
}
ConcurrentMap<long, messages::LazyLoadedImage *> &EmoteManager::getTwitchEmoteFromCache()
{
return _twitchEmoteFromCache;
}
ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getMiscImageFromCache()
{
return _miscImageFromCache;
}
void EmoteManager::loadGlobalEmotes()
{ {
loadBttvEmotes(); loadBttvEmotes();
loadFfzEmotes(); loadFfzEmotes();
} }
void void EmoteManager::loadBttvEmotes()
Emotes::loadBttvEmotes()
{ {
// bttv // bttv
QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
@ -63,21 +99,19 @@ Emotes::loadBttvEmotes()
auto emotes = root.value("emotes").toArray(); auto emotes = root.value("emotes").toArray();
QString _template = "https:" + root.value("urlTemplate").toString(); QString linkTemplate = "https:" + root.value("urlTemplate").toString();
for (const QJsonValue &emote : emotes) { for (const QJsonValue &emote : emotes) {
QString id = emote.toObject().value("id").toString(); QString id = emote.toObject().value("id").toString();
QString code = emote.toObject().value("code").toString(); QString code = emote.toObject().value("code").toString();
// emote.value("imageType").toString(); // emote.value("imageType").toString();
QString tmp = _template; QString tmp = linkTemplate;
tmp.detach(); tmp.detach();
QString url = QString url = tmp.replace("{{id}}", id).replace("{{image}}", "1x");
tmp.replace("{{id}}", id).replace("{{image}}", "1x");
Emotes::getBttvEmotes().insert( EmoteManager::getBttvEmotes().insert(
code, new messages::LazyLoadedImage( code, new LazyLoadedImage(url, 1, code, code + "\nGlobal Bttv Emote"));
url, 1, code, code + "\nGlobal Bttv Emote"));
} }
} }
@ -86,8 +120,7 @@ Emotes::loadBttvEmotes()
}); });
} }
void void EmoteManager::loadFfzEmotes()
Emotes::loadFfzEmotes()
{ {
// ffz // ffz
QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
@ -113,15 +146,14 @@ Emotes::loadFfzEmotes()
// margins // margins
int id = object.value("id").toInt(); // int id = object.value("id").toInt();
QString code = object.value("name").toString(); QString code = object.value("name").toString();
QJsonObject urls = object.value("urls").toObject(); QJsonObject urls = object.value("urls").toObject();
QString url1 = "http:" + urls.value("1").toString(); QString url1 = "http:" + urls.value("1").toString();
Emotes::getBttvEmotes().insert( EmoteManager::getBttvEmotes().insert(
code, new messages::LazyLoadedImage( code, new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote"));
url1, 1, code, code + "\nGlobal Ffz Emote"));
} }
} }
} }
@ -131,41 +163,34 @@ Emotes::loadFfzEmotes()
}); });
} }
messages::LazyLoadedImage * LazyLoadedImage *EmoteManager::getTwitchEmoteById(const QString &name, long id)
Emotes::getTwitchEmoteById(const QString &name, long id)
{ {
qDebug() << "loading twitch emote: " << id; return EmoteManager::_twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
qDebug() << "added twitch emote: " << id;
return Emotes::twitchEmoteFromCache.getOrAdd(id, [&name, &id] {
qDebug() << "loading twitch emote: " << id;
qreal scale; qreal scale;
QString url = getTwitchEmoteLink(id, scale); QString url = getTwitchEmoteLink(id, scale);
return new messages::LazyLoadedImage(url, scale, name, return new LazyLoadedImage(url, scale, name, name + "\nTwitch Emote");
name + "\nTwitch Emote");
}); });
} }
QString QString EmoteManager::getTwitchEmoteLink(long id, qreal &scale)
Emotes::getTwitchEmoteLink(long id, qreal &scale)
{ {
scale = .5; scale = .5;
QString value = Emotes::twitchEmoteTemplate; QString value = TWITCH_EMOTE_TEMPLATE;
value.detach(); value.detach();
return value.replace("{id}", QString::number(id)).replace("{scale}", "2"); return value.replace("{id}", QString::number(id)).replace("{scale}", "2");
} }
messages::LazyLoadedImage * LazyLoadedImage *EmoteManager::getCheerImage(long long amount, bool animated)
Emotes::getCheerImage(long long amount, bool animated)
{ {
// TODO: fix this xD // TODO: fix this xD
return getCheerBadge(amount); return getCheerBadge(amount);
} }
messages::LazyLoadedImage * LazyLoadedImage *EmoteManager::getCheerBadge(long long amount)
Emotes::getCheerBadge(long long amount)
{ {
if (amount >= 100000) { if (amount >= 100000) {
return Resources::getCheerBadge100000(); return Resources::getCheerBadge100000();

98
emotemanager.h Normal file
View 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
View file

@ -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
View 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
View 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

View file

@ -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
View file

@ -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
View 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>

View file

@ -1,73 +1,89 @@
#include "ircmanager.h" #include "ircmanager.h"
#include "asyncexec.h" #include "asyncexec.h"
#include "channel.h" #include "channel.h"
#include "channels.h" #include "channelmanager.h"
#include "messages/messageparseargs.h"
#include "twitch/twitchmessagebuilder.h"
#include "twitch/twitchparsemessage.h"
#include "twitch/twitchuser.h"
#include "usermanager.h"
#include <irccommand.h> #include <irccommand.h>
#include <ircconnection.h> #include <ircconnection.h>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QNetworkReply> #include <QNetworkReply>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <future> #include <future>
using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
Account *IrcManager::account = nullptr; IrcManager IrcManager::instance;
std::shared_ptr<IrcConnection> IrcManager::connection;
QMutex IrcManager::connectionMutex;
long IrcManager::connectionGeneration = 0;
const QString IrcManager::defaultClientId = "7ue61iz46fz11y3cugd0l3tawb4taal";
QNetworkAccessManager IrcManager::accessManager;
QMap<QString, bool> IrcManager::twitchBlockedUsers; const QString IrcManager::defaultClientId("7ue61iz46fz11y3cugd0l3tawb4taal");
QMutex IrcManager::twitchBlockedUsersMutex;
IrcManager::IrcManager() IrcManager::IrcManager()
: _account(AccountManager::getInstance().getAnon())
, _connection()
, _connectionMutex()
, _connectionGeneration(0)
, _twitchBlockedUsers()
, _twitchBlockedUsersMutex()
, _accessManager()
{ {
} }
void const twitch::TwitchUser &IrcManager::getUser() const
IrcManager::connect() {
return _account;
}
void IrcManager::setUser(const twitch::TwitchUser &account)
{
_account = account;
}
void IrcManager::connect()
{ {
disconnect(); disconnect();
async_exec([] { beginConnecting(); }); async_exec([this] { beginConnecting(); });
} }
void void IrcManager::beginConnecting()
IrcManager::beginConnecting()
{ {
IrcManager::account = const_cast<Account *>(Account::getAnon()); int generation = ++IrcManager::_connectionGeneration;
int generation = ++IrcManager::connectionGeneration; Communi::IrcConnection *c = new Communi::IrcConnection;
auto c = new IrcConnection(); QObject::connect(c, &Communi::IrcConnection::messageReceived, this,
&IrcManager::messageReceived);
QObject::connect(c, &Communi::IrcConnection::privateMessageReceived, this,
&IrcManager::privateMessageReceived);
QObject::connect(c, &IrcConnection::messageReceived, &messageReceived); if (_account.isAnon()) {
QObject::connect(c, &IrcConnection::privateMessageReceived,
&privateMessageReceived);
if (account->isAnon()) {
// fetch ignored users // fetch ignored users
QString username = account->getUsername(); QString username = _account.getUserName();
QString oauthClient = account->getOauthClient(); QString oauthClient = _account.getOAuthClient();
QString oauthToken = account->getOauthToken(); QString oauthToken = _account.getOAuthToken();
{ {
QString nextLink = "https://api.twitch.tv/kraken/users/" + QString nextLink = "https://api.twitch.tv/kraken/users/" + username +
username + "/blocks?limit=" + 100 + "/blocks?limit=" + 100 + "&client_id=" + oauthClient;
"&client_id=" + oauthClient;
QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken)); QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken));
QNetworkReply *reply = manager->get(req); QNetworkReply *reply = manager->get(req);
QObject::connect(reply, &QNetworkReply::finished, [=] { QObject::connect(reply, &QNetworkReply::finished, [=] {
IrcManager::twitchBlockedUsersMutex.lock(); _twitchBlockedUsersMutex.lock();
IrcManager::twitchBlockedUsers.clear(); _twitchBlockedUsers.clear();
IrcManager::twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
@ -78,15 +94,13 @@ IrcManager::beginConnecting()
auto blocks = root.value("blocks").toArray(); auto blocks = root.value("blocks").toArray();
IrcManager::twitchBlockedUsersMutex.lock(); _twitchBlockedUsersMutex.lock();
for (QJsonValue block : blocks) { for (QJsonValue block : blocks) {
QJsonObject user = QJsonObject user = block.toObject().value("user").toObject();
block.toObject().value("user").toObject();
// display_name // display_name
IrcManager::twitchBlockedUsers.insert( _twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
user.value("name").toString().toLower(), true);
} }
IrcManager::twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
manager->deleteLater(); manager->deleteLater();
}); });
@ -94,10 +108,10 @@ IrcManager::beginConnecting()
// fetch available twitch emtoes // fetch available twitch emtoes
{ {
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username +
username + "/emotes?oauth_token=" + "/emotes?oauth_token=" + oauthToken +
oauthToken + "&client_id=" + oauthClient)); "&client_id=" + oauthClient));
QNetworkReply *reply = IrcManager::accessManager.get(req); QNetworkReply *reply = _accessManager.get(req);
QObject::connect(reply, &QNetworkReply::finished, [=] { QObject::connect(reply, &QNetworkReply::finished, [=] {
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
@ -109,15 +123,13 @@ IrcManager::beginConnecting()
auto blocks = root.value("blocks").toArray(); auto blocks = root.value("blocks").toArray();
IrcManager::twitchBlockedUsersMutex.lock(); _twitchBlockedUsersMutex.lock();
for (QJsonValue block : blocks) { for (QJsonValue block : blocks) {
QJsonObject user = QJsonObject user = block.toObject().value("user").toObject();
block.toObject().value("user").toObject();
// display_name // display_name
IrcManager::twitchBlockedUsers.insert( _twitchBlockedUsers.insert(user.value("name").toString().toLower(), true);
user.value("name").toString().toLower(), true);
} }
IrcManager::twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
}); });
} }
} }
@ -129,19 +141,17 @@ IrcManager::beginConnecting()
c->setNickName("justinfan123"); c->setNickName("justinfan123");
c->setRealName("justinfan123"); c->setRealName("justinfan123");
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands")); c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags")); c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
QMutexLocker locker(&IrcManager::connectionMutex); QMutexLocker locker(&_connectionMutex);
if (generation == IrcManager::connectionGeneration) { if (generation == _connectionGeneration) {
c->moveToThread(QCoreApplication::instance()->thread()); c->moveToThread(QCoreApplication::instance()->thread());
IrcManager::connection = std::shared_ptr<IrcConnection>(c); _connection = std::shared_ptr<Communi::IrcConnection>(c);
auto channels = Channels::getItems(); for (auto &channel : ChannelManager::getInstance().getItems()) {
c->sendRaw("JOIN #" + channel->getName());
for (auto &channel : channels) {
c->sendRaw("JOIN #" + channel.get()->getName());
} }
c->open(); c->open();
@ -150,53 +160,50 @@ IrcManager::beginConnecting()
} }
} }
void void IrcManager::disconnect()
IrcManager::disconnect()
{ {
IrcManager::connectionMutex.lock(); _connectionMutex.lock();
auto c = IrcManager::connection; auto c = _connection;
if (IrcManager::connection.get() != NULL) { if (_connection.get() != NULL) {
IrcManager::connection = std::shared_ptr<IrcConnection>(); _connection = std::shared_ptr<Communi::IrcConnection>();
} }
IrcManager::connectionMutex.unlock(); _connectionMutex.unlock();
} }
void void IrcManager::send(QString raw)
IrcManager::send(QString raw)
{ {
IrcManager::connectionMutex.lock(); _connectionMutex.lock();
IrcManager::connection->sendRaw(raw);
IrcManager::connectionMutex.unlock(); _connection->sendRaw(raw);
_connectionMutex.unlock();
} }
void void IrcManager::sendJoin(const QString &channel)
IrcManager::sendJoin(const QString &channel)
{ {
IrcManager::connectionMutex.lock(); _connectionMutex.lock();
if (IrcManager::connection.get() != NULL) { if (_connection.get() != NULL) {
IrcManager::connection.get()->sendRaw("JOIN #" + channel); _connection.get()->sendRaw("JOIN #" + channel);
} }
IrcManager::connectionMutex.unlock(); _connectionMutex.unlock();
} }
void void IrcManager::partChannel(const QString &channel)
IrcManager::partChannel(const QString &channel)
{ {
IrcManager::connectionMutex.lock(); _connectionMutex.lock();
if (IrcManager::connection.get() != NULL) { if (_connection.get() != NULL) {
IrcManager::connection.get()->sendRaw("PART #" + channel); _connection.get()->sendRaw("PART #" + channel);
} }
IrcManager::connectionMutex.unlock(); _connectionMutex.unlock();
} }
void void IrcManager::messageReceived(Communi::IrcMessage *message)
IrcManager::messageReceived(IrcMessage *message)
{ {
// qInfo(message->command().toStdString().c_str()); // qInfo(message->command().toStdString().c_str());
@ -211,63 +218,51 @@ IrcManager::messageReceived(IrcMessage *message)
// } // }
} }
void void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
IrcManager::privateMessageReceived(IrcPrivateMessage *message)
{ {
// qInfo(message->content().toStdString().c_str()); auto c = ChannelManager::getInstance().getChannel(message->target().mid(1));
auto c = Channels::getChannel(message->target().mid(1));
if (c != NULL) { if (c != NULL) {
c->addMessage(std::shared_ptr<messages::Message>( messages::MessageParseArgs args;
new messages::Message(*message, *c)));
c->addMessage(twitch::TwitchMessageBuilder::parse(message, c.get(), args));
} }
} }
bool bool IrcManager::isTwitchBlockedUser(QString const &username)
IrcManager::isTwitchBlockedUser(QString const &username)
{ {
IrcManager::twitchBlockedUsersMutex.lock(); QMutexLocker locker(&_twitchBlockedUsersMutex);
auto iterator = IrcManager::twitchBlockedUsers.find(username); auto iterator = _twitchBlockedUsers.find(username);
if (iterator == IrcManager::twitchBlockedUsers.end()) { return iterator != _twitchBlockedUsers.end();
IrcManager::twitchBlockedUsersMutex.unlock();
return false;
}
IrcManager::twitchBlockedUsersMutex.unlock();
return true;
} }
bool bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage)
{ {
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() + QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
"/blocks/" + username + username + "?oauth_token=" + _account.getOAuthToken() +
"?oauth_token=" + account->getOauthToken() + "&client_id=" + _account.getOAuthClient());
"&client_id=" + account->getOauthClient());
QNetworkRequest request(url); QNetworkRequest request(url);
auto reply = IrcManager::accessManager.put(request, QByteArray()); auto reply = _accessManager.put(request, QByteArray());
reply->waitForReadyRead(10000); reply->waitForReadyRead(10000);
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
IrcManager::twitchBlockedUsersMutex.lock(); _twitchBlockedUsersMutex.lock();
IrcManager::twitchBlockedUsers.insert(username, true); _twitchBlockedUsers.insert(username, true);
IrcManager::twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
delete reply;
return true; return true;
} }
errorMessage = "Error while ignoring user \"" + username + reply->deleteLater();
"\": " + reply->errorString();
errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString();
return false; return false;
} }
void void IrcManager::addIgnoredUser(QString const &username)
IrcManager::addIgnoredUser(QString const &username)
{ {
QString errorMessage; QString errorMessage;
if (!tryAddIgnoredUser(username, errorMessage)) { if (!tryAddIgnoredUser(username, errorMessage)) {
@ -275,38 +270,40 @@ IrcManager::addIgnoredUser(QString const &username)
} }
} }
bool bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage)
{ {
QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() + QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" +
"/blocks/" + username + username + "?oauth_token=" + _account.getOAuthToken() +
"?oauth_token=" + account->getOauthToken() + "&client_id=" + _account.getOAuthClient());
"&client_id=" + account->getOauthClient());
QNetworkRequest request(url); QNetworkRequest request(url);
auto reply = IrcManager::accessManager.deleteResource(request); auto reply = _accessManager.deleteResource(request);
reply->waitForReadyRead(10000); reply->waitForReadyRead(10000);
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
IrcManager::twitchBlockedUsersMutex.lock(); _twitchBlockedUsersMutex.lock();
IrcManager::twitchBlockedUsers.remove(username); _twitchBlockedUsers.remove(username);
IrcManager::twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
delete reply;
return true; return true;
} }
errorMessage = "Error while unignoring user \"" + username + reply->deleteLater();
"\": " + reply->errorString();
errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString();
return false; return false;
} }
void void IrcManager::removeIgnoredUser(QString const &username)
IrcManager::removeIgnoredUser(QString const &username)
{ {
QString errorMessage; QString errorMessage;
if (!tryRemoveIgnoredUser(username, errorMessage)) { if (!tryRemoveIgnoredUser(username, errorMessage)) {
// TODO: Implement IrcManager::removeIgnoredUser // TODO: Implement IrcManager::removeIgnoredUser
} }
} }
QNetworkAccessManager &IrcManager::getAccessManager()
{
return _accessManager;
}
} }

View file

@ -3,8 +3,8 @@
#define TWITCH_MAX_MESSAGELENGTH 500 #define TWITCH_MAX_MESSAGELENGTH 500
#include "account.h"
#include "messages/message.h" #include "messages/message.h"
#include "twitch/twitchuser.h"
#include <IrcMessage> #include <IrcMessage>
#include <QMap> #include <QMap>
@ -16,50 +16,59 @@
namespace chatterino { namespace chatterino {
class IrcManager class IrcManager : public QObject
{ {
public: Q_OBJECT
static void connect();
static void disconnect();
static void send(QString raw); public:
static IrcManager &getInstance()
{
return instance;
}
static const QString defaultClientId; static const QString defaultClientId;
void connect();
void disconnect();
void send(QString raw);
bool isTwitchBlockedUser(QString const &username); bool isTwitchBlockedUser(QString const &username);
bool tryAddIgnoredUser(QString const &username, QString &errorMessage); bool tryAddIgnoredUser(QString const &username, QString &errorMessage);
void addIgnoredUser(QString const &username); void addIgnoredUser(QString const &username);
bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage); bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage);
void removeIgnoredUser(QString const &username); void removeIgnoredUser(QString const &username);
static Account *account; QNetworkAccessManager &getAccessManager();
static QNetworkAccessManager & void sendJoin(const QString &channel);
getAccessManager()
{
return accessManager;
}
static void sendJoin(const QString &channel); void partChannel(const QString &channel);
static void partChannel(const QString &channel); const twitch::TwitchUser &getUser() const;
void setUser(const twitch::TwitchUser &account);
private: private:
static IrcManager instance;
IrcManager(); IrcManager();
static QMap<QString, bool> twitchBlockedUsers; // variables
static QMutex twitchBlockedUsersMutex; twitch::TwitchUser _account;
static QNetworkAccessManager accessManager; std::shared_ptr<Communi::IrcConnection> _connection;
QMutex _connectionMutex;
long _connectionGeneration;
static void beginConnecting(); QMap<QString, bool> _twitchBlockedUsers;
QMutex _twitchBlockedUsersMutex;
static std::shared_ptr<IrcConnection> connection; QNetworkAccessManager _accessManager;
static QMutex connectionMutex;
static long connectionGeneration;
static void messageReceived(IrcMessage *message); // methods
static void privateMessageReceived(IrcPrivateMessage *message); void beginConnecting();
void messageReceived(Communi::IrcMessage *message);
void privateMessageReceived(Communi::IrcPrivateMessage *message);
}; };
} }

33
ircuser2.cpp Normal file
View 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
View 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

View file

@ -14,12 +14,10 @@ Channel::Channel(const QString &_channelName, const QString &_baseDirectory)
{ {
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
this->fileName = this->fileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log";
// Open file handle to log file of current date // Open file handle to log file of current date
this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileName);
this->fileName);
this->fileHandle.open(QIODevice::Append); this->fileHandle.open(QIODevice::Append);
this->appendLine(this->generateOpeningString(now)); this->appendLine(this->generateOpeningString(now));
@ -31,8 +29,7 @@ Channel::~Channel()
this->fileHandle.close(); this->fileHandle.close();
} }
void void Channel::append(std::shared_ptr<messages::Message> message)
Channel::append(std::shared_ptr<messages::Message> message)
{ {
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
@ -47,8 +44,7 @@ Channel::append(std::shared_ptr<messages::Message> message)
this->appendLine(str); this->appendLine(str);
} }
QString QString Channel::generateOpeningString(const QDateTime &now) const
Channel::generateOpeningString(const QDateTime &now) const
{ {
QString ret = QLatin1Literal("# Start logging at "); QString ret = QLatin1Literal("# Start logging at ");
@ -59,8 +55,7 @@ Channel::generateOpeningString(const QDateTime &now) const
return ret; return ret;
} }
QString QString Channel::generateClosingString(const QDateTime &now) const
Channel::generateClosingString(const QDateTime &now) const
{ {
QString ret = QLatin1Literal("# Stop logging at "); QString ret = QLatin1Literal("# Stop logging at ");
@ -71,8 +66,7 @@ Channel::generateClosingString(const QDateTime &now) const
return ret; return ret;
} }
void void Channel::appendLine(const QString &line)
Channel::appendLine(const QString &line)
{ {
this->fileHandle.write(line.toUtf8()); this->fileHandle.write(line.toUtf8());
this->fileHandle.flush(); this->fileHandle.flush();

View file

@ -15,17 +15,14 @@ namespace logging {
class Channel class Channel
{ {
public: public:
explicit Channel(const QString &_channelName, explicit Channel(const QString &_channelName, const QString &_baseDirectory);
const QString &_baseDirectory);
~Channel(); ~Channel();
void append(std::shared_ptr<messages::Message> message); void append(std::shared_ptr<messages::Message> message);
private: private:
QString generateOpeningString( QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const;
const QDateTime &now = QDateTime::currentDateTime()) const; QString generateClosingString(const QDateTime &now = QDateTime::currentDateTime()) const;
QString generateClosingString(
const QDateTime &now = QDateTime::currentDateTime()) const;
void appendLine(const QString &line); void appendLine(const QString &line);

View file

@ -15,13 +15,11 @@ static QString mentionsBasePath;
std::unordered_map<std::string, std::weak_ptr<Channel>> channels; std::unordered_map<std::string, std::weak_ptr<Channel>> channels;
void void init()
init()
{ {
// Make sure all folders are properly created // Make sure all folders are properly created
logBasePath = logBasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + "Logs";
QDir::separator() + "Logs";
channelBasePath = logBasePath + QDir::separator() + "Channels"; channelBasePath = logBasePath + QDir::separator() + "Channels";
whispersBasePath = logBasePath + QDir::separator() + "Whispers"; whispersBasePath = logBasePath + QDir::separator() + "Whispers";
mentionsBasePath = logBasePath + QDir::separator() + "Mentions"; mentionsBasePath = logBasePath + QDir::separator() + "Mentions";
@ -55,8 +53,7 @@ init()
} }
} }
static const QString & static const QString &getBaseDirectory(const QString &channelName)
getBaseDirectory(const QString &channelName)
{ {
if (channelName == "/whispers") { if (channelName == "/whispers") {
return whispersBasePath; return whispersBasePath;
@ -67,8 +64,7 @@ getBaseDirectory(const QString &channelName)
} }
} }
std::shared_ptr<Channel> std::shared_ptr<Channel> get(const QString &channelName)
get(const QString &channelName)
{ {
if (channelName.isEmpty()) { if (channelName.isEmpty()) {
return nullptr; return nullptr;

View file

@ -1,13 +1,13 @@
#include "channels.h" #include "channelmanager.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "emojis.h" #include "emojis.h"
#include "emotes.h" #include "emotemanager.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "logging/loggingmanager.h" #include "logging/loggingmanager.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settingsmanager.h"
#include "widgets/mainwindow.h" #include "widgets/mainwindow.h"
#include "windows.h" #include "windowmanager.h"
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
@ -22,26 +22,25 @@ main(int argc, char *argv[])
QApplication a(argc, argv); QApplication a(argc, argv);
chatterino::logging::init(); chatterino::logging::init();
chatterino::initChannels(); SettingsManager::getInstance().load();
Settings::getInstance().load();
Resources::load(); Resources::load();
Emojis::loadEmojis(); Emojis::loadEmojis();
Emotes::loadGlobalEmotes(); EmoteManager::getInstance().loadGlobalEmotes();
ColorScheme::getInstance().init(); ColorScheme::getInstance().init();
Windows::load(); WindowManager::load();
MainWindow &w = Windows::getMainWindow(); MainWindow &w = WindowManager::getMainWindow();
w.show(); w.show();
IrcManager::connect(); IrcManager::getInstance().connect();
int ret = a.exec(); int ret = a.exec();
Settings::getInstance().save(); SettingsManager::getInstance().save();
Windows::save(); WindowManager::save();
return ret; return ret;
} }

View file

@ -1,9 +1,9 @@
#include "messages/lazyloadedimage.h" #include "messages/lazyloadedimage.h"
#include "asyncexec.h" #include "asyncexec.h"
#include "emotes.h" #include "emotemanager.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "windows.h" #include "windowmanager.h"
#include <QBuffer> #include <QBuffer>
#include <QImageReader> #include <QImageReader>
@ -16,9 +16,8 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name,
const QString &name, const QString &tooltip, const QString &tooltip, const QMargins &margin, bool isHat)
const QMargins &margin, bool isHat)
: currentPixmap(NULL) : currentPixmap(NULL)
, allFrames() , allFrames()
, currentFrame(0) , currentFrame(0)
@ -34,9 +33,8 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale,
{ {
} }
LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name,
const QString &name, const QString &tooltip, const QString &tooltip, const QMargins &margin, bool isHat)
const QMargins &margin, bool isHat)
: currentPixmap(image) : currentPixmap(image)
, allFrames() , allFrames()
, currentFrame(0) , currentFrame(0)
@ -52,8 +50,7 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale,
{ {
} }
void void LazyLoadedImage::loadImage()
LazyLoadedImage::loadImage()
{ {
QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkAccessManager *manager = new QNetworkAccessManager();
@ -92,29 +89,25 @@ LazyLoadedImage::loadImage()
if (allFrames.size() > 1) { if (allFrames.size() > 1) {
this->animated = true; this->animated = true;
Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); }); EmoteManager::getInstance().getGifUpdateSignal().connect([this] { gifUpdateTimout(); });
} }
Emotes::incGeneration(); EmoteManager::getInstance().incGeneration();
Windows::layoutVisibleChatWidgets(); WindowManager::layoutVisibleChatWidgets();
reply->deleteLater(); reply->deleteLater();
manager->deleteLater(); manager->deleteLater();
}); });
} }
void void LazyLoadedImage::gifUpdateTimout()
LazyLoadedImage::gifUpdateTimout()
{ {
this->currentFrameOffset += GIF_FRAME_LENGTH; this->currentFrameOffset += GIF_FRAME_LENGTH;
while (true) { while (true) {
if (this->currentFrameOffset > if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) {
this->allFrames.at(this->currentFrame).duration) { this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration;
this->currentFrameOffset -= this->currentFrame = (this->currentFrame + 1) % this->allFrames.size();
this->allFrames.at(this->currentFrame).duration;
this->currentFrame =
(this->currentFrame + 1) % this->allFrames.size();
} else { } else {
break; break;
} }

View file

@ -10,19 +10,14 @@ namespace messages {
class LazyLoadedImage : QObject class LazyLoadedImage : QObject
{ {
public: public:
explicit LazyLoadedImage(const QString &url, qreal scale = 1, explicit LazyLoadedImage(const QString &url, qreal scale = 1, const QString &name = "",
const QString &name = "", const QString &tooltip = "", const QMargins &margin = QMargins(),
const QString &tooltip = "",
const QMargins &margin = QMargins(),
bool isHat = false); bool isHat = false);
explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, const QString &name = "",
const QString &name = "", const QString &tooltip = "", const QMargins &margin = QMargins(),
const QString &tooltip = "",
const QMargins &margin = QMargins(),
bool isHat = false); bool isHat = false);
const QPixmap * const QPixmap *getPixmap()
getPixmap()
{ {
if (!isLoading) { if (!isLoading) {
isLoading = true; isLoading = true;
@ -32,50 +27,42 @@ public:
return currentPixmap; return currentPixmap;
} }
qreal qreal getScale() const
getScale() const
{ {
return scale; return scale;
} }
const QString & const QString &getUrl() const
getUrl() const
{ {
return url; return url;
} }
const QString & const QString &getName() const
getName() const
{ {
return name; return name;
} }
const QString & const QString &getTooltip() const
getTooltip() const
{ {
return tooltip; return tooltip;
} }
const QMargins & const QMargins &getMargin() const
getMargin() const
{ {
return margin; return margin;
} }
bool bool getAnimated() const
getAnimated() const
{ {
return animated; return animated;
} }
bool bool getIsHat() const
getIsHat() const
{ {
return ishat; return ishat;
} }
int int getWidth() const
getWidth() const
{ {
if (currentPixmap == NULL) { if (currentPixmap == NULL) {
return 16; return 16;
@ -83,8 +70,7 @@ public:
return currentPixmap->width(); return currentPixmap->width();
} }
int int getHeight() const
getHeight() const
{ {
if (currentPixmap == NULL) { if (currentPixmap == NULL) {
return 16; return 16;

View file

@ -24,8 +24,7 @@ public:
vector->reserve(this->limit + this->buffer); vector->reserve(this->limit + this->buffer);
} }
void void clear()
clear()
{ {
std::lock_guard<std::mutex> lock(this->mutex); std::lock_guard<std::mutex> lock(this->mutex);
@ -37,8 +36,7 @@ public:
// return true if an item was deleted // return true if an item was deleted
// deleted will be set if the item was deleted // deleted will be set if the item was deleted
bool bool appendItem(const T &item, T &deleted)
appendItem(const T &item, T &deleted)
{ {
std::lock_guard<std::mutex> lock(this->mutex); std::lock_guard<std::mutex> lock(this->mutex);
@ -63,7 +61,7 @@ public:
} else { } else {
deleted = this->vector->at(this->offset); deleted = this->vector->at(this->offset);
//append item and increment offset("deleting" first element) // append item and increment offset("deleting" first element)
this->vector->push_back(item); this->vector->push_back(item);
this->offset++; this->offset++;
@ -77,12 +75,11 @@ public:
} }
} }
messages::LimitedQueueSnapshot<T> messages::LimitedQueueSnapshot<T> getSnapshot()
getSnapshot()
{ {
std::lock_guard<std::mutex> lock(this->mutex); std::lock_guard<std::mutex> lock(this->mutex);
if(vector->size() < limit) { if (vector->size() < limit) {
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size()); return LimitedQueueSnapshot<T>(this->vector, this->offset, this->vector->size());
} else { } else {
return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit); return LimitedQueueSnapshot<T>(this->vector, this->offset, this->limit);

View file

@ -11,16 +11,14 @@ template <typename T>
class LimitedQueueSnapshot class LimitedQueueSnapshot
{ {
public: public:
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset, LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> ptr, int offset, int length)
int length)
: vector(ptr) : vector(ptr)
, offset(offset) , offset(offset)
, length(length) , length(length)
{ {
} }
int int getLength()
getLength()
{ {
return this->length; return this->length;
} }

View file

@ -23,20 +23,17 @@ public:
Link(); Link();
Link(Type getType, const QString &getValue); Link(Type getType, const QString &getValue);
bool bool getIsValid() const
getIsValid() const
{ {
return type == None; return type == None;
} }
Type Type getType() const
getType() const
{ {
return type; return type;
} }
const QString & const QString &getValue() const
getValue() const
{ {
return value; return value;
} }

View file

@ -2,13 +2,13 @@
#include "channel.h" #include "channel.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "emojis.h" #include "emojis.h"
#include "emotes.h" #include "emotemanager.h"
#include "fonts.h" #include "fontmanager.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "messages/link.h" #include "messages/link.h"
#include "qcolor.h" #include "qcolor.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settingsmanager.h"
#include <QObjectUserData> #include <QObjectUserData>
#include <QStringList> #include <QStringList>
@ -19,396 +19,66 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
QRegularExpression *Message::cheerRegex =
new QRegularExpression("cheer[1-9][0-9]*");
Message::Message(const QString &text) Message::Message(const QString &text)
: _words()
{ {
words.push_back(Word(text, Word::Text, _words.push_back(
ColorScheme::getInstance().SystemMessageColor, text, Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString()));
QString()));
} }
Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, Message::Message(const std::vector<Word> &words)
bool enablePingSound, bool isReceivedWhisper, : _words(words)
bool isSentWhisper, bool includeChannel)
: content(ircMessage.content())
{ {
this->parseTime = std::chrono::system_clock::now();
auto words = std::vector<Word>();
auto tags = ircMessage.tags();
auto iterator = tags.find("id");
if (iterator != tags.end()) {
this->id = iterator.value().toString();
}
// timestamps
iterator = tags.find("tmi-sent-ts");
std::time_t time = std::time(NULL);
// if (iterator != tags.end()) {
// time = strtoll(iterator.value().toString().toStdString().c_str(),
// NULL,
// 10);
// }
char timeStampBuffer[69];
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
QString timestamp = QString(timeStampBuffer);
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
QString timestampWithSeconds = QString(timeStampBuffer);
words.push_back(Word(timestamp, Word::TimestampNoSeconds,
ColorScheme::getInstance().SystemMessageColor,
QString(), QString()));
words.push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds,
ColorScheme::getInstance().SystemMessageColor,
QString(), QString()));
// mod buttons
static QString buttonBanTooltip("Ban user");
static QString buttonTimeoutTooltip("Timeout user");
words.push_back(Word(Resources::getButtonBan(), Word::ButtonBan, QString(),
buttonBanTooltip,
Link(Link::UserBan, ircMessage.account())));
words.push_back(Word(Resources::getButtonTimeout(), Word::ButtonTimeout,
QString(), buttonTimeoutTooltip,
Link(Link::UserTimeout, ircMessage.account())));
// badges
iterator = tags.find("badges");
if (iterator != tags.end()) {
auto badges = iterator.value().toString().split(',');
for (QString badge : badges) {
if (badge.startsWith("bits/")) {
long long int cheer =
strtoll(badge.mid(5).toStdString().c_str(), NULL, 10);
words.push_back(Word(
Emotes::getCheerBadge(cheer), Word::BadgeCheer, QString(),
QString("Twitch Cheer" + QString::number(cheer))));
} else if (badge == "staff/1") {
words.push_back(Word(Resources::getBadgeStaff(),
Word::BadgeStaff, QString(),
QString("Twitch Staff")));
} else if (badge == "admin/1") {
words.push_back(Word(Resources::getBadgeAdmin(),
Word::BadgeAdmin, QString(),
QString("Twitch Admin")));
} else if (badge == "global_mod/1") {
words.push_back(Word(Resources::getBadgeGlobalmod(),
Word::BadgeGlobalMod, QString(),
QString("Global Moderator")));
} else if (badge == "moderator/1") {
// TODO: implement this xD
words.push_back(Word(
Resources::getBadgeTurbo(), Word::BadgeModerator, QString(),
QString("Channel Moderator"))); // custom badge
} else if (badge == "turbo/1") {
words.push_back(Word(Resources::getBadgeStaff(),
Word::BadgeTurbo, QString(),
QString("Turbo Subscriber")));
} else if (badge == "broadcaster/1") {
words.push_back(Word(Resources::getBadgeBroadcaster(),
Word::BadgeBroadcaster, QString(),
QString("Channel Broadcaster")));
} else if (badge == "premium/1") {
words.push_back(Word(Resources::getBadgePremium(),
Word::BadgePremium, QString(),
QString("Twitch Prime")));
}
}
}
// color
QColor usernameColor = ColorScheme::getInstance().SystemMessageColor;
iterator = tags.find("color");
if (iterator != tags.end()) {
usernameColor = QColor(iterator.value().toString());
}
// channel name
if (includeChannel) {
QString channelName("#" + channel.getName());
words.push_back(Word(
channelName, Word::Misc,
ColorScheme::getInstance().SystemMessageColor, QString(channelName),
QString(), Link(Link::Url, channel.getName() + "\n" + this->id)));
}
// username
this->userName = ircMessage.nick();
if (this->userName.isEmpty()) {
this->userName = tags.value(QLatin1String("login")).toString();
}
QString displayName;
iterator = tags.find("display-name");
if (iterator == tags.end()) {
displayName = ircMessage.account();
} else {
displayName = iterator.value().toString();
}
bool hasLocalizedName =
QString::compare(displayName, ircMessage.account()) == 0;
QString userDisplayString =
displayName +
(hasLocalizedName ? (" (" + ircMessage.account() + ")") : QString());
if (isSentWhisper) {
userDisplayString = IrcManager::account->getUsername() + " -> ";
}
if (isReceivedWhisper) {
userDisplayString += " -> " + IrcManager::account->getUsername();
}
if (!ircMessage.isAction()) {
userDisplayString += ": ";
}
words.push_back(Word(userDisplayString, Word::Username, usernameColor,
userDisplayString, QString()));
// highlights
// TODO: implement this xD
// bits
QString bits = "";
iterator = tags.find("bits");
if (iterator != tags.end()) {
bits = iterator.value().toString();
}
// twitch emotes
std::vector<std::pair<long int, LazyLoadedImage *>> twitchEmotes;
iterator = tags.find("emotes");
if (iterator != tags.end()) {
auto emotes = iterator.value().toString().split('/');
for (QString emote : emotes) {
if (!emote.contains(':'))
continue;
QStringList parameters = emote.split(':');
if (parameters.length() < 2)
continue;
long int id = std::stol(parameters.at(0).toStdString(), NULL, 10);
QStringList occurences = parameters.at(1).split(',');
for (QString occurence : occurences) {
QStringList coords = occurence.split('-');
if (coords.length() < 2)
continue;
long int start =
std::stol(coords.at(0).toStdString(), NULL, 10);
long int end = std::stol(coords.at(1).toStdString(), NULL, 10);
if (start >= end || start < 0 ||
end > ircMessage.content().length())
continue;
QString name = ircMessage.content().mid(start, end - start + 1);
twitchEmotes.push_back(std::pair<long int, LazyLoadedImage *>(
start, Emotes::getTwitchEmoteById(name, id)));
}
}
std::sort(twitchEmotes.begin(), twitchEmotes.end(), sortTwitchEmotes);
}
auto currentTwitchEmote = twitchEmotes.begin();
// words
QColor textColor =
ircMessage.isAction() ? usernameColor : ColorScheme::getInstance().Text;
QStringList splits = ircMessage.content().split(' ');
long int i = 0;
for (QString split : splits) {
// twitch emote
if (currentTwitchEmote != twitchEmotes.end() &&
currentTwitchEmote->first == i) {
words.push_back(Word(currentTwitchEmote->second,
Word::TwitchEmoteImage,
currentTwitchEmote->second->getName(),
currentTwitchEmote->second->getName() +
QString("\nTwitch Emote")));
words.push_back(Word(currentTwitchEmote->second->getName(),
Word::TwitchEmoteText, textColor,
currentTwitchEmote->second->getName(),
currentTwitchEmote->second->getName() +
QString("\nTwitch Emote")));
i += split.length() + 1;
currentTwitchEmote = std::next(currentTwitchEmote);
continue;
}
// split words
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
Emojis::parseEmojis(parsed, split);
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
LazyLoadedImage *image = std::get<0>(tuple);
if (image == NULL) { // is text
QString string = std::get<1>(tuple);
// cheers
if (!bits.isEmpty() && string.length() >= 6 &&
cheerRegex->match(string).isValid()) {
auto cheer = string.mid(5).toInt();
QString color;
QColor bitsColor;
if (cheer >= 10000) {
color = "red";
bitsColor = QColor::fromHslF(0, 1, 0.5);
} else if (cheer >= 5000) {
color = "blue";
bitsColor = QColor::fromHslF(0.61, 1, 0.4);
} else if (cheer >= 1000) {
color = "green";
bitsColor = QColor::fromHslF(0.5, 1, 0.5);
} else if (cheer >= 100) {
color = "purple";
bitsColor = QColor::fromHslF(0.8, 1, 0.5);
} else {
color = "gray";
bitsColor = QColor::fromHslF(0.5f, 0.5f, 0.5f);
}
QString bitsLinkAnimated = QString(
"http://static-cdn.jtvnw.net/bits/dark/animated/" +
color + "/1");
QString bitsLink = QString(
"http://static-cdn.jtvnw.net/bits/dark/static/" +
color + "/1");
LazyLoadedImage *imageAnimated =
Emotes::getMiscImageFromCache().getOrAdd(
bitsLinkAnimated, [&bitsLinkAnimated] {
return new LazyLoadedImage(bitsLinkAnimated);
});
LazyLoadedImage *image =
Emotes::getMiscImageFromCache().getOrAdd(
bitsLink, [&bitsLink] {
return new LazyLoadedImage(bitsLink);
});
words.push_back(
Word(imageAnimated, Word::BitsAnimated,
QString("cheer"), QString("Twitch Cheer"),
Link(Link::Url,
QString("https://blog.twitch.tv/"
"introducing-cheering-celebrate-"
"together-da62af41fac6"))));
words.push_back(
Word(image, Word::BitsStatic, QString("cheer"),
QString("Twitch Cheer"),
Link(Link::Url,
QString("https://blog.twitch.tv/"
"introducing-cheering-celebrate-"
"together-da62af41fac6"))));
words.push_back(
Word(QString("x" + string.mid(5)), Word::BitsAmount,
bitsColor, QString(string.mid(5)),
QString("Twitch Cheer"),
Link(Link::Url,
QString("https://blog.twitch.tv/"
"introducing-cheering-celebrate-"
"together-da62af41fac6"))));
continue;
}
// bttv / ffz emotes
LazyLoadedImage *bttvEmote;
// TODO: Implement this (ignored emotes)
if (Emotes::getBttvEmotes().tryGet(string, bttvEmote) ||
channel.getBttvChannelEmotes().tryGet(string, bttvEmote) ||
Emotes::getFfzEmotes().tryGet(string, bttvEmote) ||
channel.getFfzChannelEmotes().tryGet(string, bttvEmote) ||
Emotes::getChatterinoEmotes().tryGet(string, bttvEmote)) {
words.push_back(Word(bttvEmote, Word::BttvEmoteImage,
bttvEmote->getName(),
bttvEmote->getTooltip(),
Link(Link::Url, bttvEmote->getUrl())));
continue;
}
// actually just a word
QString link = matchLink(string);
words.push_back(
Word(string, Word::Text, textColor, string, QString(),
link.isEmpty() ? Link() : Link(Link::Url, link)));
} else { // is emoji
static QString emojiTooltip("Emoji");
words.push_back(Word(image, Word::EmojiImage, image->getName(),
emojiTooltip));
Word(image->getName(), Word::EmojiText, textColor,
image->getName(), emojiTooltip);
}
}
i += split.length() + 1;
}
this->words = words;
// TODO: Implement this xD
// if (!isReceivedWhisper &&
// AppSettings.HighlightIgnoredUsers.ContainsKey(Username))
// {
// HighlightTab = false;
// }
} }
QString bool Message::getCanHighlightTab() const
Message::matchLink(const QString &string)
{ {
// TODO: Implement this xD return _highlightTab;
return QString();
} }
bool const QString &Message::getTimeoutUser() const
Message::sortTwitchEmotes(const std::pair<long int, LazyLoadedImage *> &a,
const std::pair<long int, LazyLoadedImage *> &b)
{ {
return a.first < b.first; return _timeoutUser;
}
int Message::getTimeoutCount() const
{
return _timeoutCount;
}
const QString &Message::getUserName() const
{
return _userName;
}
const QString &Message::getDisplayName() const
{
return _displayName;
}
const QString &Message::getContent() const
{
return _content;
}
const std::chrono::time_point<std::chrono::system_clock> &Message::getParseTime() const
{
return _parseTime;
}
std::vector<Word> &Message::getWords()
{
return _words;
}
bool Message::isDisabled() const
{
return _isDisabled;
}
const QString &Message::getId() const
{
return _id;
} }
} // namespace messages } // namespace messages

View file

@ -1,6 +1,8 @@
#ifndef MESSAGE_H #ifndef MESSAGE_H
#define MESSAGE_H #define MESSAGE_H
#include "messages/message.h"
#include "messages/messageparseargs.h"
#include "messages/word.h" #include "messages/word.h"
#include "messages/wordpart.h" #include "messages/wordpart.h"
@ -8,84 +10,33 @@
#include <QVector> #include <QVector>
#include <chrono> #include <chrono>
#include <memory>
namespace chatterino { namespace chatterino {
class Channel; class Channel;
namespace messages { namespace messages {
class Message;
typedef std::shared_ptr<Message> SharedMessage;
class Message class Message
{ {
public: public:
Message(const QString &text); Message(const QString &text);
Message(const IrcPrivateMessage &ircMessage, Channel &channel, Message(const std::vector<messages::Word> &words);
bool enablePingSound = true, bool isReceivedWhisper = false,
bool isSentWhisper = false, bool includeChannel = false);
~Message() bool getCanHighlightTab() const;
{ const QString &getTimeoutUser() const;
} int getTimeoutCount() const;
const QString &getUserName() const;
bool const QString &getDisplayName() const;
getCanHighlightTab() const const QString &getContent() const;
{ const std::chrono::time_point<std::chrono::system_clock> &getParseTime() const;
return highlightTab; std::vector<Word> &getWords();
} bool isDisabled() const;
const QString &getId() const;
const QString &
getTimeoutUser() const
{
return timeoutUser;
}
int
getTimeoutCount() const
{
return timeoutCount;
}
const QString &
getUserName() const
{
return userName;
}
const QString &
getDisplayName() const
{
return displayName;
}
inline const QString &
getContent() const
{
return this->content;
}
inline const std::chrono::time_point<std::chrono::system_clock> &
getParseTime() const
{
return this->parseTime;
}
std::vector<Word> &
getWords()
{
return words;
}
bool
getDisabled() const
{
return disabled;
}
const QString &
getId() const
{
return id;
}
private: private:
static LazyLoadedImage *badgeStaff; static LazyLoadedImage *badgeStaff;
@ -98,24 +49,18 @@ private:
static QRegularExpression *cheerRegex; static QRegularExpression *cheerRegex;
bool highlightTab = false; bool _highlightTab = false;
QString timeoutUser = ""; QString _timeoutUser = "";
int timeoutCount = 0; int _timeoutCount = 0;
bool disabled = false; bool _isDisabled = false;
std::chrono::time_point<std::chrono::system_clock> parseTime; std::chrono::time_point<std::chrono::system_clock> _parseTime;
QString userName = ""; QString _userName = "";
QString displayName = ""; QString _displayName = "";
QString content; QString _content;
QString id = ""; QString _id = "";
std::vector<Word> words; std::vector<Word> _words;
static QString matchLink(const QString &string);
static bool sortTwitchEmotes(
const std::pair<long int, LazyLoadedImage *> &a,
const std::pair<long int, LazyLoadedImage *> &b);
}; };
} // namespace messages } // namespace messages

View 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
View 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

View 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

View file

@ -1,6 +1,6 @@
#include "messageref.h" #include "messageref.h"
#include "emotes.h" #include "emotemanager.h"
#include "settings.h" #include "settingsmanager.h"
#include <QDebug> #include <QDebug>
@ -9,82 +9,87 @@
#define MARGIN_TOP 8 #define MARGIN_TOP 8
#define MARGIN_BOTTOM 8 #define MARGIN_BOTTOM 8
using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
MessageRef::MessageRef(std::shared_ptr<Message> message) MessageRef::MessageRef(SharedMessage message)
: message(message.get()) : _message(message)
, messagePtr(message) , _wordParts()
, wordParts()
, buffer() , buffer()
{ {
} }
bool Message *MessageRef::getMessage()
MessageRef::layout(int width, bool enableEmoteMargins)
{ {
auto &settings = Settings::getInstance(); return _message.get();
}
bool sizeChanged = width != this->currentLayoutWidth; int MessageRef::getHeight() const
bool redraw = width != this->currentLayoutWidth; {
return _height;
}
bool MessageRef::layout(int width, bool enableEmoteMargins)
{
auto &settings = SettingsManager::getInstance();
bool sizeChanged = width != _currentLayoutWidth;
bool redraw = width != _currentLayoutWidth;
int spaceWidth = 4; int spaceWidth = 4;
{ int mediumTextLineHeight =
int mediumTextLineHeight = FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
Fonts::getFontMetrics(Fonts::Medium).height();
bool recalculateImages = bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration();
this->emoteGeneration != Emotes::getGeneration(); bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration();
bool recalculateText = this->fontGeneration != Fonts::getGeneration(); bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
bool newWordTypes =
this->currentWordTypes != Settings::getInstance().getWordTypeMask();
qreal emoteScale = settings.emoteScale.get(); qreal emoteScale = settings.emoteScale.get();
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get(); bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
if (recalculateImages || recalculateText || newWordTypes) { // calculate word sizes
this->emoteGeneration = Emotes::getGeneration(); if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
this->fontGeneration = Fonts::getGeneration();
redraw = true;
for (auto &word : this->message->getWords()) {
if (word.isImage()) {
if (recalculateImages) {
auto &image = word.getImage();
qreal w = image.getWidth();
qreal h = image.getHeight();
if (scaleEmotesByLineHeight) {
word.setSize(
w * mediumTextLineHeight / h * emoteScale,
mediumTextLineHeight * emoteScale);
} else {
word.setSize(w * image.getScale() * emoteScale,
h * image.getScale() * emoteScale);
}
}
} else {
if (recalculateText) {
QFontMetrics &metrics = word.getFontMetrics();
word.setSize(metrics.width(word.getText()),
metrics.height());
}
}
}
}
if (newWordTypes) {
this->currentWordTypes = Settings::getInstance().getWordTypeMask();
}
}
if (!redraw) {
return false; return false;
} }
this->currentLayoutWidth = width; _emoteGeneration = EmoteManager::getInstance().getGeneration();
_fontGeneration = FontManager::getInstance().getGeneration();
for (auto &word : _message->getWords()) {
if (word.isImage()) {
if (!recalculateImages) {
continue;
}
auto &image = word.getImage();
qreal w = image.getWidth();
qreal h = image.getHeight();
if (scaleEmotesByLineHeight) {
word.setSize(w * mediumTextLineHeight / h * emoteScale,
mediumTextLineHeight * emoteScale);
} else {
word.setSize(w * image.getScale() * emoteScale, h * image.getScale() * emoteScale);
}
} else {
if (!recalculateText) {
continue;
}
QFontMetrics &metrics = word.getFontMetrics();
word.setSize(metrics.width(word.getText()), metrics.height());
}
}
if (newWordTypes) {
_currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
}
// layout
_currentLayoutWidth = width;
int x = MARGIN_LEFT; int x = MARGIN_LEFT;
int y = MARGIN_TOP; int y = MARGIN_TOP;
@ -96,12 +101,11 @@ MessageRef::layout(int width, bool enableEmoteMargins)
int lineHeight = 0; int lineHeight = 0;
bool first = true; bool first = true;
this->wordParts.clear(); _wordParts.clear();
uint32_t flags = Settings::getInstance().getWordTypeMask(); uint32_t flags = SettingsManager::getInstance().getWordTypeMask();
for (auto it = this->message->getWords().begin(); for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) {
it != this->message->getWords().end(); ++it) {
Word &word = *it; Word &word = *it;
if ((word.getType() & flags) == Word::None) { if ((word.getType() & flags) == Word::None) {
@ -121,7 +125,7 @@ MessageRef::layout(int width, bool enableEmoteMargins)
// word wrapping // word wrapping
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) { if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
this->alignWordParts(lineStart, lineHeight); alignWordParts(lineStart, lineHeight);
y += lineHeight; y += lineHeight;
@ -144,9 +148,8 @@ MessageRef::layout(int width, bool enableEmoteMargins)
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) { if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
QString mid = text.mid(start, i - start - 1); QString mid = text.mid(start, i - start - 1);
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, _wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(),
width, word.getHeight(), lineNumber, mid, mid));
lineNumber, mid, mid));
y += metrics.height(); y += metrics.height();
@ -160,20 +163,19 @@ MessageRef::layout(int width, bool enableEmoteMargins)
QString mid(text.mid(start)); QString mid(text.mid(start));
width = metrics.width(mid); width = metrics.width(mid);
this->wordParts.push_back( _wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
WordPart(word, MARGIN_LEFT, y - word.getHeight(), width, word.getHeight(), lineNumber, mid, mid));
word.getHeight(), lineNumber, mid, mid));
x = width + MARGIN_LEFT + spaceWidth; x = width + MARGIN_LEFT + spaceWidth;
lineHeight = word.getHeight(); lineHeight = word.getHeight();
lineStart = this->wordParts.size() - 1; lineStart = _wordParts.size() - 1;
first = false; first = false;
} else if (first || x + word.getWidth() + xOffset <= right) { } else if (first || x + word.getWidth() + xOffset <= right) {
// fits in the line // fits in the line
this->wordParts.push_back(WordPart(word, x, y - word.getHeight(), _wordParts.push_back(
lineNumber, word.getCopyText())); WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
x += word.getWidth() + xOffset; x += word.getWidth() + xOffset;
x += spaceWidth; x += spaceWidth;
@ -183,15 +185,14 @@ MessageRef::layout(int width, bool enableEmoteMargins)
first = false; first = false;
} else { } else {
// doesn't fit in the line // doesn't fit in the line
this->alignWordParts(lineStart, lineHeight); alignWordParts(lineStart, lineHeight);
y += lineHeight; y += lineHeight;
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, _wordParts.push_back(
y - word.getHeight(), lineNumber, WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText()));
word.getCopyText()));
lineStart = this->wordParts.size() - 1; lineStart = _wordParts.size() - 1;
lineHeight = word.getHeight(); lineHeight = word.getHeight();
@ -202,36 +203,40 @@ MessageRef::layout(int width, bool enableEmoteMargins)
} }
} }
this->alignWordParts(lineStart, lineHeight); alignWordParts(lineStart, lineHeight);
if (this->height != y + lineHeight) { if (_height != y + lineHeight) {
sizeChanged = true; sizeChanged = true;
this->height = y + lineHeight; _height = y + lineHeight;
} }
if (sizeChanged) { if (sizeChanged) {
this->buffer = nullptr; buffer = nullptr;
} }
this->updateBuffer = true; updateBuffer = true;
return true; return true;
} }
void const std::vector<WordPart> &MessageRef::getWordParts() const
MessageRef::alignWordParts(int lineStart, int lineHeight)
{ {
for (size_t i = lineStart; i < this->wordParts.size(); i++) { return _wordParts;
WordPart &wordPart2 = this->wordParts.at(i); }
void MessageRef::alignWordParts(int lineStart, int lineHeight)
{
for (size_t i = lineStart; i < _wordParts.size(); i++) {
WordPart &wordPart2 = _wordParts.at(i);
wordPart2.setY(wordPart2.getY() + lineHeight); wordPart2.setY(wordPart2.getY() + lineHeight);
} }
} }
bool bool MessageRef::tryGetWordPart(QPoint point, Word &word)
MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
{ {
for (messages::WordPart &wordPart : this->wordParts) { // go through all words and return the first one that contains the point.
for (WordPart &wordPart : _wordParts) {
if (wordPart.getRect().contains(point)) { if (wordPart.getRect().contains(point)) {
word = wordPart.getWord(); word = wordPart.getWord();
return true; return true;
@ -241,18 +246,17 @@ MessageRef::tryGetWordPart(QPoint point, messages::Word &word)
return false; return false;
} }
int int MessageRef::getSelectionIndex(QPoint position)
MessageRef::getSelectionIndex(QPoint position)
{ {
if (this->wordParts.size() == 0) { if (_wordParts.size() == 0) {
return 0; return 0;
} }
// find out in which line the cursor is // find out in which line the cursor is
int lineNumber = 0, lineStart = 0, lineEnd = 0; int lineNumber = 0, lineStart = 0, lineEnd = 0;
for (int i = 0; i < this->wordParts.size(); i++) { for (int i = 0; i < _wordParts.size(); i++) {
WordPart &part = this->wordParts[i]; WordPart &part = _wordParts[i];
// return if curser under the word // return if curser under the word
if (position.y() >= part.getBottom()) { if (position.y() >= part.getBottom()) {
@ -271,13 +275,13 @@ MessageRef::getSelectionIndex(QPoint position)
int index = 0; int index = 0;
for (int i = 0; i < lineStart; i++) { for (int i = 0; i < lineStart; i++) {
WordPart &part = this->wordParts[i]; WordPart &part = _wordParts[i];
index += part.getWord().isImage() ? 2 : part.getText().length() + 1; index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
} }
for (int i = lineStart; i < lineEnd; i++) { for (int i = lineStart; i < lineEnd; i++) {
WordPart &part = this->wordParts[i]; WordPart &part = _wordParts[i];
// curser is left of the word part // curser is left of the word part
if (position.x() < part.getX()) { if (position.x() < part.getX()) {
@ -304,8 +308,7 @@ MessageRef::getSelectionIndex(QPoint position)
} }
index++; index++;
x = part.getX() + x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1);
part.getWord().getFontMetrics().width(text, j + 1);
} }
} }
@ -315,9 +318,9 @@ MessageRef::getSelectionIndex(QPoint position)
return index; return index;
// go through all the wordparts // go through all the wordparts
// for (int i = 0; i < this->wordParts; i < this->wordParts.size()) { // for (int i = 0; i < wordParts; i < wordParts.size()) {
// WordPart &part = this->wordParts[i]; // WordPart &part = wordParts[i];
// // return if curser under the word // // return if curser under the word
// if (position.y() >= part.getBottom()) { // if (position.y() >= part.getBottom()) {

View file

@ -9,30 +9,21 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
class MessageRef;
typedef std::shared_ptr<MessageRef> SharedMessageRef;
class MessageRef class MessageRef
{ {
public: public:
MessageRef(std::shared_ptr<Message> message); MessageRef(SharedMessage message);
Message * Message *getMessage();
getMessage() int getHeight() const;
{
return this->message;
}
int
getHeight() const
{
return height;
}
bool layout(int width, bool enableEmoteMargins = true); bool layout(int width, bool enableEmoteMargins = true);
const std::vector<WordPart> & const std::vector<WordPart> &getWordParts() const;
getWordParts() const
{
return wordParts;
}
std::shared_ptr<QPixmap> buffer = nullptr; std::shared_ptr<QPixmap> buffer = nullptr;
bool updateBuffer = false; bool updateBuffer = false;
@ -42,18 +33,18 @@ public:
int getSelectionIndex(QPoint position); int getSelectionIndex(QPoint position);
private: private:
Message *message; // variables
std::shared_ptr<Message> messagePtr; SharedMessage _message;
std::vector<messages::WordPart> _wordParts;
std::vector<messages::WordPart> wordParts; int _height = 0;
int height = 0; int _currentLayoutWidth = -1;
int _fontGeneration = -1;
int currentLayoutWidth = -1; int _emoteGeneration = -1;
int fontGeneration = -1; Word::Type _currentWordTypes = Word::None;
int emoteGeneration = -1;
Word::Type currentWordTypes = Word::None;
// methods
void alignWordParts(int lineStart, int lineHeight); void alignWordParts(int lineStart, int lineHeight);
}; };
} }

View file

@ -4,35 +4,131 @@ namespace chatterino {
namespace messages { namespace messages {
// Image word // Image word
Word::Word(LazyLoadedImage *image, Type type, const QString &copytext, Word::Word(LazyLoadedImage *image, Type type, const QString &copytext, const QString &tooltip,
const QString &tooltip, const Link &link) const Link &link)
: image(image) : _image(image)
, text() , _text()
, color() , _color()
, _isImage(true) , _isImage(true)
, type(type) , _type(type)
, copyText(copytext) , _copyText(copytext)
, tooltip(tooltip) , _tooltip(tooltip)
, link(link) , _link(link)
, characterWidthCache() , _characterWidthCache()
{ {
image->getWidth(); // professional segfault test image->getWidth(); // professional segfault test
} }
// Text word // Text word
Word::Word(const QString &text, Type type, const QColor &color, Word::Word(const QString &text, Type type, const QColor &color, const QString &copytext,
const QString &copytext, const QString &tooltip, const Link &link) const QString &tooltip, const Link &link)
: image(NULL) : _image(NULL)
, text(text) , _text(text)
, color(color) , _color(color)
, _isImage(false) , _isImage(false)
, type(type) , _type(type)
, copyText(copytext) , _copyText(copytext)
, tooltip(tooltip) , _tooltip(tooltip)
, link(link) , _link(link)
, characterWidthCache() , _characterWidthCache()
{ {
} }
LazyLoadedImage &Word::getImage() const
{
return *_image;
}
const QString &Word::getText() const
{
return _text;
}
int Word::getWidth() const
{
return _width;
}
int Word::getHeight() const
{
return _height;
}
void Word::setSize(int width, int height)
{
_width = width;
_height = height;
}
bool Word::isImage() const
{
return _isImage;
}
bool Word::isText() const
{
return !_isImage;
}
const QString &Word::getCopyText() const
{
return _copyText;
}
bool Word::hasTrailingSpace() const
{
return _hasTrailingSpace;
}
QFont &Word::getFont() const
{
return FontManager::getInstance().getFont(_font);
}
QFontMetrics &Word::getFontMetrics() const
{
return FontManager::getInstance().getFontMetrics(_font);
}
Word::Type Word::getType() const
{
return _type;
}
const QString &Word::getTooltip() const
{
return _tooltip;
}
const QColor &Word::getColor() const
{
return _color;
}
const Link &Word::getLink() const
{
return _link;
}
int Word::getXOffset() const
{
return _xOffset;
}
int Word::getYOffset() const
{
return _yOffset;
}
void Word::setOffset(int xOffset, int yOffset)
{
_xOffset = std::max(0, xOffset);
_yOffset = std::max(0, yOffset);
}
std::vector<short> &Word::getCharacterWidthCache() const
{
return _characterWidthCache;
}
} // namespace messages } // namespace messages
} // namespace chatterino } // namespace chatterino

View file

@ -1,7 +1,7 @@
#ifndef WORD_H #ifndef WORD_H
#define WORD_H #define WORD_H
#include "fonts.h" #include "fontmanager.h"
#include "messages/lazyloadedimage.h" #include "messages/lazyloadedimage.h"
#include "messages/link.h" #include "messages/link.h"
@ -31,8 +31,7 @@ public:
BttvGifEmoteText = (1 << 9), BttvGifEmoteText = (1 << 9),
FfzEmoteImage = (1 << 10), FfzEmoteImage = (1 << 10),
FfzEmoteText = (1 << 11), FfzEmoteText = (1 << 11),
EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage,
FfzEmoteImage,
BitsStatic = (1 << 12), BitsStatic = (1 << 12),
BitsAnimated = (1 << 13), BitsAnimated = (1 << 13),
@ -46,9 +45,8 @@ public:
BadgePremium = (1 << 20), BadgePremium = (1 << 20),
BadgeChatterino = (1 << 21), BadgeChatterino = (1 << 21),
BadgeCheer = (1 << 22), BadgeCheer = (1 << 22),
Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | BadgeTurbo |
BadgeTurbo | BadgeBroadcaster | BadgePremium | BadgeBroadcaster | BadgePremium | BadgeChatterino | BadgeCheer,
BadgeChatterino | BadgeCheer,
Username = (1 << 23), Username = (1 << 23),
BitsAmount = (1 << 24), BitsAmount = (1 << 24),
@ -59,157 +57,61 @@ public:
EmojiImage = (1 << 27), EmojiImage = (1 << 27),
EmojiText = (1 << 28), EmojiText = (1 << 28),
Default = TimestampNoSeconds | Badges | Username | BitsStatic | Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage | BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
TwitchEmoteImage | BitsAmount | Text | ButtonBan | ButtonBan | ButtonTimeout
ButtonTimeout
}; };
Word() Word()
{ {
} }
explicit Word(LazyLoadedImage *image, Type getType, const QString &copytext, explicit Word(LazyLoadedImage *_image, Type getType, const QString &copytext,
const QString &getTooltip, const Link &getLink = Link()); const QString &getTooltip, const Link &getLink = Link());
explicit Word(const QString &text, Type getType, const QColor &getColor, explicit Word(const QString &_text, Type getType, const QColor &getColor,
const QString &copytext, const QString &getTooltip, const QString &copytext, const QString &getTooltip, const Link &getLink = Link());
const Link &getLink = Link());
LazyLoadedImage & LazyLoadedImage &getImage() const;
getImage() const const QString &getText() const;
{ int getWidth() const;
return *image; int getHeight() const;
} void setSize(int _width, int _height);
const QString & bool isImage() const;
getText() const bool isText() const;
{ const QString &getCopyText() const;
return this->text; bool hasTrailingSpace() const;
} QFont &getFont() const;
QFontMetrics &getFontMetrics() const;
Type getType() const;
const QString &getTooltip() const;
const QColor &getColor() const;
const Link &getLink() const;
int getXOffset() const;
int getYOffset() const;
void setOffset(int _xOffset, int _yOffset);
int std::vector<short> &getCharacterWidthCache() const;
getWidth() const
{
return this->width;
}
int
getHeight() const
{
return this->height;
}
void
setSize(int width, int height)
{
this->width = width;
this->height = height;
}
bool
isImage() const
{
return this->_isImage;
}
bool
isText() const
{
return !this->_isImage;
}
const QString &
getCopyText() const
{
return this->copyText;
}
bool
hasTrailingSpace() const
{
return this->_hasTrailingSpace;
}
QFont &
getFont() const
{
return Fonts::getFont(this->font);
}
QFontMetrics &
getFontMetrics() const
{
return Fonts::getFontMetrics(this->font);
}
Type
getType() const
{
return this->type;
}
const QString &
getTooltip() const
{
return this->tooltip;
}
const QColor &
getColor() const
{
return this->color;
}
const Link &
getLink() const
{
return this->link;
}
int
getXOffset() const
{
return this->xOffset;
}
int
getYOffset() const
{
return this->yOffset;
}
void
setOffset(int xOffset, int yOffset)
{
this->xOffset = std::max(0, xOffset);
this->yOffset = std::max(0, yOffset);
}
std::vector<short> &
getCharacterWidthCache() const
{
return this->characterWidthCache;
}
private: private:
LazyLoadedImage *image; LazyLoadedImage *_image;
QString text; QString _text;
QColor color; QColor _color;
bool _isImage; bool _isImage;
Type type; Type _type;
QString copyText; QString _copyText;
QString tooltip; QString _tooltip;
int width = 16; int _width = 16;
int height = 16; int _height = 16;
int xOffset = 0; int _xOffset = 0;
int yOffset = 0; int _yOffset = 0;
bool _hasTrailingSpace; bool _hasTrailingSpace;
Fonts::Type font = Fonts::Medium; FontManager::Type _font = FontManager::Medium;
Link link; Link _link;
mutable std::vector<short> characterWidthCache; mutable std::vector<short> _characterWidthCache;
}; };
} // namespace messages } // namespace messages

View file

@ -4,33 +4,103 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
WordPart::WordPart(Word &word, int x, int y, int lineNumber, WordPart::WordPart(Word &word, int x, int y, int lineNumber, const QString &copyText,
const QString &copyText, bool allowTrailingSpace) bool allowTrailingSpace)
: m_word(word) : _word(word)
, copyText(copyText) , _copyText(copyText)
, text(word.isText() ? m_word.getText() : QString()) , _text(word.isText() ? _word.getText() : QString())
, x(x) , _x(x)
, y(y) , _y(y)
, width(word.getWidth()) , _width(word.getWidth())
, height(word.getHeight()) , _height(word.getHeight())
, lineNumber(lineNumber) , _lineNumber(lineNumber)
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) , _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
{ {
} }
WordPart::WordPart(Word &word, int x, int y, int width, int height, WordPart::WordPart(Word &word, int x, int y, int width, int height, int lineNumber,
int lineNumber, const QString &copyText, const QString &copyText, const QString &customText, bool allowTrailingSpace)
const QString &customText, bool allowTrailingSpace) : _word(word)
: m_word(word) , _copyText(copyText)
, copyText(copyText) , _text(customText)
, text(customText) , _x(x)
, x(x) , _y(y)
, y(y) , _width(width)
, width(width) , _height(height)
, height(height) , _lineNumber(lineNumber)
, lineNumber(lineNumber)
, _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) , _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
{ {
} }
const Word &WordPart::getWord() const
{
return _word;
}
int WordPart::getWidth() const
{
return _width;
}
int WordPart::getHeight() const
{
return _height;
}
int WordPart::getX() const
{
return _x;
}
int WordPart::getY() const
{
return _y;
}
void WordPart::setPosition(int x, int y)
{
_x = x;
_y = y;
}
void WordPart::setY(int y)
{
_y = y;
}
int WordPart::getRight() const
{
return _x + _width;
}
int WordPart::getBottom() const
{
return _y + _height;
}
QRect WordPart::getRect() const
{
return QRect(_x, _y, _width, _height);
}
const QString WordPart::getCopyText() const
{
return _copyText;
}
int WordPart::hasTrailingSpace() const
{
return _trailingSpace;
}
const QString &WordPart::getText() const
{
return _text;
}
int WordPart::getLineNumber()
{
return _lineNumber;
}
} }
} }

View file

@ -12,110 +12,39 @@ class Word;
class WordPart class WordPart
{ {
public: public:
WordPart(Word &getWord, int getX, int getY, int lineNumber, WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText,
const QString &getCopyText, bool allowTrailingSpace = true); bool allowTrailingSpace = true);
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber,
int lineNumber, const QString &getCopyText, const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true);
const QString &customText, bool allowTrailingSpace = true);
const Word & const Word &getWord() const;
getWord() const int getWidth() const;
{ int getHeight() const;
return this->m_word; int getX() const;
} int getY() const;
void setPosition(int _x, int _y);
int void setY(int _y);
getWidth() const int getRight() const;
{ int getBottom() const;
return this->width; QRect getRect() const;
} const QString getCopyText() const;
int hasTrailingSpace() const;
int const QString &getText() const;
getHeight() const int getLineNumber();
{
return this->height;
}
int
getX() const
{
return this->x;
}
int
getY() const
{
return this->y;
}
void
setPosition(int x, int y)
{
this->x = x;
this->y = y;
}
void
setY(int y)
{
this->y = y;
}
int
getRight() const
{
return this->x + this->width;
}
int
getBottom() const
{
return this->y + this->height;
}
QRect
getRect() const
{
return QRect(this->x, this->y, this->width, this->height);
}
const QString
getCopyText() const
{
return this->copyText;
}
int
hasTrailingSpace() const
{
return this->_trailingSpace;
}
const QString &
getText() const
{
return this->text;
}
int
getLineNumber()
{
return this->lineNumber;
}
private: private:
Word &m_word; Word &_word;
QString copyText; QString _copyText;
QString text; QString _text;
int x; int _x;
int y; int _y;
int width; int _width;
int height; int _height;
int lineNumber; int _lineNumber;
bool _trailingSpace; bool _trailingSpace;
}; };

View 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.

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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

View file

@ -26,43 +26,34 @@ Resources::Resources()
{ {
} }
void void Resources::load()
Resources::load()
{ {
// badges // badges
Resources::badgeStaff = Resources::badgeStaff = new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png"));
new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png")); Resources::badgeAdmin = new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
Resources::badgeAdmin =
new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png"));
Resources::badgeModerator = Resources::badgeModerator =
new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png")); new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png"));
Resources::badgeGlobalmod = Resources::badgeGlobalmod =
new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png")); new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png"));
Resources::badgeTurbo = Resources::badgeTurbo = new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png"));
new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png")); Resources::badgeBroadcaster =
Resources::badgeBroadcaster = new messages::LazyLoadedImage( new messages::LazyLoadedImage(new QPixmap(":/images/broadcaster_bg.png"));
new QPixmap(":/images/broadcaster_bg.png")); Resources::badgePremium =
Resources::badgePremium = new messages::LazyLoadedImage( new messages::LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png"));
new QPixmap(":/images/twitchprime_bg.png"));
// cheer badges // cheer badges
Resources::cheerBadge100000 = Resources::cheerBadge100000 =
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000")); new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000"));
Resources::cheerBadge10000 = Resources::cheerBadge10000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000"));
new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000")); Resources::cheerBadge5000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000"));
Resources::cheerBadge5000 = Resources::cheerBadge1000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000"));
new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000")); Resources::cheerBadge100 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer100"));
Resources::cheerBadge1000 = Resources::cheerBadge1 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1"));
new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000"));
Resources::cheerBadge100 =
new messages::LazyLoadedImage(new QPixmap(":/images/cheer100"));
Resources::cheerBadge1 =
new messages::LazyLoadedImage(new QPixmap(":/images/cheer1"));
// button // button
Resources::buttonBan = new messages::LazyLoadedImage( Resources::buttonBan =
new QPixmap(":/images/button_ban.png"), 0.25); new messages::LazyLoadedImage(new QPixmap(":/images/button_ban.png"), 0.25);
Resources::buttonTimeout = new messages::LazyLoadedImage( Resources::buttonTimeout =
new QPixmap(":/images/button_timeout.png"), 0.25); new messages::LazyLoadedImage(new QPixmap(":/images/button_timeout.png"), 0.25);
} }
} }

View file

@ -11,93 +11,78 @@ public:
static void load(); static void load();
// badges // badges
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeStaff()
getBadgeStaff()
{ {
return badgeStaff; return badgeStaff;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeAdmin()
getBadgeAdmin()
{ {
return badgeAdmin; return badgeAdmin;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeGlobalmod()
getBadgeGlobalmod()
{ {
return badgeGlobalmod; return badgeGlobalmod;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeModerator()
getBadgeModerator()
{ {
return badgeModerator; return badgeModerator;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeTurbo()
getBadgeTurbo()
{ {
return badgeTurbo; return badgeTurbo;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgeBroadcaster()
getBadgeBroadcaster()
{ {
return badgeBroadcaster; return badgeBroadcaster;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getBadgePremium()
getBadgePremium()
{ {
return badgePremium; return badgePremium;
} }
// cheer badges // cheer badges
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge100000()
getCheerBadge100000()
{ {
return cheerBadge100000; return cheerBadge100000;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge10000()
getCheerBadge10000()
{ {
return cheerBadge10000; return cheerBadge10000;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge5000()
getCheerBadge5000()
{ {
return cheerBadge5000; return cheerBadge5000;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge1000()
getCheerBadge1000()
{ {
return cheerBadge1000; return cheerBadge1000;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge100()
getCheerBadge100()
{ {
return cheerBadge100; return cheerBadge100;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getCheerBadge1()
getCheerBadge1()
{ {
return cheerBadge1; return cheerBadge1;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getButtonBan()
getButtonBan()
{ {
return buttonBan; return buttonBan;
} }
static messages::LazyLoadedImage * static messages::LazyLoadedImage *getButtonTimeout()
getButtonTimeout()
{ {
return buttonTimeout; return buttonTimeout;
} }

View file

@ -11,70 +11,65 @@ class BaseSetting
{ {
public: public:
BaseSetting(const QString &_name) BaseSetting(const QString &_name)
: name(_name) : _name(_name)
{ {
} }
virtual QVariant getVariant() = 0; virtual QVariant getVariant() = 0;
virtual void setVariant(QVariant value) = 0; virtual void setVariant(QVariant value) = 0;
const QString & const QString &getName() const
getName() const
{ {
return this->name; return _name;
} }
private: private:
QString name; QString _name;
}; };
template <typename T> template <typename T>
class Setting : public BaseSetting class Setting : public BaseSetting
{ {
public: public:
Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems, Setting(std::vector<std::reference_wrapper<BaseSetting>> &settingItems, const QString &_name,
const QString &_name, const T &defaultValue) const T &defaultValue)
: BaseSetting(_name) : BaseSetting(_name)
, value(defaultValue) , _value(defaultValue)
{ {
settingItems.push_back(*this); settingItems.push_back(*this);
} }
const T & const T &get() const
get() const
{ {
return this->value; return _value;
} }
void void set(const T &newValue)
set(const T &newValue)
{ {
if (this->value != newValue) { if (_value != newValue) {
this->value = newValue; _value = newValue;
this->valueChanged(newValue); valueChanged(newValue);
} }
} }
virtual QVariant virtual QVariant getVariant() final
getVariant() final
{ {
return QVariant::fromValue(value); return QVariant::fromValue(_value);
} }
virtual void virtual void setVariant(QVariant value) final
setVariant(QVariant value) final
{ {
if (value.isValid()) { if (value.isValid()) {
assert(value.canConvert<T>()); assert(value.canConvert<T>());
this->set(value.value<T>()); set(value.value<T>());
} }
} }
boost::signals2::signal<void(const T &newValue)> valueChanged; boost::signals2::signal<void(const T &newValue)> valueChanged;
private: private:
T value; T _value;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -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
View 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

View file

@ -9,75 +9,10 @@
namespace chatterino { namespace chatterino {
class Settings : public QObject class SettingsManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public:
static Settings &
getInstance()
{
return instance;
}
void load();
void save();
messages::Word::Type
getWordTypeMask()
{
return wordTypeMask;
}
bool isIgnoredEmote(const QString &emote);
bool
getPortable()
{
return portable;
}
void
setPortable(bool value)
{
portable = value;
}
QSettings &
getQSettings()
{
return settings;
}
SettingsSnapshot
createSnapshot()
{
SettingsSnapshot snapshot;
for (auto &item : this->settingsItems) {
snapshot.addItem(item, item.get().getVariant());
}
return snapshot;
}
signals:
void wordTypeMaskChanged();
private:
Settings();
static Settings instance;
void updateWordTypeMask();
QSettings settings;
std::vector<std::reference_wrapper<BaseSetting>> settingsItems;
bool portable;
messages::Word::Type wordTypeMask;
public: public:
Setting<QString> theme; Setting<QString> theme;
Setting<float> themeHue; Setting<float> themeHue;
@ -110,6 +45,41 @@ public:
Setting<bool> hideTabX; Setting<bool> hideTabX;
Setting<bool> hidePreferencesButton; Setting<bool> hidePreferencesButton;
Setting<bool> hideUserButton; Setting<bool> hideUserButton;
Setting<bool> useCustomWindowFrame;
void load();
void save();
messages::Word::Type getWordTypeMask();
bool isIgnoredEmote(const QString &emote);
bool getPortable();
void setPortable(bool value);
QSettings &getQSettings();
SettingsSnapshot createSnapshot();
signals:
void wordTypeMaskChanged();
private:
SettingsManager();
// variables
QSettings _settings;
std::vector<std::reference_wrapper<BaseSetting>> _settingsItems;
bool _portable;
messages::Word::Type _wordTypeMask;
// methods
void updateWordTypeMask();
public:
static SettingsManager &getInstance()
{
return instance;
}
private:
static SettingsManager instance;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -3,34 +3,31 @@
#include "setting.h" #include "setting.h"
struct SettingsSnapshot { namespace chatterino {
private:
std::vector<
std::pair<std::reference_wrapper<chatterino::BaseSetting>, QVariant>>
items;
struct SettingsSnapshot {
public: public:
SettingsSnapshot() SettingsSnapshot()
: items() : _items()
{ {
} }
void void addItem(std::reference_wrapper<BaseSetting> setting, const QVariant &value)
addItem(std::reference_wrapper<chatterino::BaseSetting> setting,
const QVariant &value)
{ {
items.push_back( _items.push_back(
std::pair<std::reference_wrapper<chatterino::BaseSetting>, std::pair<std::reference_wrapper<BaseSetting>, QVariant>(setting.get(), value));
QVariant>(setting.get(), value));
} }
void void apply()
apply()
{ {
for (auto &item : this->items) { for (auto &item : _items) {
item.first.get().setVariant(item.second); item.first.get().setVariant(item.second);
} }
} }
private:
std::vector<std::pair<std::reference_wrapper<BaseSetting>, QVariant>> _items;
}; };
}
#endif // SETTINGSSNAPSHOT_H #endif // SETTINGSSNAPSHOT_H

34
twitch/emotevalue.h Normal file
View 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

View 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;
//}
}
}

View 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

View 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;
//}
//}
//}
//

View 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
View 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
View 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

View file

@ -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
View 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
View 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
View 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

View file

@ -1,7 +1,7 @@
#include "widgets/chatwidget.h" #include "widgets/chatwidget.h"
#include "channels.h" #include "channelmanager.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "settings.h" #include "settingsmanager.h"
#include "widgets/textinputdialog.h" #include "widgets/textinputdialog.h"
#include <QDebug> #include <QDebug>
@ -11,110 +11,167 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
using namespace chatterino::messages;
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
ChatWidget::ChatWidget(QWidget *parent) ChatWidget::ChatWidget(QWidget *parent)
: QWidget(parent) : QWidget(parent)
, messages() , _messages()
, channel(Channels::getEmpty()) , _channel(ChannelManager::getInstance().getEmpty())
, channelName(QString()) , _channelName(QString())
, vbox(this) , _vbox(this)
, header(this) , _header(this)
, view(this) , _view(this)
, input(this) , _input(this)
{ {
this->vbox.setSpacing(0); this->_vbox.setSpacing(0);
this->vbox.setMargin(1); this->_vbox.setMargin(1);
this->vbox.addWidget(&header); this->_vbox.addWidget(&_header);
this->vbox.addWidget(&view, 1); this->_vbox.addWidget(&_view, 1);
this->vbox.addWidget(&input); this->_vbox.addWidget(&_input);
} }
ChatWidget::~ChatWidget() ChatWidget::~ChatWidget()
{ {
} }
std::shared_ptr<Channel>
ChatWidget::getChannel() const
{
return _channel;
}
const QString &
ChatWidget::getChannelName() const
{
return _channelName;
}
void void
ChatWidget::setChannelName(const QString &name) ChatWidget::setChannelName(const QString &name)
{ {
QString channel = name.trimmed(); QString channel = name.trimmed();
if (QString::compare(channel, this->channelName, Qt::CaseInsensitive) == // return if channel name is the same
0) { if (QString::compare(channel, _channelName, Qt::CaseInsensitive) == 0) {
this->channelName = channel; _channelName = channel;
this->header.updateChannelText(); _header.updateChannelText();
return; return;
} }
if (!this->channelName.isEmpty()) { // remove current channel
Channels::removeChannel(this->channelName); if (!_channelName.isEmpty()) {
ChannelManager::getInstance().removeChannel(_channelName);
this->messageAppendedConnection.disconnect(); detachChannel(_channel);
this->messageRemovedConnection.disconnect();
} }
this->channelName = channel; // update members
this->header.updateChannelText(); _channelName = channel;
this->view.layoutMessages(); // update messages
_messages.clear();
messages.clear();
if (channel.isEmpty()) { if (channel.isEmpty()) {
this->channel = NULL; _channel = NULL;
} else { } else {
this->channel = Channels::addChannel(channel); _channel = ChannelManager::getInstance().addChannel(channel);
this->messageAppendedConnection = attachChannel(_channel);
this->channel.get()->messageAppended.connect([this](
std::shared_ptr<messages::Message> &message) {
std::shared_ptr<messages::MessageRef> deleted;
auto messageRef = new messages::MessageRef(message);
this->messages.appendItem(
std::shared_ptr<messages::MessageRef>(messageRef), deleted);
});
this->messageRemovedConnection =
this->channel.get()->messageRemovedFromStart.connect(
[this](std::shared_ptr<messages::Message> &) {});
auto snapshot = this->channel.get()->getMessageSnapshot();
for (int i = 0; i < snapshot.getLength(); i++) {
std::shared_ptr<messages::MessageRef> deleted;
auto messageRef = new messages::MessageRef(snapshot[i]);
this->messages.appendItem(
std::shared_ptr<messages::MessageRef>(messageRef), deleted);
}
} }
this->view.layoutMessages(); // update header
this->view.update(); _header.updateChannelText();
// update view
_view.layoutMessages();
_view.update();
}
void
ChatWidget::attachChannel(SharedChannel channel)
{
// on new message
_messageAppendedConnection =
channel->messageAppended.connect([this](SharedMessage &message) {
SharedMessageRef deleted;
auto messageRef = new MessageRef(message);
if (_messages.appendItem(SharedMessageRef(messageRef), deleted)) {
qreal value =
std::max(0.0, _view.getScrollbar()->getDesiredValue() - 1);
_view.getScrollbar()->setDesiredValue(value, false);
}
});
// on message removed
_messageRemovedConnection =
_channel->messageRemovedFromStart.connect([this](SharedMessage &) {});
auto snapshot = _channel.get()->getMessageSnapshot();
for (int i = 0; i < snapshot.getLength(); i++) {
SharedMessageRef deleted;
auto messageRef = new MessageRef(snapshot[i]);
_messages.appendItem(SharedMessageRef(messageRef), deleted);
}
}
void
ChatWidget::detachChannel(std::shared_ptr<Channel> channel)
{
// on message added
_messageAppendedConnection.disconnect();
// on message removed
_messageRemovedConnection.disconnect();
}
LimitedQueueSnapshot<SharedMessageRef>
ChatWidget::getMessagesSnapshot()
{
return _messages.getSnapshot();
} }
void void
ChatWidget::showChangeChannelPopup() ChatWidget::showChangeChannelPopup()
{ {
// create new input dialog and execute it
TextInputDialog dialog(this); TextInputDialog dialog(this);
dialog.setText(this->channelName); dialog.setText(_channelName);
if (dialog.exec() == QDialog::Accepted) { if (dialog.exec() == QDialog::Accepted) {
setChannelName(dialog.getText()); setChannelName(dialog.getText());
} }
} }
void
ChatWidget::layoutMessages()
{
if (_view.layoutMessages()) {
_view.update();
}
}
void
ChatWidget::updateGifEmotes()
{
_view.updateGifEmotes();
}
void void
ChatWidget::paintEvent(QPaintEvent *) ChatWidget::paintEvent(QPaintEvent *)
{ {
// color the background of the chat
QPainter painter(this); QPainter painter(this);
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground); painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground);
@ -123,7 +180,7 @@ ChatWidget::paintEvent(QPaintEvent *)
void void
ChatWidget::load(const boost::property_tree::ptree &tree) ChatWidget::load(const boost::property_tree::ptree &tree)
{ {
// Load tab text // load tab text
try { try {
this->setChannelName( this->setChannelName(
QString::fromStdString(tree.get<std::string>("channelName"))); QString::fromStdString(tree.get<std::string>("channelName")));

View file

@ -27,51 +27,35 @@ public:
ChatWidget(QWidget *parent = 0); ChatWidget(QWidget *parent = 0);
~ChatWidget(); ~ChatWidget();
ChatWidgetView & SharedChannel getChannel() const;
getView() const QString &getChannelName() const;
{
return view;
}
std::shared_ptr<Channel>
getChannel() const
{
return channel;
}
const QString &
getChannelName() const
{
return channelName;
}
void setChannelName(const QString &name); void setChannelName(const QString &name);
void showChangeChannelPopup(); void showChangeChannelPopup();
messages::LimitedQueueSnapshot<messages::SharedMessageRef> getMessagesSnapshot();
messages::LimitedQueueSnapshot<std::shared_ptr<messages::MessageRef>> void layoutMessages();
getMessagesSnapshot() void updateGifEmotes();
{
return messages.getSnapshot();
}
protected: protected:
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
private: private:
messages::LimitedQueue<std::shared_ptr<messages::MessageRef>> messages; void attachChannel(std::shared_ptr<Channel> _channel);
void detachChannel(std::shared_ptr<Channel> _channel);
std::shared_ptr<Channel> channel; messages::LimitedQueue<messages::SharedMessageRef> _messages;
QString channelName;
QFont font; SharedChannel _channel;
QVBoxLayout vbox; QString _channelName;
ChatWidgetHeader header;
ChatWidgetView view;
ChatWidgetInput input;
boost::signals2::connection messageAppendedConnection; QFont _font;
boost::signals2::connection messageRemovedConnection; QVBoxLayout _vbox;
ChatWidgetHeader _header;
ChatWidgetView _view;
ChatWidgetInput _input;
boost::signals2::connection _messageAppendedConnection;
boost::signals2::connection _messageRemovedConnection;
public: public:
void load(const boost::property_tree::ptree &tree); void load(const boost::property_tree::ptree &tree);

View file

@ -13,90 +13,81 @@ namespace widgets {
ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent) ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent)
: QWidget() : QWidget()
, chatWidget(parent) , _chatWidget(parent)
, dragStart() , _dragStart()
, dragging(false) , _dragging(false)
, leftLabel() , _leftLabel()
, middleLabel() , _middleLabel()
, rightLabel() , _rightLabel()
, leftMenu(this) , _leftMenu(this)
, rightMenu(this) , _rightMenu(this)
{ {
setFixedHeight(32); setFixedHeight(32);
updateColors(); updateColors();
updateChannelText(); updateChannelText();
setLayout(&this->hbox); setLayout(&_hbox);
this->hbox.setMargin(0); _hbox.setMargin(0);
this->hbox.addWidget(&this->leftLabel); _hbox.addWidget(&_leftLabel);
this->hbox.addWidget(&this->middleLabel, 1); _hbox.addWidget(&_middleLabel, 1);
this->hbox.addWidget(&this->rightLabel); _hbox.addWidget(&_rightLabel);
// left // left
this->leftLabel.getLabel().setTextFormat(Qt::RichText); _leftLabel.getLabel().setTextFormat(Qt::RichText);
this->leftLabel.getLabel().setText( _leftLabel.getLabel().setText("<img src=':/images/tool_moreCollapser_off16.png' />");
"<img src=':/images/tool_moreCollapser_off16.png' />");
QObject::connect(&this->leftLabel, &ChatWidgetHeaderButton::clicked, this, QObject::connect(&_leftLabel, &ChatWidgetHeaderButton::clicked, this,
&ChatWidgetHeader::leftButtonClicked); &ChatWidgetHeader::leftButtonClicked);
this->leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), _leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), QKeySequence(tr("Ctrl+T")));
QKeySequence(tr("Ctrl+T"))); _leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), QKeySequence(tr("Ctrl+W")));
this->leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), _leftMenu.addAction("Move split", this, SLOT(menuMoveSplit()));
QKeySequence(tr("Ctrl+W"))); _leftMenu.addAction("Popup", this, SLOT(menuPopup()));
this->leftMenu.addAction("Move split", this, SLOT(menuMoveSplit())); _leftMenu.addSeparator();
this->leftMenu.addAction("Popup", this, SLOT(menuPopup())); _leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()),
this->leftMenu.addSeparator(); QKeySequence(tr("Ctrl+R")));
this->leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()), _leftMenu.addAction("Clear chat", this, SLOT(menuClearChat()));
QKeySequence(tr("Ctrl+R"))); _leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel()));
this->leftMenu.addAction("Clear chat", this, SLOT(menuClearChat())); _leftMenu.addAction("Open pop-out player", this, SLOT(menuPopupPlayer()));
this->leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel())); _leftMenu.addSeparator();
this->leftMenu.addAction("Open pop-out player", this, _leftMenu.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes()));
SLOT(menuPopupPlayer())); _leftMenu.addAction("Manual reconnect", this, SLOT(menuManualReconnect()));
this->leftMenu.addSeparator(); _leftMenu.addSeparator();
this->leftMenu.addAction("Reload channel emotes", this, _leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
SLOT(menuReloadChannelEmotes()));
this->leftMenu.addAction("Manual reconnect", this,
SLOT(menuManualReconnect()));
this->leftMenu.addSeparator();
this->leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog()));
// middle // middle
this->middleLabel.setAlignment(Qt::AlignCenter); _middleLabel.setAlignment(Qt::AlignCenter);
connect(&this->middleLabel, &SignalLabel::mouseDoubleClick, this, connect(&_middleLabel, &SignalLabel::mouseDoubleClick, this,
&ChatWidgetHeader::mouseDoubleClickEvent); &ChatWidgetHeader::mouseDoubleClickEvent);
// connect(&this->middleLabel, &SignalLabel::mouseDown, this, // connect(&this->middleLabel, &SignalLabel::mouseDown, this,
// &ChatWidgetHeader::mouseDoubleClickEvent); // &ChatWidgetHeader::mouseDoubleClickEvent);
// right // right
this->rightLabel.setMinimumWidth(height()); _rightLabel.setMinimumWidth(height());
this->rightLabel.getLabel().setTextFormat(Qt::RichText); _rightLabel.getLabel().setTextFormat(Qt::RichText);
this->rightLabel.getLabel().setText("ayy"); _rightLabel.getLabel().setText("ayy");
} }
void void ChatWidgetHeader::updateColors()
ChatWidgetHeader::updateColors()
{ {
QPalette palette; QPalette palette;
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
this->leftLabel.setPalette(palette); _leftLabel.setPalette(palette);
this->middleLabel.setPalette(palette); _middleLabel.setPalette(palette);
this->rightLabel.setPalette(palette); _rightLabel.setPalette(palette);
} }
void void ChatWidgetHeader::updateChannelText()
ChatWidgetHeader::updateChannelText()
{ {
const QString &c = this->chatWidget->getChannelName(); const QString &c = _chatWidget->getChannelName();
this->middleLabel.setText(c.isEmpty() ? "<no channel>" : c); _middleLabel.setText(c.isEmpty() ? "<no channel>" : c);
} }
void void ChatWidgetHeader::paintEvent(QPaintEvent *)
ChatWidgetHeader::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -105,21 +96,19 @@ ChatWidgetHeader::paintEvent(QPaintEvent *)
painter.drawRect(0, 0, width() - 1, height() - 1); painter.drawRect(0, 0, width() - 1, height() - 1);
} }
void void ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
{ {
this->dragging = true; _dragging = true;
this->dragStart = event->pos(); _dragStart = event->pos();
} }
void void ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
{ {
if (this->dragging) { if (_dragging) {
if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || if (std::abs(_dragStart.x() - event->pos().x()) > 12 ||
std::abs(this->dragStart.y() - event->pos().y()) > 12) { std::abs(_dragStart.y() - event->pos().y()) > 12) {
auto chatWidget = this->chatWidget; auto chatWidget = _chatWidget;
auto page = static_cast<NotebookPage *>(chatWidget->parentWidget()); auto page = static_cast<NotebookPage *>(chatWidget->parentWidget());
if (page != NULL) { if (page != NULL) {
@ -149,73 +138,58 @@ ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
} }
} }
void void ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->chatWidget->showChangeChannelPopup(); _chatWidget->showChangeChannelPopup();
} }
} }
void void ChatWidgetHeader::leftButtonClicked()
ChatWidgetHeader::leftButtonClicked()
{ {
this->leftMenu.move( _leftMenu.move(_leftLabel.mapToGlobal(QPoint(0, _leftLabel.height())));
this->leftLabel.mapToGlobal(QPoint(0, this->leftLabel.height()))); _leftMenu.show();
this->leftMenu.show();
} }
void void ChatWidgetHeader::rightButtonClicked()
ChatWidgetHeader::rightButtonClicked()
{ {
} }
void void ChatWidgetHeader::menuAddSplit()
ChatWidgetHeader::menuAddSplit()
{ {
} }
void void ChatWidgetHeader::menuCloseSplit()
ChatWidgetHeader::menuCloseSplit()
{ {
} }
void void ChatWidgetHeader::menuMoveSplit()
ChatWidgetHeader::menuMoveSplit()
{ {
} }
void void ChatWidgetHeader::menuPopup()
ChatWidgetHeader::menuPopup()
{ {
auto widget = new ChatWidget(); auto widget = new ChatWidget();
widget->setChannelName(this->chatWidget->getChannelName()); widget->setChannelName(_chatWidget->getChannelName());
widget->show(); widget->show();
} }
void void ChatWidgetHeader::menuChangeChannel()
ChatWidgetHeader::menuChangeChannel()
{ {
this->chatWidget->showChangeChannelPopup(); _chatWidget->showChangeChannelPopup();
} }
void void ChatWidgetHeader::menuClearChat()
ChatWidgetHeader::menuClearChat()
{ {
} }
void void ChatWidgetHeader::menuOpenChannel()
ChatWidgetHeader::menuOpenChannel()
{ {
} }
void void ChatWidgetHeader::menuPopupPlayer()
ChatWidgetHeader::menuPopupPlayer()
{ {
} }
void void ChatWidgetHeader::menuReloadChannelEmotes()
ChatWidgetHeader::menuReloadChannelEmotes()
{ {
} }
void void ChatWidgetHeader::menuManualReconnect()
ChatWidgetHeader::menuManualReconnect()
{ {
} }
void void ChatWidgetHeader::menuShowChangelog()
ChatWidgetHeader::menuShowChangelog()
{ {
} }
} }

View file

@ -24,10 +24,9 @@ class ChatWidgetHeader : public QWidget
public: public:
explicit ChatWidgetHeader(ChatWidget *parent); explicit ChatWidgetHeader(ChatWidget *parent);
ChatWidget * ChatWidget *getChatWidget()
getChatWidget()
{ {
return chatWidget; return _chatWidget;
} }
void updateColors(); void updateColors();
@ -40,19 +39,19 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event);
private: private:
ChatWidget *chatWidget; ChatWidget *_chatWidget;
QPoint dragStart; QPoint _dragStart;
bool dragging; bool _dragging;
QHBoxLayout hbox; QHBoxLayout _hbox;
ChatWidgetHeaderButton leftLabel; ChatWidgetHeaderButton _leftLabel;
SignalLabel middleLabel; SignalLabel _middleLabel;
ChatWidgetHeaderButton rightLabel; ChatWidgetHeaderButton _rightLabel;
QMenu leftMenu; QMenu _leftMenu;
QMenu rightMenu; QMenu _rightMenu;
void leftButtonClicked(); void leftButtonClicked();
void rightButtonClicked(); void rightButtonClicked();

View file

@ -9,59 +9,55 @@ namespace widgets {
ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing) ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing)
: QWidget() : QWidget()
, hbox() , _hbox()
, label() , _label()
, mouseOver(false) , _mouseOver(false)
, mouseDown(false) , _mouseDown(false)
{ {
setLayout(&hbox); setLayout(&_hbox);
label.setAlignment(Qt::AlignCenter); _label.setAlignment(Qt::AlignCenter);
hbox.setMargin(0); _hbox.setMargin(0);
hbox.addSpacing(spacing); _hbox.addSpacing(spacing);
hbox.addWidget(&this->label); _hbox.addWidget(&_label);
hbox.addSpacing(spacing); _hbox.addSpacing(spacing);
QObject::connect(&this->label, &SignalLabel::mouseUp, this, QObject::connect(&_label, &SignalLabel::mouseUp, this,
&ChatWidgetHeaderButton::labelMouseUp); &ChatWidgetHeaderButton::labelMouseUp);
QObject::connect(&this->label, &SignalLabel::mouseDown, this, QObject::connect(&_label, &SignalLabel::mouseDown, this,
&ChatWidgetHeaderButton::labelMouseDown); &ChatWidgetHeaderButton::labelMouseDown);
} }
void void ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
QBrush brush(ColorScheme::getInstance().IsLightTheme QBrush brush(ColorScheme::getInstance().IsLightTheme ? QColor(0, 0, 0, 32)
? QColor(0, 0, 0, 32) : QColor(255, 255, 255, 32));
: QColor(255, 255, 255, 32));
if (this->mouseDown) { if (_mouseDown) {
painter.fillRect(rect(), brush); painter.fillRect(rect(), brush);
} }
if (this->mouseOver) { if (_mouseOver) {
painter.fillRect(rect(), brush); painter.fillRect(rect(), brush);
} }
} }
void void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->mouseDown = true; _mouseDown = true;
update(); update();
} }
} }
void void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
this->mouseDown = false; _mouseDown = false;
update(); update();
@ -69,36 +65,32 @@ ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
} }
} }
void void ChatWidgetHeaderButton::enterEvent(QEvent *)
ChatWidgetHeaderButton::enterEvent(QEvent *)
{ {
this->mouseOver = true; _mouseOver = true;
update(); update();
} }
void void ChatWidgetHeaderButton::leaveEvent(QEvent *)
ChatWidgetHeaderButton::leaveEvent(QEvent *)
{ {
this->mouseOver = false; _mouseOver = false;
update(); update();
} }
void void ChatWidgetHeaderButton::labelMouseUp()
ChatWidgetHeaderButton::labelMouseUp()
{ {
this->mouseDown = false; _mouseDown = false;
update(); update();
emit clicked(); emit clicked();
} }
void void ChatWidgetHeaderButton::labelMouseDown()
ChatWidgetHeaderButton::labelMouseDown()
{ {
this->mouseDown = true; _mouseDown = true;
update(); update();
} }

View file

@ -18,10 +18,9 @@ class ChatWidgetHeaderButton : public QWidget
public: public:
explicit ChatWidgetHeaderButton(int spacing = 6); explicit ChatWidgetHeaderButton(int spacing = 6);
SignalLabel & SignalLabel &getLabel()
getLabel()
{ {
return label; return _label;
} }
signals: signals:
@ -37,11 +36,11 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
private: private:
QHBoxLayout hbox; QHBoxLayout _hbox;
SignalLabel label; SignalLabel _label;
bool mouseOver; bool _mouseOver;
bool mouseDown; bool _mouseDown;
void labelMouseUp(); void labelMouseUp();
void labelMouseDown(); void labelMouseDown();

View file

@ -2,7 +2,7 @@
#include "chatwidget.h" #include "chatwidget.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "ircmanager.h" #include "ircmanager.h"
#include "settings.h" #include "settingsmanager.h"
#include <QCompleter> #include <QCompleter>
#include <QPainter> #include <QPainter>
@ -12,44 +12,43 @@ namespace chatterino {
namespace widgets { namespace widgets {
ChatWidgetInput::ChatWidgetInput(ChatWidget *widget) ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
: chatWidget(widget) : _chatWidget(widget)
, hbox() , _hbox()
, vbox() , _vbox()
, editContainer() , _editContainer()
, edit() , _edit()
, textLengthLabel() , _textLengthLabel()
, emotesLabel(0) , _emotesLabel(0)
{ {
this->setLayout(&this->hbox); setLayout(&_hbox);
this->setMaximumHeight(150); setMaximumHeight(150);
this->hbox.setMargin(4); _hbox.setMargin(4);
this->hbox.addLayout(&this->editContainer); _hbox.addLayout(&_editContainer);
this->hbox.addLayout(&this->vbox); _hbox.addLayout(&_vbox);
this->editContainer.addWidget(&this->edit); _editContainer.addWidget(&_edit);
this->editContainer.setMargin(4); _editContainer.setMargin(4);
this->vbox.addWidget(&this->textLengthLabel); _vbox.addWidget(&_textLengthLabel);
this->vbox.addStretch(1); _vbox.addStretch(1);
this->vbox.addWidget(&this->emotesLabel); _vbox.addWidget(&_emotesLabel);
this->textLengthLabel.setText("100"); _textLengthLabel.setText("100");
this->textLengthLabel.setAlignment(Qt::AlignRight); _textLengthLabel.setAlignment(Qt::AlignRight);
this->emotesLabel.getLabel().setTextFormat(Qt::RichText); _emotesLabel.getLabel().setTextFormat(Qt::RichText);
this->emotesLabel.getLabel().setText( _emotesLabel.getLabel().setText(
"<img src=':/images/Emoji_Color_1F60A_19.png' width='12' height='12' " "<img src=':/images/Emoji_Color_1F60A_19.png' width='12' height='12' "
"/>"); "/>");
QObject::connect(&edit, &ResizingTextEdit::textChanged, this, QObject::connect(&_edit, &ResizingTextEdit::textChanged, this,
&ChatWidgetInput::editTextChanged); &ChatWidgetInput::editTextChanged);
// QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this, // QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this,
// &ChatWidgetInput::editKeyPressed); // &ChatWidgetInput::editKeyPressed);
this->refreshTheme(); refreshTheme();
this->setMessageLengthVisisble( setMessageLengthVisisble(SettingsManager::getInstance().showMessageLength.get());
Settings::getInstance().showMessageLength.get());
QStringList list; QStringList list;
list.append("asd"); list.append("asd");
@ -57,20 +56,20 @@ ChatWidgetInput::ChatWidgetInput(ChatWidget *widget)
list.append("asdg"); list.append("asdg");
list.append("asdh"); list.append("asdh");
QCompleter *completer = new QCompleter(list, &edit); QCompleter *completer = new QCompleter(list, &_edit);
completer->setWidget(&edit); completer->setWidget(&_edit);
this->edit.keyPressed.connect([this, completer](QKeyEvent *event) { _edit.keyPressed.connect([this, completer](QKeyEvent *event) {
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
auto ptr = this->chatWidget->getChannel(); auto ptr = _chatWidget->getChannel();
Channel *c = ptr.get(); Channel *c = ptr.get();
if (c != nullptr) { if (c != nullptr) {
IrcManager::send("PRIVMSG #" + c->getName() + ": " + IrcManager::getInstance().send("PRIVMSG #" + c->getName() + ": " +
this->edit.toPlainText()); _edit.toPlainText());
event->accept(); event->accept();
this->edit.setText(QString()); _edit.setText(QString());
} }
} }
// else { // else {
@ -97,20 +96,18 @@ ChatWidgetInput::~ChatWidgetInput()
*/ */
} }
void void ChatWidgetInput::refreshTheme()
ChatWidgetInput::refreshTheme()
{ {
QPalette palette; QPalette palette;
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text);
this->textLengthLabel.setPalette(palette); _textLengthLabel.setPalette(palette);
edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet); _edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet);
} }
void void ChatWidgetInput::editTextChanged()
ChatWidgetInput::editTextChanged()
{ {
} }
@ -124,8 +121,7 @@ ChatWidgetInput::editTextChanged()
// } // }
//} //}
void void ChatWidgetInput::paintEvent(QPaintEvent *)
ChatWidgetInput::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -134,13 +130,12 @@ ChatWidgetInput::paintEvent(QPaintEvent *)
painter.drawRect(0, 0, width() - 1, height() - 1); painter.drawRect(0, 0, width() - 1, height() - 1);
} }
void void ChatWidgetInput::resizeEvent(QResizeEvent *)
ChatWidgetInput::resizeEvent(QResizeEvent *)
{ {
if (height() == this->maximumHeight()) { if (height() == maximumHeight()) {
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); _edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
} else { } else {
edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); _edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
} }
} }
} }

View file

@ -31,21 +31,20 @@ protected:
void resizeEvent(QResizeEvent *); void resizeEvent(QResizeEvent *);
private: private:
ChatWidget *chatWidget; ChatWidget *_chatWidget;
QHBoxLayout hbox; QHBoxLayout _hbox;
QVBoxLayout vbox; QVBoxLayout _vbox;
QHBoxLayout editContainer; QHBoxLayout _editContainer;
ResizingTextEdit edit; ResizingTextEdit _edit;
QLabel textLengthLabel; QLabel _textLengthLabel;
ChatWidgetHeaderButton emotesLabel; ChatWidgetHeaderButton _emotesLabel;
private slots: private slots:
void refreshTheme(); void refreshTheme();
void void setMessageLengthVisisble(bool value)
setMessageLengthVisisble(bool value)
{ {
this->textLengthLabel.setHidden(!value); _textLengthLabel.setHidden(!value);
} }
void editTextChanged(); void editTextChanged();
// void editKeyPressed(QKeyEvent *event); // void editKeyPressed(QKeyEvent *event);

View file

@ -1,9 +1,10 @@
#include "widgets/chatwidgetview.h" #include "widgets/chatwidgetview.h"
#include "channels.h" #include "channelmanager.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "messages/message.h" #include "messages/message.h"
#include "messages/wordpart.h" #include "messages/wordpart.h"
#include "settings.h" #include "settingsmanager.h"
#include "ui_userpopup.h"
#include "widgets/chatwidget.h" #include "widgets/chatwidget.h"
#include <math.h> #include <math.h>
@ -18,46 +19,46 @@ namespace widgets {
ChatWidgetView::ChatWidgetView(ChatWidget *parent) ChatWidgetView::ChatWidgetView(ChatWidget *parent)
: QWidget() : QWidget()
, chatWidget(parent) , _chatWidget(parent)
, scrollbar(this) , _scrollbar(this)
, onlyUpdateEmotes(false) , _userPopupWidget(_chatWidget->getChannel())
, _onlyUpdateEmotes(false)
, _mouseDown(false)
, _lastPressPosition()
{ {
this->setAttribute(Qt::WA_OpaquePaintEvent); this->setAttribute(Qt::WA_OpaquePaintEvent);
this->scrollbar.setSmallChange(5); _scrollbar.setSmallChange(5);
this->setMouseTracking(true); this->setMouseTracking(true);
QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged, QObject::connect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged, this,
this, &ChatWidgetView::wordTypeMaskChanged); &ChatWidgetView::wordTypeMaskChanged);
this->scrollbar.getCurrentValueChanged().connect([this] { update(); }); _scrollbar.getCurrentValueChanged().connect([this] { update(); });
} }
ChatWidgetView::~ChatWidgetView() ChatWidgetView::~ChatWidgetView()
{ {
QObject::disconnect(&Settings::getInstance(), QObject::disconnect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged,
&Settings::wordTypeMaskChanged, this, this, &ChatWidgetView::wordTypeMaskChanged);
&ChatWidgetView::wordTypeMaskChanged);
} }
bool bool ChatWidgetView::layoutMessages()
ChatWidgetView::layoutMessages()
{ {
auto messages = chatWidget->getMessagesSnapshot(); auto messages = _chatWidget->getMessagesSnapshot();
if (messages.getLength() == 0) { if (messages.getLength() == 0) {
this->scrollbar.setVisible(false); _scrollbar.setVisible(false);
return false; return false;
} }
bool showScrollbar = false, redraw = false; bool showScrollbar = false, redraw = false;
int start = this->scrollbar.getCurrentValue(); int start = _scrollbar.getCurrentValue();
// layout the visible messages in the view // layout the visible messages in the view
if (messages.getLength() > start) { if (messages.getLength() > start) {
int y = -(messages[start].get()->getHeight() * int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
(fmod(this->scrollbar.getCurrentValue(), 1)));
for (int i = start; i < messages.getLength(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
auto messagePtr = messages[i]; auto messagePtr = messages[i];
@ -84,39 +85,47 @@ ChatWidgetView::layoutMessages()
h -= message->getHeight(); h -= message->getHeight();
if (h < 0) { if (h < 0) {
this->scrollbar.setLargeChange((messages.getLength() - i) + _scrollbar.setLargeChange((messages.getLength() - i) + (qreal)h / message->getHeight());
(qreal)h / message->getHeight()); _scrollbar.setDesiredValue(_scrollbar.getDesiredValue());
this->scrollbar.setDesiredValue(this->scrollbar.getDesiredValue());
showScrollbar = true; showScrollbar = true;
break; break;
} }
} }
this->scrollbar.setVisible(showScrollbar); _scrollbar.setVisible(showScrollbar);
if (!showScrollbar) { if (!showScrollbar) {
this->scrollbar.setDesiredValue(0); _scrollbar.setDesiredValue(0);
} }
this->scrollbar.setMaximum(messages.getLength()); _scrollbar.setMaximum(messages.getLength());
return redraw; return redraw;
} }
void void ChatWidgetView::updateGifEmotes()
ChatWidgetView::resizeEvent(QResizeEvent *)
{ {
this->scrollbar.resize(this->scrollbar.width(), height()); _onlyUpdateEmotes = true;
this->scrollbar.move(width() - this->scrollbar.width(), 0); this->update();
}
ScrollBar *ChatWidgetView::getScrollbar()
{
return &_scrollbar;
}
void ChatWidgetView::resizeEvent(QResizeEvent *)
{
_scrollbar.resize(_scrollbar.width(), height());
_scrollbar.move(width() - _scrollbar.width(), 0);
layoutMessages(); layoutMessages();
update(); update();
} }
void void ChatWidgetView::paintEvent(QPaintEvent *event)
ChatWidgetView::paintEvent(QPaintEvent *event)
{ {
QPainter _painter(this); QPainter _painter(this);
@ -125,10 +134,10 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
ColorScheme &scheme = ColorScheme::getInstance(); ColorScheme &scheme = ColorScheme::getInstance();
// only update gif emotes // only update gif emotes
if (onlyUpdateEmotes) { if (_onlyUpdateEmotes) {
onlyUpdateEmotes = false; _onlyUpdateEmotes = false;
for (GifEmoteData &item : this->gifEmotes) { for (GifEmoteData &item : _gifEmotes) {
_painter.fillRect(item.rect, scheme.ChatBackground); _painter.fillRect(item.rect, scheme.ChatBackground);
_painter.drawPixmap(item.rect, *item.image->getPixmap()); _painter.drawPixmap(item.rect, *item.image->getPixmap());
@ -138,7 +147,7 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
} }
// update all messages // update all messages
gifEmotes.clear(); _gifEmotes.clear();
_painter.fillRect(rect(), scheme.ChatBackground); _painter.fillRect(rect(), scheme.ChatBackground);
@ -175,16 +184,15 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
painter.fillRect(QRect(0, 9, 500, 2), QColor(0, 0, 0));*/ painter.fillRect(QRect(0, 9, 500, 2), QColor(0, 0, 0));*/
auto messages = chatWidget->getMessagesSnapshot(); auto messages = _chatWidget->getMessagesSnapshot();
int start = this->scrollbar.getCurrentValue(); int start = _scrollbar.getCurrentValue();
if (start >= messages.getLength()) { if (start >= messages.getLength()) {
return; return;
} }
int y = -(messages[start].get()->getHeight() * int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1)));
(fmod(this->scrollbar.getCurrentValue(), 1)));
for (int i = start; i < messages.getLength(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
messages::MessageRef *messageRef = messages[i].get(); messages::MessageRef *messageRef = messages[i].get();
@ -205,20 +213,17 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
QPainter painter(buffer); QPainter painter(buffer);
painter.fillRect(buffer->rect(), scheme.ChatBackground); painter.fillRect(buffer->rect(), scheme.ChatBackground);
for (messages::WordPart const &wordPart : for (messages::WordPart const &wordPart : messageRef->getWordParts()) {
messageRef->getWordParts()) {
// image // image
if (wordPart.getWord().isImage()) { if (wordPart.getWord().isImage()) {
messages::LazyLoadedImage &lli = messages::LazyLoadedImage &lli = wordPart.getWord().getImage();
wordPart.getWord().getImage();
const QPixmap *image = lli.getPixmap(); const QPixmap *image = lli.getPixmap();
if (image != NULL) { if (image != NULL) {
painter.drawPixmap( painter.drawPixmap(QRect(wordPart.getX(), wordPart.getY(),
QRect(wordPart.getX(), wordPart.getY(), wordPart.getWidth(), wordPart.getHeight()),
wordPart.getWidth(), wordPart.getHeight()), *image);
*image);
} }
} }
// text // text
@ -230,10 +235,8 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
painter.setPen(color); painter.setPen(color);
painter.setFont(wordPart.getWord().getFont()); painter.setFont(wordPart.getWord().getFont());
painter.drawText( painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000), wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop));
wordPart.getText(),
QTextOption(Qt::AlignLeft | Qt::AlignTop));
} }
} }
@ -248,12 +251,12 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
if (lli.getAnimated()) { if (lli.getAnimated()) {
GifEmoteData data; GifEmoteData data;
data.image = &lli; data.image = &lli;
QRect rect(wordPart.getX(), wordPart.getY() + y, QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(),
wordPart.getWidth(), wordPart.getHeight()); wordPart.getHeight());
data.rect = rect; data.rect = rect;
gifEmotes.push_back(data); _gifEmotes.push_back(data);
} }
} }
} }
@ -269,27 +272,24 @@ ChatWidgetView::paintEvent(QPaintEvent *event)
} }
} }
for (GifEmoteData &item : this->gifEmotes) { for (GifEmoteData &item : _gifEmotes) {
_painter.fillRect(item.rect, scheme.ChatBackground); _painter.fillRect(item.rect, scheme.ChatBackground);
_painter.drawPixmap(item.rect, *item.image->getPixmap()); _painter.drawPixmap(item.rect, *item.image->getPixmap());
} }
} }
void void ChatWidgetView::wheelEvent(QWheelEvent *event)
ChatWidgetView::wheelEvent(QWheelEvent *event)
{ {
if (this->scrollbar.isVisible()) { if (_scrollbar.isVisible()) {
this->scrollbar.setDesiredValue( _scrollbar.setDesiredValue(
this->scrollbar.getDesiredValue() - _scrollbar.getDesiredValue() -
event->delta() / 10.0 * event->delta() / 10.0 * SettingsManager::getInstance().mouseScrollMultiplier.get(),
Settings::getInstance().mouseScrollMultiplier.get(),
true); true);
} }
} }
void void ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
{ {
std::shared_ptr<messages::MessageRef> message; std::shared_ptr<messages::MessageRef> message;
QPoint relativePos; QPoint relativePos;
@ -311,32 +311,86 @@ ChatWidgetView::mouseMoveEvent(QMouseEvent *event)
int index = message->getSelectionIndex(relativePos); int index = message->getSelectionIndex(relativePos);
qDebug() << index;
if (hoverWord.getLink().getIsValid()) { if (hoverWord.getLink().getIsValid()) {
this->setCursor(Qt::PointingHandCursor); this->setCursor(Qt::PointingHandCursor);
qDebug() << hoverWord.getLink().getValue();
} else { } else {
this->setCursor(Qt::ArrowCursor); this->setCursor(Qt::ArrowCursor);
} }
} }
bool void ChatWidgetView::mousePressEvent(QMouseEvent *event)
ChatWidgetView::tryGetMessageAt(QPoint p,
std::shared_ptr<messages::MessageRef> &_message,
QPoint &relativePos)
{ {
auto messages = chatWidget->getMessagesSnapshot(); _mouseDown = true;
_lastPressPosition = event->screenPos();
}
int start = this->scrollbar.getCurrentValue(); static float distanceBetweenPoints(const QPointF &p1, const QPointF &p2)
{
QPointF tmp = p1 - p2;
float distance = 0.f;
distance += tmp.x() * tmp.x();
distance += tmp.y() * tmp.y();
return std::sqrt(distance);
}
void ChatWidgetView::mouseReleaseEvent(QMouseEvent *event)
{
if (!_mouseDown) {
// We didn't grab the mouse press, so we shouldn't be handling the mouse
// release
return;
}
_mouseDown = false;
float distance = distanceBetweenPoints(_lastPressPosition, event->screenPos());
qDebug() << "Distance: " << distance;
if (fabsf(distance) > 15.f) {
// It wasn't a proper click, so we don't care about that here
return;
}
// If you clicked and released less than X pixels away, it counts
// as a click!
// show user thing pajaW
std::shared_ptr<messages::MessageRef> message;
QPoint relativePos;
if (!tryGetMessageAt(event->pos(), message, relativePos)) {
// No message at clicked position
_userPopupWidget.hide();
return;
}
auto _message = message->getMessage();
auto user = _message->getUserName();
qDebug() << "Clicked " << user << "s message";
_userPopupWidget.setName(user);
_userPopupWidget.move(event->screenPos().toPoint());
_userPopupWidget.show();
_userPopupWidget.setFocus();
}
bool ChatWidgetView::tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageRef> &_message,
QPoint &relativePos)
{
auto messages = _chatWidget->getMessagesSnapshot();
int start = _scrollbar.getCurrentValue();
if (start >= messages.getLength()) { if (start >= messages.getLength()) {
return false; return false;
} }
int y = -(messages[start].get()->getHeight() * int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))) + 12;
(fmod(this->scrollbar.getCurrentValue(), 1))) +
12;
for (int i = start; i < messages.getLength(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
auto message = messages[i]; auto message = messages[i];
@ -352,5 +406,5 @@ ChatWidgetView::tryGetMessageAt(QPoint p,
return false; return false;
} }
} } // namespace widgets
} } // namespace chatterino

View file

@ -6,6 +6,7 @@
#include "messages/messageref.h" #include "messages/messageref.h"
#include "messages/word.h" #include "messages/word.h"
#include "widgets/scrollbar.h" #include "widgets/scrollbar.h"
#include "widgets/userpopupwidget.h"
#include <QPaintEvent> #include <QPaintEvent>
#include <QScroller> #include <QScroller>
@ -18,20 +19,14 @@ class ChatWidget;
class ChatWidgetView : public QWidget class ChatWidgetView : public QWidget
{ {
Q_OBJECT
public: public:
explicit ChatWidgetView(ChatWidget *parent); explicit ChatWidgetView(ChatWidget *parent);
~ChatWidgetView(); ~ChatWidgetView();
bool layoutMessages(); bool layoutMessages();
void void updateGifEmotes();
updateGifEmotes() ScrollBar *getScrollbar();
{
this->onlyUpdateEmotes = true;
this->update();
}
protected: protected:
void resizeEvent(QResizeEvent *); void resizeEvent(QResizeEvent *);
@ -40,9 +35,10 @@ protected:
void wheelEvent(QWheelEvent *event); void wheelEvent(QWheelEvent *event);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
bool tryGetMessageAt(QPoint p, bool tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageRef> &message,
std::shared_ptr<messages::MessageRef> &message,
QPoint &relativePos); QPoint &relativePos);
private: private:
@ -51,22 +47,27 @@ private:
QRect rect; QRect rect;
}; };
std::vector<GifEmoteData> gifEmotes; std::vector<GifEmoteData> _gifEmotes;
ChatWidget *chatWidget; ChatWidget *_chatWidget;
ScrollBar scrollbar; ScrollBar _scrollbar;
bool onlyUpdateEmotes;
UserPopupWidget _userPopupWidget;
bool _onlyUpdateEmotes;
// Mouse event variables
bool _mouseDown;
QPointF _lastPressPosition;
private slots: private slots:
void void wordTypeMaskChanged()
wordTypeMaskChanged()
{ {
layoutMessages(); layoutMessages();
update(); update();
} }
}; };
} } // namespace widgets
} } // namespace chatterino
#endif // CHATVIEW_H #endif // CHATVIEW_H

141
widgets/fancybutton.cpp Normal file
View 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
View 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

View file

@ -1,24 +1,46 @@
#include "widgets/mainwindow.h" #include "widgets/mainwindow.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "settingsmanager.h"
#include "widgets/chatwidget.h" #include "widgets/chatwidget.h"
#include "widgets/notebook.h" #include "widgets/notebook.h"
#include <QDebug> #include <QDebug>
#include <QPalette> #include <QPalette>
#include <QVBoxLayout>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#ifdef USEWINSDK
#include "Windows.h"
#endif
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QWidget(parent)
, notebook(this) , _notebook(this)
, _loaded(false)
, _titleBar()
{ {
setCentralWidget(&this->notebook); QVBoxLayout *layout = new QVBoxLayout(this);
// add titlebar
// if (SettingsManager::getInstance().useCustomWindowFrame.get()) {
// layout->addWidget(&_titleBar);
// }
layout->addWidget(&_notebook);
setLayout(layout);
// set margin
// if (SettingsManager::getInstance().useCustomWindowFrame.get()) {
// layout->setMargin(1);
// } else {
layout->setMargin(0);
// }
QPalette palette; QPalette palette;
palette.setColor(QPalette::Background, palette.setColor(QPalette::Background, ColorScheme::getInstance().TabPanelBackground);
ColorScheme::getInstance().TabPanelBackground);
setPalette(palette); setPalette(palette);
resize(1280, 800); resize(1280, 800);
@ -28,10 +50,9 @@ MainWindow::~MainWindow()
{ {
} }
void void MainWindow::layoutVisibleChatWidgets(Channel *channel)
MainWindow::layoutVisibleChatWidgets(Channel *channel)
{ {
auto *page = notebook.getSelectedPage(); auto *page = _notebook.getSelectedPage();
if (page == NULL) { if (page == NULL) {
return; return;
@ -43,17 +64,14 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel)
ChatWidget *widget = *it; ChatWidget *widget = *it;
if (channel == NULL || channel == widget->getChannel().get()) { if (channel == NULL || channel == widget->getChannel().get()) {
if (widget->getView().layoutMessages()) { widget->layoutMessages();
widget->getView().update();
}
} }
} }
} }
void void MainWindow::repaintVisibleChatWidgets(Channel *channel)
MainWindow::repaintVisibleChatWidgets(Channel *channel)
{ {
auto *page = notebook.getSelectedPage(); auto *page = _notebook.getSelectedPage();
if (page == NULL) { if (page == NULL) {
return; return;
@ -65,16 +83,14 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel)
ChatWidget *widget = *it; ChatWidget *widget = *it;
if (channel == NULL || channel == widget->getChannel().get()) { if (channel == NULL || channel == widget->getChannel().get()) {
widget->getView().layoutMessages(); widget->layoutMessages();
widget->getView().update();
} }
} }
} }
void void MainWindow::repaintGifEmotes()
MainWindow::repaintGifEmotes()
{ {
auto *page = notebook.getSelectedPage(); auto *page = _notebook.getSelectedPage();
if (page == NULL) { if (page == NULL) {
return; return;
@ -85,37 +101,43 @@ MainWindow::repaintGifEmotes()
for (auto it = widgets.begin(); it != widgets.end(); ++it) { for (auto it = widgets.begin(); it != widgets.end(); ++it) {
ChatWidget *widget = *it; ChatWidget *widget = *it;
widget->getView().updateGifEmotes(); widget->updateGifEmotes();
} }
} }
void void MainWindow::load(const boost::property_tree::ptree &tree)
MainWindow::load(const boost::property_tree::ptree &tree)
{ {
this->notebook.load(tree); this->_notebook.load(tree);
this->loaded = true; _loaded = true;
} }
boost::property_tree::ptree boost::property_tree::ptree MainWindow::save()
MainWindow::save()
{ {
boost::property_tree::ptree child; boost::property_tree::ptree child;
child.put("type", "main"); child.put("type", "main");
this->notebook.save(child); _notebook.save(child);
return child; return child;
} }
void void MainWindow::loadDefaults()
MainWindow::loadDefaults()
{ {
this->notebook.loadDefaults(); _notebook.loadDefaults();
this->loaded = true; _loaded = true;
} }
bool MainWindow::isLoaded() const
{
return _loaded;
}
Notebook &MainWindow::getNotebook()
{
return _notebook;
}
} // namespace widgets } // namespace widgets
} // namespace chatterino } // namespace chatterino

View file

@ -2,38 +2,39 @@
#define MAINWINDOW_H #define MAINWINDOW_H
#include "widgets/notebook.h" #include "widgets/notebook.h"
#include "widgets/titlebar.h"
#include <platform/borderless/qwinwidget.h>
#include <QMainWindow> #include <QMainWindow>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
class MainWindow : public QMainWindow class MainWindow : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = 0); explicit MainWindow(QWidget *parent = 0);
~MainWindow(); ~MainWindow();
Notebook notebook;
void layoutVisibleChatWidgets(Channel *channel = NULL); void layoutVisibleChatWidgets(Channel *channel = nullptr);
void repaintVisibleChatWidgets(Channel *channel = NULL); void repaintVisibleChatWidgets(Channel *channel = nullptr);
void repaintGifEmotes(); void repaintGifEmotes();
void load(const boost::property_tree::ptree &tree); void load(const boost::property_tree::ptree &tree);
boost::property_tree::ptree save(); boost::property_tree::ptree save();
void loadDefaults(); void loadDefaults();
bool bool isLoaded() const;
isLoaded() const
{ Notebook &getNotebook();
return this->loaded;
}
private: private:
bool loaded = false; Notebook _notebook;
bool _loaded;
TitleBar _titleBar;
}; };
} // namespace widgets } // namespace widgets

View file

@ -19,106 +19,99 @@ namespace widgets {
Notebook::Notebook(QWidget *parent) Notebook::Notebook(QWidget *parent)
: QWidget(parent) : QWidget(parent)
, addButton(this) , _addButton(this)
, settingsButton(this) , _settingsButton(this)
, userButton(this) , _userButton(this)
, selectedPage(nullptr) , _selectedPage(nullptr)
{ {
connect(&this->settingsButton, SIGNAL(clicked()), this, connect(&_settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
SLOT(settingsButtonClicked())); connect(&_userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked()));
connect(&this->userButton, SIGNAL(clicked()), this, connect(&_addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked()));
SLOT(usersButtonClicked()));
connect(&this->addButton, SIGNAL(clicked()), this,
SLOT(addPageButtonClicked()));
this->settingsButton.resize(24, 24); _settingsButton.resize(24, 24);
this->settingsButton.icon = NotebookButton::IconSettings; _settingsButton.icon = NotebookButton::IconSettings;
this->userButton.resize(24, 24); _userButton.resize(24, 24);
this->userButton.move(24, 0); _userButton.move(24, 0);
this->userButton.icon = NotebookButton::IconUser; _userButton.icon = NotebookButton::IconUser;
this->addButton.resize(24, 24); _addButton.resize(24, 24);
Settings::getInstance().hidePreferencesButton.valueChanged.connect( SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect(
[this](const bool &) { this->performLayout(); }); [this](const bool &) { performLayout(); });
Settings::getInstance().hideUserButton.valueChanged.connect( SettingsManager::getInstance().hideUserButton.valueChanged.connect(
[this](const bool &) { this->performLayout(); }); [this](const bool &) { performLayout(); });
} }
NotebookPage * NotebookPage *Notebook::addPage(bool select)
Notebook::addPage(bool select)
{ {
auto tab = new NotebookTab(this); auto tab = new NotebookTab(this);
auto page = new NotebookPage(this, tab); auto page = new NotebookPage(this, tab);
tab->show(); tab->show();
if (select || this->pages.count() == 0) { if (select || _pages.count() == 0) {
this->select(page); this->select(page);
} }
this->pages.append(page); _pages.append(page);
this->performLayout(); performLayout();
return page; return page;
} }
void void Notebook::removePage(NotebookPage *page)
Notebook::removePage(NotebookPage *page)
{ {
int index = this->pages.indexOf(page); int index = _pages.indexOf(page);
if (pages.size() == 1) { if (_pages.size() == 1) {
this->select(NULL); select(NULL);
} else if (index == pages.count() - 1) { } else if (index == _pages.count() - 1) {
this->select(pages[index - 1]); select(_pages[index - 1]);
} else { } else {
this->select(pages[index + 1]); select(_pages[index + 1]);
} }
delete page->tab; delete page->getTab();
delete page; delete page;
this->pages.removeOne(page); _pages.removeOne(page);
if (this->pages.size() == 0) { if (_pages.size() == 0) {
addPage(); addPage();
} }
performLayout(); performLayout();
} }
void void Notebook::select(NotebookPage *page)
Notebook::select(NotebookPage *page)
{ {
if (page == this->selectedPage) if (page == _selectedPage)
return; return;
if (page != nullptr) { if (page != nullptr) {
page->setHidden(false); page->setHidden(false);
page->tab->setSelected(true); page->getTab()->setSelected(true);
page->tab->raise(); page->getTab()->raise();
} }
if (this->selectedPage != nullptr) { if (_selectedPage != nullptr) {
this->selectedPage->setHidden(true); _selectedPage->setHidden(true);
this->selectedPage->tab->setSelected(false); _selectedPage->getTab()->setSelected(false);
} }
this->selectedPage = page; _selectedPage = page;
performLayout(); performLayout();
} }
NotebookPage * NotebookPage *Notebook::tabAt(QPoint point, int &index)
Notebook::tabAt(QPoint point, int &index)
{ {
int i = 0; int i = 0;
for (auto *page : pages) { for (auto *page : _pages) {
if (page->tab->getDesiredRect().contains(point)) { if (page->getTab()->getDesiredRect().contains(point)) {
index = i; index = i;
return page; return page;
} }
@ -130,97 +123,87 @@ Notebook::tabAt(QPoint point, int &index)
return nullptr; return nullptr;
} }
void void Notebook::rearrangePage(NotebookPage *page, int index)
Notebook::rearrangePage(NotebookPage *page, int index)
{ {
pages.move(pages.indexOf(page), index); _pages.move(_pages.indexOf(page), index);
performLayout(); performLayout();
} }
void void Notebook::performLayout(bool animated)
Notebook::performLayout(bool animated)
{ {
int x = 0, y = 0; int x = 0, y = 0;
if (Settings::getInstance().hidePreferencesButton.get()) { if (SettingsManager::getInstance().hidePreferencesButton.get()) {
settingsButton.hide(); _settingsButton.hide();
} else { } else {
settingsButton.show(); _settingsButton.show();
x += 24; x += 24;
} }
if (Settings::getInstance().hideUserButton.get()) { if (SettingsManager::getInstance().hideUserButton.get()) {
userButton.hide(); _userButton.hide();
} else { } else {
userButton.move(x, 0); _userButton.move(x, 0);
userButton.show(); _userButton.show();
x += 24; x += 24;
} }
int tabHeight = 16; int tabHeight = 16;
bool first = true; bool first = true;
for (auto &i : this->pages) { for (auto &i : _pages) {
tabHeight = i->tab->height(); tabHeight = i->getTab()->height();
if (!first && if (!first && (i == _pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
(i == this->pages.last() ? tabHeight : 0) + x + i->tab->width() > y += i->getTab()->height();
width()) { i->getTab()->moveAnimated(QPoint(0, y), animated);
y += i->tab->height(); x = i->getTab()->width();
i->tab->moveAnimated(QPoint(0, y), animated);
x = i->tab->width();
} else { } else {
i->tab->moveAnimated(QPoint(x, y), animated); i->getTab()->moveAnimated(QPoint(x, y), animated);
x += i->tab->width(); x += i->getTab()->width();
} }
first = false; first = false;
} }
this->addButton.move(x, y); _addButton.move(x, y);
if (this->selectedPage != nullptr) { if (_selectedPage != nullptr) {
this->selectedPage->move(0, y + tabHeight); _selectedPage->move(0, y + tabHeight);
this->selectedPage->resize(width(), height() - y - tabHeight); _selectedPage->resize(width(), height() - y - tabHeight);
} }
} }
void void Notebook::resizeEvent(QResizeEvent *)
Notebook::resizeEvent(QResizeEvent *)
{ {
performLayout(false); performLayout(false);
} }
void void Notebook::settingsButtonClicked()
Notebook::settingsButtonClicked()
{ {
SettingsDialog *a = new SettingsDialog(); SettingsDialog *a = new SettingsDialog();
a->show(); a->show();
} }
void void Notebook::usersButtonClicked()
Notebook::usersButtonClicked()
{ {
} }
void void Notebook::addPageButtonClicked()
Notebook::addPageButtonClicked()
{ {
addPage(true); addPage(true);
} }
void void Notebook::load(const boost::property_tree::ptree &tree)
Notebook::load(const boost::property_tree::ptree &tree)
{ {
// Read a list of tabs // Read a list of tabs
try { try {
BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, tree.get_child("tabs.")) {
tree.get_child("tabs.")) {
bool select = v.second.get<bool>("selected", false); bool select = v.second.get<bool>("selected", false);
auto page = this->addPage(select); auto page = addPage(select);
auto tab = page->tab; auto tab = page->getTab();
tab->load(v.second); tab->load(v.second);
page->load(v.second); page->load(v.second);
} }
@ -228,20 +211,19 @@ Notebook::load(const boost::property_tree::ptree &tree)
// can't read tabs // can't read tabs
} }
if (this->pages.size() == 0) { if (_pages.size() == 0) {
// No pages saved, show default stuff // No pages saved, show default stuff
this->loadDefaults(); loadDefaults();
} }
} }
void void Notebook::save(boost::property_tree::ptree &tree)
Notebook::save(boost::property_tree::ptree &tree)
{ {
boost::property_tree::ptree tabs; boost::property_tree::ptree tabs;
// Iterate through all tabs and add them to our tabs property thing // Iterate through all tabs and add them to our tabs property thing
for (const auto &page : this->pages) { for (const auto &page : _pages) {
boost::property_tree::ptree pTab = page->tab->save(); boost::property_tree::ptree pTab = page->getTab()->save();
boost::property_tree::ptree pChats = page->save(); boost::property_tree::ptree pChats = page->save();
@ -255,10 +237,9 @@ Notebook::save(boost::property_tree::ptree &tree)
tree.add_child("tabs", tabs); tree.add_child("tabs", tabs);
} }
void void Notebook::loadDefaults()
Notebook::loadDefaults()
{ {
this->addPage(); addPage();
} }
} // namespace widgets } // namespace widgets

View file

@ -26,10 +26,9 @@ public:
void removePage(NotebookPage *page); void removePage(NotebookPage *page);
void select(NotebookPage *page); void select(NotebookPage *page);
NotebookPage * NotebookPage *getSelectedPage()
getSelectedPage()
{ {
return selectedPage; return _selectedPage;
} }
void performLayout(bool animate = true); void performLayout(bool animate = true);
@ -48,13 +47,13 @@ public slots:
void addPageButtonClicked(); void addPageButtonClicked();
private: private:
QList<NotebookPage *> pages; QList<NotebookPage *> _pages;
NotebookButton addButton; NotebookButton _addButton;
NotebookButton settingsButton; NotebookButton _settingsButton;
NotebookButton userButton; NotebookButton _userButton;
NotebookPage *selectedPage; NotebookPage *_selectedPage;
public: public:
void load(const boost::property_tree::ptree &tree); void load(const boost::property_tree::ptree &tree);

View file

@ -1,20 +1,22 @@
#include "widgets/notebookbutton.h" #include "widgets/notebookbutton.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "widgets/fancybutton.h"
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QPainterPath> #include <QPainterPath>
#include <QRadialGradient>
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
NotebookButton::NotebookButton(QWidget *parent) NotebookButton::NotebookButton(QWidget *parent)
: QWidget(parent) : FancyButton(parent)
{ {
setMouseEffectColor(QColor(0, 0, 0));
} }
void void NotebookButton::paintEvent(QPaintEvent *)
NotebookButton::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -23,29 +25,30 @@ NotebookButton::paintEvent(QPaintEvent *)
auto &colorScheme = ColorScheme::getInstance(); auto &colorScheme = ColorScheme::getInstance();
if (mouseDown) { if (_mouseDown) {
background = colorScheme.TabSelectedBackground; background = colorScheme.TabSelectedBackground;
foreground = colorScheme.TabSelectedText; foreground = colorScheme.TabSelectedText;
} else if (mouseOver) { } else if (_mouseOver) {
background = colorScheme.TabHoverBackground; background = colorScheme.TabHoverBackground;
foreground = colorScheme.TabSelectedBackground; foreground = colorScheme.TabSelectedBackground;
} else { } else {
background = colorScheme.TabPanelBackground; background = colorScheme.TabPanelBackground;
foreground = colorScheme.TabSelectedBackground; // foreground = colorScheme.TabSelectedBackground;
foreground = QColor(230, 230, 230);
} }
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
painter.fillRect(this->rect(), background); painter.fillRect(this->rect(), background);
float h = this->height(), w = this->width(); float h = height(), w = width();
if (icon == IconPlus) { if (icon == IconPlus) {
painter.fillRect(QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, painter.fillRect(
w - ((h / 12) * 5), (h / 12) * 1), QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, w - ((h / 12) * 5), (h / 12) * 1),
foreground); foreground);
painter.fillRect(QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, painter.fillRect(
(h / 12) * 1, w - ((h / 12) * 5)), QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, (h / 12) * 1, w - ((h / 12) * 5)),
foreground); foreground);
} else if (icon == IconUser) { } else if (icon == IconUser) {
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.setRenderHint(QPainter::HighQualityAntialiasing);
@ -74,10 +77,8 @@ NotebookButton::paintEvent(QPaintEvent *)
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0)); path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), (360 / 32.0));
(360 / 32.0)); path.arcTo(2 * a, 2 * a, 4 * a, 4 * a, i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a,
i * (360 / 8.0) + (360 / 32.0), (360 / 32.0));
} }
painter.fillPath(path, foreground); painter.fillPath(path, foreground);
@ -85,44 +86,21 @@ NotebookButton::paintEvent(QPaintEvent *)
painter.setBrush(background); painter.setBrush(background);
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a); painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
} }
fancyPaint(painter);
} }
void void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
NotebookButton::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
mouseDown = true; _mouseDown = false;
this->update(); update();
}
}
void
NotebookButton::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
mouseDown = false;
this->update();
emit clicked(); emit clicked();
} }
}
void FancyButton::mouseReleaseEvent(event);
NotebookButton::enterEvent(QEvent *)
{
mouseOver = true;
this->update();
}
void
NotebookButton::leaveEvent(QEvent *)
{
mouseOver = false;
this->update();
} }
} }
} }

View file

@ -1,14 +1,17 @@
#ifndef NOTEBOOKBUTTON_H #ifndef NOTEBOOKBUTTON_H
#define NOTEBOOKBUTTON_H #define NOTEBOOKBUTTON_H
#include "fancybutton.h"
#include <QWidget> #include <QWidget>
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
class NotebookButton : public QWidget class NotebookButton : public FancyButton
{ {
Q_OBJECT Q_OBJECT
public: public:
static const int IconPlus = 0; static const int IconPlus = 0;
static const int IconUser = 1; static const int IconUser = 1;
@ -18,18 +21,17 @@ public:
NotebookButton(QWidget *parent); NotebookButton(QWidget *parent);
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *) override;
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) override;
void enterEvent(QEvent *) Q_DECL_OVERRIDE;
void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
signals: signals:
void clicked(); void clicked();
private: private:
bool mouseOver = false; bool _mouseOver = false;
bool mouseDown = false; bool _mouseDown = false;
QPoint _mousePos;
}; };
} }
} }

View file

@ -21,49 +21,60 @@ std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1);
NotebookPage::NotebookPage(QWidget *parent, NotebookTab *tab) NotebookPage::NotebookPage(QWidget *parent, NotebookTab *tab)
: QWidget(parent) : QWidget(parent)
, parentbox(this) , _tab(tab)
, chatWidgets() , _parentbox(this)
, preview(this) , _chatWidgets()
, _preview(this)
{ {
this->tab = tab;
tab->page = this; tab->page = this;
setHidden(true); setHidden(true);
setAcceptDrops(true); setAcceptDrops(true);
this->parentbox.addSpacing(2); _parentbox.addSpacing(2);
this->parentbox.addLayout(&this->hbox); _parentbox.addLayout(&_hbox);
this->parentbox.setMargin(0); _parentbox.setMargin(0);
this->hbox.setSpacing(1); _hbox.setSpacing(1);
this->hbox.setMargin(0); _hbox.setMargin(0);
} }
std::pair<int, int> const std::vector<ChatWidget *> &NotebookPage::getChatWidgets() const
NotebookPage::removeFromLayout(ChatWidget *widget)
{ {
for (auto it = this->chatWidgets.begin(); it != this->chatWidgets.end(); return _chatWidgets;
++it) { }
NotebookTab *NotebookPage::getTab() const
{
return _tab;
}
std::pair<int, int> NotebookPage::removeFromLayout(ChatWidget *widget)
{
// remove from chatWidgets vector
for (auto it = _chatWidgets.begin(); it != _chatWidgets.end(); ++it) {
if (*it == widget) { if (*it == widget) {
this->chatWidgets.erase(it); _chatWidgets.erase(it);
break; break;
} }
} }
for (int i = 0; i < this->hbox.count(); ++i) { // remove from box and return location
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i)); for (int i = 0; i < _hbox.count(); ++i) {
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(i));
for (int j = 0; j < vbox->count(); ++j) { for (int j = 0; j < vbox->count(); ++j) {
if (vbox->itemAt(j)->widget() != widget) if (vbox->itemAt(j)->widget() != widget) {
continue; continue;
}
widget->setParent(NULL); widget->setParent(NULL);
bool isLastItem = vbox->count() == 0; bool isLastItem = vbox->count() == 0;
if (isLastItem) { if (isLastItem) {
this->hbox.removeItem(vbox); _hbox.removeItem(vbox);
delete vbox; delete vbox;
} }
@ -75,19 +86,17 @@ NotebookPage::removeFromLayout(ChatWidget *widget)
return std::pair<int, int>(-1, -1); return std::pair<int, int>(-1, -1);
} }
void void NotebookPage::addToLayout(ChatWidget *widget,
NotebookPage::addToLayout( std::pair<int, int> position = std::pair<int, int>(-1, -1))
ChatWidget *widget,
std::pair<int, int> position = std::pair<int, int>(-1, -1))
{ {
this->chatWidgets.push_back(widget); _chatWidgets.push_back(widget);
// add vbox at the end // add vbox at the end
if (position.first < 0 || position.first >= this->hbox.count()) { if (position.first < 0 || position.first >= _hbox.count()) {
auto vbox = new QVBoxLayout(); auto vbox = new QVBoxLayout();
vbox->addWidget(widget); vbox->addWidget(widget);
this->hbox.addLayout(vbox, 1); _hbox.addLayout(vbox, 1);
return; return;
} }
@ -96,101 +105,91 @@ NotebookPage::addToLayout(
auto vbox = new QVBoxLayout(); auto vbox = new QVBoxLayout();
vbox->addWidget(widget); vbox->addWidget(widget);
this->hbox.insertLayout(position.first, vbox, 1); _hbox.insertLayout(position.first, vbox, 1);
return; return;
} }
// add to existing vbox // add to existing vbox
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(position.first)); auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(position.first));
vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget);
widget);
} }
void void NotebookPage::enterEvent(QEvent *)
NotebookPage::enterEvent(QEvent *)
{ {
if (this->hbox.count() == 0) { if (_hbox.count() == 0) {
setCursor(QCursor(Qt::PointingHandCursor)); setCursor(QCursor(Qt::PointingHandCursor));
} else { } else {
setCursor(QCursor(Qt::ArrowCursor)); setCursor(QCursor(Qt::ArrowCursor));
} }
} }
void void NotebookPage::leaveEvent(QEvent *)
NotebookPage::leaveEvent(QEvent *)
{ {
} }
void void NotebookPage::mouseReleaseEvent(QMouseEvent *event)
NotebookPage::mouseReleaseEvent(QMouseEvent *event)
{ {
if (this->hbox.count() == 0 && event->button() == Qt::LeftButton) { if (_hbox.count() == 0 && event->button() == Qt::LeftButton) {
// "Add Chat" was clicked // "Add Chat" was clicked
this->addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1)); addToLayout(new ChatWidget(), std::pair<int, int>(-1, -1));
setCursor(QCursor(Qt::ArrowCursor)); setCursor(QCursor(Qt::ArrowCursor));
} }
} }
void void NotebookPage::dragEnterEvent(QDragEnterEvent *event)
NotebookPage::dragEnterEvent(QDragEnterEvent *event)
{ {
if (!event->mimeData()->hasFormat("chatterino/split")) if (!event->mimeData()->hasFormat("chatterino/split"))
return; return;
if (isDraggingSplit) { if (isDraggingSplit) {
this->dropRegions.clear(); return;
}
if (this->hbox.count() == 0) { _dropRegions.clear();
this->dropRegions.push_back(
DropRegion(rect(), std::pair<int, int>(-1, -1)));
} else {
for (int i = 0; i < this->hbox.count() + 1; ++i) {
this->dropRegions.push_back(DropRegion(
QRect(((i * 4 - 1) * width() / this->hbox.count()) / 4, 0,
width() / this->hbox.count() / 2 + 1, height() + 1),
std::pair<int, int>(i, -1))); if (_hbox.count() == 0) {
} _dropRegions.push_back(DropRegion(rect(), std::pair<int, int>(-1, -1)));
} else {
for (int i = 0; i < this->hbox.count(); ++i) { for (int i = 0; i < _hbox.count() + 1; ++i) {
auto vbox = static_cast<QVBoxLayout *>(this->hbox.itemAt(i)); _dropRegions.push_back(DropRegion(QRect(((i * 4 - 1) * width() / _hbox.count()) / 4, 0,
width() / _hbox.count() / 2 + 1, height() + 1),
for (int j = 0; j < vbox->count() + 1; ++j) { std::pair<int, int>(i, -1)));
this->dropRegions.push_back(DropRegion(
QRect(i * width() / this->hbox.count(),
((j * 2 - 1) * height() / vbox->count()) / 2,
width() / this->hbox.count() + 1,
height() / vbox->count() + 1),
std::pair<int, int>(i, j)));
}
}
} }
setPreviewRect(event->pos()); for (int i = 0; i < _hbox.count(); ++i) {
auto vbox = static_cast<QVBoxLayout *>(_hbox.itemAt(i));
event->acceptProposedAction(); for (int j = 0; j < vbox->count() + 1; ++j) {
_dropRegions.push_back(DropRegion(
QRect(i * width() / _hbox.count(), ((j * 2 - 1) * height() / vbox->count()) / 2,
width() / _hbox.count() + 1, height() / vbox->count() + 1),
std::pair<int, int>(i, j)));
}
}
} }
setPreviewRect(event->pos());
event->acceptProposedAction();
} }
void void NotebookPage::dragMoveEvent(QDragMoveEvent *event)
NotebookPage::dragMoveEvent(QDragMoveEvent *event)
{ {
setPreviewRect(event->pos()); setPreviewRect(event->pos());
} }
void void NotebookPage::setPreviewRect(QPoint mousePos)
NotebookPage::setPreviewRect(QPoint mousePos)
{ {
for (DropRegion region : this->dropRegions) { for (DropRegion region : _dropRegions) {
if (region.rect.contains(mousePos)) { if (region.rect.contains(mousePos)) {
this->preview.setBounds(region.rect); _preview.setBounds(region.rect);
if (!this->preview.isVisible()) { if (!_preview.isVisible()) {
this->preview.show(); _preview.show();
this->preview.raise(); _preview.raise();
} }
dropPosition = region.position; dropPosition = region.position;
@ -199,17 +198,15 @@ NotebookPage::setPreviewRect(QPoint mousePos)
} }
} }
this->preview.hide(); _preview.hide();
} }
void void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
NotebookPage::dragLeaveEvent(QDragLeaveEvent *event)
{ {
this->preview.hide(); _preview.hide();
} }
void void NotebookPage::dropEvent(QDropEvent *event)
NotebookPage::dropEvent(QDropEvent *event)
{ {
if (isDraggingSplit) { if (isDraggingSplit) {
event->acceptProposedAction(); event->acceptProposedAction();
@ -219,33 +216,28 @@ NotebookPage::dropEvent(QDropEvent *event)
addToLayout(NotebookPage::draggingSplit, dropPosition); addToLayout(NotebookPage::draggingSplit, dropPosition);
} }
this->preview.hide(); _preview.hide();
} }
void void NotebookPage::paintEvent(QPaintEvent *)
NotebookPage::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
if (this->hbox.count() == 0) { if (_hbox.count() == 0) {
painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground); painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground);
painter.fillRect(0, 0, width(), 2, painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
ColorScheme::getInstance().TabSelectedBackground);
painter.setPen(ColorScheme::getInstance().Text); painter.setPen(ColorScheme::getInstance().Text);
painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter)); painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
} else { } else {
painter.fillRect(rect(), painter.fillRect(rect(), ColorScheme::getInstance().TabSelectedBackground);
ColorScheme::getInstance().TabSelectedBackground);
painter.fillRect(0, 0, width(), 2, painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground);
ColorScheme::getInstance().TabSelectedBackground);
} }
} }
static std::pair<int, int> static std::pair<int, int> getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
{ {
for (int i = 0; i < layout->count(); ++i) { for (int i = 0; i < layout->count(); ++i) {
printf("xD\n"); printf("xD\n");
@ -254,10 +246,9 @@ getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget)
return std::make_pair(-1, -1); return std::make_pair(-1, -1);
} }
std::pair<int, int> std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
NotebookPage::getChatPosition(const ChatWidget *chatWidget)
{ {
auto layout = this->hbox.layout(); auto layout = _hbox.layout();
if (layout == nullptr) { if (layout == nullptr) {
return std::make_pair(-1, -1); return std::make_pair(-1, -1);
@ -266,8 +257,7 @@ NotebookPage::getChatPosition(const ChatWidget *chatWidget)
return getWidgetPositionInLayout(layout, chatWidget); return getWidgetPositionInLayout(layout, chatWidget);
} }
void void NotebookPage::load(const boost::property_tree::ptree &tree)
NotebookPage::load(const boost::property_tree::ptree &tree)
{ {
try { try {
int column = 0; int column = 0;
@ -276,7 +266,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
for (const auto &innerV : v.second.get_child("")) { for (const auto &innerV : v.second.get_child("")) {
auto widget = new ChatWidget(); auto widget = new ChatWidget();
widget->load(innerV.second); widget->load(innerV.second);
this->addToLayout(widget, std::pair<int, int>(column, row)); addToLayout(widget, std::pair<int, int>(column, row));
++row; ++row;
} }
++column; ++column;
@ -286,8 +276,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree)
} }
} }
static void static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
{ {
for (int i = 0; i < layout->count(); ++i) { for (int i = 0; i < layout->count(); ++i) {
auto item = layout->itemAt(i); auto item = layout->itemAt(i);
@ -323,17 +312,16 @@ saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree)
} }
} }
boost::property_tree::ptree boost::property_tree::ptree NotebookPage::save()
NotebookPage::save()
{ {
boost::property_tree::ptree tree; boost::property_tree::ptree tree;
auto layout = this->hbox.layout(); auto layout = _hbox.layout();
saveFromLayout(layout, tree); saveFromLayout(layout, tree);
/* /*
for (const auto &chat : this->chatWidgets) { for (const auto &chat : chatWidgets) {
boost::property_tree::ptree child = chat->save(); boost::property_tree::ptree child = chat->save();
// Set child position // Set child position

View file

@ -23,34 +23,31 @@ class NotebookPage : public QWidget
Q_OBJECT Q_OBJECT
public: public:
NotebookPage(QWidget *parent, NotebookTab *tab); NotebookPage(QWidget *parent, NotebookTab *_tab);
NotebookTab *tab;
std::pair<int, int> removeFromLayout(ChatWidget *widget); std::pair<int, int> removeFromLayout(ChatWidget *widget);
void addToLayout(ChatWidget *widget, std::pair<int, int> position); void addToLayout(ChatWidget *widget, std::pair<int, int> position);
const std::vector<ChatWidget *> & const std::vector<ChatWidget *> &getChatWidgets() const;
getChatWidgets() const NotebookTab *getTab() const;
{
return chatWidgets;
}
static bool isDraggingSplit; static bool isDraggingSplit;
static ChatWidget *draggingSplit; static ChatWidget *draggingSplit;
static std::pair<int, int> dropPosition; static std::pair<int, int> dropPosition;
protected: protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *) override;
void enterEvent(QEvent *) override; void enterEvent(QEvent *) override;
void leaveEvent(QEvent *) override; void leaveEvent(QEvent *) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE; void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; void dragLeaveEvent(QDragLeaveEvent *event) override;
void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE; void dropEvent(QDropEvent *event) override;
private:
struct DropRegion { struct DropRegion {
QRect rect; QRect rect;
std::pair<int, int> position; std::pair<int, int> position;
@ -62,15 +59,16 @@ protected:
} }
}; };
QVBoxLayout parentbox; NotebookTab *_tab;
QHBoxLayout hbox;
std::vector<ChatWidget *> chatWidgets; QVBoxLayout _parentbox;
std::vector<DropRegion> dropRegions; QHBoxLayout _hbox;
NotebookPageDropPreview preview; std::vector<ChatWidget *> _chatWidgets;
std::vector<DropRegion> _dropRegions;
NotebookPageDropPreview _preview;
private:
void setPreviewRect(QPoint mousePos); void setPreviewRect(QPoint mousePos);
std::pair<int, int> getChatPosition(const ChatWidget *chatWidget); std::pair<int, int> getChatPosition(const ChatWidget *chatWidget);

View file

@ -17,8 +17,7 @@ NotebookPageDropPreview::NotebookPageDropPreview(QWidget *parent)
this->setHidden(true); this->setHidden(true);
} }
void void NotebookPageDropPreview::paintEvent(QPaintEvent *)
NotebookPageDropPreview::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -26,14 +25,12 @@ NotebookPageDropPreview::paintEvent(QPaintEvent *)
ColorScheme::getInstance().DropPreviewBackground); ColorScheme::getInstance().DropPreviewBackground);
} }
void void NotebookPageDropPreview::hideEvent(QHideEvent *)
NotebookPageDropPreview::hideEvent(QHideEvent *)
{ {
animate = false; animate = false;
} }
void void NotebookPageDropPreview::setBounds(const QRect &rect)
NotebookPageDropPreview::setBounds(const QRect &rect)
{ {
if (rect == this->desiredGeometry) { if (rect == this->desiredGeometry) {
return; return;

View file

@ -1,6 +1,6 @@
#include "widgets/notebooktab.h" #include "widgets/notebooktab.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "settings.h" #include "settingsmanager.h"
#include "widgets/notebook.h" #include "widgets/notebook.h"
#include <QPainter> #include <QPainter>
@ -10,75 +10,113 @@ namespace widgets {
NotebookTab::NotebookTab(Notebook *notebook) NotebookTab::NotebookTab(Notebook *notebook)
: QWidget(notebook) : QWidget(notebook)
, posAnimation(this, "pos") , _posAnimation(this, "pos")
, posAnimated(false) , _posAnimated(false)
, posAnimationDesired() , _posAnimationDesired()
, notebook(notebook) , _notebook(notebook)
, title("<no title>") , _title("<no title>")
, selected(false) , _selected(false)
, mouseOver(false) , _mouseOver(false)
, mouseDown(false) , _mouseDown(false)
, mouseOverX(false) , _mouseOverX(false)
, mouseDownX(false) , _mouseDownX(false)
, highlightStyle(HighlightNone) , _highlightStyle(HighlightNone)
{ {
this->calcSize(); this->calcSize();
this->setAcceptDrops(true); this->setAcceptDrops(true);
posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); _posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
this->hideXConnection = this->_hideXConnection = SettingsManager::getInstance().hideTabX.valueChanged.connect(
Settings::getInstance().hideTabX.valueChanged.connect( boost::bind(&NotebookTab::hideTabXChanged, this, _1));
boost::bind(&NotebookTab::hideTabXChanged, this, _1));
this->setMouseTracking(true); this->setMouseTracking(true);
} }
NotebookTab::~NotebookTab() NotebookTab::~NotebookTab()
{ {
this->hideXConnection.disconnect(); this->_hideXConnection.disconnect();
} }
void void NotebookTab::calcSize()
NotebookTab::calcSize()
{ {
if (Settings::getInstance().hideTabX.get()) { if (SettingsManager::getInstance().hideTabX.get()) {
this->resize(this->fontMetrics().width(this->title) + 8, 24); resize(fontMetrics().width(_title) + 8, 24);
} else { } else {
this->resize(this->fontMetrics().width(this->title) + 8 + 24, 24); resize(fontMetrics().width(_title) + 8 + 24, 24);
} }
if (this->parent() != nullptr) { if (parent() != nullptr) {
((Notebook *)this->parent())->performLayout(true); ((Notebook *)parent())->performLayout(true);
} }
} }
void const QString &NotebookTab::getTitle() const
NotebookTab::moveAnimated(QPoint pos, bool animated)
{ {
posAnimationDesired = pos; return _title;
}
if ((this->window() != NULL && !this->window()->isVisible()) || !animated || void NotebookTab::setTitle(const QString &title)
posAnimated == false) { {
_title = title;
}
bool NotebookTab::getSelected()
{
return _selected;
}
void NotebookTab::setSelected(bool value)
{
_selected = value;
update();
}
NotebookTab::HighlightStyle NotebookTab::getHighlightStyle() const
{
return _highlightStyle;
}
void NotebookTab::setHighlightStyle(HighlightStyle style)
{
_highlightStyle = style;
update();
}
QRect NotebookTab::getDesiredRect() const
{
return QRect(_posAnimationDesired, size());
}
void NotebookTab::hideTabXChanged(bool)
{
calcSize();
update();
}
void NotebookTab::moveAnimated(QPoint pos, bool animated)
{
_posAnimationDesired = pos;
if ((window() != NULL && !window()->isVisible()) || !animated || _posAnimated == false) {
move(pos); move(pos);
posAnimated = true; _posAnimated = true;
return; return;
} }
if (this->posAnimation.endValue() == pos) { if (_posAnimation.endValue() == pos) {
return; return;
} }
this->posAnimation.stop(); _posAnimation.stop();
this->posAnimation.setDuration(75); _posAnimation.setDuration(75);
this->posAnimation.setStartValue(this->pos()); _posAnimation.setStartValue(this->pos());
this->posAnimation.setEndValue(pos); _posAnimation.setEndValue(pos);
this->posAnimation.start(); _posAnimation.start();
} }
void void NotebookTab::paintEvent(QPaintEvent *)
NotebookTab::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
@ -86,16 +124,16 @@ NotebookTab::paintEvent(QPaintEvent *)
auto &colorScheme = ColorScheme::getInstance(); auto &colorScheme = ColorScheme::getInstance();
if (this->selected) { if (_selected) {
painter.fillRect(rect(), colorScheme.TabSelectedBackground); painter.fillRect(rect(), colorScheme.TabSelectedBackground);
fg = colorScheme.TabSelectedText; fg = colorScheme.TabSelectedText;
} else if (this->mouseOver) { } else if (_mouseOver) {
painter.fillRect(rect(), colorScheme.TabHoverBackground); painter.fillRect(rect(), colorScheme.TabHoverBackground);
fg = colorScheme.TabHoverText; fg = colorScheme.TabHoverText;
} else if (this->highlightStyle == HighlightHighlighted) { } else if (_highlightStyle == HighlightHighlighted) {
painter.fillRect(rect(), colorScheme.TabHighlightedBackground); painter.fillRect(rect(), colorScheme.TabHighlightedBackground);
fg = colorScheme.TabHighlightedText; fg = colorScheme.TabHighlightedText;
} else if (this->highlightStyle == HighlightNewMessage) { } else if (_highlightStyle == HighlightNewMessage) {
painter.fillRect(rect(), colorScheme.TabNewMessageBackground); painter.fillRect(rect(), colorScheme.TabNewMessageBackground);
fg = colorScheme.TabHighlightedText; fg = colorScheme.TabHighlightedText;
} else { } else {
@ -105,116 +143,105 @@ NotebookTab::paintEvent(QPaintEvent *)
painter.setPen(fg); painter.setPen(fg);
QRect rect(0, 0, QRect rect(0, 0, width() - (SettingsManager::getInstance().hideTabX.get() ? 0 : 16), height());
width() - (Settings::getInstance().hideTabX.get() ? 0 : 16),
height());
painter.drawText(rect, this->title, QTextOption(Qt::AlignCenter)); painter.drawText(rect, _title, QTextOption(Qt::AlignCenter));
if (!Settings::getInstance().hideTabX.get() && if (!SettingsManager::getInstance().hideTabX.get() && (_mouseOver || _selected)) {
(this->mouseOver || this->selected)) { if (_mouseOverX) {
if (this->mouseOverX) { painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64));
if (this->mouseDownX) { if (_mouseDownX) {
painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64)); painter.fillRect(getXRect(), QColor(0, 0, 0, 64));
} }
} }
painter.drawLine(this->getXRect().topLeft() + QPoint(4, 4), painter.drawLine(getXRect().topLeft() + QPoint(4, 4),
this->getXRect().bottomRight() + QPoint(-4, -4)); getXRect().bottomRight() + QPoint(-4, -4));
painter.drawLine(this->getXRect().topRight() + QPoint(-4, 4), painter.drawLine(getXRect().topRight() + QPoint(-4, 4),
this->getXRect().bottomLeft() + QPoint(4, -4)); getXRect().bottomLeft() + QPoint(4, -4));
} }
} }
void void NotebookTab::mousePressEvent(QMouseEvent *event)
NotebookTab::mousePressEvent(QMouseEvent *event)
{ {
this->mouseDown = true; _mouseDown = true;
this->mouseDownX = this->getXRect().contains(event->pos()); _mouseDownX = getXRect().contains(event->pos());
this->update(); update();
this->notebook->select(page); _notebook->select(page);
} }
void void NotebookTab::mouseReleaseEvent(QMouseEvent *event)
NotebookTab::mouseReleaseEvent(QMouseEvent *event)
{ {
this->mouseDown = false; _mouseDown = false;
if (!Settings::getInstance().hideTabX.get() && this->mouseDownX && if (!SettingsManager::getInstance().hideTabX.get() && _mouseDownX &&
this->getXRect().contains(event->pos())) { getXRect().contains(event->pos())) {
this->mouseDownX = false; _mouseDownX = false;
this->notebook->removePage(this->page); _notebook->removePage(page);
} else { } else {
update(); update();
} }
} }
void void NotebookTab::enterEvent(QEvent *)
NotebookTab::enterEvent(QEvent *)
{ {
this->mouseOver = true; _mouseOver = true;
update(); update();
} }
void void NotebookTab::leaveEvent(QEvent *)
NotebookTab::leaveEvent(QEvent *)
{ {
this->mouseOverX = this->mouseOver = false; _mouseOverX = _mouseOver = false;
update(); update();
} }
void void NotebookTab::dragEnterEvent(QDragEnterEvent *)
NotebookTab::dragEnterEvent(QDragEnterEvent *)
{ {
this->notebook->select(page); _notebook->select(page);
} }
void void NotebookTab::mouseMoveEvent(QMouseEvent *event)
NotebookTab::mouseMoveEvent(QMouseEvent *event)
{ {
bool overX = this->getXRect().contains(event->pos()); bool overX = getXRect().contains(event->pos());
if (overX != this->mouseOverX) { if (overX != _mouseOverX) {
this->mouseOverX = overX && !Settings::getInstance().hideTabX.get(); _mouseOverX = overX && !SettingsManager::getInstance().hideTabX.get();
this->update(); update();
} }
if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) { if (_mouseDown && !getDesiredRect().contains(event->pos())) {
QPoint relPoint = this->mapToParent(event->pos()); QPoint relPoint = mapToParent(event->pos());
int index; int index;
NotebookPage *page = notebook->tabAt(relPoint, index); NotebookPage *page = _notebook->tabAt(relPoint, index);
if (page != nullptr && page != this->page) { if (page != nullptr && page != page) {
notebook->rearrangePage(this->page, index); _notebook->rearrangePage(page, index);
} }
} }
} }
void void NotebookTab::load(const boost::property_tree::ptree &tree)
NotebookTab::load(const boost::property_tree::ptree &tree)
{ {
// Load tab title // Load tab title
try { try {
this->setTitle(QString::fromStdString(tree.get<std::string>("title"))); setTitle(QString::fromStdString(tree.get<std::string>("title")));
} catch (boost::property_tree::ptree_error) { } catch (boost::property_tree::ptree_error) {
} }
} }
boost::property_tree::ptree boost::property_tree::ptree NotebookTab::save()
NotebookTab::save()
{ {
boost::property_tree::ptree tree; boost::property_tree::ptree tree;
tree.put("title", this->getTitle().toStdString()); tree.put("title", getTitle().toStdString());
return tree; return tree;
} }

View file

@ -18,72 +18,27 @@ class NotebookTab : public QWidget
Q_OBJECT Q_OBJECT
public: public:
enum HighlightStyle { enum HighlightStyle { HighlightNone, HighlightHighlighted, HighlightNewMessage };
HighlightNone,
HighlightHighlighted,
HighlightNewMessage
};
explicit NotebookTab(Notebook *notebook); explicit NotebookTab(Notebook *_notebook);
~NotebookTab(); ~NotebookTab();
void calcSize(); void calcSize();
NotebookPage *page; NotebookPage *page;
const QString & const QString &getTitle() const;
getTitle() const void setTitle(const QString &title);
{ bool getSelected();
return this->title; void setSelected(bool value);
}
void HighlightStyle getHighlightStyle() const;
setTitle(const QString &title) void setHighlightStyle(HighlightStyle style);
{
this->title = title;
}
bool
getSelected()
{
return this->selected;
}
void
setSelected(bool value)
{
this->selected = value;
update();
}
HighlightStyle
getHighlightStyle() const
{
return this->highlightStyle;
}
void
setHighlightStyle(HighlightStyle style)
{
this->highlightStyle = style;
update();
}
void moveAnimated(QPoint pos, bool animated = true); void moveAnimated(QPoint pos, bool animated = true);
public: QRect getDesiredRect() const;
QRect void hideTabXChanged(bool);
getDesiredRect() const
{
return QRect(posAnimationDesired, this->size());
}
void
hideTabXChanged(bool)
{
calcSize();
update();
}
protected: protected:
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
@ -98,26 +53,25 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
private: private:
boost::signals2::connection hideXConnection; boost::signals2::connection _hideXConnection;
QPropertyAnimation posAnimation; QPropertyAnimation _posAnimation;
bool posAnimated; bool _posAnimated;
QPoint posAnimationDesired; QPoint _posAnimationDesired;
Notebook *notebook; Notebook *_notebook;
QString title; QString _title;
bool selected; bool _selected;
bool mouseOver; bool _mouseOver;
bool mouseDown; bool _mouseDown;
bool mouseOverX; bool _mouseOverX;
bool mouseDownX; bool _mouseDownX;
HighlightStyle highlightStyle; HighlightStyle _highlightStyle;
QRect QRect getXRect()
getXRect()
{ {
return QRect(this->width() - 20, 4, 16, 16); return QRect(this->width() - 20, 4, 16, 16);
} }

View file

@ -16,18 +16,15 @@ public:
sizePolicy.setVerticalPolicy(QSizePolicy::Preferred); sizePolicy.setVerticalPolicy(QSizePolicy::Preferred);
this->setSizePolicy(sizePolicy); this->setSizePolicy(sizePolicy);
QObject::connect(this, &QTextEdit::textChanged, this, QObject::connect(this, &QTextEdit::textChanged, this, &QWidget::updateGeometry);
&QWidget::updateGeometry);
} }
QSize QSize sizeHint() const override
sizeHint() const override
{ {
return QSize(this->width(), this->heightForWidth(this->width())); return QSize(this->width(), this->heightForWidth(this->width()));
} }
bool bool hasHeightForWidth() const override
hasHeightForWidth() const override
{ {
return true; return true;
} }
@ -35,17 +32,14 @@ public:
boost::signals2::signal<void(QKeyEvent *)> keyPressed; boost::signals2::signal<void(QKeyEvent *)> keyPressed;
protected: protected:
int int heightForWidth(int) const override
heightForWidth(int) const override
{ {
auto margins = this->contentsMargins(); auto margins = this->contentsMargins();
return margins.top() + document()->size().height() + margins.bottom() + return margins.top() + document()->size().height() + margins.bottom() + 5;
5;
} }
void void keyPressEvent(QKeyEvent *event)
keyPressEvent(QKeyEvent *event)
{ {
event->ignore(); event->ignore();

View file

@ -11,35 +11,34 @@ namespace widgets {
ScrollBar::ScrollBar(QWidget *widget) ScrollBar::ScrollBar(QWidget *widget)
: QWidget(widget) : QWidget(widget)
, mutex() , _mutex()
, currentValueAnimation(this, "currentValue") , _currentValueAnimation(this, "currentValue")
, highlights(NULL) , _highlights(NULL)
, mouseOverIndex(-1) , _mouseOverIndex(-1)
, mouseDownIndex(-1) , _mouseDownIndex(-1)
, lastMousePosition() , _lastMousePosition()
, buttonHeight(16) , _buttonHeight(16)
, trackHeight(100) , _trackHeight(100)
, thumbRect() , _thumbRect()
, maximum() , _maximum()
, minimum() , _minimum()
, largeChange() , _largeChange()
, smallChange() , _smallChange()
, desiredValue() , _desiredValue()
, currentValueChanged() , _currentValueChanged()
, currentValue() , _currentValue()
{ {
this->resize(16, 100); resize(16, 100);
this->currentValueAnimation.setDuration(300); _currentValueAnimation.setDuration(250);
this->currentValueAnimation.setEasingCurve( _currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic));
QEasingCurve(QEasingCurve::OutCubic));
this->setMouseTracking(true); setMouseTracking(true);
} }
ScrollBar::~ScrollBar() ScrollBar::~ScrollBar()
{ {
auto highlight = this->highlights; auto highlight = _highlights;
while (highlight != NULL) { while (highlight != NULL) {
auto tmp = highlight->next; auto tmp = highlight->next;
@ -48,18 +47,17 @@ ScrollBar::~ScrollBar()
} }
} }
void void ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
{ {
this->mutex.lock(); _mutex.lock();
ScrollBarHighlight *last = NULL; ScrollBarHighlight *last = NULL;
ScrollBarHighlight *current = this->highlights; ScrollBarHighlight *current = _highlights;
while (current != NULL) { while (current != NULL) {
if (func(*current)) { if (func(*current)) {
if (last == NULL) { if (last == NULL) {
this->highlights = current->next; _highlights = current->next;
} else { } else {
last->next = current->next; last->next = current->next;
} }
@ -73,150 +71,233 @@ ScrollBar::removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func)
} }
} }
this->mutex.unlock(); _mutex.unlock();
} }
void void ScrollBar::addHighlight(ScrollBarHighlight *highlight)
ScrollBar::addHighlight(ScrollBarHighlight *highlight)
{ {
this->mutex.lock(); _mutex.lock();
if (this->highlights == NULL) { if (_highlights == NULL) {
this->highlights = highlight; _highlights = highlight;
} else { } else {
highlight->next = this->highlights->next; highlight->next = _highlights->next;
this->highlights->next = highlight; _highlights->next = highlight;
} }
this->mutex.unlock(); _mutex.unlock();
} }
void void ScrollBar::setMaximum(qreal value)
ScrollBar::paintEvent(QPaintEvent *) {
_maximum = value;
updateScroll();
}
void ScrollBar::setMinimum(qreal value)
{
_minimum = value;
updateScroll();
}
void ScrollBar::setLargeChange(qreal value)
{
_largeChange = value;
updateScroll();
}
void ScrollBar::setSmallChange(qreal value)
{
_smallChange = value;
updateScroll();
}
void ScrollBar::setDesiredValue(qreal value, bool animated)
{
value = std::max(_minimum, std::min(_maximum - _largeChange, value));
if (_desiredValue != value) {
if (animated) {
_currentValueAnimation.stop();
_currentValueAnimation.setStartValue(_currentValue);
_currentValueAnimation.setEndValue(value);
_currentValueAnimation.start();
} else {
if (_currentValueAnimation.state() != QPropertyAnimation::Running) {
// currentValueAnimation.stop();
setCurrentValue(value);
}
}
}
_desiredValue = value;
}
qreal ScrollBar::getMaximum() const
{
return _maximum;
}
qreal ScrollBar::getMinimum() const
{
return _minimum;
}
qreal ScrollBar::getLargeChange() const
{
return _largeChange;
}
qreal ScrollBar::getSmallChange() const
{
return _smallChange;
}
qreal ScrollBar::getDesiredValue() const
{
return _desiredValue;
}
qreal ScrollBar::getCurrentValue() const
{
return _currentValue;
}
boost::signals2::signal<void()> &ScrollBar::getCurrentValueChanged()
{
return _currentValueChanged;
}
void ScrollBar::setCurrentValue(qreal value)
{
value = std::max(_minimum, std::min(_maximum - _largeChange, value));
if (_currentValue != value) {
_currentValue = value;
updateScroll();
_currentValueChanged();
update();
}
}
void ScrollBar::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG); painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG);
painter.fillRect(QRect(0, 0, width(), this->buttonHeight), painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0));
QColor(255, 0, 0)); painter.fillRect(QRect(0, height() - _buttonHeight, width(), _buttonHeight), QColor(255, 0, 0));
painter.fillRect(
QRect(0, height() - this->buttonHeight, width(), this->buttonHeight),
QColor(255, 0, 0));
painter.fillRect(this->thumbRect, QColor(0, 255, 255)); painter.fillRect(_thumbRect, QColor(0, 255, 255));
ScrollBarHighlight *highlight = this->highlights; // ScrollBarHighlight *highlight = highlights;
this->mutex.lock(); _mutex.lock();
// do { // do {
// painter.fillRect(); // painter.fillRect();
// } while ((highlight = highlight->next()) != NULL); // } while ((highlight = highlight->next()) != NULL);
this->mutex.unlock(); _mutex.unlock();
} }
void void ScrollBar::mouseMoveEvent(QMouseEvent *event)
ScrollBar::mouseMoveEvent(QMouseEvent *event)
{ {
if (this->mouseDownIndex == -1) { if (_mouseDownIndex == -1) {
int y = event->pos().y(); int y = event->pos().y();
auto oldIndex = this->mouseOverIndex; auto oldIndex = _mouseOverIndex;
if (y < this->buttonHeight) { if (y < _buttonHeight) {
this->mouseOverIndex = 0; _mouseOverIndex = 0;
} else if (y < this->thumbRect.y()) { } else if (y < _thumbRect.y()) {
this->mouseOverIndex = 1; _mouseOverIndex = 1;
} else if (this->thumbRect.contains(2, y)) { } else if (_thumbRect.contains(2, y)) {
this->mouseOverIndex = 2; _mouseOverIndex = 2;
} else if (y < height() - this->buttonHeight) { } else if (y < height() - _buttonHeight) {
this->mouseOverIndex = 3; _mouseOverIndex = 3;
} else { } else {
this->mouseOverIndex = 4; _mouseOverIndex = 4;
} }
if (oldIndex != this->mouseOverIndex) { if (oldIndex != _mouseOverIndex) {
this->update(); update();
} }
} else if (this->mouseDownIndex == 2) { } else if (_mouseDownIndex == 2) {
int delta = event->pos().y() - lastMousePosition.y(); int delta = event->pos().y() - _lastMousePosition.y();
this->setDesiredValue(this->desiredValue + setDesiredValue(_desiredValue + (qreal)delta / _trackHeight * _maximum);
(qreal)delta / this->trackHeight * maximum);
} }
this->lastMousePosition = event->pos(); _lastMousePosition = event->pos();
} }
void void ScrollBar::mousePressEvent(QMouseEvent *event)
ScrollBar::mousePressEvent(QMouseEvent *event)
{ {
int y = event->pos().y(); int y = event->pos().y();
if (y < this->buttonHeight) { if (y < _buttonHeight) {
this->mouseDownIndex = 0; _mouseDownIndex = 0;
} else if (y < this->thumbRect.y()) { } else if (y < _thumbRect.y()) {
this->mouseDownIndex = 1; _mouseDownIndex = 1;
} else if (this->thumbRect.contains(2, y)) { } else if (_thumbRect.contains(2, y)) {
this->mouseDownIndex = 2; _mouseDownIndex = 2;
} else if (y < height() - this->buttonHeight) { } else if (y < height() - _buttonHeight) {
this->mouseDownIndex = 3; _mouseDownIndex = 3;
} else { } else {
this->mouseDownIndex = 4; _mouseDownIndex = 4;
} }
} }
void void ScrollBar::mouseReleaseEvent(QMouseEvent *event)
ScrollBar::mouseReleaseEvent(QMouseEvent *event)
{ {
int y = event->pos().y(); int y = event->pos().y();
if (y < this->buttonHeight) { if (y < _buttonHeight) {
if (this->mouseDownIndex == 0) { if (_mouseDownIndex == 0) {
this->setDesiredValue(this->desiredValue - this->smallChange, true); setDesiredValue(_desiredValue - _smallChange, true);
} }
} else if (y < this->thumbRect.y()) { } else if (y < _thumbRect.y()) {
if (this->mouseDownIndex == 1) { if (_mouseDownIndex == 1) {
this->setDesiredValue(this->desiredValue - this->smallChange, true); setDesiredValue(_desiredValue - _smallChange, true);
} }
} else if (this->thumbRect.contains(2, y)) { } else if (_thumbRect.contains(2, y)) {
// do nothing // do nothing
} else if (y < height() - this->buttonHeight) { } else if (y < height() - _buttonHeight) {
if (this->mouseDownIndex == 3) { if (_mouseDownIndex == 3) {
this->setDesiredValue(this->desiredValue + this->smallChange, true); setDesiredValue(_desiredValue + _smallChange, true);
} }
} else { } else {
if (this->mouseDownIndex == 4) { if (_mouseDownIndex == 4) {
this->setDesiredValue(this->desiredValue + this->smallChange, true); setDesiredValue(_desiredValue + _smallChange, true);
} }
} }
this->mouseDownIndex = -1; _mouseDownIndex = -1;
update(); update();
} }
void void ScrollBar::leaveEvent(QEvent *)
ScrollBar::leaveEvent(QEvent *)
{ {
this->mouseOverIndex = -1; _mouseOverIndex = -1;
update(); update();
} }
void void ScrollBar::updateScroll()
ScrollBar::updateScroll()
{ {
this->trackHeight = height() - this->buttonHeight - this->buttonHeight - _trackHeight = height() - _buttonHeight - _buttonHeight - MIN_THUMB_HEIGHT - 1;
MIN_THUMB_HEIGHT - 1;
this->thumbRect = _thumbRect = QRect(0, (int)(_currentValue / _maximum * _trackHeight) + 1 + _buttonHeight,
QRect(0, width(), (int)(_largeChange / _maximum * _trackHeight) + MIN_THUMB_HEIGHT);
(int)(this->currentValue / this->maximum * this->trackHeight) +
1 + this->buttonHeight,
width(),
(int)(this->largeChange / this->maximum * this->trackHeight) +
MIN_THUMB_HEIGHT);
update(); update();
} }

View file

@ -23,128 +23,29 @@ public:
void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func); void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func);
void addHighlight(ScrollBarHighlight *highlight); void addHighlight(ScrollBarHighlight *highlight);
Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue) Q_PROPERTY(qreal _desiredValue READ getDesiredValue WRITE setDesiredValue)
void void setMaximum(qreal value);
setMaximum(qreal value) void setMinimum(qreal value);
{ void setLargeChange(qreal value);
this->maximum = value; void setSmallChange(qreal value);
void setDesiredValue(qreal value, bool animated = false);
this->updateScroll(); qreal getMaximum() const;
} qreal getMinimum() const;
qreal getLargeChange() const;
void qreal getSmallChange() const;
setMinimum(qreal value) qreal getDesiredValue() const;
{ qreal getCurrentValue() const;
this->minimum = value; boost::signals2::signal<void()> &getCurrentValueChanged();
void setCurrentValue(qreal value);
this->updateScroll();
}
void
setLargeChange(qreal value)
{
this->largeChange = value;
this->updateScroll();
}
void
setSmallChange(qreal value)
{
this->smallChange = value;
this->updateScroll();
}
void
setDesiredValue(qreal value, bool animated = false)
{
value = std::max(this->minimum,
std::min(this->maximum - this->largeChange, value));
if (this->desiredValue != value) {
if (animated) {
this->currentValueAnimation.stop();
this->currentValueAnimation.setStartValue(this->currentValue);
this->currentValueAnimation.setEndValue(value);
this->currentValueAnimation.start();
} else {
this->currentValueAnimation.stop();
this->setCurrentValue(value);
}
}
this->desiredValue = value;
}
qreal
getMaximum() const
{
return this->maximum;
}
qreal
getMinimum() const
{
return this->minimum;
}
qreal
getLargeChange() const
{
return this->largeChange;
}
qreal
getSmallChange() const
{
return this->smallChange;
}
qreal
getDesiredValue() const
{
return this->desiredValue;
}
qreal
getCurrentValue() const
{
return this->currentValue;
}
boost::signals2::signal<void()> &
getCurrentValueChanged()
{
return currentValueChanged;
}
void
setCurrentValue(qreal value)
{
value = std::max(this->minimum,
std::min(this->maximum - this->largeChange, value));
if (this->currentValue != value) {
this->currentValue = value;
this->updateScroll();
this->currentValueChanged();
this->update();
}
}
private: private:
Q_PROPERTY(qreal currentValue READ getCurrentValue WRITE setCurrentValue) Q_PROPERTY(qreal _currentValue READ getCurrentValue WRITE setCurrentValue)
QMutex mutex; QMutex _mutex;
ScrollBarHighlight *highlights; ScrollBarHighlight *_highlights;
QPropertyAnimation currentValueAnimation; QPropertyAnimation _currentValueAnimation;
void paintEvent(QPaintEvent *); void paintEvent(QPaintEvent *);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
@ -152,23 +53,23 @@ private:
void mouseReleaseEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event);
void leaveEvent(QEvent *); void leaveEvent(QEvent *);
int mouseOverIndex; int _mouseOverIndex;
int mouseDownIndex; int _mouseDownIndex;
QPoint lastMousePosition; QPoint _lastMousePosition;
int buttonHeight; int _buttonHeight;
int trackHeight; int _trackHeight;
QRect thumbRect; QRect _thumbRect;
qreal maximum; qreal _maximum;
qreal minimum; qreal _minimum;
qreal largeChange; qreal _largeChange;
qreal smallChange; qreal _smallChange;
qreal desiredValue; qreal _desiredValue;
qreal currentValue; qreal _currentValue;
boost::signals2::signal<void()> currentValueChanged; boost::signals2::signal<void()> _currentValueChanged;
void updateScroll(); void updateScroll();
}; };

View file

@ -4,13 +4,11 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag)
Style style, QString tag) : _position(position)
: position(position) , _colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex)))
, colorIndex(std::max( , _style(style)
0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex))) , _tag(tag)
, style(style)
, tag(tag)
, next(NULL) , next(NULL)
{ {
} }

Some files were not shown because too many files have changed in this diff Show more