From 96db82e867e19c8a3c459a0317bc025fbad1537d Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 12 Apr 2017 17:46:44 +0200 Subject: [PATCH] refactoring --- account.cpp | 13 - account.h | 52 --- asyncexec.h | 9 +- channel.cpp | 263 ++++++----- channel.h | 132 ++---- channelmanager.cpp | 122 ++++++ channelmanager.h | 41 ++ channels.cpp | 133 ------ channels.h | 34 -- chatterino.pro | 72 ++- clangformat.txt | 5 +- colorscheme.cpp | 63 +-- colorscheme.h | 7 +- concurrentmap.h | 40 +- emojis.cpp | 45 +- emojis.h | 5 +- emotes.cpp => emotemanager.cpp | 131 +++--- emotemanager.h | 98 +++++ emotes.h | 140 ------ fontmanager.cpp | 62 +++ fontmanager.h | 55 +++ fonts.cpp | 64 --- fonts.h | 57 --- forms/userpopup.ui | 124 ++++++ ircmanager.cpp | 257 ++++++----- ircmanager.h | 55 ++- ircuser2.cpp | 33 ++ ircuser2.h | 27 ++ logging/loggingchannel.cpp | 18 +- logging/loggingchannel.h | 9 +- logging/loggingmanager.cpp | 14 +- main.cpp | 23 +- messages/lazyloadedimage.cpp | 35 +- messages/lazyloadedimage.h | 42 +- messages/limitedqueue.h | 13 +- messages/limitedqueuesnapshot.h | 6 +- messages/link.h | 9 +- messages/message.cpp | 434 +++--------------- messages/message.h | 109 ++--- messages/messagebuilder.cpp | 54 +++ messages/messagebuilder.h | 29 ++ messages/messageparseargs.h | 17 + messages/messageref.cpp | 209 ++++----- messages/messageref.h | 43 +- messages/word.cpp | 136 +++++- messages/word.h | 182 ++------ messages/wordpart.cpp | 112 ++++- messages/wordpart.h | 123 ++---- platform/borderless/LICENSE | 21 + platform/borderless/qwinwidget.cpp | 555 ++++++++++++++++++++++++ platform/borderless/qwinwidget.h | 109 +++++ platform/borderless/widget.cpp | 11 + platform/borderless/widget.h | 28 ++ platform/borderless/winnativewindow.cpp | 235 ++++++++++ platform/borderless/winnativewindow.h | 53 +++ resources.cpp | 43 +- resources.h | 45 +- setting.h | 41 +- settings.cpp | 127 ------ settingsmanager.cpp | 146 +++++++ settings.h => settingsmanager.h | 102 ++--- settingssnapshot.h | 27 +- twitch/emotevalue.h | 34 ++ twitch/twitchmessagebuilder.cpp | 347 +++++++++++++++ twitch/twitchmessagebuilder.h | 31 ++ twitch/twitchparsemessage.cpp | 349 +++++++++++++++ twitch/twitchparsemessage.h | 13 + twitch/twitchuser.cpp | 28 ++ twitch/twitchuser.h | 28 ++ twitchemotevalue.h | 35 -- usermanager.cpp | 16 + usermanager.h | 27 ++ util/urlfetch.h | 55 +++ widgets/chatwidget.cpp | 177 +++++--- widgets/chatwidget.h | 52 +-- widgets/chatwidgetheader.cpp | 168 +++---- widgets/chatwidgetheader.h | 23 +- widgets/chatwidgetheaderbutton.cpp | 66 ++- widgets/chatwidgetheaderbutton.h | 13 +- widgets/chatwidgetinput.cpp | 87 ++-- widgets/chatwidgetinput.h | 19 +- widgets/chatwidgetview.cpp | 210 +++++---- widgets/chatwidgetview.h | 37 +- widgets/fancybutton.cpp | 141 ++++++ widgets/fancybutton.h | 55 +++ widgets/mainwindow.cpp | 84 ++-- widgets/mainwindow.h | 21 +- widgets/notebook.cpp | 177 ++++---- widgets/notebook.h | 15 +- widgets/notebookbutton.cpp | 70 +-- widgets/notebookbutton.h | 18 +- widgets/notebookpage.cpp | 200 ++++----- widgets/notebookpage.h | 34 +- widgets/notebookpagedroppreview.cpp | 9 +- widgets/notebooktab.cpp | 217 +++++---- widgets/notebooktab.h | 92 +--- widgets/resizingtextedit.h | 18 +- widgets/scrollbar.cpp | 295 ++++++++----- widgets/scrollbar.h | 161 ++----- widgets/scrollbarhighlight.cpp | 12 +- widgets/scrollbarhighlight.h | 32 +- widgets/settingsdialog.cpp | 136 +++--- widgets/settingsdialog.h | 20 +- widgets/settingsdialogtab.cpp | 51 ++- widgets/settingsdialogtab.h | 46 +- widgets/signallabel.h | 12 +- widgets/textinputdialog.cpp | 36 +- widgets/textinputdialog.h | 20 +- widgets/titlebar.cpp | 11 + widgets/titlebar.h | 16 + widgets/userpopupwidget.cpp | 41 ++ widgets/userpopupwidget.h | 35 ++ windows.cpp => windowmanager.cpp | 59 ++- windows.h => windowmanager.h | 9 +- 114 files changed, 5554 insertions(+), 3703 deletions(-) delete mode 100644 account.cpp delete mode 100644 account.h create mode 100644 channelmanager.cpp create mode 100644 channelmanager.h delete mode 100644 channels.cpp delete mode 100644 channels.h rename emotes.cpp => emotemanager.cpp (53%) create mode 100644 emotemanager.h delete mode 100644 emotes.h create mode 100644 fontmanager.cpp create mode 100644 fontmanager.h delete mode 100644 fonts.cpp delete mode 100644 fonts.h create mode 100644 forms/userpopup.ui create mode 100644 ircuser2.cpp create mode 100644 ircuser2.h create mode 100644 messages/messagebuilder.cpp create mode 100644 messages/messagebuilder.h create mode 100644 messages/messageparseargs.h create mode 100644 platform/borderless/LICENSE create mode 100644 platform/borderless/qwinwidget.cpp create mode 100644 platform/borderless/qwinwidget.h create mode 100644 platform/borderless/widget.cpp create mode 100644 platform/borderless/widget.h create mode 100644 platform/borderless/winnativewindow.cpp create mode 100644 platform/borderless/winnativewindow.h delete mode 100644 settings.cpp create mode 100644 settingsmanager.cpp rename settings.h => settingsmanager.h (66%) create mode 100644 twitch/emotevalue.h create mode 100644 twitch/twitchmessagebuilder.cpp create mode 100644 twitch/twitchmessagebuilder.h create mode 100644 twitch/twitchparsemessage.cpp create mode 100644 twitch/twitchparsemessage.h create mode 100644 twitch/twitchuser.cpp create mode 100644 twitch/twitchuser.h delete mode 100644 twitchemotevalue.h create mode 100644 usermanager.cpp create mode 100644 usermanager.h create mode 100644 util/urlfetch.h create mode 100644 widgets/fancybutton.cpp create mode 100644 widgets/fancybutton.h create mode 100644 widgets/titlebar.cpp create mode 100644 widgets/titlebar.h create mode 100644 widgets/userpopupwidget.cpp create mode 100644 widgets/userpopupwidget.h rename windows.cpp => windowmanager.cpp (57%) rename windows.h => windowmanager.h (84%) diff --git a/account.cpp b/account.cpp deleted file mode 100644 index d60079fe1..000000000 --- a/account.cpp +++ /dev/null @@ -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; -} -} diff --git a/account.h b/account.h deleted file mode 100644 index 4deb92061..000000000 --- a/account.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ACCOUNT_H -#define ACCOUNT_H - -#include - -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 diff --git a/asyncexec.h b/asyncexec.h index 2b498966f..5696c3bd3 100644 --- a/asyncexec.h +++ b/asyncexec.h @@ -7,19 +7,16 @@ #include #include -#define async_exec(a) \ - QThreadPool::globalInstance()->start(new LambdaRunnable(a)); +#define async_exec(a) QThreadPool::globalInstance()->start(new LambdaRunnable(a)); -class LambdaRunnable : public QRunnable -{ +class LambdaRunnable : public QRunnable { public: LambdaRunnable(std::function action) { this->action = action; } - void - run() + void run() { this->action(); } diff --git a/channel.cpp b/channel.cpp index b652f6e57..965b61467 100644 --- a/channel.cpp +++ b/channel.cpp @@ -1,8 +1,9 @@ #include "channel.h" -#include "emotes.h" +#include "emotemanager.h" #include "logging/loggingmanager.h" #include "messages/message.h" -#include "windows.h" +#include "util/urlfetch.h" +#include "windowmanager.h" #include #include @@ -14,133 +15,177 @@ #include +using namespace chatterino::messages; + namespace chatterino { Channel::Channel(const QString &channel) - : messages() - , name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) - : channel) - , bttvChannelEmotes() - , ffzChannelEmotes() - , subLink("https://www.twitch.tv/" + name + - "/subscribe?ref=in_chat_subscriber_link") - , channelLink("https://twitch.tv/" + name) - , popoutPlayerLink("https://player.twitch.tv/?channel=" + name) - , loggingChannel(logging::get(name)) + : _messages() + , _name((channel.length() > 0 && channel[0] == '#') ? channel.mid(1) : channel) + , _bttvChannelEmotes() + , _ffzChannelEmotes() + , _subLink("https://www.twitch.tv/" + _name + "/subscribe?ref=in_chat_subscriber_link") + , _channelLink("https://twitch.tv/" + _name) + , _popoutPlayerLink("https://player.twitch.tv/?channel=" + _name) +//, _loggingChannel(logging::get(_name)) { reloadChannelEmotes(); } -void -Channel::reloadBttvEmotes() +// +// properties +// +ConcurrentMap &Channel::getBttvChannelEmotes() { - // bttv - QNetworkAccessManager *manager = new QNetworkAccessManager(); - - QUrl url("https://api.betterttv.net/2/channels/" + this->name); - QNetworkRequest request(url); - - QNetworkReply *reply = manager->get(request); - - QObject::connect(reply, &QNetworkReply::finished, [=] { - if (reply->error() == QNetworkReply::NetworkError::NoError) { - QByteArray data = reply->readAll(); - QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); - QJsonObject root = jsonDoc.object(); - - auto emotes = root.value("emotes").toArray(); - - QString _template = "https:" + root.value("urlTemplate").toString(); - - for (const QJsonValue &emote : emotes) { - QString id = emote.toObject().value("id").toString(); - QString code = emote.toObject().value("code").toString(); - // emote.value("imageType").toString(); - - QString tmp = _template; - tmp.detach(); - QString url = - tmp.replace("{{id}}", id).replace("{{image}}", "1x"); - - this->getBttvChannelEmotes().insert( - code, Emotes::getBttvChannelEmoteFromCaches().getOrAdd( - id, [&code, &url] { - return new messages::LazyLoadedImage( - url, 1, code, - code + "\nChannel Bttv Emote"); - })); - } - } - - reply->deleteLater(); - manager->deleteLater(); - }); + return _bttvChannelEmotes; } -void -Channel::reloadFfzEmotes() +ConcurrentMap &Channel::getFfzChannelEmotes() { - QNetworkAccessManager *manager = new QNetworkAccessManager(); - - QUrl url("http://api.frankerfacez.com/v1/room/" + this->name); - QNetworkRequest request(url); - - QNetworkReply *reply = manager->get(request); - - QObject::connect(reply, &QNetworkReply::finished, [=] { - if (reply->error() == QNetworkReply::NetworkError::NoError) { - QByteArray data = reply->readAll(); - QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); - QJsonObject root = jsonDoc.object(); - - auto sets = root.value("sets").toObject(); - - for (const QJsonValue &set : sets) { - auto emoticons = set.toObject().value("emoticons").toArray(); - - for (const QJsonValue &emote : emoticons) { - QJsonObject object = emote.toObject(); - - // margins - - int id = object.value("id").toInt(); - QString code = object.value("name").toString(); - - QJsonObject urls = object.value("urls").toObject(); - QString url1 = "http:" + urls.value("1").toString(); - - this->getFfzChannelEmotes().insert( - code, Emotes::getFfzChannelEmoteFromCaches().getOrAdd( - id, [&code, &url1] { - return new messages::LazyLoadedImage( - url1, 1, code, - code + "\nGlobal Ffz Emote"); - })); - } - } - } - - reply->deleteLater(); - manager->deleteLater(); - }); + return _ffzChannelEmotes; } -void -Channel::addMessage(std::shared_ptr message) +bool Channel::isEmpty() const { - std::shared_ptr deleted; + return _name.isEmpty(); +} - if (this->loggingChannel.get() != nullptr) { - this->loggingChannel->append(message); - } +const QString &Channel::getName() const +{ + return _name; +} - if (this->messages.appendItem(message, deleted)) { - this->messageRemovedFromStart(deleted); +int Channel::getRoomID() const +{ + return _roomID; +} + +const QString &Channel::getSubLink() const +{ + return _subLink; +} + +const QString &Channel::getChannelLink() const +{ + return _channelLink; +} + +const QString &Channel::getPopoutPlayerLink() const +{ + return _popoutPlayerLink; +} + +bool Channel::getIsLive() const +{ + return _isLive; +} + +int Channel::getStreamViewerCount() const +{ + return _streamViewerCount; +} + +const QString &Channel::getStreamStatus() const +{ + return _streamStatus; +} + +const QString &Channel::getStreamGame() const +{ + return _streamGame; +} + +messages::LimitedQueueSnapshot Channel::getMessageSnapshot() +{ + return _messages.getSnapshot(); +} + +// +// methods +// +void Channel::addMessage(std::shared_ptr message) +{ + std::shared_ptr deleted; + + // if (_loggingChannel.get() != nullptr) { + // _loggingChannel->append(message); + // } + + if (_messages.appendItem(message, deleted)) { + messageRemovedFromStart(deleted); } this->messageAppended(message); - Windows::repaintVisibleChatWidgets(this); + WindowManager::repaintVisibleChatWidgets(this); +} + +// private methods +void Channel::reloadChannelEmotes() +{ + reloadBttvEmotes(); + reloadFfzEmotes(); +} + +void Channel::reloadBttvEmotes() +{ + util::urlJsonFetch( + "https://api.betterttv.net/2/channels/" + _name, [this](QJsonObject &rootNode) { + auto emotesNode = rootNode.value("emotes").toArray(); + + QString linkTemplate = "https:" + rootNode.value("urlTemplate").toString(); + + for (const QJsonValue &emoteNode : emotesNode) { + QJsonObject emoteObject = emoteNode.toObject(); + + QString id = emoteObject.value("id").toString(); + QString code = emoteObject.value("code").toString(); + // emoteObject.value("imageType").toString(); + + QString link = linkTemplate; + link.detach(); + + link = link.replace("{{id}}", id).replace("{{image}}", "1x"); + + auto emote = EmoteManager::getInstance().getBttvChannelEmoteFromCaches().getOrAdd( + id, [&code, &link] { + return new LazyLoadedImage(link, 1, code, code + "\nChannel Bttv Emote"); + }); + + this->getBttvChannelEmotes().insert(code, emote); + } + }); +} + +void Channel::reloadFfzEmotes() +{ + util::urlJsonFetch("http://api.frankerfacez.com/v1/room/" + _name, [this]( + QJsonObject &rootNode) { + auto setsNode = rootNode.value("sets").toObject(); + + for (const QJsonValue &setNode : setsNode) { + auto emotesNode = setNode.toObject().value("emoticons").toArray(); + + for (const QJsonValue &emoteNode : emotesNode) { + QJsonObject emoteObject = emoteNode.toObject(); + + // margins + + int id = emoteObject.value("id").toInt(); + QString code = emoteObject.value("name").toString(); + + QJsonObject urls = emoteObject.value("urls").toObject(); + QString url1 = "http:" + urls.value("1").toString(); + + auto emote = EmoteManager::getInstance().getFfzChannelEmoteFromCaches().getOrAdd( + id, [&code, &url1] { + return new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote"); + }); + + getFfzChannelEmotes().insert(code, emote); + } + } + }); } } // namespace chatterino diff --git a/channel.h b/channel.h index bde15b923..b24991649 100644 --- a/channel.h +++ b/channel.h @@ -18,119 +18,59 @@ namespace messages { class Message; } +class ChannelManager; + +typedef std::shared_ptr SharedChannel; + class Channel { public: Channel(const QString &channel); - boost::signals2::signal &)> - messageRemovedFromStart; - boost::signals2::signal &)> - messageAppended; + boost::signals2::signal messageRemovedFromStart; + boost::signals2::signal messageAppended; // properties - ConcurrentMap & - getBttvChannelEmotes() - { - return bttvChannelEmotes; - } + ConcurrentMap &getBttvChannelEmotes(); + ConcurrentMap &getFfzChannelEmotes(); - ConcurrentMap & - getFfzChannelEmotes() - { - return ffzChannelEmotes; - } - - bool - isEmpty() const - { - return name.isEmpty(); - } - - const QString & - getName() const - { - return name; - } - - int - getRoomID() const - { - return roomID; - } - - const QString & - getSubLink() const - { - return subLink; - } - const QString & - getChannelLink() const - { - return channelLink; - } - const QString & - getPopoutPlayerLink() const - { - return popoutPlayerLink; - } - - bool - getIsLive() const - { - return isLive; - } - int - getStreamViewerCount() const - { - return streamViewerCount; - } - const QString & - getStreamStatus() const - { - return streamStatus; - } - const QString & - getStreamGame() const - { - return streamGame; - } + bool isEmpty() const; + const QString &getName() const; + int getRoomID() const; + const QString &getSubLink() const; + const QString &getChannelLink() const; + const QString &getPopoutPlayerLink() const; + bool getIsLive() const; + int getStreamViewerCount() const; + const QString &getStreamStatus() const; + const QString &getStreamGame() const; + messages::LimitedQueueSnapshot getMessageSnapshot(); // methods - messages::LimitedQueueSnapshot> - getMessageSnapshot() - { - return messages.getSnapshot(); - } - - void addMessage(std::shared_ptr message); - - void - reloadChannelEmotes() - { - this->reloadBttvEmotes(); - this->reloadFfzEmotes(); - } + void addMessage(messages::SharedMessage message); + void reloadChannelEmotes(); private: - messages::LimitedQueue> messages; + // variabeles + messages::LimitedQueue _messages; - QString name; - int roomID; + QString _name; + int _roomID; - ConcurrentMap bttvChannelEmotes; - ConcurrentMap ffzChannelEmotes; + ConcurrentMap _bttvChannelEmotes; + ConcurrentMap _ffzChannelEmotes; - QString subLink; - QString channelLink; - QString popoutPlayerLink; + QString _subLink; + QString _channelLink; + QString _popoutPlayerLink; - bool isLive; - int streamViewerCount; - QString streamStatus; - QString streamGame; - std::shared_ptr loggingChannel; + bool _isLive; + int _streamViewerCount; + QString _streamStatus; + QString _streamGame; + // std::shared_ptr _loggingChannel; + // methods void reloadBttvEmotes(); void reloadFfzEmotes(); }; diff --git a/channelmanager.cpp b/channelmanager.cpp new file mode 100644 index 000000000..7985e357a --- /dev/null +++ b/channelmanager.cpp @@ -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 ChannelManager::getItems() +{ + QMutexLocker locker(&_channelsMutex); + + std::vector 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 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 diff --git a/channelmanager.h b/channelmanager.h new file mode 100644 index 000000000..7df13f38f --- /dev/null +++ b/channelmanager.h @@ -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 getItems(); + + SharedChannel addChannel(const QString &channel); + SharedChannel getChannel(const QString &channel); + void removeChannel(const QString &channel); + +private: + static ChannelManager instance; + + ChannelManager(); + + QMap> _channels; + QMutex _channelsMutex; + + SharedChannel _whispers; + SharedChannel _mentions; + SharedChannel _empty; +}; + +} // namespace chatterino + +#endif // CHANNELS_H diff --git a/channels.cpp b/channels.cpp deleted file mode 100644 index 4758693cd..000000000 --- a/channels.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "channels.h" -#include "ircmanager.h" - -namespace chatterino { - -static std::shared_ptr whispers(nullptr); -static std::shared_ptr mentions(nullptr); -static std::shared_ptr empty(nullptr); - -QMap, 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 -Channels::getWhispers() -{ - return whispers; -} - -std::shared_ptr -Channels::getMentions() -{ - return mentions; -} - -std::shared_ptr -Channels::getEmpty() -{ - return empty; -} - -const std::vector> -Channels::getItems() -{ - QMutexLocker locker(&Channels::channelsMutex); - - std::vector> items; - - for (auto &item : Channels::channels.values()) { - items.push_back(std::get<0>(item)); - } - - return items; -} - -std::shared_ptr -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(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 -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 diff --git a/channels.h b/channels.h deleted file mode 100644 index 46b547e06..000000000 --- a/channels.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CHANNELS_H -#define CHANNELS_H - -#include "channel.h" - -namespace chatterino { - -void initChannels(); - -class Channels -{ -public: - static std::shared_ptr getWhispers(); - static std::shared_ptr getMentions(); - static std::shared_ptr getEmpty(); - - const static std::vector> getItems(); - - static std::shared_ptr addChannel(const QString &channel); - static std::shared_ptr getChannel(const QString &channel); - static void removeChannel(const QString &channel); - -private: - Channels() - { - } - - static QMap, int>> channels; - static QMutex channelsMutex; -}; - -} // namespace chatterino - -#endif // CHANNELS_H diff --git a/chatterino.pro b/chatterino.pro index 13819c5e2..7334d331a 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -17,17 +17,14 @@ TARGET = chatterino TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += IRC_NAMESPACE=Communi #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += main.cpp\ - account.cpp \ channel.cpp \ - channels.cpp \ colorscheme.cpp \ emojis.cpp \ - emotes.cpp \ - fonts.cpp \ ircmanager.cpp \ messages/lazyloadedimage.cpp \ messages/link.cpp \ @@ -35,7 +32,6 @@ SOURCES += main.cpp\ messages/word.cpp \ messages/wordpart.cpp \ resources.cpp \ - settings.cpp \ widgets/chatwidget.cpp \ widgets/chatwidgetheader.cpp \ widgets/chatwidgetheaderbutton.cpp \ @@ -52,21 +48,31 @@ SOURCES += main.cpp\ widgets/settingsdialog.cpp \ widgets/settingsdialogtab.cpp \ widgets/textinputdialog.cpp \ - windows.cpp \ messages/messageref.cpp \ logging/loggingmanager.cpp \ - logging/loggingchannel.cpp + logging/loggingchannel.cpp \ + windowmanager.cpp \ + channelmanager.cpp \ + fontmanager.cpp \ + settingsmanager.cpp \ + emotemanager.cpp \ + usermanager.cpp \ + twitch/twitchuser.cpp \ + messages/messagebuilder.cpp \ + twitch/twitchmessagebuilder.cpp \ + ircuser2.cpp \ + twitch/twitchparsemessage.cpp \ + widgets/fancybutton.cpp \ + widgets/titlebar.cpp \ + widgets/userpopupwidget.cpp -HEADERS += account.h \ +HEADERS += \ asyncexec.h \ channel.h \ - channels.h \ colorscheme.h \ common.h \ concurrentmap.h \ emojis.h \ - emotes.h \ - fonts.h \ ircmanager.h \ messages/lazyloadedimage.h \ messages/link.h \ @@ -75,8 +81,7 @@ HEADERS += account.h \ messages/wordpart.h \ resources.h \ setting.h \ - settings.h \ - twitchemotevalue.h \ + twitch/emotevalue.h \ widgets/chatwidget.h \ widgets/chatwidgetheader.h \ widgets/chatwidgetheaderbutton.h \ @@ -94,14 +99,29 @@ HEADERS += account.h \ widgets/settingsdialogtab.h \ widgets/signallabel.h \ widgets/textinputdialog.h \ - windows.h \ widgets/resizingtextedit.h \ settingssnapshot.h \ messages/limitedqueue.h \ messages/limitedqueuesnapshot.h \ messages/messageref.h \ logging/loggingmanager.h \ - logging/loggingchannel.h + logging/loggingchannel.h \ + channelmanager.h \ + windowmanager.h \ + settingsmanager.h \ + fontmanager.h \ + emotemanager.h \ + util/urlfetch.h \ + usermanager.h \ + twitch/twitchuser.h \ + messages/messageparseargs.h \ + messages/messagebuilder.h \ + twitch/twitchmessagebuilder.h \ + ircuser2.h \ + twitch/twitchparsemessage.h \ + widgets/fancybutton.h \ + widgets/titlebar.h \ + widgets/userpopupwidget.h PRECOMPILED_HEADER = @@ -112,9 +132,29 @@ DISTFILES += # Include boost win32 { - INCLUDEPATH += C:\local\boost\ + INCLUDEPATH += C:\local\boost\ +} + +# Optional dependency on windows sdk 7.1 +win32:exists(C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\Windows.h) { + LIBS += -L"C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib" \ + -ldwmapi \ + -lgdi32 + + SOURCES += platform/borderless/qwinwidget.cpp \ + platform/borderless/winnativewindow.cpp \ + platform/borderless/widget.cpp + + HEADERS += platform/borderless/qwinwidget.h \ + platform/borderless/winnativewindow.h \ + platform/borderless/widget.h + + DEFINES += "USEWINSDK" } macx { INCLUDEPATH += /usr/local/include } + +FORMS += \ + forms/userpopup.ui diff --git a/clangformat.txt b/clangformat.txt index b40edcf56..ada6c6752 100644 --- a/clangformat.txt +++ b/clangformat.txt @@ -7,7 +7,7 @@ Language: Cpp SpacesBeforeTrailingComments: 2 AccessModifierOffset: -1 AlignEscapedNewlinesLeft: true -AlwaysBreakAfterDefinitionReturnType: true +AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakBeforeMultilineStrings: false BreakConstructorInitializersBeforeComma: true # BreakBeforeBraces: Linux @@ -25,4 +25,5 @@ BraceWrapping: { AfterControlStatement: 'false' AfterFunction: 'true' BeforeCatch: 'false' -} \ No newline at end of file +} +ColumnLimit: 100 \ No newline at end of file diff --git a/colorscheme.cpp b/colorscheme.cpp index 8487fa4b0..024436255 100644 --- a/colorscheme.cpp +++ b/colorscheme.cpp @@ -1,38 +1,36 @@ #define LOOKUP_COLOR_COUNT 360 #include "colorscheme.h" -#include "settings.h" -#include "windows.h" +#include "settingsmanager.h" +#include "windowmanager.h" #include namespace chatterino { -void -ColorScheme::init() +void ColorScheme::init() { static bool initiated = false; if (!initiated) { initiated = true; ColorScheme::getInstance().update(); - Settings::getInstance().theme.valueChanged.connect( + SettingsManager::getInstance().theme.valueChanged.connect( [](const QString &) { ColorScheme::getInstance().update(); }); - Settings::getInstance().themeHue.valueChanged.connect( + SettingsManager::getInstance().themeHue.valueChanged.connect( [](const float &) { ColorScheme::getInstance().update(); }); ColorScheme::getInstance().updated.connect( - [] { Windows::repaintVisibleChatWidgets(); }); + [] { WindowManager::repaintVisibleChatWidgets(); }); } } -void -ColorScheme::update() +void ColorScheme::update() { - QString theme = Settings::getInstance().theme.get(); + QString theme = SettingsManager::getInstance().theme.get(); theme = theme.toLower(); - qreal hue = Settings::getInstance().themeHue.get(); + qreal hue = SettingsManager::getInstance().themeHue.get(); if (theme == "light") { setColors(hue, 0.8); @@ -47,33 +45,40 @@ ColorScheme::update() // hue: theme color (0 - 1) // multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black -void -ColorScheme::setColors(float hue, float multiplyer) +void ColorScheme::setColors(float hue, float multiplyer) { IsLightTheme = multiplyer > 0; + bool hasDarkBorder = false; SystemMessageColor = QColor(140, 127, 127); auto isLightTheme = IsLightTheme; - auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, - qreal a = 1.0) { + auto getColor = [isLightTheme, multiplyer](qreal h, qreal s, qreal l, qreal a = 1.0) { return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a); }; - DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.3); + DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.6); Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255); // tab - TabPanelBackground = QColor(255, 255, 255); - TabBackground = QColor(255, 255, 255); - TabHoverBackground = getColor(hue, 0, 0.05); + if (hasDarkBorder) { + // TabPanelBackground = getColor(hue, 0, 0.8); + // TabBackground = getColor(hue, 0, 0.8); + // TabHoverBackground = getColor(hue, 0, 0.8); + } else { + TabPanelBackground = QColor(255, 255, 255); + TabBackground = QColor(255, 255, 255); + TabHoverBackground = getColor(hue, 0, 0.05); + } TabSelectedBackground = getColor(hue, 0.5, 0.5); TabHighlightedBackground = getColor(hue, 0.5, 0.2); - TabNewMessageBackground = - QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern); - TabText = QColor(0, 0, 0); + TabNewMessageBackground = QBrush(getColor(hue, 0.5, 0.2), Qt::DiagCrossPattern); + if (hasDarkBorder) + // TabText = QColor(210, 210, 210); + // TabHoverText = QColor(210, 210, 210); + TabText = QColor(0, 0, 0); TabHoverText = QColor(0, 0, 0); TabSelectedText = QColor(255, 255, 255); TabHighlightedText = QColor(0, 0, 0); @@ -103,17 +108,14 @@ ColorScheme::setColors(float hue, float multiplyer) fillLookupTableValues(this->minLookupTable, 0.833, 1.000, 0.30, 0.33); // stylesheet - InputStyleSheet = - "background:" + ChatInputBackground.name() + ";" + - "border:" + TabSelectedBackground.name() + ";" + - "color:" + Text.name() + ";" + - "selection-background-color:" + TabSelectedBackground.name(); + InputStyleSheet = "background:" + ChatInputBackground.name() + ";" + + "border:" + TabSelectedBackground.name() + ";" + "color:" + Text.name() + + ";" + "selection-background-color:" + TabSelectedBackground.name(); updated(); } -void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, - qreal to, qreal fromValue, +void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue, qreal toValue) { qreal diff = toValue - fromValue; @@ -127,8 +129,7 @@ void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, } } -void -ColorScheme::normalizeColor(QColor &color) +void ColorScheme::normalizeColor(QColor &color) { // qreal l = color.lightnessF(); // qreal s = color.saturationF(); diff --git a/colorscheme.h b/colorscheme.h index 0711978ba..dfbcf82fa 100644 --- a/colorscheme.h +++ b/colorscheme.h @@ -61,8 +61,7 @@ public: const int HighlightColorCount = 3; QColor HighlightColors[3]; - static ColorScheme & - getInstance() + static ColorScheme &getInstance() { static ColorScheme instance; @@ -87,8 +86,8 @@ private: qreal middleLookupTable[360] = {}; qreal minLookupTable[360] = {}; - void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, - qreal fromValue, qreal toValue); + void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue, + qreal toValue); }; } diff --git a/concurrentmap.h b/concurrentmap.h index f16ee6a0e..1e5fba6f8 100644 --- a/concurrentmap.h +++ b/concurrentmap.h @@ -14,17 +14,16 @@ class ConcurrentMap { public: ConcurrentMap() - : map() + : _map() { } - bool - tryGet(const TKey &name, TValue &value) const + bool tryGet(const TKey &name, TValue &value) const { - QMutexLocker lock(&this->mutex); + QMutexLocker lock(&_mutex); - auto a = map.find(name); - if (a == map.end()) { + auto a = _map.find(name); + if (a == _map.end()) { return false; } @@ -33,40 +32,37 @@ public: return true; } - TValue - getOrAdd(const TKey &name, std::function addLambda) + TValue getOrAdd(const TKey &name, std::function addLambda) { - QMutexLocker lock(&this->mutex); + QMutexLocker lock(&_mutex); - auto a = map.find(name); - if (a == map.end()) { + auto a = _map.find(name); + if (a == _map.end()) { TValue value = addLambda(); - map.insert(name, value); + _map.insert(name, value); return value; } return a.value(); } - void - clear() + void clear() { - QMutexLocker lock(&this->mutex); + QMutexLocker lock(&_mutex); - map.clear(); + _map.clear(); } - void - insert(const TKey &name, const TValue &value) + void insert(const TKey &name, const TValue &value) { - QMutexLocker lock(&this->mutex); + QMutexLocker lock(&_mutex); - map.insert(name, value); + _map.insert(name, value); } private: - mutable QMutex mutex; - QMap map; + mutable QMutex _mutex; + QMap _map; }; } // namespace chatterino diff --git a/emojis.cpp b/emojis.cpp index 5130c0427..9ad302c8d 100644 --- a/emojis.cpp +++ b/emojis.cpp @@ -1,5 +1,5 @@ #include "emojis.h" -#include "emotes.h" +#include "emotemanager.h" #include #include @@ -15,17 +15,14 @@ QMap> Emojis::firstEmojiChars; ConcurrentMap Emojis::imageCache; -QString -Emojis::replaceShortCodes(const QString &text) +QString Emojis::replaceShortCodes(const QString &text) { // TODO: Implement this xD return text; } -void -Emojis::parseEmojis( - std::vector> &vector, - const QString &text) +void Emojis::parseEmojis(std::vector> &vector, + const QString &text) { long lastSlice = 0; @@ -44,21 +41,14 @@ Emojis::parseEmojis( emojiIter.value() + ".png"; if (i - lastSlice != 0) { - vector.push_back( - std::tuple( - NULL, text.mid(lastSlice, i - lastSlice))); + vector.push_back(std::tuple( + NULL, text.mid(lastSlice, i - lastSlice))); } - vector.push_back( - std::tuple( - imageCache.getOrAdd( - url, - [&url] { - return new messages::LazyLoadedImage( - url, 0.35); - }), - QString())); + vector.push_back(std::tuple( + imageCache.getOrAdd( + url, [&url] { return new messages::LazyLoadedImage(url, 0.35); }), + QString())); i += j - 1; @@ -72,13 +62,12 @@ Emojis::parseEmojis( } if (lastSlice < text.length()) { - vector.push_back(std::tuple( - NULL, text.mid(lastSlice))); + vector.push_back( + std::tuple(NULL, text.mid(lastSlice))); } } -void -Emojis::loadEmojis() +void Emojis::loadEmojis() { QFile file(":/emojidata.txt"); file.open(QFile::ReadOnly); @@ -106,8 +95,7 @@ Emojis::loadEmojis() emotes[i++] = QString(item).toUInt(nullptr, 16); } - shortCodeToEmoji.insert( - a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)}); + shortCodeToEmoji.insert(a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)}); } for (auto const &emoji : shortCodeToEmoji.toStdMap()) { @@ -122,9 +110,8 @@ Emojis::loadEmojis() continue; } - firstEmojiChars.insert( - emoji.first.at(0), - QMap{{emoji.second.value, emoji.second.code}}); + firstEmojiChars.insert(emoji.first.at(0), + QMap{{emoji.second.value, emoji.second.code}}); } } } diff --git a/emojis.h b/emojis.h index d5d03f452..6a172a486 100644 --- a/emojis.h +++ b/emojis.h @@ -14,9 +14,8 @@ namespace chatterino { class Emojis { public: - static void parseEmojis( - std::vector> &vector, - const QString &text); + static void parseEmojis(std::vector> &vector, + const QString &text); static void loadEmojis(); diff --git a/emotes.cpp b/emotemanager.cpp similarity index 53% rename from emotes.cpp rename to emotemanager.cpp index 7fa16e9e3..4f2e26adc 100644 --- a/emotes.cpp +++ b/emotemanager.cpp @@ -1,4 +1,4 @@ -#include "emotes.h" +#include "emotemanager.h" #include "resources.h" #include @@ -11,41 +11,77 @@ #include #include +#define TWITCH_EMOTE_TEMPLATE "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0" + +using namespace chatterino::messages; + namespace chatterino { -QString Emotes::twitchEmoteTemplate( - "https://static-cdn.jtvnw.net/emoticons/v1/{id}/{scale}.0"); +EmoteManager EmoteManager::instance; -ConcurrentMap Emotes::twitchEmotes; -ConcurrentMap Emotes::bttvEmotes; -ConcurrentMap Emotes::ffzEmotes; -ConcurrentMap Emotes::chatterinoEmotes; -ConcurrentMap - Emotes::bttvChannelEmoteFromCaches; -ConcurrentMap - Emotes::ffzChannelEmoteFromCaches; -ConcurrentMap Emotes::twitchEmoteFromCache; -ConcurrentMap Emotes::miscImageFromCache; -boost::signals2::signal Emotes::gifUpdateTimerSignal; - -QTimer Emotes::gifUpdateTimer; -bool Emotes::gifUpdateTimerInitiated(false); - -int Emotes::generation = 0; - -Emotes::Emotes() +EmoteManager::EmoteManager() + : _twitchEmotes() + , _bttvEmotes() + , _ffzEmotes() + , _chatterinoEmotes() + , _bttvChannelEmoteFromCaches() + , _ffzChannelEmoteFromCaches() + , _twitchEmoteFromCache() + , _miscImageFromCache() + , _gifUpdateTimerSignal() + , _gifUpdateTimer() + , _gifUpdateTimerInitiated(false) + , _generation(0) { } -void -Emotes::loadGlobalEmotes() +ConcurrentMap &EmoteManager::getTwitchEmotes() +{ + return _twitchEmotes; +} + +ConcurrentMap &EmoteManager::getBttvEmotes() +{ + return _bttvEmotes; +} + +ConcurrentMap &EmoteManager::getFfzEmotes() +{ + return _ffzEmotes; +} + +ConcurrentMap &EmoteManager::getChatterinoEmotes() +{ + return _chatterinoEmotes; +} + +ConcurrentMap &EmoteManager::getBttvChannelEmoteFromCaches() +{ + return _bttvChannelEmoteFromCaches; +} + +ConcurrentMap &EmoteManager::getFfzChannelEmoteFromCaches() +{ + return _ffzChannelEmoteFromCaches; +} + +ConcurrentMap &EmoteManager::getTwitchEmoteFromCache() +{ + return _twitchEmoteFromCache; +} + +ConcurrentMap &EmoteManager::getMiscImageFromCache() +{ + return _miscImageFromCache; +} + +void EmoteManager::loadGlobalEmotes() { loadBttvEmotes(); loadFfzEmotes(); } -void -Emotes::loadBttvEmotes() +void EmoteManager::loadBttvEmotes() { // bttv QNetworkAccessManager *manager = new QNetworkAccessManager(); @@ -63,21 +99,19 @@ Emotes::loadBttvEmotes() auto emotes = root.value("emotes").toArray(); - QString _template = "https:" + root.value("urlTemplate").toString(); + QString linkTemplate = "https:" + root.value("urlTemplate").toString(); for (const QJsonValue &emote : emotes) { QString id = emote.toObject().value("id").toString(); QString code = emote.toObject().value("code").toString(); // emote.value("imageType").toString(); - QString tmp = _template; + QString tmp = linkTemplate; tmp.detach(); - QString url = - tmp.replace("{{id}}", id).replace("{{image}}", "1x"); + QString url = tmp.replace("{{id}}", id).replace("{{image}}", "1x"); - Emotes::getBttvEmotes().insert( - code, new messages::LazyLoadedImage( - url, 1, code, code + "\nGlobal Bttv Emote")); + EmoteManager::getBttvEmotes().insert( + code, new LazyLoadedImage(url, 1, code, code + "\nGlobal Bttv Emote")); } } @@ -86,8 +120,7 @@ Emotes::loadBttvEmotes() }); } -void -Emotes::loadFfzEmotes() +void EmoteManager::loadFfzEmotes() { // ffz QNetworkAccessManager *manager = new QNetworkAccessManager(); @@ -113,15 +146,14 @@ Emotes::loadFfzEmotes() // margins - int id = object.value("id").toInt(); + // int id = object.value("id").toInt(); QString code = object.value("name").toString(); QJsonObject urls = object.value("urls").toObject(); QString url1 = "http:" + urls.value("1").toString(); - Emotes::getBttvEmotes().insert( - code, new messages::LazyLoadedImage( - url1, 1, code, code + "\nGlobal Ffz Emote")); + EmoteManager::getBttvEmotes().insert( + code, new LazyLoadedImage(url1, 1, code, code + "\nGlobal Ffz Emote")); } } } @@ -131,41 +163,34 @@ Emotes::loadFfzEmotes() }); } -messages::LazyLoadedImage * -Emotes::getTwitchEmoteById(const QString &name, long id) +LazyLoadedImage *EmoteManager::getTwitchEmoteById(const QString &name, long id) { - qDebug() << "loading twitch emote: " << id; - - return Emotes::twitchEmoteFromCache.getOrAdd(id, [&name, &id] { - qDebug() << "loading twitch emote: " << id; + return EmoteManager::_twitchEmoteFromCache.getOrAdd(id, [&name, &id] { + qDebug() << "added twitch emote: " << id; qreal scale; QString url = getTwitchEmoteLink(id, scale); - return new messages::LazyLoadedImage(url, scale, name, - name + "\nTwitch Emote"); + return new LazyLoadedImage(url, scale, name, name + "\nTwitch Emote"); }); } -QString -Emotes::getTwitchEmoteLink(long id, qreal &scale) +QString EmoteManager::getTwitchEmoteLink(long id, qreal &scale) { scale = .5; - QString value = Emotes::twitchEmoteTemplate; + QString value = TWITCH_EMOTE_TEMPLATE; value.detach(); return value.replace("{id}", QString::number(id)).replace("{scale}", "2"); } -messages::LazyLoadedImage * -Emotes::getCheerImage(long long amount, bool animated) +LazyLoadedImage *EmoteManager::getCheerImage(long long amount, bool animated) { // TODO: fix this xD return getCheerBadge(amount); } -messages::LazyLoadedImage * -Emotes::getCheerBadge(long long amount) +LazyLoadedImage *EmoteManager::getCheerBadge(long long amount) { if (amount >= 100000) { return Resources::getCheerBadge100000(); diff --git a/emotemanager.h b/emotemanager.h new file mode 100644 index 000000000..be8a4a987 --- /dev/null +++ b/emotemanager.h @@ -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 +#include +#include +#include + +namespace chatterino { +class EmoteManager +{ +public: + static EmoteManager &getInstance() + { + return instance; + } + + ConcurrentMap &getTwitchEmotes(); + ConcurrentMap &getBttvEmotes(); + ConcurrentMap &getFfzEmotes(); + ConcurrentMap &getChatterinoEmotes(); + ConcurrentMap &getBttvChannelEmoteFromCaches(); + ConcurrentMap &getFfzChannelEmoteFromCaches(); + ConcurrentMap &getTwitchEmoteFromCache(); + ConcurrentMap &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 &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 _twitchEmotes; + ConcurrentMap _bttvEmotes; + ConcurrentMap _ffzEmotes; + ConcurrentMap _chatterinoEmotes; + ConcurrentMap _bttvChannelEmoteFromCaches; + ConcurrentMap _ffzChannelEmoteFromCaches; + ConcurrentMap _twitchEmoteFromCache; + ConcurrentMap _miscImageFromCache; + + QTimer _gifUpdateTimer; + bool _gifUpdateTimerInitiated; + + int _generation; + + boost::signals2::signal _gifUpdateTimerSignal; + + // methods + static QString getTwitchEmoteLink(long id, qreal &scale); + + void loadFfzEmotes(); + void loadBttvEmotes(); +}; +} + +#endif // EMOTES_H diff --git a/emotes.h b/emotes.h deleted file mode 100644 index 1ae669b4b..000000000 --- a/emotes.h +++ /dev/null @@ -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 -#include -#include -#include - -namespace chatterino { - -class Emotes -{ -public: - static ConcurrentMap & - getTwitchEmotes() - { - return twitchEmotes; - } - - static ConcurrentMap & - getBttvEmotes() - { - return bttvEmotes; - } - - static ConcurrentMap & - getFfzEmotes() - { - return ffzEmotes; - } - - static ConcurrentMap & - getChatterinoEmotes() - { - return chatterinoEmotes; - } - - static ConcurrentMap & - getBttvChannelEmoteFromCaches() - { - return bttvChannelEmoteFromCaches; - } - - static ConcurrentMap & - getFfzChannelEmoteFromCaches() - { - return ffzChannelEmoteFromCaches; - } - - static ConcurrentMap & - getTwitchEmoteFromCache() - { - return twitchEmoteFromCache; - } - - static ConcurrentMap & - 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 & - 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 twitchEmotes; - static ConcurrentMap bttvEmotes; - static ConcurrentMap ffzEmotes; - static ConcurrentMap chatterinoEmotes; - static ConcurrentMap - bttvChannelEmoteFromCaches; - static ConcurrentMap - ffzChannelEmoteFromCaches; - static ConcurrentMap - twitchEmoteFromCache; - static ConcurrentMap - 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 gifUpdateTimerSignal; -}; -} - -#endif // EMOTES_H diff --git a/fontmanager.cpp b/fontmanager.cpp new file mode 100644 index 000000000..ab0ddbdf6 --- /dev/null +++ b/fontmanager.cpp @@ -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; +} +} diff --git a/fontmanager.h b/fontmanager.h new file mode 100644 index 000000000..2edf2f16a --- /dev/null +++ b/fontmanager.h @@ -0,0 +1,55 @@ +#ifndef FONTS_H +#define FONTS_H + +#include +#include + +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 diff --git a/fonts.cpp b/fonts.cpp deleted file mode 100644 index 4800890a0..000000000 --- a/fonts.cpp +++ /dev/null @@ -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; -} -} diff --git a/fonts.h b/fonts.h deleted file mode 100644 index 3c55c5226..000000000 --- a/fonts.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef FONTS_H -#define FONTS_H - -#include -#include - -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 diff --git a/forms/userpopup.ui b/forms/userpopup.ui new file mode 100644 index 000000000..8b32b504d --- /dev/null +++ b/forms/userpopup.ui @@ -0,0 +1,124 @@ + + + UserPopup + + + + 0 + 0 + 400 + 131 + + + + + 0 + 0 + + + + UserPopup + + + + + + Purge + + + + + + + + + Views + + + + + + + 420 + + + + + + + Followers + + + + + + + 69 + + + + + + + Account Age + + + + + + + 6 years + + + + + + + + + + + + 0 + 0 + + + + + 14 + 75 + true + + + + USERNAME + + + + + + + + 0 + 0 + + + + Close + + + + + + + + + AVATAR + + + + + + + + diff --git a/ircmanager.cpp b/ircmanager.cpp index 0d363cadf..de1790d1d 100644 --- a/ircmanager.cpp +++ b/ircmanager.cpp @@ -1,73 +1,89 @@ #include "ircmanager.h" #include "asyncexec.h" #include "channel.h" -#include "channels.h" +#include "channelmanager.h" +#include "messages/messageparseargs.h" +#include "twitch/twitchmessagebuilder.h" +#include "twitch/twitchparsemessage.h" +#include "twitch/twitchuser.h" +#include "usermanager.h" #include #include + #include #include #include #include #include + #include +using namespace chatterino::messages; + namespace chatterino { -Account *IrcManager::account = nullptr; -std::shared_ptr IrcManager::connection; -QMutex IrcManager::connectionMutex; -long IrcManager::connectionGeneration = 0; -const QString IrcManager::defaultClientId = "7ue61iz46fz11y3cugd0l3tawb4taal"; -QNetworkAccessManager IrcManager::accessManager; +IrcManager IrcManager::instance; -QMap IrcManager::twitchBlockedUsers; -QMutex IrcManager::twitchBlockedUsersMutex; +const QString IrcManager::defaultClientId("7ue61iz46fz11y3cugd0l3tawb4taal"); IrcManager::IrcManager() + : _account(AccountManager::getInstance().getAnon()) + , _connection() + , _connectionMutex() + , _connectionGeneration(0) + , _twitchBlockedUsers() + , _twitchBlockedUsersMutex() + , _accessManager() { } -void -IrcManager::connect() +const twitch::TwitchUser &IrcManager::getUser() const +{ + return _account; +} + +void IrcManager::setUser(const twitch::TwitchUser &account) +{ + _account = account; +} + +void IrcManager::connect() { disconnect(); - async_exec([] { beginConnecting(); }); + async_exec([this] { beginConnecting(); }); } -void -IrcManager::beginConnecting() +void IrcManager::beginConnecting() { - IrcManager::account = const_cast(Account::getAnon()); + int generation = ++IrcManager::_connectionGeneration; - int generation = ++IrcManager::connectionGeneration; + Communi::IrcConnection *c = new Communi::IrcConnection; - auto c = new IrcConnection(); + QObject::connect(c, &Communi::IrcConnection::messageReceived, this, + &IrcManager::messageReceived); + QObject::connect(c, &Communi::IrcConnection::privateMessageReceived, this, + &IrcManager::privateMessageReceived); - QObject::connect(c, &IrcConnection::messageReceived, &messageReceived); - QObject::connect(c, &IrcConnection::privateMessageReceived, - &privateMessageReceived); - - if (account->isAnon()) { + if (_account.isAnon()) { // fetch ignored users - QString username = account->getUsername(); - QString oauthClient = account->getOauthClient(); - QString oauthToken = account->getOauthToken(); + QString username = _account.getUserName(); + QString oauthClient = _account.getOAuthClient(); + QString oauthToken = _account.getOAuthToken(); { - QString nextLink = "https://api.twitch.tv/kraken/users/" + - username + "/blocks?limit=" + 100 + - "&client_id=" + oauthClient; + QString nextLink = "https://api.twitch.tv/kraken/users/" + username + + "/blocks?limit=" + 100 + "&client_id=" + oauthClient; QNetworkAccessManager *manager = new QNetworkAccessManager(); QNetworkRequest req(QUrl(nextLink + "&oauth_token=" + oauthToken)); QNetworkReply *reply = manager->get(req); QObject::connect(reply, &QNetworkReply::finished, [=] { - IrcManager::twitchBlockedUsersMutex.lock(); - IrcManager::twitchBlockedUsers.clear(); - IrcManager::twitchBlockedUsersMutex.unlock(); + _twitchBlockedUsersMutex.lock(); + _twitchBlockedUsers.clear(); + _twitchBlockedUsersMutex.unlock(); QByteArray data = reply->readAll(); QJsonDocument jsonDoc(QJsonDocument::fromJson(data)); @@ -78,15 +94,13 @@ IrcManager::beginConnecting() auto blocks = root.value("blocks").toArray(); - IrcManager::twitchBlockedUsersMutex.lock(); + _twitchBlockedUsersMutex.lock(); for (QJsonValue block : blocks) { - QJsonObject user = - block.toObject().value("user").toObject(); + QJsonObject user = block.toObject().value("user").toObject(); // display_name - IrcManager::twitchBlockedUsers.insert( - user.value("name").toString().toLower(), true); + _twitchBlockedUsers.insert(user.value("name").toString().toLower(), true); } - IrcManager::twitchBlockedUsersMutex.unlock(); + _twitchBlockedUsersMutex.unlock(); manager->deleteLater(); }); @@ -94,10 +108,10 @@ IrcManager::beginConnecting() // fetch available twitch emtoes { - QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + - username + "/emotes?oauth_token=" + - oauthToken + "&client_id=" + oauthClient)); - QNetworkReply *reply = IrcManager::accessManager.get(req); + QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username + + "/emotes?oauth_token=" + oauthToken + + "&client_id=" + oauthClient)); + QNetworkReply *reply = _accessManager.get(req); QObject::connect(reply, &QNetworkReply::finished, [=] { QByteArray data = reply->readAll(); @@ -109,15 +123,13 @@ IrcManager::beginConnecting() auto blocks = root.value("blocks").toArray(); - IrcManager::twitchBlockedUsersMutex.lock(); + _twitchBlockedUsersMutex.lock(); for (QJsonValue block : blocks) { - QJsonObject user = - block.toObject().value("user").toObject(); + QJsonObject user = block.toObject().value("user").toObject(); // display_name - IrcManager::twitchBlockedUsers.insert( - user.value("name").toString().toLower(), true); + _twitchBlockedUsers.insert(user.value("name").toString().toLower(), true); } - IrcManager::twitchBlockedUsersMutex.unlock(); + _twitchBlockedUsersMutex.unlock(); }); } } @@ -129,19 +141,17 @@ IrcManager::beginConnecting() c->setNickName("justinfan123"); c->setRealName("justinfan123"); - c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands")); - c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags")); + c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands")); + c->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags")); - QMutexLocker locker(&IrcManager::connectionMutex); + QMutexLocker locker(&_connectionMutex); - if (generation == IrcManager::connectionGeneration) { + if (generation == _connectionGeneration) { c->moveToThread(QCoreApplication::instance()->thread()); - IrcManager::connection = std::shared_ptr(c); + _connection = std::shared_ptr(c); - auto channels = Channels::getItems(); - - for (auto &channel : channels) { - c->sendRaw("JOIN #" + channel.get()->getName()); + for (auto &channel : ChannelManager::getInstance().getItems()) { + c->sendRaw("JOIN #" + channel->getName()); } c->open(); @@ -150,53 +160,50 @@ IrcManager::beginConnecting() } } -void -IrcManager::disconnect() +void IrcManager::disconnect() { - IrcManager::connectionMutex.lock(); + _connectionMutex.lock(); - auto c = IrcManager::connection; - if (IrcManager::connection.get() != NULL) { - IrcManager::connection = std::shared_ptr(); + auto c = _connection; + if (_connection.get() != NULL) { + _connection = std::shared_ptr(); } - IrcManager::connectionMutex.unlock(); + _connectionMutex.unlock(); } -void -IrcManager::send(QString raw) +void IrcManager::send(QString raw) { - IrcManager::connectionMutex.lock(); - IrcManager::connection->sendRaw(raw); - IrcManager::connectionMutex.unlock(); + _connectionMutex.lock(); + + _connection->sendRaw(raw); + + _connectionMutex.unlock(); } -void -IrcManager::sendJoin(const QString &channel) +void IrcManager::sendJoin(const QString &channel) { - IrcManager::connectionMutex.lock(); + _connectionMutex.lock(); - if (IrcManager::connection.get() != NULL) { - IrcManager::connection.get()->sendRaw("JOIN #" + channel); + if (_connection.get() != NULL) { + _connection.get()->sendRaw("JOIN #" + channel); } - IrcManager::connectionMutex.unlock(); + _connectionMutex.unlock(); } -void -IrcManager::partChannel(const QString &channel) +void IrcManager::partChannel(const QString &channel) { - IrcManager::connectionMutex.lock(); + _connectionMutex.lock(); - if (IrcManager::connection.get() != NULL) { - IrcManager::connection.get()->sendRaw("PART #" + channel); + if (_connection.get() != NULL) { + _connection.get()->sendRaw("PART #" + channel); } - IrcManager::connectionMutex.unlock(); + _connectionMutex.unlock(); } -void -IrcManager::messageReceived(IrcMessage *message) +void IrcManager::messageReceived(Communi::IrcMessage *message) { // qInfo(message->command().toStdString().c_str()); @@ -211,63 +218,51 @@ IrcManager::messageReceived(IrcMessage *message) // } } -void -IrcManager::privateMessageReceived(IrcPrivateMessage *message) +void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message) { - // qInfo(message->content().toStdString().c_str()); - - auto c = Channels::getChannel(message->target().mid(1)); + auto c = ChannelManager::getInstance().getChannel(message->target().mid(1)); if (c != NULL) { - c->addMessage(std::shared_ptr( - new messages::Message(*message, *c))); + messages::MessageParseArgs args; + + c->addMessage(twitch::TwitchMessageBuilder::parse(message, c.get(), args)); } } -bool -IrcManager::isTwitchBlockedUser(QString const &username) +bool IrcManager::isTwitchBlockedUser(QString const &username) { - IrcManager::twitchBlockedUsersMutex.lock(); + QMutexLocker locker(&_twitchBlockedUsersMutex); - auto iterator = IrcManager::twitchBlockedUsers.find(username); + auto iterator = _twitchBlockedUsers.find(username); - if (iterator == IrcManager::twitchBlockedUsers.end()) { - IrcManager::twitchBlockedUsersMutex.unlock(); - return false; - } - - IrcManager::twitchBlockedUsersMutex.unlock(); - return true; + return iterator != _twitchBlockedUsers.end(); } -bool -IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage) +bool IrcManager::tryAddIgnoredUser(QString const &username, QString &errorMessage) { - QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() + - "/blocks/" + username + - "?oauth_token=" + account->getOauthToken() + - "&client_id=" + account->getOauthClient()); + QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" + + username + "?oauth_token=" + _account.getOAuthToken() + + "&client_id=" + _account.getOAuthClient()); QNetworkRequest request(url); - auto reply = IrcManager::accessManager.put(request, QByteArray()); + auto reply = _accessManager.put(request, QByteArray()); reply->waitForReadyRead(10000); if (reply->error() == QNetworkReply::NoError) { - IrcManager::twitchBlockedUsersMutex.lock(); - IrcManager::twitchBlockedUsers.insert(username, true); - IrcManager::twitchBlockedUsersMutex.unlock(); + _twitchBlockedUsersMutex.lock(); + _twitchBlockedUsers.insert(username, true); + _twitchBlockedUsersMutex.unlock(); - delete reply; return true; } - errorMessage = "Error while ignoring user \"" + username + - "\": " + reply->errorString(); + reply->deleteLater(); + + errorMessage = "Error while ignoring user \"" + username + "\": " + reply->errorString(); return false; } -void -IrcManager::addIgnoredUser(QString const &username) +void IrcManager::addIgnoredUser(QString const &username) { QString errorMessage; if (!tryAddIgnoredUser(username, errorMessage)) { @@ -275,38 +270,40 @@ IrcManager::addIgnoredUser(QString const &username) } } -bool -IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage) +bool IrcManager::tryRemoveIgnoredUser(QString const &username, QString &errorMessage) { - QUrl url("https://api.twitch.tv/kraken/users/" + account->getUsername() + - "/blocks/" + username + - "?oauth_token=" + account->getOauthToken() + - "&client_id=" + account->getOauthClient()); + QUrl url("https://api.twitch.tv/kraken/users/" + _account.getUserName() + "/blocks/" + + username + "?oauth_token=" + _account.getOAuthToken() + + "&client_id=" + _account.getOAuthClient()); QNetworkRequest request(url); - auto reply = IrcManager::accessManager.deleteResource(request); + auto reply = _accessManager.deleteResource(request); reply->waitForReadyRead(10000); if (reply->error() == QNetworkReply::NoError) { - IrcManager::twitchBlockedUsersMutex.lock(); - IrcManager::twitchBlockedUsers.remove(username); - IrcManager::twitchBlockedUsersMutex.unlock(); + _twitchBlockedUsersMutex.lock(); + _twitchBlockedUsers.remove(username); + _twitchBlockedUsersMutex.unlock(); - delete reply; return true; } - errorMessage = "Error while unignoring user \"" + username + - "\": " + reply->errorString(); + reply->deleteLater(); + + errorMessage = "Error while unignoring user \"" + username + "\": " + reply->errorString(); return false; } -void -IrcManager::removeIgnoredUser(QString const &username) +void IrcManager::removeIgnoredUser(QString const &username) { QString errorMessage; if (!tryRemoveIgnoredUser(username, errorMessage)) { // TODO: Implement IrcManager::removeIgnoredUser } } + +QNetworkAccessManager &IrcManager::getAccessManager() +{ + return _accessManager; +} } diff --git a/ircmanager.h b/ircmanager.h index 17f9a0124..dc44e1a7f 100644 --- a/ircmanager.h +++ b/ircmanager.h @@ -3,8 +3,8 @@ #define TWITCH_MAX_MESSAGELENGTH 500 -#include "account.h" #include "messages/message.h" +#include "twitch/twitchuser.h" #include #include @@ -16,50 +16,59 @@ namespace chatterino { -class IrcManager +class IrcManager : public QObject { -public: - static void connect(); - static void disconnect(); + Q_OBJECT - static void send(QString raw); +public: + static IrcManager &getInstance() + { + return instance; + } static const QString defaultClientId; + void connect(); + void disconnect(); + + void send(QString raw); + bool isTwitchBlockedUser(QString const &username); bool tryAddIgnoredUser(QString const &username, QString &errorMessage); void addIgnoredUser(QString const &username); bool tryRemoveIgnoredUser(QString const &username, QString &errorMessage); void removeIgnoredUser(QString const &username); - static Account *account; + QNetworkAccessManager &getAccessManager(); - static QNetworkAccessManager & - getAccessManager() - { - return accessManager; - } + void sendJoin(const QString &channel); - static void sendJoin(const QString &channel); + void partChannel(const QString &channel); - static void partChannel(const QString &channel); + const twitch::TwitchUser &getUser() const; + void setUser(const twitch::TwitchUser &account); private: + static IrcManager instance; IrcManager(); - static QMap twitchBlockedUsers; - static QMutex twitchBlockedUsersMutex; + // variables + twitch::TwitchUser _account; - static QNetworkAccessManager accessManager; + std::shared_ptr _connection; + QMutex _connectionMutex; + long _connectionGeneration; - static void beginConnecting(); + QMap _twitchBlockedUsers; + QMutex _twitchBlockedUsersMutex; - static std::shared_ptr connection; - static QMutex connectionMutex; - static long connectionGeneration; + QNetworkAccessManager _accessManager; - static void messageReceived(IrcMessage *message); - static void privateMessageReceived(IrcPrivateMessage *message); + // methods + void beginConnecting(); + + void messageReceived(Communi::IrcMessage *message); + void privateMessageReceived(Communi::IrcPrivateMessage *message); }; } diff --git a/ircuser2.cpp b/ircuser2.cpp new file mode 100644 index 000000000..4c221719e --- /dev/null +++ b/ircuser2.cpp @@ -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; +} +} diff --git a/ircuser2.h b/ircuser2.h new file mode 100644 index 000000000..210d4f5e6 --- /dev/null +++ b/ircuser2.h @@ -0,0 +1,27 @@ +#ifndef IRCUSER_H +#define IRCUSER_H + +#include + +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 diff --git a/logging/loggingchannel.cpp b/logging/loggingchannel.cpp index fa58fac3b..8c0f3748a 100644 --- a/logging/loggingchannel.cpp +++ b/logging/loggingchannel.cpp @@ -14,12 +14,10 @@ Channel::Channel(const QString &_channelName, const QString &_baseDirectory) { QDateTime now = QDateTime::currentDateTime(); - this->fileName = - this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log"; + this->fileName = this->channelName + "-" + now.toString("yyyy-MM-dd") + ".log"; // Open file handle to log file of current date - this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + - this->fileName); + this->fileHandle.setFileName(this->baseDirectory + QDir::separator() + this->fileName); this->fileHandle.open(QIODevice::Append); this->appendLine(this->generateOpeningString(now)); @@ -31,8 +29,7 @@ Channel::~Channel() this->fileHandle.close(); } -void -Channel::append(std::shared_ptr message) +void Channel::append(std::shared_ptr message) { QDateTime now = QDateTime::currentDateTime(); @@ -47,8 +44,7 @@ Channel::append(std::shared_ptr message) this->appendLine(str); } -QString -Channel::generateOpeningString(const QDateTime &now) const +QString Channel::generateOpeningString(const QDateTime &now) const { QString ret = QLatin1Literal("# Start logging at "); @@ -59,8 +55,7 @@ Channel::generateOpeningString(const QDateTime &now) const return ret; } -QString -Channel::generateClosingString(const QDateTime &now) const +QString Channel::generateClosingString(const QDateTime &now) const { QString ret = QLatin1Literal("# Stop logging at "); @@ -71,8 +66,7 @@ Channel::generateClosingString(const QDateTime &now) const return ret; } -void -Channel::appendLine(const QString &line) +void Channel::appendLine(const QString &line) { this->fileHandle.write(line.toUtf8()); this->fileHandle.flush(); diff --git a/logging/loggingchannel.h b/logging/loggingchannel.h index 7e777ff7c..1ca7f4ce5 100644 --- a/logging/loggingchannel.h +++ b/logging/loggingchannel.h @@ -15,17 +15,14 @@ namespace logging { class Channel { public: - explicit Channel(const QString &_channelName, - const QString &_baseDirectory); + explicit Channel(const QString &_channelName, const QString &_baseDirectory); ~Channel(); void append(std::shared_ptr message); private: - QString generateOpeningString( - const QDateTime &now = QDateTime::currentDateTime()) const; - QString generateClosingString( - const QDateTime &now = QDateTime::currentDateTime()) const; + QString generateOpeningString(const QDateTime &now = QDateTime::currentDateTime()) const; + QString generateClosingString(const QDateTime &now = QDateTime::currentDateTime()) const; void appendLine(const QString &line); diff --git a/logging/loggingmanager.cpp b/logging/loggingmanager.cpp index 15a92ce66..750b10384 100644 --- a/logging/loggingmanager.cpp +++ b/logging/loggingmanager.cpp @@ -15,13 +15,11 @@ static QString mentionsBasePath; std::unordered_map> channels; -void -init() +void init() { // Make sure all folders are properly created - logBasePath = - QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + - QDir::separator() + "Logs"; + logBasePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + QDir::separator() + "Logs"; channelBasePath = logBasePath + QDir::separator() + "Channels"; whispersBasePath = logBasePath + QDir::separator() + "Whispers"; mentionsBasePath = logBasePath + QDir::separator() + "Mentions"; @@ -55,8 +53,7 @@ init() } } -static const QString & -getBaseDirectory(const QString &channelName) +static const QString &getBaseDirectory(const QString &channelName) { if (channelName == "/whispers") { return whispersBasePath; @@ -67,8 +64,7 @@ getBaseDirectory(const QString &channelName) } } -std::shared_ptr -get(const QString &channelName) +std::shared_ptr get(const QString &channelName) { if (channelName.isEmpty()) { return nullptr; diff --git a/main.cpp b/main.cpp index 2b502fdc2..0f59faf2e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,13 +1,13 @@ -#include "channels.h" +#include "channelmanager.h" #include "colorscheme.h" #include "emojis.h" -#include "emotes.h" +#include "emotemanager.h" #include "ircmanager.h" #include "logging/loggingmanager.h" #include "resources.h" -#include "settings.h" +#include "settingsmanager.h" #include "widgets/mainwindow.h" -#include "windows.h" +#include "windowmanager.h" #include #include @@ -22,26 +22,25 @@ main(int argc, char *argv[]) QApplication a(argc, argv); chatterino::logging::init(); - chatterino::initChannels(); - Settings::getInstance().load(); + SettingsManager::getInstance().load(); Resources::load(); Emojis::loadEmojis(); - Emotes::loadGlobalEmotes(); + EmoteManager::getInstance().loadGlobalEmotes(); ColorScheme::getInstance().init(); - Windows::load(); + WindowManager::load(); - MainWindow &w = Windows::getMainWindow(); + MainWindow &w = WindowManager::getMainWindow(); w.show(); - IrcManager::connect(); + IrcManager::getInstance().connect(); int ret = a.exec(); - Settings::getInstance().save(); + SettingsManager::getInstance().save(); - Windows::save(); + WindowManager::save(); return ret; } diff --git a/messages/lazyloadedimage.cpp b/messages/lazyloadedimage.cpp index 4886d17ea..422e9d0d7 100644 --- a/messages/lazyloadedimage.cpp +++ b/messages/lazyloadedimage.cpp @@ -1,9 +1,9 @@ #include "messages/lazyloadedimage.h" #include "asyncexec.h" -#include "emotes.h" +#include "emotemanager.h" #include "ircmanager.h" -#include "windows.h" +#include "windowmanager.h" #include #include @@ -16,9 +16,8 @@ namespace chatterino { namespace messages { -LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, - const QString &name, const QString &tooltip, - const QMargins &margin, bool isHat) +LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, const QString &name, + const QString &tooltip, const QMargins &margin, bool isHat) : currentPixmap(NULL) , allFrames() , currentFrame(0) @@ -34,9 +33,8 @@ LazyLoadedImage::LazyLoadedImage(const QString &url, qreal scale, { } -LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, - const QString &name, const QString &tooltip, - const QMargins &margin, bool isHat) +LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, const QString &name, + const QString &tooltip, const QMargins &margin, bool isHat) : currentPixmap(image) , allFrames() , currentFrame(0) @@ -52,8 +50,7 @@ LazyLoadedImage::LazyLoadedImage(QPixmap *image, qreal scale, { } -void -LazyLoadedImage::loadImage() +void LazyLoadedImage::loadImage() { QNetworkAccessManager *manager = new QNetworkAccessManager(); @@ -92,29 +89,25 @@ LazyLoadedImage::loadImage() if (allFrames.size() > 1) { this->animated = true; - Emotes::getGifUpdateSignal().connect([this] { gifUpdateTimout(); }); + EmoteManager::getInstance().getGifUpdateSignal().connect([this] { gifUpdateTimout(); }); } - Emotes::incGeneration(); - Windows::layoutVisibleChatWidgets(); + EmoteManager::getInstance().incGeneration(); + WindowManager::layoutVisibleChatWidgets(); reply->deleteLater(); manager->deleteLater(); }); } -void -LazyLoadedImage::gifUpdateTimout() +void LazyLoadedImage::gifUpdateTimout() { this->currentFrameOffset += GIF_FRAME_LENGTH; while (true) { - if (this->currentFrameOffset > - this->allFrames.at(this->currentFrame).duration) { - this->currentFrameOffset -= - this->allFrames.at(this->currentFrame).duration; - this->currentFrame = - (this->currentFrame + 1) % this->allFrames.size(); + if (this->currentFrameOffset > this->allFrames.at(this->currentFrame).duration) { + this->currentFrameOffset -= this->allFrames.at(this->currentFrame).duration; + this->currentFrame = (this->currentFrame + 1) % this->allFrames.size(); } else { break; } diff --git a/messages/lazyloadedimage.h b/messages/lazyloadedimage.h index 564d3c21c..e3e1d3b09 100644 --- a/messages/lazyloadedimage.h +++ b/messages/lazyloadedimage.h @@ -10,19 +10,14 @@ namespace messages { class LazyLoadedImage : QObject { public: - explicit LazyLoadedImage(const QString &url, qreal scale = 1, - const QString &name = "", - const QString &tooltip = "", - const QMargins &margin = QMargins(), + explicit LazyLoadedImage(const QString &url, qreal scale = 1, const QString &name = "", + const QString &tooltip = "", const QMargins &margin = QMargins(), bool isHat = false); - explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, - const QString &name = "", - const QString &tooltip = "", - const QMargins &margin = QMargins(), + explicit LazyLoadedImage(QPixmap *currentPixmap, qreal scale = 1, const QString &name = "", + const QString &tooltip = "", const QMargins &margin = QMargins(), bool isHat = false); - const QPixmap * - getPixmap() + const QPixmap *getPixmap() { if (!isLoading) { isLoading = true; @@ -32,50 +27,42 @@ public: return currentPixmap; } - qreal - getScale() const + qreal getScale() const { return scale; } - const QString & - getUrl() const + const QString &getUrl() const { return url; } - const QString & - getName() const + const QString &getName() const { return name; } - const QString & - getTooltip() const + const QString &getTooltip() const { return tooltip; } - const QMargins & - getMargin() const + const QMargins &getMargin() const { return margin; } - bool - getAnimated() const + bool getAnimated() const { return animated; } - bool - getIsHat() const + bool getIsHat() const { return ishat; } - int - getWidth() const + int getWidth() const { if (currentPixmap == NULL) { return 16; @@ -83,8 +70,7 @@ public: return currentPixmap->width(); } - int - getHeight() const + int getHeight() const { if (currentPixmap == NULL) { return 16; diff --git a/messages/limitedqueue.h b/messages/limitedqueue.h index 64e3e6c2e..3ea0f17fa 100644 --- a/messages/limitedqueue.h +++ b/messages/limitedqueue.h @@ -24,8 +24,7 @@ public: vector->reserve(this->limit + this->buffer); } - void - clear() + void clear() { std::lock_guard lock(this->mutex); @@ -37,8 +36,7 @@ public: // return true if an item was deleted // deleted will be set if the item was deleted - bool - appendItem(const T &item, T &deleted) + bool appendItem(const T &item, T &deleted) { std::lock_guard lock(this->mutex); @@ -63,7 +61,7 @@ public: } else { deleted = this->vector->at(this->offset); - //append item and increment offset("deleting" first element) + // append item and increment offset("deleting" first element) this->vector->push_back(item); this->offset++; @@ -77,12 +75,11 @@ public: } } - messages::LimitedQueueSnapshot - getSnapshot() + messages::LimitedQueueSnapshot getSnapshot() { std::lock_guard lock(this->mutex); - if(vector->size() < limit) { + if (vector->size() < limit) { return LimitedQueueSnapshot(this->vector, this->offset, this->vector->size()); } else { return LimitedQueueSnapshot(this->vector, this->offset, this->limit); diff --git a/messages/limitedqueuesnapshot.h b/messages/limitedqueuesnapshot.h index fd76b4489..9bb87b426 100644 --- a/messages/limitedqueuesnapshot.h +++ b/messages/limitedqueuesnapshot.h @@ -11,16 +11,14 @@ template class LimitedQueueSnapshot { public: - LimitedQueueSnapshot(std::shared_ptr> ptr, int offset, - int length) + LimitedQueueSnapshot(std::shared_ptr> ptr, int offset, int length) : vector(ptr) , offset(offset) , length(length) { } - int - getLength() + int getLength() { return this->length; } diff --git a/messages/link.h b/messages/link.h index 1fe5f545b..99dfd7ecd 100644 --- a/messages/link.h +++ b/messages/link.h @@ -23,20 +23,17 @@ public: Link(); Link(Type getType, const QString &getValue); - bool - getIsValid() const + bool getIsValid() const { return type == None; } - Type - getType() const + Type getType() const { return type; } - const QString & - getValue() const + const QString &getValue() const { return value; } diff --git a/messages/message.cpp b/messages/message.cpp index acf020192..493509a22 100644 --- a/messages/message.cpp +++ b/messages/message.cpp @@ -2,13 +2,13 @@ #include "channel.h" #include "colorscheme.h" #include "emojis.h" -#include "emotes.h" -#include "fonts.h" +#include "emotemanager.h" +#include "fontmanager.h" #include "ircmanager.h" #include "messages/link.h" #include "qcolor.h" #include "resources.h" -#include "settings.h" +#include "settingsmanager.h" #include #include @@ -19,396 +19,66 @@ namespace chatterino { namespace messages { -QRegularExpression *Message::cheerRegex = - new QRegularExpression("cheer[1-9][0-9]*"); - Message::Message(const QString &text) + : _words() { - words.push_back(Word(text, Word::Text, - ColorScheme::getInstance().SystemMessageColor, text, - QString())); + _words.push_back( + Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString())); } -Message::Message(const IrcPrivateMessage &ircMessage, Channel &channel, - bool enablePingSound, bool isReceivedWhisper, - bool isSentWhisper, bool includeChannel) - : content(ircMessage.content()) +Message::Message(const std::vector &words) + : _words(words) { - this->parseTime = std::chrono::system_clock::now(); - - auto words = std::vector(); - - 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> 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( - 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> parsed; - - Emojis::parseEmojis(parsed, split); - for (const std::tuple &tuple : parsed) { - LazyLoadedImage *image = std::get<0>(tuple); - - if (image == NULL) { // is text - QString string = std::get<1>(tuple); - - // cheers - if (!bits.isEmpty() && string.length() >= 6 && - cheerRegex->match(string).isValid()) { - auto cheer = string.mid(5).toInt(); - - QString color; - - QColor bitsColor; - - if (cheer >= 10000) { - color = "red"; - bitsColor = QColor::fromHslF(0, 1, 0.5); - } else if (cheer >= 5000) { - color = "blue"; - bitsColor = QColor::fromHslF(0.61, 1, 0.4); - } else if (cheer >= 1000) { - color = "green"; - bitsColor = QColor::fromHslF(0.5, 1, 0.5); - } else if (cheer >= 100) { - color = "purple"; - bitsColor = QColor::fromHslF(0.8, 1, 0.5); - } else { - color = "gray"; - bitsColor = QColor::fromHslF(0.5f, 0.5f, 0.5f); - } - - QString bitsLinkAnimated = QString( - "http://static-cdn.jtvnw.net/bits/dark/animated/" + - color + "/1"); - QString bitsLink = QString( - "http://static-cdn.jtvnw.net/bits/dark/static/" + - color + "/1"); - - LazyLoadedImage *imageAnimated = - Emotes::getMiscImageFromCache().getOrAdd( - bitsLinkAnimated, [&bitsLinkAnimated] { - return new LazyLoadedImage(bitsLinkAnimated); - }); - LazyLoadedImage *image = - Emotes::getMiscImageFromCache().getOrAdd( - bitsLink, [&bitsLink] { - return new LazyLoadedImage(bitsLink); - }); - - words.push_back( - Word(imageAnimated, Word::BitsAnimated, - QString("cheer"), QString("Twitch Cheer"), - Link(Link::Url, - QString("https://blog.twitch.tv/" - "introducing-cheering-celebrate-" - "together-da62af41fac6")))); - words.push_back( - Word(image, Word::BitsStatic, QString("cheer"), - QString("Twitch Cheer"), - Link(Link::Url, - QString("https://blog.twitch.tv/" - "introducing-cheering-celebrate-" - "together-da62af41fac6")))); - - words.push_back( - Word(QString("x" + string.mid(5)), Word::BitsAmount, - bitsColor, QString(string.mid(5)), - QString("Twitch Cheer"), - Link(Link::Url, - QString("https://blog.twitch.tv/" - "introducing-cheering-celebrate-" - "together-da62af41fac6")))); - - continue; - } - - // bttv / ffz emotes - LazyLoadedImage *bttvEmote; - - // TODO: Implement this (ignored emotes) - if (Emotes::getBttvEmotes().tryGet(string, bttvEmote) || - channel.getBttvChannelEmotes().tryGet(string, bttvEmote) || - Emotes::getFfzEmotes().tryGet(string, bttvEmote) || - channel.getFfzChannelEmotes().tryGet(string, bttvEmote) || - Emotes::getChatterinoEmotes().tryGet(string, bttvEmote)) { - words.push_back(Word(bttvEmote, Word::BttvEmoteImage, - bttvEmote->getName(), - bttvEmote->getTooltip(), - Link(Link::Url, bttvEmote->getUrl()))); - - continue; - } - - // actually just a word - QString link = matchLink(string); - - words.push_back( - Word(string, Word::Text, textColor, string, QString(), - link.isEmpty() ? Link() : Link(Link::Url, link))); - } else { // is emoji - static QString emojiTooltip("Emoji"); - - words.push_back(Word(image, Word::EmojiImage, image->getName(), - emojiTooltip)); - Word(image->getName(), Word::EmojiText, textColor, - image->getName(), emojiTooltip); - } - } - - i += split.length() + 1; - } - - this->words = words; - - // TODO: Implement this xD - // if (!isReceivedWhisper && - // AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) - // { - // HighlightTab = false; - // } } -QString -Message::matchLink(const QString &string) +bool Message::getCanHighlightTab() const { - // TODO: Implement this xD - return QString(); + return _highlightTab; } -bool -Message::sortTwitchEmotes(const std::pair &a, - const std::pair &b) +const QString &Message::getTimeoutUser() const { - return a.first < b.first; + return _timeoutUser; +} + +int Message::getTimeoutCount() const +{ + return _timeoutCount; +} + +const QString &Message::getUserName() const +{ + return _userName; +} + +const QString &Message::getDisplayName() const +{ + return _displayName; +} + +const QString &Message::getContent() const +{ + return _content; +} + +const std::chrono::time_point &Message::getParseTime() const +{ + return _parseTime; +} + +std::vector &Message::getWords() +{ + return _words; +} + +bool Message::isDisabled() const +{ + return _isDisabled; +} + +const QString &Message::getId() const +{ + return _id; } } // namespace messages diff --git a/messages/message.h b/messages/message.h index da3c0a964..89c3c3dc7 100644 --- a/messages/message.h +++ b/messages/message.h @@ -1,6 +1,8 @@ #ifndef MESSAGE_H #define MESSAGE_H +#include "messages/message.h" +#include "messages/messageparseargs.h" #include "messages/word.h" #include "messages/wordpart.h" @@ -8,84 +10,33 @@ #include #include +#include namespace chatterino { class Channel; namespace messages { +class Message; + +typedef std::shared_ptr SharedMessage; class Message { public: Message(const QString &text); - Message(const IrcPrivateMessage &ircMessage, Channel &channel, - bool enablePingSound = true, bool isReceivedWhisper = false, - bool isSentWhisper = false, bool includeChannel = false); + Message(const std::vector &words); - ~Message() - { - } - - bool - getCanHighlightTab() const - { - return highlightTab; - } - - const QString & - getTimeoutUser() const - { - return timeoutUser; - } - - int - getTimeoutCount() const - { - return timeoutCount; - } - - const QString & - getUserName() const - { - return userName; - } - - const QString & - getDisplayName() const - { - return displayName; - } - - inline const QString & - getContent() const - { - return this->content; - } - - inline const std::chrono::time_point & - getParseTime() const - { - return this->parseTime; - } - - std::vector & - getWords() - { - return words; - } - - bool - getDisabled() const - { - return disabled; - } - - const QString & - getId() const - { - return id; - } + bool getCanHighlightTab() const; + const QString &getTimeoutUser() const; + int getTimeoutCount() const; + const QString &getUserName() const; + const QString &getDisplayName() const; + const QString &getContent() const; + const std::chrono::time_point &getParseTime() const; + std::vector &getWords(); + bool isDisabled() const; + const QString &getId() const; private: static LazyLoadedImage *badgeStaff; @@ -98,24 +49,18 @@ private: static QRegularExpression *cheerRegex; - bool highlightTab = false; - QString timeoutUser = ""; - int timeoutCount = 0; - bool disabled = false; - std::chrono::time_point parseTime; + bool _highlightTab = false; + QString _timeoutUser = ""; + int _timeoutCount = 0; + bool _isDisabled = false; + std::chrono::time_point _parseTime; - QString userName = ""; - QString displayName = ""; - QString content; - QString id = ""; + QString _userName = ""; + QString _displayName = ""; + QString _content; + QString _id = ""; - std::vector words; - - static QString matchLink(const QString &string); - - static bool sortTwitchEmotes( - const std::pair &a, - const std::pair &b); + std::vector _words; }; } // namespace messages diff --git a/messages/messagebuilder.cpp b/messages/messagebuilder.cpp new file mode 100644 index 000000000..e44282efc --- /dev/null +++ b/messages/messagebuilder.cpp @@ -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(); +} +} +} diff --git a/messages/messagebuilder.h b/messages/messagebuilder.h new file mode 100644 index 000000000..5ec2e0953 --- /dev/null +++ b/messages/messagebuilder.h @@ -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 _words; + std::chrono::time_point _parseTime; +}; +} +} + +#endif // MESSAGEBUILDER_H diff --git a/messages/messageparseargs.h b/messages/messageparseargs.h new file mode 100644 index 000000000..8acfab565 --- /dev/null +++ b/messages/messageparseargs.h @@ -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 diff --git a/messages/messageref.cpp b/messages/messageref.cpp index 7f4edce1e..d9d6797b0 100644 --- a/messages/messageref.cpp +++ b/messages/messageref.cpp @@ -1,6 +1,6 @@ #include "messageref.h" -#include "emotes.h" -#include "settings.h" +#include "emotemanager.h" +#include "settingsmanager.h" #include @@ -9,82 +9,87 @@ #define MARGIN_TOP 8 #define MARGIN_BOTTOM 8 +using namespace chatterino::messages; + namespace chatterino { namespace messages { -MessageRef::MessageRef(std::shared_ptr message) - : message(message.get()) - , messagePtr(message) - , wordParts() +MessageRef::MessageRef(SharedMessage message) + : _message(message) + , _wordParts() , buffer() { } -bool -MessageRef::layout(int width, bool enableEmoteMargins) +Message *MessageRef::getMessage() { - auto &settings = Settings::getInstance(); + return _message.get(); +} - bool sizeChanged = width != this->currentLayoutWidth; - bool redraw = width != this->currentLayoutWidth; +int MessageRef::getHeight() const +{ + return _height; +} + +bool MessageRef::layout(int width, bool enableEmoteMargins) +{ + auto &settings = SettingsManager::getInstance(); + + bool sizeChanged = width != _currentLayoutWidth; + bool redraw = width != _currentLayoutWidth; int spaceWidth = 4; - { - int mediumTextLineHeight = - Fonts::getFontMetrics(Fonts::Medium).height(); + int mediumTextLineHeight = + FontManager::getInstance().getFontMetrics(FontManager::Medium).height(); - bool recalculateImages = - this->emoteGeneration != Emotes::getGeneration(); - bool recalculateText = this->fontGeneration != Fonts::getGeneration(); - bool newWordTypes = - this->currentWordTypes != Settings::getInstance().getWordTypeMask(); + bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration(); + bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration(); + bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask(); - qreal emoteScale = settings.emoteScale.get(); - bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get(); + qreal emoteScale = settings.emoteScale.get(); + bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get(); - if (recalculateImages || recalculateText || newWordTypes) { - this->emoteGeneration = Emotes::getGeneration(); - this->fontGeneration = Fonts::getGeneration(); - - redraw = true; - - for (auto &word : this->message->getWords()) { - if (word.isImage()) { - if (recalculateImages) { - auto &image = word.getImage(); - - qreal w = image.getWidth(); - qreal h = image.getHeight(); - - if (scaleEmotesByLineHeight) { - word.setSize( - w * mediumTextLineHeight / h * emoteScale, - mediumTextLineHeight * emoteScale); - } else { - word.setSize(w * image.getScale() * emoteScale, - h * image.getScale() * emoteScale); - } - } - } else { - if (recalculateText) { - QFontMetrics &metrics = word.getFontMetrics(); - word.setSize(metrics.width(word.getText()), - metrics.height()); - } - } - } - } - - if (newWordTypes) { - this->currentWordTypes = Settings::getInstance().getWordTypeMask(); - } - } - - if (!redraw) { + // calculate word sizes + if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) { return false; } - this->currentLayoutWidth = width; + _emoteGeneration = EmoteManager::getInstance().getGeneration(); + _fontGeneration = FontManager::getInstance().getGeneration(); + + for (auto &word : _message->getWords()) { + if (word.isImage()) { + if (!recalculateImages) { + continue; + } + + auto &image = word.getImage(); + + qreal w = image.getWidth(); + qreal h = image.getHeight(); + + if (scaleEmotesByLineHeight) { + word.setSize(w * mediumTextLineHeight / h * emoteScale, + mediumTextLineHeight * emoteScale); + } else { + word.setSize(w * image.getScale() * emoteScale, h * image.getScale() * emoteScale); + } + } else { + if (!recalculateText) { + continue; + } + + QFontMetrics &metrics = word.getFontMetrics(); + word.setSize(metrics.width(word.getText()), metrics.height()); + } + } + + if (newWordTypes) { + _currentWordTypes = SettingsManager::getInstance().getWordTypeMask(); + } + + // layout + _currentLayoutWidth = width; int x = MARGIN_LEFT; int y = MARGIN_TOP; @@ -96,12 +101,11 @@ MessageRef::layout(int width, bool enableEmoteMargins) int lineHeight = 0; bool first = true; - this->wordParts.clear(); + _wordParts.clear(); - uint32_t flags = Settings::getInstance().getWordTypeMask(); + uint32_t flags = SettingsManager::getInstance().getWordTypeMask(); - for (auto it = this->message->getWords().begin(); - it != this->message->getWords().end(); ++it) { + for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) { Word &word = *it; if ((word.getType() & flags) == Word::None) { @@ -121,7 +125,7 @@ MessageRef::layout(int width, bool enableEmoteMargins) // word wrapping if (word.isText() && word.getWidth() + MARGIN_LEFT > right) { - this->alignWordParts(lineStart, lineHeight); + alignWordParts(lineStart, lineHeight); y += lineHeight; @@ -144,9 +148,8 @@ MessageRef::layout(int width, bool enableEmoteMargins) if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) { QString mid = text.mid(start, i - start - 1); - this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, - width, word.getHeight(), - lineNumber, mid, mid)); + _wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(), + lineNumber, mid, mid)); y += metrics.height(); @@ -160,20 +163,19 @@ MessageRef::layout(int width, bool enableEmoteMargins) QString mid(text.mid(start)); width = metrics.width(mid); - this->wordParts.push_back( - WordPart(word, MARGIN_LEFT, y - word.getHeight(), width, - word.getHeight(), lineNumber, mid, mid)); + _wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width, + word.getHeight(), lineNumber, mid, mid)); x = width + MARGIN_LEFT + spaceWidth; lineHeight = word.getHeight(); - lineStart = this->wordParts.size() - 1; + lineStart = _wordParts.size() - 1; first = false; } else if (first || x + word.getWidth() + xOffset <= right) { // fits in the line - this->wordParts.push_back(WordPart(word, x, y - word.getHeight(), - lineNumber, word.getCopyText())); + _wordParts.push_back( + WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText())); x += word.getWidth() + xOffset; x += spaceWidth; @@ -183,15 +185,14 @@ MessageRef::layout(int width, bool enableEmoteMargins) first = false; } else { // doesn't fit in the line - this->alignWordParts(lineStart, lineHeight); + alignWordParts(lineStart, lineHeight); y += lineHeight; - this->wordParts.push_back(WordPart(word, MARGIN_LEFT, - y - word.getHeight(), lineNumber, - word.getCopyText())); + _wordParts.push_back( + WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText())); - lineStart = this->wordParts.size() - 1; + lineStart = _wordParts.size() - 1; lineHeight = word.getHeight(); @@ -202,36 +203,40 @@ MessageRef::layout(int width, bool enableEmoteMargins) } } - this->alignWordParts(lineStart, lineHeight); + alignWordParts(lineStart, lineHeight); - if (this->height != y + lineHeight) { + if (_height != y + lineHeight) { sizeChanged = true; - this->height = y + lineHeight; + _height = y + lineHeight; } if (sizeChanged) { - this->buffer = nullptr; + buffer = nullptr; } - this->updateBuffer = true; + updateBuffer = true; return true; } -void -MessageRef::alignWordParts(int lineStart, int lineHeight) +const std::vector &MessageRef::getWordParts() const { - for (size_t i = lineStart; i < this->wordParts.size(); i++) { - WordPart &wordPart2 = this->wordParts.at(i); + return _wordParts; +} + +void MessageRef::alignWordParts(int lineStart, int lineHeight) +{ + for (size_t i = lineStart; i < _wordParts.size(); i++) { + WordPart &wordPart2 = _wordParts.at(i); wordPart2.setY(wordPart2.getY() + lineHeight); } } -bool -MessageRef::tryGetWordPart(QPoint point, messages::Word &word) +bool MessageRef::tryGetWordPart(QPoint point, Word &word) { - for (messages::WordPart &wordPart : this->wordParts) { + // go through all words and return the first one that contains the point. + for (WordPart &wordPart : _wordParts) { if (wordPart.getRect().contains(point)) { word = wordPart.getWord(); return true; @@ -241,18 +246,17 @@ MessageRef::tryGetWordPart(QPoint point, messages::Word &word) return false; } -int -MessageRef::getSelectionIndex(QPoint position) +int MessageRef::getSelectionIndex(QPoint position) { - if (this->wordParts.size() == 0) { + if (_wordParts.size() == 0) { return 0; } // find out in which line the cursor is int lineNumber = 0, lineStart = 0, lineEnd = 0; - for (int i = 0; i < this->wordParts.size(); i++) { - WordPart &part = this->wordParts[i]; + for (int i = 0; i < _wordParts.size(); i++) { + WordPart &part = _wordParts[i]; // return if curser under the word if (position.y() >= part.getBottom()) { @@ -271,13 +275,13 @@ MessageRef::getSelectionIndex(QPoint position) int index = 0; for (int i = 0; i < lineStart; i++) { - WordPart &part = this->wordParts[i]; + WordPart &part = _wordParts[i]; index += part.getWord().isImage() ? 2 : part.getText().length() + 1; } for (int i = lineStart; i < lineEnd; i++) { - WordPart &part = this->wordParts[i]; + WordPart &part = _wordParts[i]; // curser is left of the word part if (position.x() < part.getX()) { @@ -304,8 +308,7 @@ MessageRef::getSelectionIndex(QPoint position) } index++; - x = part.getX() + - part.getWord().getFontMetrics().width(text, j + 1); + x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1); } } @@ -315,9 +318,9 @@ MessageRef::getSelectionIndex(QPoint position) return index; // go through all the wordparts - // for (int i = 0; i < this->wordParts; i < this->wordParts.size()) { + // for (int i = 0; i < wordParts; i < wordParts.size()) { - // WordPart &part = this->wordParts[i]; + // WordPart &part = wordParts[i]; // // return if curser under the word // if (position.y() >= part.getBottom()) { diff --git a/messages/messageref.h b/messages/messageref.h index 95339779d..487100477 100644 --- a/messages/messageref.h +++ b/messages/messageref.h @@ -9,30 +9,21 @@ namespace chatterino { namespace messages { +class MessageRef; + +typedef std::shared_ptr SharedMessageRef; + class MessageRef { public: - MessageRef(std::shared_ptr message); + MessageRef(SharedMessage message); - Message * - getMessage() - { - return this->message; - } - - int - getHeight() const - { - return height; - } + Message *getMessage(); + int getHeight() const; bool layout(int width, bool enableEmoteMargins = true); - const std::vector & - getWordParts() const - { - return wordParts; - } + const std::vector &getWordParts() const; std::shared_ptr buffer = nullptr; bool updateBuffer = false; @@ -42,18 +33,18 @@ public: int getSelectionIndex(QPoint position); private: - Message *message; - std::shared_ptr messagePtr; + // variables + SharedMessage _message; + std::vector _wordParts; - std::vector wordParts; + int _height = 0; - int height = 0; - - int currentLayoutWidth = -1; - int fontGeneration = -1; - int emoteGeneration = -1; - Word::Type currentWordTypes = Word::None; + int _currentLayoutWidth = -1; + int _fontGeneration = -1; + int _emoteGeneration = -1; + Word::Type _currentWordTypes = Word::None; + // methods void alignWordParts(int lineStart, int lineHeight); }; } diff --git a/messages/word.cpp b/messages/word.cpp index 6e31794c3..58ef5a75d 100644 --- a/messages/word.cpp +++ b/messages/word.cpp @@ -4,35 +4,131 @@ namespace chatterino { namespace messages { // Image word -Word::Word(LazyLoadedImage *image, Type type, const QString ©text, - const QString &tooltip, const Link &link) - : image(image) - , text() - , color() +Word::Word(LazyLoadedImage *image, Type type, const QString ©text, const QString &tooltip, + const Link &link) + : _image(image) + , _text() + , _color() , _isImage(true) - , type(type) - , copyText(copytext) - , tooltip(tooltip) - , link(link) - , characterWidthCache() + , _type(type) + , _copyText(copytext) + , _tooltip(tooltip) + , _link(link) + , _characterWidthCache() { image->getWidth(); // professional segfault test } // Text word -Word::Word(const QString &text, Type type, const QColor &color, - const QString ©text, const QString &tooltip, const Link &link) - : image(NULL) - , text(text) - , color(color) +Word::Word(const QString &text, Type type, const QColor &color, const QString ©text, + const QString &tooltip, const Link &link) + : _image(NULL) + , _text(text) + , _color(color) , _isImage(false) - , type(type) - , copyText(copytext) - , tooltip(tooltip) - , link(link) - , characterWidthCache() + , _type(type) + , _copyText(copytext) + , _tooltip(tooltip) + , _link(link) + , _characterWidthCache() { } +LazyLoadedImage &Word::getImage() const +{ + return *_image; +} + +const QString &Word::getText() const +{ + return _text; +} + +int Word::getWidth() const +{ + return _width; +} + +int Word::getHeight() const +{ + return _height; +} + +void Word::setSize(int width, int height) +{ + _width = width; + _height = height; +} + +bool Word::isImage() const +{ + return _isImage; +} + +bool Word::isText() const +{ + return !_isImage; +} + +const QString &Word::getCopyText() const +{ + return _copyText; +} + +bool Word::hasTrailingSpace() const +{ + return _hasTrailingSpace; +} + +QFont &Word::getFont() const +{ + return FontManager::getInstance().getFont(_font); +} + +QFontMetrics &Word::getFontMetrics() const +{ + return FontManager::getInstance().getFontMetrics(_font); +} + +Word::Type Word::getType() const +{ + return _type; +} + +const QString &Word::getTooltip() const +{ + return _tooltip; +} + +const QColor &Word::getColor() const +{ + return _color; +} + +const Link &Word::getLink() const +{ + return _link; +} + +int Word::getXOffset() const +{ + return _xOffset; +} + +int Word::getYOffset() const +{ + return _yOffset; +} + +void Word::setOffset(int xOffset, int yOffset) +{ + _xOffset = std::max(0, xOffset); + _yOffset = std::max(0, yOffset); +} + +std::vector &Word::getCharacterWidthCache() const +{ + return _characterWidthCache; +} } // namespace messages } // namespace chatterino diff --git a/messages/word.h b/messages/word.h index 018743a51..d4b72f2aa 100644 --- a/messages/word.h +++ b/messages/word.h @@ -1,7 +1,7 @@ #ifndef WORD_H #define WORD_H -#include "fonts.h" +#include "fontmanager.h" #include "messages/lazyloadedimage.h" #include "messages/link.h" @@ -31,8 +31,7 @@ public: BttvGifEmoteText = (1 << 9), FfzEmoteImage = (1 << 10), FfzEmoteText = (1 << 11), - EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | - FfzEmoteImage, + EmoteImages = TwitchEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage, BitsStatic = (1 << 12), BitsAnimated = (1 << 13), @@ -46,9 +45,8 @@ public: BadgePremium = (1 << 20), BadgeChatterino = (1 << 21), BadgeCheer = (1 << 22), - Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | - BadgeTurbo | BadgeBroadcaster | BadgePremium | - BadgeChatterino | BadgeCheer, + Badges = BadgeStaff | BadgeAdmin | BadgeGlobalMod | BadgeModerator | BadgeTurbo | + BadgeBroadcaster | BadgePremium | BadgeChatterino | BadgeCheer, Username = (1 << 23), BitsAmount = (1 << 24), @@ -59,157 +57,61 @@ public: EmojiImage = (1 << 27), EmojiText = (1 << 28), - Default = TimestampNoSeconds | Badges | Username | BitsStatic | - FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage | - TwitchEmoteImage | BitsAmount | Text | ButtonBan | - ButtonTimeout + Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage | + BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text | + ButtonBan | ButtonTimeout }; Word() { } - explicit Word(LazyLoadedImage *image, Type getType, const QString ©text, + explicit Word(LazyLoadedImage *_image, Type getType, const QString ©text, const QString &getTooltip, const Link &getLink = Link()); - explicit Word(const QString &text, Type getType, const QColor &getColor, - const QString ©text, const QString &getTooltip, - const Link &getLink = Link()); + explicit Word(const QString &_text, Type getType, const QColor &getColor, + const QString ©text, const QString &getTooltip, const Link &getLink = Link()); - LazyLoadedImage & - getImage() const - { - return *image; - } + LazyLoadedImage &getImage() const; + const QString &getText() const; + int getWidth() const; + int getHeight() const; + void setSize(int _width, int _height); - const QString & - getText() const - { - return this->text; - } + bool isImage() const; + bool isText() const; + const QString &getCopyText() const; + bool hasTrailingSpace() const; + QFont &getFont() const; + QFontMetrics &getFontMetrics() const; + Type getType() const; + const QString &getTooltip() const; + const QColor &getColor() const; + const Link &getLink() const; + int getXOffset() const; + int getYOffset() const; + void setOffset(int _xOffset, int _yOffset); - int - getWidth() const - { - return this->width; - } - - int - getHeight() const - { - return this->height; - } - - void - setSize(int width, int height) - { - this->width = width; - this->height = height; - } - - bool - isImage() const - { - return this->_isImage; - } - - bool - isText() const - { - return !this->_isImage; - } - - const QString & - getCopyText() const - { - return this->copyText; - } - - bool - hasTrailingSpace() const - { - return this->_hasTrailingSpace; - } - - QFont & - getFont() const - { - return Fonts::getFont(this->font); - } - - QFontMetrics & - getFontMetrics() const - { - return Fonts::getFontMetrics(this->font); - } - - Type - getType() const - { - return this->type; - } - - const QString & - getTooltip() const - { - return this->tooltip; - } - - const QColor & - getColor() const - { - return this->color; - } - - const Link & - getLink() const - { - return this->link; - } - - int - getXOffset() const - { - return this->xOffset; - } - - int - getYOffset() const - { - return this->yOffset; - } - - void - setOffset(int xOffset, int yOffset) - { - this->xOffset = std::max(0, xOffset); - this->yOffset = std::max(0, yOffset); - } - - std::vector & - getCharacterWidthCache() const - { - return this->characterWidthCache; - } + std::vector &getCharacterWidthCache() const; private: - LazyLoadedImage *image; - QString text; - QColor color; + LazyLoadedImage *_image; + QString _text; + QColor _color; bool _isImage; - Type type; - QString copyText; - QString tooltip; + Type _type; + QString _copyText; + QString _tooltip; - int width = 16; - int height = 16; - int xOffset = 0; - int yOffset = 0; + int _width = 16; + int _height = 16; + int _xOffset = 0; + int _yOffset = 0; bool _hasTrailingSpace; - Fonts::Type font = Fonts::Medium; - Link link; + FontManager::Type _font = FontManager::Medium; + Link _link; - mutable std::vector characterWidthCache; + mutable std::vector _characterWidthCache; }; } // namespace messages diff --git a/messages/wordpart.cpp b/messages/wordpart.cpp index 0080b223c..57deade4c 100644 --- a/messages/wordpart.cpp +++ b/messages/wordpart.cpp @@ -4,33 +4,103 @@ namespace chatterino { namespace messages { -WordPart::WordPart(Word &word, int x, int y, int lineNumber, - const QString ©Text, bool allowTrailingSpace) - : m_word(word) - , copyText(copyText) - , text(word.isText() ? m_word.getText() : QString()) - , x(x) - , y(y) - , width(word.getWidth()) - , height(word.getHeight()) - , lineNumber(lineNumber) +WordPart::WordPart(Word &word, int x, int y, int lineNumber, const QString ©Text, + bool allowTrailingSpace) + : _word(word) + , _copyText(copyText) + , _text(word.isText() ? _word.getText() : QString()) + , _x(x) + , _y(y) + , _width(word.getWidth()) + , _height(word.getHeight()) + , _lineNumber(lineNumber) , _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) { } -WordPart::WordPart(Word &word, int x, int y, int width, int height, - int lineNumber, const QString ©Text, - const QString &customText, bool allowTrailingSpace) - : m_word(word) - , copyText(copyText) - , text(customText) - , x(x) - , y(y) - , width(width) - , height(height) - , lineNumber(lineNumber) +WordPart::WordPart(Word &word, int x, int y, int width, int height, int lineNumber, + const QString ©Text, const QString &customText, bool allowTrailingSpace) + : _word(word) + , _copyText(copyText) + , _text(customText) + , _x(x) + , _y(y) + , _width(width) + , _height(height) + , _lineNumber(lineNumber) , _trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) { } + +const Word &WordPart::getWord() const +{ + return _word; +} + +int WordPart::getWidth() const +{ + return _width; +} + +int WordPart::getHeight() const +{ + return _height; +} + +int WordPart::getX() const +{ + return _x; +} + +int WordPart::getY() const +{ + return _y; +} + +void WordPart::setPosition(int x, int y) +{ + _x = x; + _y = y; +} + +void WordPart::setY(int y) +{ + _y = y; +} + +int WordPart::getRight() const +{ + return _x + _width; +} + +int WordPart::getBottom() const +{ + return _y + _height; +} + +QRect WordPart::getRect() const +{ + return QRect(_x, _y, _width, _height); +} + +const QString WordPart::getCopyText() const +{ + return _copyText; +} + +int WordPart::hasTrailingSpace() const +{ + return _trailingSpace; +} + +const QString &WordPart::getText() const +{ + return _text; +} + +int WordPart::getLineNumber() +{ + return _lineNumber; +} } } diff --git a/messages/wordpart.h b/messages/wordpart.h index 0a19026fe..15904c94c 100644 --- a/messages/wordpart.h +++ b/messages/wordpart.h @@ -12,110 +12,39 @@ class Word; class WordPart { public: - WordPart(Word &getWord, int getX, int getY, int lineNumber, - const QString &getCopyText, bool allowTrailingSpace = true); + WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText, + bool allowTrailingSpace = true); - WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, - int lineNumber, const QString &getCopyText, - const QString &customText, bool allowTrailingSpace = true); + WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber, + const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true); - const Word & - getWord() const - { - return this->m_word; - } - - int - getWidth() const - { - return this->width; - } - - int - getHeight() const - { - return this->height; - } - - int - getX() const - { - return this->x; - } - - int - getY() const - { - return this->y; - } - - void - setPosition(int x, int y) - { - this->x = x; - this->y = y; - } - - void - setY(int y) - { - this->y = y; - } - - int - getRight() const - { - return this->x + this->width; - } - - int - getBottom() const - { - return this->y + this->height; - } - - QRect - getRect() const - { - return QRect(this->x, this->y, this->width, this->height); - } - - const QString - getCopyText() const - { - return this->copyText; - } - - int - hasTrailingSpace() const - { - return this->_trailingSpace; - } - - const QString & - getText() const - { - return this->text; - } - - int - getLineNumber() - { - return this->lineNumber; - } + const Word &getWord() const; + int getWidth() const; + int getHeight() const; + int getX() const; + int getY() const; + void setPosition(int _x, int _y); + void setY(int _y); + int getRight() const; + int getBottom() const; + QRect getRect() const; + const QString getCopyText() const; + int hasTrailingSpace() const; + const QString &getText() const; + int getLineNumber(); private: - Word &m_word; + Word &_word; - QString copyText; - QString text; + QString _copyText; + QString _text; - int x; - int y; - int width; - int height; + int _x; + int _y; + int _width; + int _height; - int lineNumber; + int _lineNumber; bool _trailingSpace; }; diff --git a/platform/borderless/LICENSE b/platform/borderless/LICENSE new file mode 100644 index 000000000..35de9c004 --- /dev/null +++ b/platform/borderless/LICENSE @@ -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. diff --git a/platform/borderless/qwinwidget.cpp b/platform/borderless/qwinwidget.cpp new file mode 100644 index 000000000..e91132d23 --- /dev/null +++ b/platform/borderless/qwinwidget.cpp @@ -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 +#include +#include +#include +#include + +/*! + \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(); + 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; +} diff --git a/platform/borderless/qwinwidget.h b/platform/borderless/qwinwidget.h new file mode 100644 index 000000000..e9e307c18 --- /dev/null +++ b/platform/borderless/qwinwidget.h @@ -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 +#include + +#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 diff --git a/platform/borderless/widget.cpp b/platform/borderless/widget.cpp new file mode 100644 index 000000000..23cc7cbca --- /dev/null +++ b/platform/borderless/widget.cpp @@ -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); +} diff --git a/platform/borderless/widget.h b/platform/borderless/widget.h new file mode 100644 index 000000000..3730798a5 --- /dev/null +++ b/platform/borderless/widget.h @@ -0,0 +1,28 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include +#include + +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 diff --git a/platform/borderless/winnativewindow.cpp b/platform/borderless/winnativewindow.cpp new file mode 100644 index 000000000..a6939e757 --- /dev/null +++ b/platform/borderless/winnativewindow.cpp @@ -0,0 +1,235 @@ +#include "WinNativeWindow.h" + +#include +#include + +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(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(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; +} diff --git a/platform/borderless/winnativewindow.h b/platform/borderless/winnativewindow.h new file mode 100644 index 000000000..accb97505 --- /dev/null +++ b/platform/borderless/winnativewindow.h @@ -0,0 +1,53 @@ +#ifndef WINNATIVEWINDOW_H +#define WINNATIVEWINDOW_H + +#include "Windows.h" +#include "Windowsx.h" + +#include + +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 diff --git a/resources.cpp b/resources.cpp index faaf0f270..762ed7336 100644 --- a/resources.cpp +++ b/resources.cpp @@ -26,43 +26,34 @@ Resources::Resources() { } -void -Resources::load() +void Resources::load() { // badges - Resources::badgeStaff = - new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png")); - Resources::badgeAdmin = - new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png")); + Resources::badgeStaff = new messages::LazyLoadedImage(new QPixmap(":/images/staff_bg.png")); + Resources::badgeAdmin = new messages::LazyLoadedImage(new QPixmap(":/images/admin_bg.png")); Resources::badgeModerator = new messages::LazyLoadedImage(new QPixmap(":/images/moderator_bg.png")); Resources::badgeGlobalmod = new messages::LazyLoadedImage(new QPixmap(":/images/globalmod_bg.png")); - Resources::badgeTurbo = - new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png")); - Resources::badgeBroadcaster = new messages::LazyLoadedImage( - new QPixmap(":/images/broadcaster_bg.png")); - Resources::badgePremium = new messages::LazyLoadedImage( - new QPixmap(":/images/twitchprime_bg.png")); + Resources::badgeTurbo = new messages::LazyLoadedImage(new QPixmap(":/images/turbo_bg.png")); + Resources::badgeBroadcaster = + new messages::LazyLoadedImage(new QPixmap(":/images/broadcaster_bg.png")); + Resources::badgePremium = + new messages::LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png")); // cheer badges Resources::cheerBadge100000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer100000")); - Resources::cheerBadge10000 = - new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000")); - Resources::cheerBadge5000 = - new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000")); - Resources::cheerBadge1000 = - new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000")); - Resources::cheerBadge100 = - new messages::LazyLoadedImage(new QPixmap(":/images/cheer100")); - Resources::cheerBadge1 = - new messages::LazyLoadedImage(new QPixmap(":/images/cheer1")); + Resources::cheerBadge10000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer10000")); + Resources::cheerBadge5000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer5000")); + Resources::cheerBadge1000 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1000")); + Resources::cheerBadge100 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer100")); + Resources::cheerBadge1 = new messages::LazyLoadedImage(new QPixmap(":/images/cheer1")); // button - Resources::buttonBan = new messages::LazyLoadedImage( - new QPixmap(":/images/button_ban.png"), 0.25); - Resources::buttonTimeout = new messages::LazyLoadedImage( - new QPixmap(":/images/button_timeout.png"), 0.25); + Resources::buttonBan = + new messages::LazyLoadedImage(new QPixmap(":/images/button_ban.png"), 0.25); + Resources::buttonTimeout = + new messages::LazyLoadedImage(new QPixmap(":/images/button_timeout.png"), 0.25); } } diff --git a/resources.h b/resources.h index 74bb1609b..4bd165155 100644 --- a/resources.h +++ b/resources.h @@ -11,93 +11,78 @@ public: static void load(); // badges - static messages::LazyLoadedImage * - getBadgeStaff() + static messages::LazyLoadedImage *getBadgeStaff() { return badgeStaff; } - static messages::LazyLoadedImage * - getBadgeAdmin() + static messages::LazyLoadedImage *getBadgeAdmin() { return badgeAdmin; } - static messages::LazyLoadedImage * - getBadgeGlobalmod() + static messages::LazyLoadedImage *getBadgeGlobalmod() { return badgeGlobalmod; } - static messages::LazyLoadedImage * - getBadgeModerator() + static messages::LazyLoadedImage *getBadgeModerator() { return badgeModerator; } - static messages::LazyLoadedImage * - getBadgeTurbo() + static messages::LazyLoadedImage *getBadgeTurbo() { return badgeTurbo; } - static messages::LazyLoadedImage * - getBadgeBroadcaster() + static messages::LazyLoadedImage *getBadgeBroadcaster() { return badgeBroadcaster; } - static messages::LazyLoadedImage * - getBadgePremium() + static messages::LazyLoadedImage *getBadgePremium() { return badgePremium; } // cheer badges - static messages::LazyLoadedImage * - getCheerBadge100000() + static messages::LazyLoadedImage *getCheerBadge100000() { return cheerBadge100000; } - static messages::LazyLoadedImage * - getCheerBadge10000() + static messages::LazyLoadedImage *getCheerBadge10000() { return cheerBadge10000; } - static messages::LazyLoadedImage * - getCheerBadge5000() + static messages::LazyLoadedImage *getCheerBadge5000() { return cheerBadge5000; } - static messages::LazyLoadedImage * - getCheerBadge1000() + static messages::LazyLoadedImage *getCheerBadge1000() { return cheerBadge1000; } - static messages::LazyLoadedImage * - getCheerBadge100() + static messages::LazyLoadedImage *getCheerBadge100() { return cheerBadge100; } - static messages::LazyLoadedImage * - getCheerBadge1() + static messages::LazyLoadedImage *getCheerBadge1() { return cheerBadge1; } - static messages::LazyLoadedImage * - getButtonBan() + static messages::LazyLoadedImage *getButtonBan() { return buttonBan; } - static messages::LazyLoadedImage * - getButtonTimeout() + static messages::LazyLoadedImage *getButtonTimeout() { return buttonTimeout; } diff --git a/setting.h b/setting.h index 6c0b1e61c..76eceedb7 100644 --- a/setting.h +++ b/setting.h @@ -11,70 +11,65 @@ class BaseSetting { public: BaseSetting(const QString &_name) - : name(_name) + : _name(_name) { } virtual QVariant getVariant() = 0; virtual void setVariant(QVariant value) = 0; - const QString & - getName() const + const QString &getName() const { - return this->name; + return _name; } private: - QString name; + QString _name; }; template class Setting : public BaseSetting { public: - Setting(std::vector> &settingItems, - const QString &_name, const T &defaultValue) + Setting(std::vector> &settingItems, const QString &_name, + const T &defaultValue) : BaseSetting(_name) - , value(defaultValue) + , _value(defaultValue) { settingItems.push_back(*this); } - const T & - get() const + const T &get() const { - return this->value; + return _value; } - void - set(const T &newValue) + void set(const T &newValue) { - if (this->value != newValue) { - this->value = newValue; + if (_value != newValue) { + _value = newValue; - this->valueChanged(newValue); + valueChanged(newValue); } } - virtual QVariant - getVariant() final + virtual QVariant getVariant() final { - return QVariant::fromValue(value); + return QVariant::fromValue(_value); } - virtual void - setVariant(QVariant value) final + virtual void setVariant(QVariant value) final { if (value.isValid()) { assert(value.canConvert()); - this->set(value.value()); + set(value.value()); } } boost::signals2::signal valueChanged; private: - T value; + T _value; }; } // namespace chatterino diff --git a/settings.cpp b/settings.cpp deleted file mode 100644 index 808f850b9..000000000 --- a/settings.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "settings.h" - -#include -#include -#include - -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 diff --git a/settingsmanager.cpp b/settingsmanager.cpp new file mode 100644 index 000000000..57e4e960b --- /dev/null +++ b/settingsmanager.cpp @@ -0,0 +1,146 @@ +#include "settingsmanager.h" + +#include +#include +#include + +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 diff --git a/settings.h b/settingsmanager.h similarity index 66% rename from settings.h rename to settingsmanager.h index 94d605a1c..541c82852 100644 --- a/settings.h +++ b/settingsmanager.h @@ -9,75 +9,10 @@ namespace chatterino { -class Settings : public QObject +class SettingsManager : public QObject { Q_OBJECT -public: - static Settings & - getInstance() - { - return instance; - } - - void load(); - void save(); - - messages::Word::Type - getWordTypeMask() - { - return wordTypeMask; - } - - bool isIgnoredEmote(const QString &emote); - - bool - getPortable() - { - return portable; - } - - void - setPortable(bool value) - { - portable = value; - } - - QSettings & - getQSettings() - { - return settings; - } - - SettingsSnapshot - createSnapshot() - { - SettingsSnapshot snapshot; - - for (auto &item : this->settingsItems) { - snapshot.addItem(item, item.get().getVariant()); - } - - return snapshot; - } - -signals: - void wordTypeMaskChanged(); - -private: - Settings(); - - static Settings instance; - - void updateWordTypeMask(); - - QSettings settings; - std::vector> settingsItems; - - bool portable; - - messages::Word::Type wordTypeMask; - public: Setting theme; Setting themeHue; @@ -110,6 +45,41 @@ public: Setting hideTabX; Setting hidePreferencesButton; Setting hideUserButton; + Setting 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> _settingsItems; + bool _portable; + messages::Word::Type _wordTypeMask; + + // methods + void updateWordTypeMask(); + +public: + static SettingsManager &getInstance() + { + return instance; + } + +private: + static SettingsManager instance; }; } // namespace chatterino diff --git a/settingssnapshot.h b/settingssnapshot.h index 18a83737b..7c75d2d28 100644 --- a/settingssnapshot.h +++ b/settingssnapshot.h @@ -3,34 +3,31 @@ #include "setting.h" -struct SettingsSnapshot { -private: - std::vector< - std::pair, QVariant>> - items; +namespace chatterino { +struct SettingsSnapshot { public: SettingsSnapshot() - : items() + : _items() { } - void - addItem(std::reference_wrapper setting, - const QVariant &value) + void addItem(std::reference_wrapper setting, const QVariant &value) { - items.push_back( - std::pair, - QVariant>(setting.get(), value)); + _items.push_back( + std::pair, QVariant>(setting.get(), value)); } - void - apply() + void apply() { - for (auto &item : this->items) { + for (auto &item : _items) { item.first.get().setVariant(item.second); } } + +private: + std::vector, QVariant>> _items; }; +} #endif // SETTINGSSNAPSHOT_H diff --git a/twitch/emotevalue.h b/twitch/emotevalue.h new file mode 100644 index 000000000..9170b713c --- /dev/null +++ b/twitch/emotevalue.h @@ -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 diff --git a/twitch/twitchmessagebuilder.cpp b/twitch/twitchmessagebuilder.cpp new file mode 100644 index 000000000..0835577df --- /dev/null +++ b/twitch/twitchmessagebuilder.cpp @@ -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> 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( + start, EmoteManager::getInstance().getTwitchEmoteById(name, id))); + } + } + + struct { + bool operator()(const std::pair &a, + const std::pair &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> parsed; + + Emojis::parseEmojis(parsed, split); + + for (const std::tuple &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 &a, +// const std::pair &b) +//{ +// return a.first < b.first; +//} +} +} diff --git a/twitch/twitchmessagebuilder.h b/twitch/twitchmessagebuilder.h new file mode 100644 index 000000000..d8bbfff2d --- /dev/null +++ b/twitch/twitchmessagebuilder.h @@ -0,0 +1,31 @@ +#ifndef TWITCHMESSAGEBUILDER_H +#define TWITCHMESSAGEBUILDER_H + +#include "channel.h" +#include "messages/messagebuilder.h" + +#include + +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 &a, + // const std::pair &b); +}; +} +} +#endif // TWITCHMESSAGEBUILDER_H diff --git a/twitch/twitchparsemessage.cpp b/twitch/twitchparsemessage.cpp new file mode 100644 index 000000000..2c677bc30 --- /dev/null +++ b/twitch/twitchparsemessage.cpp @@ -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 +// +// 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> 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( +// 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> parsed; +// +// Emojis::parseEmojis(parsed, split); +// +// for (const std::tuple &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 &a, +// const std::pair &b) +//{ +// return a.first < b.first; +//} +//} +//} +// diff --git a/twitch/twitchparsemessage.h b/twitch/twitchparsemessage.h new file mode 100644 index 000000000..23aec2f80 --- /dev/null +++ b/twitch/twitchparsemessage.h @@ -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 diff --git a/twitch/twitchuser.cpp b/twitch/twitchuser.cpp new file mode 100644 index 000000000..4bf9c1873 --- /dev/null +++ b/twitch/twitchuser.cpp @@ -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"); +} +} +} diff --git a/twitch/twitchuser.h b/twitch/twitchuser.h new file mode 100644 index 000000000..ca1f6ab4b --- /dev/null +++ b/twitch/twitchuser.h @@ -0,0 +1,28 @@ +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include "ircuser2.h" + +#include + +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 diff --git a/twitchemotevalue.h b/twitchemotevalue.h deleted file mode 100644 index 9918b350f..000000000 --- a/twitchemotevalue.h +++ /dev/null @@ -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 diff --git a/usermanager.cpp b/usermanager.cpp new file mode 100644 index 000000000..49c47f5a7 --- /dev/null +++ b/usermanager.cpp @@ -0,0 +1,16 @@ +#include "usermanager.h" + +namespace chatterino { + +AccountManager AccountManager::instance; + +AccountManager::AccountManager() + : _anon("justinfan64537", "", "") +{ +} + +twitch::TwitchUser &AccountManager::getAnon() +{ + return _anon; +} +} diff --git a/usermanager.h b/usermanager.h new file mode 100644 index 000000000..0302a07a3 --- /dev/null +++ b/usermanager.h @@ -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 diff --git a/util/urlfetch.h b/util/urlfetch.h new file mode 100644 index 000000000..34786c6ba --- /dev/null +++ b/util/urlfetch.h @@ -0,0 +1,55 @@ +#ifndef URLFETCH_H +#define URLFETCH_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chatterino { +namespace util { + +static void urlFetch(const QString &url, std::function 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 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 diff --git a/widgets/chatwidget.cpp b/widgets/chatwidget.cpp index fb2f5abf4..529b4c533 100644 --- a/widgets/chatwidget.cpp +++ b/widgets/chatwidget.cpp @@ -1,7 +1,7 @@ #include "widgets/chatwidget.h" -#include "channels.h" +#include "channelmanager.h" #include "colorscheme.h" -#include "settings.h" +#include "settingsmanager.h" #include "widgets/textinputdialog.h" #include @@ -11,110 +11,167 @@ #include #include +using namespace chatterino::messages; + namespace chatterino { namespace widgets { ChatWidget::ChatWidget(QWidget *parent) : QWidget(parent) - , messages() - , channel(Channels::getEmpty()) - , channelName(QString()) - , vbox(this) - , header(this) - , view(this) - , input(this) + , _messages() + , _channel(ChannelManager::getInstance().getEmpty()) + , _channelName(QString()) + , _vbox(this) + , _header(this) + , _view(this) + , _input(this) { - this->vbox.setSpacing(0); - this->vbox.setMargin(1); + this->_vbox.setSpacing(0); + this->_vbox.setMargin(1); - this->vbox.addWidget(&header); - this->vbox.addWidget(&view, 1); - this->vbox.addWidget(&input); + this->_vbox.addWidget(&_header); + this->_vbox.addWidget(&_view, 1); + this->_vbox.addWidget(&_input); } ChatWidget::~ChatWidget() { } +std::shared_ptr +ChatWidget::getChannel() const +{ + return _channel; +} + +const QString & +ChatWidget::getChannelName() const +{ + return _channelName; +} + void ChatWidget::setChannelName(const QString &name) { QString channel = name.trimmed(); - if (QString::compare(channel, this->channelName, Qt::CaseInsensitive) == - 0) { - this->channelName = channel; - this->header.updateChannelText(); + // return if channel name is the same + if (QString::compare(channel, _channelName, Qt::CaseInsensitive) == 0) { + _channelName = channel; + _header.updateChannelText(); + return; } - if (!this->channelName.isEmpty()) { - Channels::removeChannel(this->channelName); + // remove current channel + if (!_channelName.isEmpty()) { + ChannelManager::getInstance().removeChannel(_channelName); - this->messageAppendedConnection.disconnect(); - this->messageRemovedConnection.disconnect(); + detachChannel(_channel); } - this->channelName = channel; - this->header.updateChannelText(); + // update members + _channelName = channel; - this->view.layoutMessages(); - - messages.clear(); + // update messages + _messages.clear(); if (channel.isEmpty()) { - this->channel = NULL; - + _channel = NULL; } else { - this->channel = Channels::addChannel(channel); + _channel = ChannelManager::getInstance().addChannel(channel); - this->messageAppendedConnection = - this->channel.get()->messageAppended.connect([this]( - std::shared_ptr &message) { - - std::shared_ptr deleted; - - auto messageRef = new messages::MessageRef(message); - - this->messages.appendItem( - std::shared_ptr(messageRef), deleted); - }); - - this->messageRemovedConnection = - this->channel.get()->messageRemovedFromStart.connect( - [this](std::shared_ptr &) {}); - - auto snapshot = this->channel.get()->getMessageSnapshot(); - - for (int i = 0; i < snapshot.getLength(); i++) { - std::shared_ptr deleted; - - auto messageRef = new messages::MessageRef(snapshot[i]); - - this->messages.appendItem( - std::shared_ptr(messageRef), deleted); - } + attachChannel(_channel); } - this->view.layoutMessages(); - this->view.update(); + // update header + _header.updateChannelText(); + + // update view + _view.layoutMessages(); + _view.update(); +} + +void +ChatWidget::attachChannel(SharedChannel channel) +{ + // on new message + _messageAppendedConnection = + channel->messageAppended.connect([this](SharedMessage &message) { + SharedMessageRef deleted; + + auto messageRef = new MessageRef(message); + + if (_messages.appendItem(SharedMessageRef(messageRef), deleted)) { + qreal value = + std::max(0.0, _view.getScrollbar()->getDesiredValue() - 1); + + _view.getScrollbar()->setDesiredValue(value, false); + } + }); + + // on message removed + _messageRemovedConnection = + _channel->messageRemovedFromStart.connect([this](SharedMessage &) {}); + + auto snapshot = _channel.get()->getMessageSnapshot(); + + for (int i = 0; i < snapshot.getLength(); i++) { + SharedMessageRef deleted; + + auto messageRef = new MessageRef(snapshot[i]); + + _messages.appendItem(SharedMessageRef(messageRef), deleted); + } +} + +void +ChatWidget::detachChannel(std::shared_ptr channel) +{ + // on message added + _messageAppendedConnection.disconnect(); + + // on message removed + _messageRemovedConnection.disconnect(); +} + +LimitedQueueSnapshot +ChatWidget::getMessagesSnapshot() +{ + return _messages.getSnapshot(); } void ChatWidget::showChangeChannelPopup() { + // create new input dialog and execute it TextInputDialog dialog(this); - dialog.setText(this->channelName); + dialog.setText(_channelName); if (dialog.exec() == QDialog::Accepted) { setChannelName(dialog.getText()); } } +void +ChatWidget::layoutMessages() +{ + if (_view.layoutMessages()) { + _view.update(); + } +} + +void +ChatWidget::updateGifEmotes() +{ + _view.updateGifEmotes(); +} + void ChatWidget::paintEvent(QPaintEvent *) { + // color the background of the chat QPainter painter(this); painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground); @@ -123,7 +180,7 @@ ChatWidget::paintEvent(QPaintEvent *) void ChatWidget::load(const boost::property_tree::ptree &tree) { - // Load tab text + // load tab text try { this->setChannelName( QString::fromStdString(tree.get("channelName"))); diff --git a/widgets/chatwidget.h b/widgets/chatwidget.h index 9c568939a..0ae900400 100644 --- a/widgets/chatwidget.h +++ b/widgets/chatwidget.h @@ -27,51 +27,35 @@ public: ChatWidget(QWidget *parent = 0); ~ChatWidget(); - ChatWidgetView & - getView() - { - return view; - } - - std::shared_ptr - getChannel() const - { - return channel; - } - - const QString & - getChannelName() const - { - return channelName; - } - + SharedChannel getChannel() const; + const QString &getChannelName() const; void setChannelName(const QString &name); void showChangeChannelPopup(); - - messages::LimitedQueueSnapshot> - getMessagesSnapshot() - { - return messages.getSnapshot(); - } + messages::LimitedQueueSnapshot getMessagesSnapshot(); + void layoutMessages(); + void updateGifEmotes(); protected: void paintEvent(QPaintEvent *) override; private: - messages::LimitedQueue> messages; + void attachChannel(std::shared_ptr _channel); + void detachChannel(std::shared_ptr _channel); - std::shared_ptr channel; - QString channelName; + messages::LimitedQueue _messages; - QFont font; - QVBoxLayout vbox; - ChatWidgetHeader header; - ChatWidgetView view; - ChatWidgetInput input; + SharedChannel _channel; + QString _channelName; - boost::signals2::connection messageAppendedConnection; - boost::signals2::connection messageRemovedConnection; + QFont _font; + QVBoxLayout _vbox; + ChatWidgetHeader _header; + ChatWidgetView _view; + ChatWidgetInput _input; + + boost::signals2::connection _messageAppendedConnection; + boost::signals2::connection _messageRemovedConnection; public: void load(const boost::property_tree::ptree &tree); diff --git a/widgets/chatwidgetheader.cpp b/widgets/chatwidgetheader.cpp index 9e2bab37f..213ee1e4a 100644 --- a/widgets/chatwidgetheader.cpp +++ b/widgets/chatwidgetheader.cpp @@ -13,90 +13,81 @@ namespace widgets { ChatWidgetHeader::ChatWidgetHeader(ChatWidget *parent) : QWidget() - , chatWidget(parent) - , dragStart() - , dragging(false) - , leftLabel() - , middleLabel() - , rightLabel() - , leftMenu(this) - , rightMenu(this) + , _chatWidget(parent) + , _dragStart() + , _dragging(false) + , _leftLabel() + , _middleLabel() + , _rightLabel() + , _leftMenu(this) + , _rightMenu(this) { setFixedHeight(32); updateColors(); updateChannelText(); - setLayout(&this->hbox); - this->hbox.setMargin(0); - this->hbox.addWidget(&this->leftLabel); - this->hbox.addWidget(&this->middleLabel, 1); - this->hbox.addWidget(&this->rightLabel); + setLayout(&_hbox); + _hbox.setMargin(0); + _hbox.addWidget(&_leftLabel); + _hbox.addWidget(&_middleLabel, 1); + _hbox.addWidget(&_rightLabel); // left - this->leftLabel.getLabel().setTextFormat(Qt::RichText); - this->leftLabel.getLabel().setText( - ""); + _leftLabel.getLabel().setTextFormat(Qt::RichText); + _leftLabel.getLabel().setText(""); - QObject::connect(&this->leftLabel, &ChatWidgetHeaderButton::clicked, this, + QObject::connect(&_leftLabel, &ChatWidgetHeaderButton::clicked, this, &ChatWidgetHeader::leftButtonClicked); - this->leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), - QKeySequence(tr("Ctrl+T"))); - this->leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), - QKeySequence(tr("Ctrl+W"))); - this->leftMenu.addAction("Move split", this, SLOT(menuMoveSplit())); - this->leftMenu.addAction("Popup", this, SLOT(menuPopup())); - this->leftMenu.addSeparator(); - this->leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()), - QKeySequence(tr("Ctrl+R"))); - this->leftMenu.addAction("Clear chat", this, SLOT(menuClearChat())); - this->leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel())); - this->leftMenu.addAction("Open pop-out player", this, - SLOT(menuPopupPlayer())); - this->leftMenu.addSeparator(); - this->leftMenu.addAction("Reload channel emotes", this, - SLOT(menuReloadChannelEmotes())); - this->leftMenu.addAction("Manual reconnect", this, - SLOT(menuManualReconnect())); - this->leftMenu.addSeparator(); - this->leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog())); + _leftMenu.addAction("Add new split", this, SLOT(menuAddSplit()), QKeySequence(tr("Ctrl+T"))); + _leftMenu.addAction("Close split", this, SLOT(menuCloseSplit()), QKeySequence(tr("Ctrl+W"))); + _leftMenu.addAction("Move split", this, SLOT(menuMoveSplit())); + _leftMenu.addAction("Popup", this, SLOT(menuPopup())); + _leftMenu.addSeparator(); + _leftMenu.addAction("Change channel", this, SLOT(menuChangeChannel()), + QKeySequence(tr("Ctrl+R"))); + _leftMenu.addAction("Clear chat", this, SLOT(menuClearChat())); + _leftMenu.addAction("Open channel", this, SLOT(menuOpenChannel())); + _leftMenu.addAction("Open pop-out player", this, SLOT(menuPopupPlayer())); + _leftMenu.addSeparator(); + _leftMenu.addAction("Reload channel emotes", this, SLOT(menuReloadChannelEmotes())); + _leftMenu.addAction("Manual reconnect", this, SLOT(menuManualReconnect())); + _leftMenu.addSeparator(); + _leftMenu.addAction("Show changelog", this, SLOT(menuShowChangelog())); // middle - this->middleLabel.setAlignment(Qt::AlignCenter); + _middleLabel.setAlignment(Qt::AlignCenter); - connect(&this->middleLabel, &SignalLabel::mouseDoubleClick, this, + connect(&_middleLabel, &SignalLabel::mouseDoubleClick, this, &ChatWidgetHeader::mouseDoubleClickEvent); // connect(&this->middleLabel, &SignalLabel::mouseDown, this, // &ChatWidgetHeader::mouseDoubleClickEvent); // right - this->rightLabel.setMinimumWidth(height()); - this->rightLabel.getLabel().setTextFormat(Qt::RichText); - this->rightLabel.getLabel().setText("ayy"); + _rightLabel.setMinimumWidth(height()); + _rightLabel.getLabel().setTextFormat(Qt::RichText); + _rightLabel.getLabel().setText("ayy"); } -void -ChatWidgetHeader::updateColors() +void ChatWidgetHeader::updateColors() { QPalette palette; palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); - this->leftLabel.setPalette(palette); - this->middleLabel.setPalette(palette); - this->rightLabel.setPalette(palette); + _leftLabel.setPalette(palette); + _middleLabel.setPalette(palette); + _rightLabel.setPalette(palette); } -void -ChatWidgetHeader::updateChannelText() +void ChatWidgetHeader::updateChannelText() { - const QString &c = this->chatWidget->getChannelName(); + const QString &c = _chatWidget->getChannelName(); - this->middleLabel.setText(c.isEmpty() ? "" : c); + _middleLabel.setText(c.isEmpty() ? "" : c); } -void -ChatWidgetHeader::paintEvent(QPaintEvent *) +void ChatWidgetHeader::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -105,21 +96,19 @@ ChatWidgetHeader::paintEvent(QPaintEvent *) painter.drawRect(0, 0, width() - 1, height() - 1); } -void -ChatWidgetHeader::mousePressEvent(QMouseEvent *event) +void ChatWidgetHeader::mousePressEvent(QMouseEvent *event) { - this->dragging = true; + _dragging = true; - this->dragStart = event->pos(); + _dragStart = event->pos(); } -void -ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event) +void ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event) { - if (this->dragging) { - if (std::abs(this->dragStart.x() - event->pos().x()) > 12 || - std::abs(this->dragStart.y() - event->pos().y()) > 12) { - auto chatWidget = this->chatWidget; + if (_dragging) { + if (std::abs(_dragStart.x() - event->pos().x()) > 12 || + std::abs(_dragStart.y() - event->pos().y()) > 12) { + auto chatWidget = _chatWidget; auto page = static_cast(chatWidget->parentWidget()); if (page != NULL) { @@ -149,73 +138,58 @@ ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event) } } -void -ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event) +void ChatWidgetHeader::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { - this->chatWidget->showChangeChannelPopup(); + _chatWidget->showChangeChannelPopup(); } } -void -ChatWidgetHeader::leftButtonClicked() +void ChatWidgetHeader::leftButtonClicked() { - this->leftMenu.move( - this->leftLabel.mapToGlobal(QPoint(0, this->leftLabel.height()))); - this->leftMenu.show(); + _leftMenu.move(_leftLabel.mapToGlobal(QPoint(0, _leftLabel.height()))); + _leftMenu.show(); } -void -ChatWidgetHeader::rightButtonClicked() +void ChatWidgetHeader::rightButtonClicked() { } -void -ChatWidgetHeader::menuAddSplit() +void ChatWidgetHeader::menuAddSplit() { } -void -ChatWidgetHeader::menuCloseSplit() +void ChatWidgetHeader::menuCloseSplit() { } -void -ChatWidgetHeader::menuMoveSplit() +void ChatWidgetHeader::menuMoveSplit() { } -void -ChatWidgetHeader::menuPopup() +void ChatWidgetHeader::menuPopup() { auto widget = new ChatWidget(); - widget->setChannelName(this->chatWidget->getChannelName()); + widget->setChannelName(_chatWidget->getChannelName()); widget->show(); } -void -ChatWidgetHeader::menuChangeChannel() +void ChatWidgetHeader::menuChangeChannel() { - this->chatWidget->showChangeChannelPopup(); + _chatWidget->showChangeChannelPopup(); } -void -ChatWidgetHeader::menuClearChat() +void ChatWidgetHeader::menuClearChat() { } -void -ChatWidgetHeader::menuOpenChannel() +void ChatWidgetHeader::menuOpenChannel() { } -void -ChatWidgetHeader::menuPopupPlayer() +void ChatWidgetHeader::menuPopupPlayer() { } -void -ChatWidgetHeader::menuReloadChannelEmotes() +void ChatWidgetHeader::menuReloadChannelEmotes() { } -void -ChatWidgetHeader::menuManualReconnect() +void ChatWidgetHeader::menuManualReconnect() { } -void -ChatWidgetHeader::menuShowChangelog() +void ChatWidgetHeader::menuShowChangelog() { } } diff --git a/widgets/chatwidgetheader.h b/widgets/chatwidgetheader.h index b6937d1ea..eca9cbaca 100644 --- a/widgets/chatwidgetheader.h +++ b/widgets/chatwidgetheader.h @@ -24,10 +24,9 @@ class ChatWidgetHeader : public QWidget public: explicit ChatWidgetHeader(ChatWidget *parent); - ChatWidget * - getChatWidget() + ChatWidget *getChatWidget() { - return chatWidget; + return _chatWidget; } void updateColors(); @@ -40,19 +39,19 @@ protected: void mouseDoubleClickEvent(QMouseEvent *event); private: - ChatWidget *chatWidget; + ChatWidget *_chatWidget; - QPoint dragStart; - bool dragging; + QPoint _dragStart; + bool _dragging; - QHBoxLayout hbox; + QHBoxLayout _hbox; - ChatWidgetHeaderButton leftLabel; - SignalLabel middleLabel; - ChatWidgetHeaderButton rightLabel; + ChatWidgetHeaderButton _leftLabel; + SignalLabel _middleLabel; + ChatWidgetHeaderButton _rightLabel; - QMenu leftMenu; - QMenu rightMenu; + QMenu _leftMenu; + QMenu _rightMenu; void leftButtonClicked(); void rightButtonClicked(); diff --git a/widgets/chatwidgetheaderbutton.cpp b/widgets/chatwidgetheaderbutton.cpp index fb62ad5ae..c48017fad 100644 --- a/widgets/chatwidgetheaderbutton.cpp +++ b/widgets/chatwidgetheaderbutton.cpp @@ -9,59 +9,55 @@ namespace widgets { ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing) : QWidget() - , hbox() - , label() - , mouseOver(false) - , mouseDown(false) + , _hbox() + , _label() + , _mouseOver(false) + , _mouseDown(false) { - setLayout(&hbox); + setLayout(&_hbox); - label.setAlignment(Qt::AlignCenter); + _label.setAlignment(Qt::AlignCenter); - hbox.setMargin(0); - hbox.addSpacing(spacing); - hbox.addWidget(&this->label); - hbox.addSpacing(spacing); + _hbox.setMargin(0); + _hbox.addSpacing(spacing); + _hbox.addWidget(&_label); + _hbox.addSpacing(spacing); - QObject::connect(&this->label, &SignalLabel::mouseUp, this, + QObject::connect(&_label, &SignalLabel::mouseUp, this, &ChatWidgetHeaderButton::labelMouseUp); - QObject::connect(&this->label, &SignalLabel::mouseDown, this, + QObject::connect(&_label, &SignalLabel::mouseDown, this, &ChatWidgetHeaderButton::labelMouseDown); } -void -ChatWidgetHeaderButton::paintEvent(QPaintEvent *) +void ChatWidgetHeaderButton::paintEvent(QPaintEvent *) { QPainter painter(this); - QBrush brush(ColorScheme::getInstance().IsLightTheme - ? QColor(0, 0, 0, 32) - : QColor(255, 255, 255, 32)); + QBrush brush(ColorScheme::getInstance().IsLightTheme ? QColor(0, 0, 0, 32) + : QColor(255, 255, 255, 32)); - if (this->mouseDown) { + if (_mouseDown) { painter.fillRect(rect(), brush); } - if (this->mouseOver) { + if (_mouseOver) { painter.fillRect(rect(), brush); } } -void -ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event) +void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { - this->mouseDown = true; + _mouseDown = true; update(); } } -void -ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event) +void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { - this->mouseDown = false; + _mouseDown = false; update(); @@ -69,36 +65,32 @@ ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event) } } -void -ChatWidgetHeaderButton::enterEvent(QEvent *) +void ChatWidgetHeaderButton::enterEvent(QEvent *) { - this->mouseOver = true; + _mouseOver = true; update(); } -void -ChatWidgetHeaderButton::leaveEvent(QEvent *) +void ChatWidgetHeaderButton::leaveEvent(QEvent *) { - this->mouseOver = false; + _mouseOver = false; update(); } -void -ChatWidgetHeaderButton::labelMouseUp() +void ChatWidgetHeaderButton::labelMouseUp() { - this->mouseDown = false; + _mouseDown = false; update(); emit clicked(); } -void -ChatWidgetHeaderButton::labelMouseDown() +void ChatWidgetHeaderButton::labelMouseDown() { - this->mouseDown = true; + _mouseDown = true; update(); } diff --git a/widgets/chatwidgetheaderbutton.h b/widgets/chatwidgetheaderbutton.h index 0b422e9e9..7f0838c63 100644 --- a/widgets/chatwidgetheaderbutton.h +++ b/widgets/chatwidgetheaderbutton.h @@ -18,10 +18,9 @@ class ChatWidgetHeaderButton : public QWidget public: explicit ChatWidgetHeaderButton(int spacing = 6); - SignalLabel & - getLabel() + SignalLabel &getLabel() { - return label; + return _label; } signals: @@ -37,11 +36,11 @@ protected: void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; private: - QHBoxLayout hbox; - SignalLabel label; + QHBoxLayout _hbox; + SignalLabel _label; - bool mouseOver; - bool mouseDown; + bool _mouseOver; + bool _mouseDown; void labelMouseUp(); void labelMouseDown(); diff --git a/widgets/chatwidgetinput.cpp b/widgets/chatwidgetinput.cpp index f903b109b..a66ad3e19 100644 --- a/widgets/chatwidgetinput.cpp +++ b/widgets/chatwidgetinput.cpp @@ -2,7 +2,7 @@ #include "chatwidget.h" #include "colorscheme.h" #include "ircmanager.h" -#include "settings.h" +#include "settingsmanager.h" #include #include @@ -12,44 +12,43 @@ namespace chatterino { namespace widgets { ChatWidgetInput::ChatWidgetInput(ChatWidget *widget) - : chatWidget(widget) - , hbox() - , vbox() - , editContainer() - , edit() - , textLengthLabel() - , emotesLabel(0) + : _chatWidget(widget) + , _hbox() + , _vbox() + , _editContainer() + , _edit() + , _textLengthLabel() + , _emotesLabel(0) { - this->setLayout(&this->hbox); - this->setMaximumHeight(150); - this->hbox.setMargin(4); + setLayout(&_hbox); + setMaximumHeight(150); + _hbox.setMargin(4); - this->hbox.addLayout(&this->editContainer); - this->hbox.addLayout(&this->vbox); + _hbox.addLayout(&_editContainer); + _hbox.addLayout(&_vbox); - this->editContainer.addWidget(&this->edit); - this->editContainer.setMargin(4); + _editContainer.addWidget(&_edit); + _editContainer.setMargin(4); - this->vbox.addWidget(&this->textLengthLabel); - this->vbox.addStretch(1); - this->vbox.addWidget(&this->emotesLabel); + _vbox.addWidget(&_textLengthLabel); + _vbox.addStretch(1); + _vbox.addWidget(&_emotesLabel); - this->textLengthLabel.setText("100"); - this->textLengthLabel.setAlignment(Qt::AlignRight); - this->emotesLabel.getLabel().setTextFormat(Qt::RichText); - this->emotesLabel.getLabel().setText( + _textLengthLabel.setText("100"); + _textLengthLabel.setAlignment(Qt::AlignRight); + _emotesLabel.getLabel().setTextFormat(Qt::RichText); + _emotesLabel.getLabel().setText( ""); - QObject::connect(&edit, &ResizingTextEdit::textChanged, this, + QObject::connect(&_edit, &ResizingTextEdit::textChanged, this, &ChatWidgetInput::editTextChanged); // QObject::connect(&edit, &ResizingTextEdit::keyPressEvent, this, // &ChatWidgetInput::editKeyPressed); - this->refreshTheme(); - this->setMessageLengthVisisble( - Settings::getInstance().showMessageLength.get()); + refreshTheme(); + setMessageLengthVisisble(SettingsManager::getInstance().showMessageLength.get()); QStringList list; list.append("asd"); @@ -57,20 +56,20 @@ ChatWidgetInput::ChatWidgetInput(ChatWidget *widget) list.append("asdg"); list.append("asdh"); - QCompleter *completer = new QCompleter(list, &edit); + QCompleter *completer = new QCompleter(list, &_edit); - completer->setWidget(&edit); + completer->setWidget(&_edit); - this->edit.keyPressed.connect([this, completer](QKeyEvent *event) { + _edit.keyPressed.connect([this, completer](QKeyEvent *event) { if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { - auto ptr = this->chatWidget->getChannel(); + auto ptr = _chatWidget->getChannel(); Channel *c = ptr.get(); if (c != nullptr) { - IrcManager::send("PRIVMSG #" + c->getName() + ": " + - this->edit.toPlainText()); + IrcManager::getInstance().send("PRIVMSG #" + c->getName() + ": " + + _edit.toPlainText()); event->accept(); - this->edit.setText(QString()); + _edit.setText(QString()); } } // else { @@ -97,20 +96,18 @@ ChatWidgetInput::~ChatWidgetInput() */ } -void -ChatWidgetInput::refreshTheme() +void ChatWidgetInput::refreshTheme() { QPalette palette; palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); - this->textLengthLabel.setPalette(palette); + _textLengthLabel.setPalette(palette); - edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet); + _edit.setStyleSheet(ColorScheme::getInstance().InputStyleSheet); } -void -ChatWidgetInput::editTextChanged() +void ChatWidgetInput::editTextChanged() { } @@ -124,8 +121,7 @@ ChatWidgetInput::editTextChanged() // } //} -void -ChatWidgetInput::paintEvent(QPaintEvent *) +void ChatWidgetInput::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -134,13 +130,12 @@ ChatWidgetInput::paintEvent(QPaintEvent *) painter.drawRect(0, 0, width() - 1, height() - 1); } -void -ChatWidgetInput::resizeEvent(QResizeEvent *) +void ChatWidgetInput::resizeEvent(QResizeEvent *) { - if (height() == this->maximumHeight()) { - edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + if (height() == maximumHeight()) { + _edit.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } else { - edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + _edit.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } } } diff --git a/widgets/chatwidgetinput.h b/widgets/chatwidgetinput.h index 9c6512536..80203d8a0 100644 --- a/widgets/chatwidgetinput.h +++ b/widgets/chatwidgetinput.h @@ -31,21 +31,20 @@ protected: void resizeEvent(QResizeEvent *); private: - ChatWidget *chatWidget; + ChatWidget *_chatWidget; - QHBoxLayout hbox; - QVBoxLayout vbox; - QHBoxLayout editContainer; - ResizingTextEdit edit; - QLabel textLengthLabel; - ChatWidgetHeaderButton emotesLabel; + QHBoxLayout _hbox; + QVBoxLayout _vbox; + QHBoxLayout _editContainer; + ResizingTextEdit _edit; + QLabel _textLengthLabel; + ChatWidgetHeaderButton _emotesLabel; private slots: void refreshTheme(); - void - setMessageLengthVisisble(bool value) + void setMessageLengthVisisble(bool value) { - this->textLengthLabel.setHidden(!value); + _textLengthLabel.setHidden(!value); } void editTextChanged(); // void editKeyPressed(QKeyEvent *event); diff --git a/widgets/chatwidgetview.cpp b/widgets/chatwidgetview.cpp index 8fd4fbefb..4c27d46a0 100644 --- a/widgets/chatwidgetview.cpp +++ b/widgets/chatwidgetview.cpp @@ -1,9 +1,10 @@ #include "widgets/chatwidgetview.h" -#include "channels.h" +#include "channelmanager.h" #include "colorscheme.h" #include "messages/message.h" #include "messages/wordpart.h" -#include "settings.h" +#include "settingsmanager.h" +#include "ui_userpopup.h" #include "widgets/chatwidget.h" #include @@ -18,46 +19,46 @@ namespace widgets { ChatWidgetView::ChatWidgetView(ChatWidget *parent) : QWidget() - , chatWidget(parent) - , scrollbar(this) - , onlyUpdateEmotes(false) + , _chatWidget(parent) + , _scrollbar(this) + , _userPopupWidget(_chatWidget->getChannel()) + , _onlyUpdateEmotes(false) + , _mouseDown(false) + , _lastPressPosition() { this->setAttribute(Qt::WA_OpaquePaintEvent); - this->scrollbar.setSmallChange(5); + _scrollbar.setSmallChange(5); this->setMouseTracking(true); - QObject::connect(&Settings::getInstance(), &Settings::wordTypeMaskChanged, - this, &ChatWidgetView::wordTypeMaskChanged); + QObject::connect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged, this, + &ChatWidgetView::wordTypeMaskChanged); - this->scrollbar.getCurrentValueChanged().connect([this] { update(); }); + _scrollbar.getCurrentValueChanged().connect([this] { update(); }); } ChatWidgetView::~ChatWidgetView() { - QObject::disconnect(&Settings::getInstance(), - &Settings::wordTypeMaskChanged, this, - &ChatWidgetView::wordTypeMaskChanged); + QObject::disconnect(&SettingsManager::getInstance(), &SettingsManager::wordTypeMaskChanged, + this, &ChatWidgetView::wordTypeMaskChanged); } -bool -ChatWidgetView::layoutMessages() +bool ChatWidgetView::layoutMessages() { - auto messages = chatWidget->getMessagesSnapshot(); + auto messages = _chatWidget->getMessagesSnapshot(); if (messages.getLength() == 0) { - this->scrollbar.setVisible(false); + _scrollbar.setVisible(false); return false; } bool showScrollbar = false, redraw = false; - int start = this->scrollbar.getCurrentValue(); + int start = _scrollbar.getCurrentValue(); // layout the visible messages in the view if (messages.getLength() > start) { - int y = -(messages[start].get()->getHeight() * - (fmod(this->scrollbar.getCurrentValue(), 1))); + int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))); for (int i = start; i < messages.getLength(); ++i) { auto messagePtr = messages[i]; @@ -84,39 +85,47 @@ ChatWidgetView::layoutMessages() h -= message->getHeight(); if (h < 0) { - this->scrollbar.setLargeChange((messages.getLength() - i) + - (qreal)h / message->getHeight()); - this->scrollbar.setDesiredValue(this->scrollbar.getDesiredValue()); + _scrollbar.setLargeChange((messages.getLength() - i) + (qreal)h / message->getHeight()); + _scrollbar.setDesiredValue(_scrollbar.getDesiredValue()); showScrollbar = true; break; } } - this->scrollbar.setVisible(showScrollbar); + _scrollbar.setVisible(showScrollbar); if (!showScrollbar) { - this->scrollbar.setDesiredValue(0); + _scrollbar.setDesiredValue(0); } - this->scrollbar.setMaximum(messages.getLength()); + _scrollbar.setMaximum(messages.getLength()); return redraw; } -void -ChatWidgetView::resizeEvent(QResizeEvent *) +void ChatWidgetView::updateGifEmotes() { - this->scrollbar.resize(this->scrollbar.width(), height()); - this->scrollbar.move(width() - this->scrollbar.width(), 0); + _onlyUpdateEmotes = true; + this->update(); +} + +ScrollBar *ChatWidgetView::getScrollbar() +{ + return &_scrollbar; +} + +void ChatWidgetView::resizeEvent(QResizeEvent *) +{ + _scrollbar.resize(_scrollbar.width(), height()); + _scrollbar.move(width() - _scrollbar.width(), 0); layoutMessages(); update(); } -void -ChatWidgetView::paintEvent(QPaintEvent *event) +void ChatWidgetView::paintEvent(QPaintEvent *event) { QPainter _painter(this); @@ -125,10 +134,10 @@ ChatWidgetView::paintEvent(QPaintEvent *event) ColorScheme &scheme = ColorScheme::getInstance(); // only update gif emotes - if (onlyUpdateEmotes) { - onlyUpdateEmotes = false; + if (_onlyUpdateEmotes) { + _onlyUpdateEmotes = false; - for (GifEmoteData &item : this->gifEmotes) { + for (GifEmoteData &item : _gifEmotes) { _painter.fillRect(item.rect, scheme.ChatBackground); _painter.drawPixmap(item.rect, *item.image->getPixmap()); @@ -138,7 +147,7 @@ ChatWidgetView::paintEvent(QPaintEvent *event) } // update all messages - gifEmotes.clear(); + _gifEmotes.clear(); _painter.fillRect(rect(), scheme.ChatBackground); @@ -175,16 +184,15 @@ ChatWidgetView::paintEvent(QPaintEvent *event) painter.fillRect(QRect(0, 9, 500, 2), QColor(0, 0, 0));*/ - auto messages = chatWidget->getMessagesSnapshot(); + auto messages = _chatWidget->getMessagesSnapshot(); - int start = this->scrollbar.getCurrentValue(); + int start = _scrollbar.getCurrentValue(); if (start >= messages.getLength()) { return; } - int y = -(messages[start].get()->getHeight() * - (fmod(this->scrollbar.getCurrentValue(), 1))); + int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))); for (int i = start; i < messages.getLength(); ++i) { messages::MessageRef *messageRef = messages[i].get(); @@ -205,20 +213,17 @@ ChatWidgetView::paintEvent(QPaintEvent *event) QPainter painter(buffer); painter.fillRect(buffer->rect(), scheme.ChatBackground); - for (messages::WordPart const &wordPart : - messageRef->getWordParts()) { + for (messages::WordPart const &wordPart : messageRef->getWordParts()) { // image if (wordPart.getWord().isImage()) { - messages::LazyLoadedImage &lli = - wordPart.getWord().getImage(); + messages::LazyLoadedImage &lli = wordPart.getWord().getImage(); const QPixmap *image = lli.getPixmap(); if (image != NULL) { - painter.drawPixmap( - QRect(wordPart.getX(), wordPart.getY(), - wordPart.getWidth(), wordPart.getHeight()), - *image); + painter.drawPixmap(QRect(wordPart.getX(), wordPart.getY(), + wordPart.getWidth(), wordPart.getHeight()), + *image); } } // text @@ -230,10 +235,8 @@ ChatWidgetView::paintEvent(QPaintEvent *event) painter.setPen(color); painter.setFont(wordPart.getWord().getFont()); - painter.drawText( - QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000), - wordPart.getText(), - QTextOption(Qt::AlignLeft | Qt::AlignTop)); + painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000), + wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop)); } } @@ -248,12 +251,12 @@ ChatWidgetView::paintEvent(QPaintEvent *event) if (lli.getAnimated()) { GifEmoteData data; data.image = &lli; - QRect rect(wordPart.getX(), wordPart.getY() + y, - wordPart.getWidth(), wordPart.getHeight()); + QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(), + wordPart.getHeight()); data.rect = rect; - gifEmotes.push_back(data); + _gifEmotes.push_back(data); } } } @@ -269,27 +272,24 @@ ChatWidgetView::paintEvent(QPaintEvent *event) } } - for (GifEmoteData &item : this->gifEmotes) { + for (GifEmoteData &item : _gifEmotes) { _painter.fillRect(item.rect, scheme.ChatBackground); _painter.drawPixmap(item.rect, *item.image->getPixmap()); } } -void -ChatWidgetView::wheelEvent(QWheelEvent *event) +void ChatWidgetView::wheelEvent(QWheelEvent *event) { - if (this->scrollbar.isVisible()) { - this->scrollbar.setDesiredValue( - this->scrollbar.getDesiredValue() - - event->delta() / 10.0 * - Settings::getInstance().mouseScrollMultiplier.get(), + if (_scrollbar.isVisible()) { + _scrollbar.setDesiredValue( + _scrollbar.getDesiredValue() - + event->delta() / 10.0 * SettingsManager::getInstance().mouseScrollMultiplier.get(), true); } } -void -ChatWidgetView::mouseMoveEvent(QMouseEvent *event) +void ChatWidgetView::mouseMoveEvent(QMouseEvent *event) { std::shared_ptr message; QPoint relativePos; @@ -311,32 +311,86 @@ ChatWidgetView::mouseMoveEvent(QMouseEvent *event) int index = message->getSelectionIndex(relativePos); - qDebug() << index; - if (hoverWord.getLink().getIsValid()) { this->setCursor(Qt::PointingHandCursor); - qDebug() << hoverWord.getLink().getValue(); } else { this->setCursor(Qt::ArrowCursor); } } -bool -ChatWidgetView::tryGetMessageAt(QPoint p, - std::shared_ptr &_message, - QPoint &relativePos) +void ChatWidgetView::mousePressEvent(QMouseEvent *event) { - auto messages = chatWidget->getMessagesSnapshot(); + _mouseDown = true; + _lastPressPosition = event->screenPos(); +} - int start = this->scrollbar.getCurrentValue(); +static float distanceBetweenPoints(const QPointF &p1, const QPointF &p2) +{ + QPointF tmp = p1 - p2; + + float distance = 0.f; + distance += tmp.x() * tmp.x(); + distance += tmp.y() * tmp.y(); + + return std::sqrt(distance); +} + +void ChatWidgetView::mouseReleaseEvent(QMouseEvent *event) +{ + if (!_mouseDown) { + // We didn't grab the mouse press, so we shouldn't be handling the mouse + // release + return; + } + + _mouseDown = false; + + float distance = distanceBetweenPoints(_lastPressPosition, event->screenPos()); + + qDebug() << "Distance: " << distance; + + if (fabsf(distance) > 15.f) { + // It wasn't a proper click, so we don't care about that here + return; + } + + // If you clicked and released less than X pixels away, it counts + // as a click! + + // show user thing pajaW + + std::shared_ptr 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 &_message, + QPoint &relativePos) +{ + auto messages = _chatWidget->getMessagesSnapshot(); + + int start = _scrollbar.getCurrentValue(); if (start >= messages.getLength()) { return false; } - int y = -(messages[start].get()->getHeight() * - (fmod(this->scrollbar.getCurrentValue(), 1))) + - 12; + int y = -(messages[start].get()->getHeight() * (fmod(_scrollbar.getCurrentValue(), 1))) + 12; for (int i = start; i < messages.getLength(); ++i) { auto message = messages[i]; @@ -352,5 +406,5 @@ ChatWidgetView::tryGetMessageAt(QPoint p, return false; } -} -} +} // namespace widgets +} // namespace chatterino diff --git a/widgets/chatwidgetview.h b/widgets/chatwidgetview.h index fdbf834b8..9d67d5ab9 100644 --- a/widgets/chatwidgetview.h +++ b/widgets/chatwidgetview.h @@ -6,6 +6,7 @@ #include "messages/messageref.h" #include "messages/word.h" #include "widgets/scrollbar.h" +#include "widgets/userpopupwidget.h" #include #include @@ -18,20 +19,14 @@ class ChatWidget; class ChatWidgetView : public QWidget { - Q_OBJECT - public: explicit ChatWidgetView(ChatWidget *parent); ~ChatWidgetView(); bool layoutMessages(); - void - updateGifEmotes() - { - this->onlyUpdateEmotes = true; - this->update(); - } + void updateGifEmotes(); + ScrollBar *getScrollbar(); protected: void resizeEvent(QResizeEvent *); @@ -40,9 +35,10 @@ protected: void wheelEvent(QWheelEvent *event); void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); - bool tryGetMessageAt(QPoint p, - std::shared_ptr &message, + bool tryGetMessageAt(QPoint p, std::shared_ptr &message, QPoint &relativePos); private: @@ -51,22 +47,27 @@ private: QRect rect; }; - std::vector gifEmotes; + std::vector _gifEmotes; - ChatWidget *chatWidget; + ChatWidget *_chatWidget; - ScrollBar scrollbar; - bool onlyUpdateEmotes; + ScrollBar _scrollbar; + + UserPopupWidget _userPopupWidget; + bool _onlyUpdateEmotes; + + // Mouse event variables + bool _mouseDown; + QPointF _lastPressPosition; private slots: - void - wordTypeMaskChanged() + void wordTypeMaskChanged() { layoutMessages(); update(); } }; -} -} +} // namespace widgets +} // namespace chatterino #endif // CHATVIEW_H diff --git a/widgets/fancybutton.cpp b/widgets/fancybutton.cpp new file mode 100644 index 000000000..dc70feae0 --- /dev/null +++ b/widgets/fancybutton.cpp @@ -0,0 +1,141 @@ +#include "fancybutton.h" + +#include +#include + +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(); + } +} +} +} diff --git a/widgets/fancybutton.h b/widgets/fancybutton.h new file mode 100644 index 000000000..736e784d4 --- /dev/null +++ b/widgets/fancybutton.h @@ -0,0 +1,55 @@ +#ifndef FANCYBUTTON_H +#define FANCYBUTTON_H + +#include +#include +#include +#include +#include + +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 _clickEffects; + QColor _mouseEffectColor; + + void onMouseEffectTimeout(); +}; +} +} + +#endif // FANCYBUTTON_H diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index f684ffa1e..1a5061162 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -1,24 +1,46 @@ #include "widgets/mainwindow.h" #include "colorscheme.h" +#include "settingsmanager.h" #include "widgets/chatwidget.h" #include "widgets/notebook.h" #include #include +#include #include +#ifdef USEWINSDK +#include "Windows.h" +#endif + namespace chatterino { namespace widgets { MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , notebook(this) + : QWidget(parent) + , _notebook(this) + , _loaded(false) + , _titleBar() { - setCentralWidget(&this->notebook); + QVBoxLayout *layout = new QVBoxLayout(this); + + // add titlebar + // if (SettingsManager::getInstance().useCustomWindowFrame.get()) { + // layout->addWidget(&_titleBar); + // } + + layout->addWidget(&_notebook); + setLayout(layout); + + // set margin + // if (SettingsManager::getInstance().useCustomWindowFrame.get()) { + // layout->setMargin(1); + // } else { + layout->setMargin(0); + // } QPalette palette; - palette.setColor(QPalette::Background, - ColorScheme::getInstance().TabPanelBackground); + palette.setColor(QPalette::Background, ColorScheme::getInstance().TabPanelBackground); setPalette(palette); resize(1280, 800); @@ -28,10 +50,9 @@ MainWindow::~MainWindow() { } -void -MainWindow::layoutVisibleChatWidgets(Channel *channel) +void MainWindow::layoutVisibleChatWidgets(Channel *channel) { - auto *page = notebook.getSelectedPage(); + auto *page = _notebook.getSelectedPage(); if (page == NULL) { return; @@ -43,17 +64,14 @@ MainWindow::layoutVisibleChatWidgets(Channel *channel) ChatWidget *widget = *it; if (channel == NULL || channel == widget->getChannel().get()) { - if (widget->getView().layoutMessages()) { - widget->getView().update(); - } + widget->layoutMessages(); } } } -void -MainWindow::repaintVisibleChatWidgets(Channel *channel) +void MainWindow::repaintVisibleChatWidgets(Channel *channel) { - auto *page = notebook.getSelectedPage(); + auto *page = _notebook.getSelectedPage(); if (page == NULL) { return; @@ -65,16 +83,14 @@ MainWindow::repaintVisibleChatWidgets(Channel *channel) ChatWidget *widget = *it; if (channel == NULL || channel == widget->getChannel().get()) { - widget->getView().layoutMessages(); - widget->getView().update(); + widget->layoutMessages(); } } } -void -MainWindow::repaintGifEmotes() +void MainWindow::repaintGifEmotes() { - auto *page = notebook.getSelectedPage(); + auto *page = _notebook.getSelectedPage(); if (page == NULL) { return; @@ -85,37 +101,43 @@ MainWindow::repaintGifEmotes() for (auto it = widgets.begin(); it != widgets.end(); ++it) { ChatWidget *widget = *it; - widget->getView().updateGifEmotes(); + widget->updateGifEmotes(); } } -void -MainWindow::load(const boost::property_tree::ptree &tree) +void MainWindow::load(const boost::property_tree::ptree &tree) { - this->notebook.load(tree); + this->_notebook.load(tree); - this->loaded = true; + _loaded = true; } -boost::property_tree::ptree -MainWindow::save() +boost::property_tree::ptree MainWindow::save() { boost::property_tree::ptree child; child.put("type", "main"); - this->notebook.save(child); + _notebook.save(child); return child; } -void -MainWindow::loadDefaults() +void MainWindow::loadDefaults() { - this->notebook.loadDefaults(); + _notebook.loadDefaults(); - this->loaded = true; + _loaded = true; } +bool MainWindow::isLoaded() const +{ + return _loaded; +} + +Notebook &MainWindow::getNotebook() +{ + return _notebook; +} } // namespace widgets } // namespace chatterino diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index e77e587df..61d00c548 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -2,38 +2,39 @@ #define MAINWINDOW_H #include "widgets/notebook.h" +#include "widgets/titlebar.h" +#include #include #include namespace chatterino { namespace widgets { -class MainWindow : public QMainWindow +class MainWindow : public QWidget { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); - Notebook notebook; - void layoutVisibleChatWidgets(Channel *channel = NULL); - void repaintVisibleChatWidgets(Channel *channel = NULL); + void layoutVisibleChatWidgets(Channel *channel = nullptr); + void repaintVisibleChatWidgets(Channel *channel = nullptr); void repaintGifEmotes(); void load(const boost::property_tree::ptree &tree); boost::property_tree::ptree save(); void loadDefaults(); - bool - isLoaded() const - { - return this->loaded; - } + bool isLoaded() const; + + Notebook &getNotebook(); private: - bool loaded = false; + Notebook _notebook; + bool _loaded; + TitleBar _titleBar; }; } // namespace widgets diff --git a/widgets/notebook.cpp b/widgets/notebook.cpp index 5c110db88..ceb348c46 100644 --- a/widgets/notebook.cpp +++ b/widgets/notebook.cpp @@ -19,106 +19,99 @@ namespace widgets { Notebook::Notebook(QWidget *parent) : QWidget(parent) - , addButton(this) - , settingsButton(this) - , userButton(this) - , selectedPage(nullptr) + , _addButton(this) + , _settingsButton(this) + , _userButton(this) + , _selectedPage(nullptr) { - connect(&this->settingsButton, SIGNAL(clicked()), this, - SLOT(settingsButtonClicked())); - connect(&this->userButton, SIGNAL(clicked()), this, - SLOT(usersButtonClicked())); - connect(&this->addButton, SIGNAL(clicked()), this, - SLOT(addPageButtonClicked())); + connect(&_settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked())); + connect(&_userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked())); + connect(&_addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked())); - this->settingsButton.resize(24, 24); - this->settingsButton.icon = NotebookButton::IconSettings; + _settingsButton.resize(24, 24); + _settingsButton.icon = NotebookButton::IconSettings; - this->userButton.resize(24, 24); - this->userButton.move(24, 0); - this->userButton.icon = NotebookButton::IconUser; + _userButton.resize(24, 24); + _userButton.move(24, 0); + _userButton.icon = NotebookButton::IconUser; - this->addButton.resize(24, 24); + _addButton.resize(24, 24); - Settings::getInstance().hidePreferencesButton.valueChanged.connect( - [this](const bool &) { this->performLayout(); }); - Settings::getInstance().hideUserButton.valueChanged.connect( - [this](const bool &) { this->performLayout(); }); + SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect( + [this](const bool &) { performLayout(); }); + SettingsManager::getInstance().hideUserButton.valueChanged.connect( + [this](const bool &) { performLayout(); }); } -NotebookPage * -Notebook::addPage(bool select) +NotebookPage *Notebook::addPage(bool select) { auto tab = new NotebookTab(this); auto page = new NotebookPage(this, tab); tab->show(); - if (select || this->pages.count() == 0) { + if (select || _pages.count() == 0) { this->select(page); } - this->pages.append(page); + _pages.append(page); - this->performLayout(); + performLayout(); return page; } -void -Notebook::removePage(NotebookPage *page) +void Notebook::removePage(NotebookPage *page) { - int index = this->pages.indexOf(page); + int index = _pages.indexOf(page); - if (pages.size() == 1) { - this->select(NULL); - } else if (index == pages.count() - 1) { - this->select(pages[index - 1]); + if (_pages.size() == 1) { + select(NULL); + } else if (index == _pages.count() - 1) { + select(_pages[index - 1]); } else { - this->select(pages[index + 1]); + select(_pages[index + 1]); } - delete page->tab; + delete page->getTab(); delete page; - this->pages.removeOne(page); + _pages.removeOne(page); - if (this->pages.size() == 0) { + if (_pages.size() == 0) { addPage(); } performLayout(); } -void -Notebook::select(NotebookPage *page) +void Notebook::select(NotebookPage *page) { - if (page == this->selectedPage) + if (page == _selectedPage) return; if (page != nullptr) { page->setHidden(false); - page->tab->setSelected(true); - page->tab->raise(); + page->getTab()->setSelected(true); + page->getTab()->raise(); } - if (this->selectedPage != nullptr) { - this->selectedPage->setHidden(true); - this->selectedPage->tab->setSelected(false); + if (_selectedPage != nullptr) { + _selectedPage->setHidden(true); + _selectedPage->getTab()->setSelected(false); } - this->selectedPage = page; + _selectedPage = page; performLayout(); } -NotebookPage * -Notebook::tabAt(QPoint point, int &index) +NotebookPage *Notebook::tabAt(QPoint point, int &index) { int i = 0; - for (auto *page : pages) { - if (page->tab->getDesiredRect().contains(point)) { + for (auto *page : _pages) { + if (page->getTab()->getDesiredRect().contains(point)) { index = i; return page; } @@ -130,97 +123,87 @@ Notebook::tabAt(QPoint point, int &index) return nullptr; } -void -Notebook::rearrangePage(NotebookPage *page, int index) +void Notebook::rearrangePage(NotebookPage *page, int index) { - pages.move(pages.indexOf(page), index); + _pages.move(_pages.indexOf(page), index); performLayout(); } -void -Notebook::performLayout(bool animated) +void Notebook::performLayout(bool animated) { int x = 0, y = 0; - if (Settings::getInstance().hidePreferencesButton.get()) { - settingsButton.hide(); + if (SettingsManager::getInstance().hidePreferencesButton.get()) { + _settingsButton.hide(); } else { - settingsButton.show(); + _settingsButton.show(); x += 24; } - if (Settings::getInstance().hideUserButton.get()) { - userButton.hide(); + if (SettingsManager::getInstance().hideUserButton.get()) { + _userButton.hide(); } else { - userButton.move(x, 0); - userButton.show(); + _userButton.move(x, 0); + _userButton.show(); x += 24; } int tabHeight = 16; bool first = true; - for (auto &i : this->pages) { - tabHeight = i->tab->height(); + for (auto &i : _pages) { + tabHeight = i->getTab()->height(); - if (!first && - (i == this->pages.last() ? tabHeight : 0) + x + i->tab->width() > - width()) { - y += i->tab->height(); - i->tab->moveAnimated(QPoint(0, y), animated); - x = i->tab->width(); + if (!first && (i == _pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) { + y += i->getTab()->height(); + i->getTab()->moveAnimated(QPoint(0, y), animated); + x = i->getTab()->width(); } else { - i->tab->moveAnimated(QPoint(x, y), animated); - x += i->tab->width(); + i->getTab()->moveAnimated(QPoint(x, y), animated); + x += i->getTab()->width(); } first = false; } - this->addButton.move(x, y); + _addButton.move(x, y); - if (this->selectedPage != nullptr) { - this->selectedPage->move(0, y + tabHeight); - this->selectedPage->resize(width(), height() - y - tabHeight); + if (_selectedPage != nullptr) { + _selectedPage->move(0, y + tabHeight); + _selectedPage->resize(width(), height() - y - tabHeight); } } -void -Notebook::resizeEvent(QResizeEvent *) +void Notebook::resizeEvent(QResizeEvent *) { performLayout(false); } -void -Notebook::settingsButtonClicked() +void Notebook::settingsButtonClicked() { SettingsDialog *a = new SettingsDialog(); a->show(); } -void -Notebook::usersButtonClicked() +void Notebook::usersButtonClicked() { } -void -Notebook::addPageButtonClicked() +void Notebook::addPageButtonClicked() { addPage(true); } -void -Notebook::load(const boost::property_tree::ptree &tree) +void Notebook::load(const boost::property_tree::ptree &tree) { // Read a list of tabs try { - BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, - tree.get_child("tabs.")) { + BOOST_FOREACH (const boost::property_tree::ptree::value_type &v, tree.get_child("tabs.")) { bool select = v.second.get("selected", false); - auto page = this->addPage(select); - auto tab = page->tab; + auto page = addPage(select); + auto tab = page->getTab(); tab->load(v.second); page->load(v.second); } @@ -228,20 +211,19 @@ Notebook::load(const boost::property_tree::ptree &tree) // can't read tabs } - if (this->pages.size() == 0) { + if (_pages.size() == 0) { // No pages saved, show default stuff - this->loadDefaults(); + loadDefaults(); } } -void -Notebook::save(boost::property_tree::ptree &tree) +void Notebook::save(boost::property_tree::ptree &tree) { boost::property_tree::ptree tabs; // Iterate through all tabs and add them to our tabs property thing - for (const auto &page : this->pages) { - boost::property_tree::ptree pTab = page->tab->save(); + for (const auto &page : _pages) { + boost::property_tree::ptree pTab = page->getTab()->save(); boost::property_tree::ptree pChats = page->save(); @@ -255,10 +237,9 @@ Notebook::save(boost::property_tree::ptree &tree) tree.add_child("tabs", tabs); } -void -Notebook::loadDefaults() +void Notebook::loadDefaults() { - this->addPage(); + addPage(); } } // namespace widgets diff --git a/widgets/notebook.h b/widgets/notebook.h index d5ef07fbb..ae334cd07 100644 --- a/widgets/notebook.h +++ b/widgets/notebook.h @@ -26,10 +26,9 @@ public: void removePage(NotebookPage *page); void select(NotebookPage *page); - NotebookPage * - getSelectedPage() + NotebookPage *getSelectedPage() { - return selectedPage; + return _selectedPage; } void performLayout(bool animate = true); @@ -48,13 +47,13 @@ public slots: void addPageButtonClicked(); private: - QList pages; + QList _pages; - NotebookButton addButton; - NotebookButton settingsButton; - NotebookButton userButton; + NotebookButton _addButton; + NotebookButton _settingsButton; + NotebookButton _userButton; - NotebookPage *selectedPage; + NotebookPage *_selectedPage; public: void load(const boost::property_tree::ptree &tree); diff --git a/widgets/notebookbutton.cpp b/widgets/notebookbutton.cpp index c37fb7954..91b32b699 100644 --- a/widgets/notebookbutton.cpp +++ b/widgets/notebookbutton.cpp @@ -1,20 +1,22 @@ #include "widgets/notebookbutton.h" #include "colorscheme.h" +#include "widgets/fancybutton.h" #include #include #include +#include namespace chatterino { namespace widgets { NotebookButton::NotebookButton(QWidget *parent) - : QWidget(parent) + : FancyButton(parent) { + setMouseEffectColor(QColor(0, 0, 0)); } -void -NotebookButton::paintEvent(QPaintEvent *) +void NotebookButton::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -23,29 +25,30 @@ NotebookButton::paintEvent(QPaintEvent *) auto &colorScheme = ColorScheme::getInstance(); - if (mouseDown) { + if (_mouseDown) { background = colorScheme.TabSelectedBackground; foreground = colorScheme.TabSelectedText; - } else if (mouseOver) { + } else if (_mouseOver) { background = colorScheme.TabHoverBackground; foreground = colorScheme.TabSelectedBackground; } else { background = colorScheme.TabPanelBackground; - foreground = colorScheme.TabSelectedBackground; + // foreground = colorScheme.TabSelectedBackground; + foreground = QColor(230, 230, 230); } painter.setPen(Qt::NoPen); painter.fillRect(this->rect(), background); - float h = this->height(), w = this->width(); + float h = height(), w = width(); if (icon == IconPlus) { - painter.fillRect(QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, - w - ((h / 12) * 5), (h / 12) * 1), - foreground); - painter.fillRect(QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, - (h / 12) * 1, w - ((h / 12) * 5)), - foreground); + painter.fillRect( + QRectF((h / 12) * 2 + 1, (h / 12) * 5 + 1, w - ((h / 12) * 5), (h / 12) * 1), + foreground); + painter.fillRect( + QRectF((h / 12) * 5 + 1, (h / 12) * 2 + 1, (h / 12) * 1, w - ((h / 12) * 5)), + foreground); } else if (icon == IconUser) { painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::HighQualityAntialiasing); @@ -74,10 +77,8 @@ NotebookButton::paintEvent(QPaintEvent *) path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0)); for (int i = 0; i < 8; i++) { - path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), - (360 / 32.0)); - path.arcTo(2 * a, 2 * a, 4 * a, 4 * a, - i * (360 / 8.0) + (360 / 32.0), (360 / 32.0)); + path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), (360 / 32.0)); + path.arcTo(2 * a, 2 * a, 4 * a, 4 * a, i * (360 / 8.0) + (360 / 32.0), (360 / 32.0)); } painter.fillPath(path, foreground); @@ -85,44 +86,21 @@ NotebookButton::paintEvent(QPaintEvent *) painter.setBrush(background); painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a); } + + fancyPaint(painter); } -void -NotebookButton::mousePressEvent(QMouseEvent *event) +void NotebookButton::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { - mouseDown = true; + _mouseDown = false; - this->update(); - } -} - -void -NotebookButton::mouseReleaseEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) { - mouseDown = false; - - this->update(); + update(); emit clicked(); } -} -void -NotebookButton::enterEvent(QEvent *) -{ - mouseOver = true; - - this->update(); -} - -void -NotebookButton::leaveEvent(QEvent *) -{ - mouseOver = false; - - this->update(); + FancyButton::mouseReleaseEvent(event); } } } diff --git a/widgets/notebookbutton.h b/widgets/notebookbutton.h index 24183b148..2e2edf7ba 100644 --- a/widgets/notebookbutton.h +++ b/widgets/notebookbutton.h @@ -1,14 +1,17 @@ #ifndef NOTEBOOKBUTTON_H #define NOTEBOOKBUTTON_H +#include "fancybutton.h" + #include namespace chatterino { namespace widgets { -class NotebookButton : public QWidget +class NotebookButton : public FancyButton { Q_OBJECT + public: static const int IconPlus = 0; static const int IconUser = 1; @@ -18,18 +21,17 @@ public: NotebookButton(QWidget *parent); - void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; - void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; - void enterEvent(QEvent *) Q_DECL_OVERRIDE; - void leaveEvent(QEvent *) Q_DECL_OVERRIDE; +protected: + void paintEvent(QPaintEvent *) override; + void mouseReleaseEvent(QMouseEvent *event) override; signals: void clicked(); private: - bool mouseOver = false; - bool mouseDown = false; + bool _mouseOver = false; + bool _mouseDown = false; + QPoint _mousePos; }; } } diff --git a/widgets/notebookpage.cpp b/widgets/notebookpage.cpp index 51b44fb16..0e7d9472a 100644 --- a/widgets/notebookpage.cpp +++ b/widgets/notebookpage.cpp @@ -21,49 +21,60 @@ std::pair NotebookPage::dropPosition = std::pair(-1, -1); NotebookPage::NotebookPage(QWidget *parent, NotebookTab *tab) : QWidget(parent) - , parentbox(this) - , chatWidgets() - , preview(this) + , _tab(tab) + , _parentbox(this) + , _chatWidgets() + , _preview(this) { - this->tab = tab; tab->page = this; setHidden(true); setAcceptDrops(true); - this->parentbox.addSpacing(2); - this->parentbox.addLayout(&this->hbox); - this->parentbox.setMargin(0); + _parentbox.addSpacing(2); + _parentbox.addLayout(&_hbox); + _parentbox.setMargin(0); - this->hbox.setSpacing(1); - this->hbox.setMargin(0); + _hbox.setSpacing(1); + _hbox.setMargin(0); } -std::pair -NotebookPage::removeFromLayout(ChatWidget *widget) +const std::vector &NotebookPage::getChatWidgets() const { - for (auto it = this->chatWidgets.begin(); it != this->chatWidgets.end(); - ++it) { + return _chatWidgets; +} + +NotebookTab *NotebookPage::getTab() const +{ + return _tab; +} + +std::pair NotebookPage::removeFromLayout(ChatWidget *widget) +{ + // remove from chatWidgets vector + for (auto it = _chatWidgets.begin(); it != _chatWidgets.end(); ++it) { if (*it == widget) { - this->chatWidgets.erase(it); + _chatWidgets.erase(it); break; } } - for (int i = 0; i < this->hbox.count(); ++i) { - auto vbox = static_cast(this->hbox.itemAt(i)); + // remove from box and return location + for (int i = 0; i < _hbox.count(); ++i) { + auto vbox = static_cast(_hbox.itemAt(i)); for (int j = 0; j < vbox->count(); ++j) { - if (vbox->itemAt(j)->widget() != widget) + if (vbox->itemAt(j)->widget() != widget) { continue; + } widget->setParent(NULL); bool isLastItem = vbox->count() == 0; if (isLastItem) { - this->hbox.removeItem(vbox); + _hbox.removeItem(vbox); delete vbox; } @@ -75,19 +86,17 @@ NotebookPage::removeFromLayout(ChatWidget *widget) return std::pair(-1, -1); } -void -NotebookPage::addToLayout( - ChatWidget *widget, - std::pair position = std::pair(-1, -1)) +void NotebookPage::addToLayout(ChatWidget *widget, + std::pair position = std::pair(-1, -1)) { - this->chatWidgets.push_back(widget); + _chatWidgets.push_back(widget); // add vbox at the end - if (position.first < 0 || position.first >= this->hbox.count()) { + if (position.first < 0 || position.first >= _hbox.count()) { auto vbox = new QVBoxLayout(); vbox->addWidget(widget); - this->hbox.addLayout(vbox, 1); + _hbox.addLayout(vbox, 1); return; } @@ -96,101 +105,91 @@ NotebookPage::addToLayout( auto vbox = new QVBoxLayout(); vbox->addWidget(widget); - this->hbox.insertLayout(position.first, vbox, 1); + _hbox.insertLayout(position.first, vbox, 1); return; } // add to existing vbox - auto vbox = static_cast(this->hbox.itemAt(position.first)); + auto vbox = static_cast(_hbox.itemAt(position.first)); - vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), - widget); + vbox->insertWidget(std::max(0, std::min(vbox->count(), position.second)), widget); } -void -NotebookPage::enterEvent(QEvent *) +void NotebookPage::enterEvent(QEvent *) { - if (this->hbox.count() == 0) { + if (_hbox.count() == 0) { setCursor(QCursor(Qt::PointingHandCursor)); } else { setCursor(QCursor(Qt::ArrowCursor)); } } -void -NotebookPage::leaveEvent(QEvent *) +void NotebookPage::leaveEvent(QEvent *) { } -void -NotebookPage::mouseReleaseEvent(QMouseEvent *event) +void NotebookPage::mouseReleaseEvent(QMouseEvent *event) { - if (this->hbox.count() == 0 && event->button() == Qt::LeftButton) { + if (_hbox.count() == 0 && event->button() == Qt::LeftButton) { // "Add Chat" was clicked - this->addToLayout(new ChatWidget(), std::pair(-1, -1)); + addToLayout(new ChatWidget(), std::pair(-1, -1)); setCursor(QCursor(Qt::ArrowCursor)); } } -void -NotebookPage::dragEnterEvent(QDragEnterEvent *event) +void NotebookPage::dragEnterEvent(QDragEnterEvent *event) { if (!event->mimeData()->hasFormat("chatterino/split")) return; if (isDraggingSplit) { - this->dropRegions.clear(); + return; + } - if (this->hbox.count() == 0) { - this->dropRegions.push_back( - DropRegion(rect(), std::pair(-1, -1))); - } else { - for (int i = 0; i < this->hbox.count() + 1; ++i) { - this->dropRegions.push_back(DropRegion( - QRect(((i * 4 - 1) * width() / this->hbox.count()) / 4, 0, - width() / this->hbox.count() / 2 + 1, height() + 1), + _dropRegions.clear(); - std::pair(i, -1))); - } - - for (int i = 0; i < this->hbox.count(); ++i) { - auto vbox = static_cast(this->hbox.itemAt(i)); - - for (int j = 0; j < vbox->count() + 1; ++j) { - this->dropRegions.push_back(DropRegion( - QRect(i * width() / this->hbox.count(), - ((j * 2 - 1) * height() / vbox->count()) / 2, - width() / this->hbox.count() + 1, - height() / vbox->count() + 1), - - std::pair(i, j))); - } - } + if (_hbox.count() == 0) { + _dropRegions.push_back(DropRegion(rect(), std::pair(-1, -1))); + } else { + for (int i = 0; i < _hbox.count() + 1; ++i) { + _dropRegions.push_back(DropRegion(QRect(((i * 4 - 1) * width() / _hbox.count()) / 4, 0, + width() / _hbox.count() / 2 + 1, height() + 1), + std::pair(i, -1))); } - setPreviewRect(event->pos()); + for (int i = 0; i < _hbox.count(); ++i) { + auto vbox = static_cast(_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(i, j))); + } + } } + + setPreviewRect(event->pos()); + + event->acceptProposedAction(); } -void -NotebookPage::dragMoveEvent(QDragMoveEvent *event) +void NotebookPage::dragMoveEvent(QDragMoveEvent *event) { setPreviewRect(event->pos()); } -void -NotebookPage::setPreviewRect(QPoint mousePos) +void NotebookPage::setPreviewRect(QPoint mousePos) { - for (DropRegion region : this->dropRegions) { + for (DropRegion region : _dropRegions) { if (region.rect.contains(mousePos)) { - this->preview.setBounds(region.rect); + _preview.setBounds(region.rect); - if (!this->preview.isVisible()) { - this->preview.show(); - this->preview.raise(); + if (!_preview.isVisible()) { + _preview.show(); + _preview.raise(); } dropPosition = region.position; @@ -199,17 +198,15 @@ NotebookPage::setPreviewRect(QPoint mousePos) } } - this->preview.hide(); + _preview.hide(); } -void -NotebookPage::dragLeaveEvent(QDragLeaveEvent *event) +void NotebookPage::dragLeaveEvent(QDragLeaveEvent *event) { - this->preview.hide(); + _preview.hide(); } -void -NotebookPage::dropEvent(QDropEvent *event) +void NotebookPage::dropEvent(QDropEvent *event) { if (isDraggingSplit) { event->acceptProposedAction(); @@ -219,33 +216,28 @@ NotebookPage::dropEvent(QDropEvent *event) addToLayout(NotebookPage::draggingSplit, dropPosition); } - this->preview.hide(); + _preview.hide(); } -void -NotebookPage::paintEvent(QPaintEvent *) +void NotebookPage::paintEvent(QPaintEvent *) { QPainter painter(this); - if (this->hbox.count() == 0) { + if (_hbox.count() == 0) { painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground); - painter.fillRect(0, 0, width(), 2, - ColorScheme::getInstance().TabSelectedBackground); + painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground); painter.setPen(ColorScheme::getInstance().Text); painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter)); } else { - painter.fillRect(rect(), - ColorScheme::getInstance().TabSelectedBackground); + painter.fillRect(rect(), ColorScheme::getInstance().TabSelectedBackground); - painter.fillRect(0, 0, width(), 2, - ColorScheme::getInstance().TabSelectedBackground); + painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground); } } -static std::pair -getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget) +static std::pair getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget) { for (int i = 0; i < layout->count(); ++i) { printf("xD\n"); @@ -254,10 +246,9 @@ getWidgetPositionInLayout(QLayout *layout, const ChatWidget *chatWidget) return std::make_pair(-1, -1); } -std::pair -NotebookPage::getChatPosition(const ChatWidget *chatWidget) +std::pair NotebookPage::getChatPosition(const ChatWidget *chatWidget) { - auto layout = this->hbox.layout(); + auto layout = _hbox.layout(); if (layout == nullptr) { return std::make_pair(-1, -1); @@ -266,8 +257,7 @@ NotebookPage::getChatPosition(const ChatWidget *chatWidget) return getWidgetPositionInLayout(layout, chatWidget); } -void -NotebookPage::load(const boost::property_tree::ptree &tree) +void NotebookPage::load(const boost::property_tree::ptree &tree) { try { int column = 0; @@ -276,7 +266,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree) for (const auto &innerV : v.second.get_child("")) { auto widget = new ChatWidget(); widget->load(innerV.second); - this->addToLayout(widget, std::pair(column, row)); + addToLayout(widget, std::pair(column, row)); ++row; } ++column; @@ -286,8 +276,7 @@ NotebookPage::load(const boost::property_tree::ptree &tree) } } -static void -saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree) +static void saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree) { for (int i = 0; i < layout->count(); ++i) { auto item = layout->itemAt(i); @@ -323,17 +312,16 @@ saveFromLayout(QLayout *layout, boost::property_tree::ptree &tree) } } -boost::property_tree::ptree -NotebookPage::save() +boost::property_tree::ptree NotebookPage::save() { boost::property_tree::ptree tree; - auto layout = this->hbox.layout(); + auto layout = _hbox.layout(); saveFromLayout(layout, tree); /* - for (const auto &chat : this->chatWidgets) { + for (const auto &chat : chatWidgets) { boost::property_tree::ptree child = chat->save(); // Set child position diff --git a/widgets/notebookpage.h b/widgets/notebookpage.h index c1dde3af2..713c6a05f 100644 --- a/widgets/notebookpage.h +++ b/widgets/notebookpage.h @@ -23,34 +23,31 @@ class NotebookPage : public QWidget Q_OBJECT public: - NotebookPage(QWidget *parent, NotebookTab *tab); - NotebookTab *tab; + NotebookPage(QWidget *parent, NotebookTab *_tab); std::pair removeFromLayout(ChatWidget *widget); void addToLayout(ChatWidget *widget, std::pair position); - const std::vector & - getChatWidgets() const - { - return chatWidgets; - } + const std::vector &getChatWidgets() const; + NotebookTab *getTab() const; static bool isDraggingSplit; static ChatWidget *draggingSplit; static std::pair dropPosition; protected: - void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *) override; void enterEvent(QEvent *) override; void leaveEvent(QEvent *) override; void mouseReleaseEvent(QMouseEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; - void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE; - void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; - void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE; + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; +private: struct DropRegion { QRect rect; std::pair position; @@ -62,15 +59,16 @@ protected: } }; - QVBoxLayout parentbox; - QHBoxLayout hbox; + NotebookTab *_tab; - std::vector chatWidgets; - std::vector dropRegions; + QVBoxLayout _parentbox; + QHBoxLayout _hbox; - NotebookPageDropPreview preview; + std::vector _chatWidgets; + std::vector _dropRegions; + + NotebookPageDropPreview _preview; -private: void setPreviewRect(QPoint mousePos); std::pair getChatPosition(const ChatWidget *chatWidget); diff --git a/widgets/notebookpagedroppreview.cpp b/widgets/notebookpagedroppreview.cpp index 682e5a66c..f135bfbd0 100644 --- a/widgets/notebookpagedroppreview.cpp +++ b/widgets/notebookpagedroppreview.cpp @@ -17,8 +17,7 @@ NotebookPageDropPreview::NotebookPageDropPreview(QWidget *parent) this->setHidden(true); } -void -NotebookPageDropPreview::paintEvent(QPaintEvent *) +void NotebookPageDropPreview::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -26,14 +25,12 @@ NotebookPageDropPreview::paintEvent(QPaintEvent *) ColorScheme::getInstance().DropPreviewBackground); } -void -NotebookPageDropPreview::hideEvent(QHideEvent *) +void NotebookPageDropPreview::hideEvent(QHideEvent *) { animate = false; } -void -NotebookPageDropPreview::setBounds(const QRect &rect) +void NotebookPageDropPreview::setBounds(const QRect &rect) { if (rect == this->desiredGeometry) { return; diff --git a/widgets/notebooktab.cpp b/widgets/notebooktab.cpp index 03c8085bc..acbfac6aa 100644 --- a/widgets/notebooktab.cpp +++ b/widgets/notebooktab.cpp @@ -1,6 +1,6 @@ #include "widgets/notebooktab.h" #include "colorscheme.h" -#include "settings.h" +#include "settingsmanager.h" #include "widgets/notebook.h" #include @@ -10,75 +10,113 @@ namespace widgets { NotebookTab::NotebookTab(Notebook *notebook) : QWidget(notebook) - , posAnimation(this, "pos") - , posAnimated(false) - , posAnimationDesired() - , notebook(notebook) - , title("") - , selected(false) - , mouseOver(false) - , mouseDown(false) - , mouseOverX(false) - , mouseDownX(false) - , highlightStyle(HighlightNone) + , _posAnimation(this, "pos") + , _posAnimated(false) + , _posAnimationDesired() + , _notebook(notebook) + , _title("") + , _selected(false) + , _mouseOver(false) + , _mouseDown(false) + , _mouseOverX(false) + , _mouseDownX(false) + , _highlightStyle(HighlightNone) { this->calcSize(); this->setAcceptDrops(true); - posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); + _posAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); - this->hideXConnection = - Settings::getInstance().hideTabX.valueChanged.connect( - boost::bind(&NotebookTab::hideTabXChanged, this, _1)); + this->_hideXConnection = SettingsManager::getInstance().hideTabX.valueChanged.connect( + boost::bind(&NotebookTab::hideTabXChanged, this, _1)); this->setMouseTracking(true); } NotebookTab::~NotebookTab() { - this->hideXConnection.disconnect(); + this->_hideXConnection.disconnect(); } -void -NotebookTab::calcSize() +void NotebookTab::calcSize() { - if (Settings::getInstance().hideTabX.get()) { - this->resize(this->fontMetrics().width(this->title) + 8, 24); + if (SettingsManager::getInstance().hideTabX.get()) { + resize(fontMetrics().width(_title) + 8, 24); } else { - this->resize(this->fontMetrics().width(this->title) + 8 + 24, 24); + resize(fontMetrics().width(_title) + 8 + 24, 24); } - if (this->parent() != nullptr) { - ((Notebook *)this->parent())->performLayout(true); + if (parent() != nullptr) { + ((Notebook *)parent())->performLayout(true); } } -void -NotebookTab::moveAnimated(QPoint pos, bool animated) +const QString &NotebookTab::getTitle() const { - posAnimationDesired = pos; + return _title; +} - if ((this->window() != NULL && !this->window()->isVisible()) || !animated || - posAnimated == false) { +void NotebookTab::setTitle(const QString &title) +{ + _title = title; +} + +bool NotebookTab::getSelected() +{ + return _selected; +} + +void NotebookTab::setSelected(bool value) +{ + _selected = value; + update(); +} + +NotebookTab::HighlightStyle NotebookTab::getHighlightStyle() const +{ + return _highlightStyle; +} + +void NotebookTab::setHighlightStyle(HighlightStyle style) +{ + _highlightStyle = style; + update(); +} + +QRect NotebookTab::getDesiredRect() const +{ + return QRect(_posAnimationDesired, size()); +} + +void NotebookTab::hideTabXChanged(bool) +{ + calcSize(); + update(); +} + +void NotebookTab::moveAnimated(QPoint pos, bool animated) +{ + _posAnimationDesired = pos; + + if ((window() != NULL && !window()->isVisible()) || !animated || _posAnimated == false) { move(pos); - posAnimated = true; + _posAnimated = true; return; } - if (this->posAnimation.endValue() == pos) { + if (_posAnimation.endValue() == pos) { return; } - this->posAnimation.stop(); - this->posAnimation.setDuration(75); - this->posAnimation.setStartValue(this->pos()); - this->posAnimation.setEndValue(pos); - this->posAnimation.start(); + _posAnimation.stop(); + _posAnimation.setDuration(75); + _posAnimation.setStartValue(this->pos()); + _posAnimation.setEndValue(pos); + _posAnimation.start(); } -void -NotebookTab::paintEvent(QPaintEvent *) +void NotebookTab::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -86,16 +124,16 @@ NotebookTab::paintEvent(QPaintEvent *) auto &colorScheme = ColorScheme::getInstance(); - if (this->selected) { + if (_selected) { painter.fillRect(rect(), colorScheme.TabSelectedBackground); fg = colorScheme.TabSelectedText; - } else if (this->mouseOver) { + } else if (_mouseOver) { painter.fillRect(rect(), colorScheme.TabHoverBackground); fg = colorScheme.TabHoverText; - } else if (this->highlightStyle == HighlightHighlighted) { + } else if (_highlightStyle == HighlightHighlighted) { painter.fillRect(rect(), colorScheme.TabHighlightedBackground); fg = colorScheme.TabHighlightedText; - } else if (this->highlightStyle == HighlightNewMessage) { + } else if (_highlightStyle == HighlightNewMessage) { painter.fillRect(rect(), colorScheme.TabNewMessageBackground); fg = colorScheme.TabHighlightedText; } else { @@ -105,116 +143,105 @@ NotebookTab::paintEvent(QPaintEvent *) painter.setPen(fg); - QRect rect(0, 0, - width() - (Settings::getInstance().hideTabX.get() ? 0 : 16), - height()); + QRect rect(0, 0, width() - (SettingsManager::getInstance().hideTabX.get() ? 0 : 16), height()); - painter.drawText(rect, this->title, QTextOption(Qt::AlignCenter)); + painter.drawText(rect, _title, QTextOption(Qt::AlignCenter)); - if (!Settings::getInstance().hideTabX.get() && - (this->mouseOver || this->selected)) { - if (this->mouseOverX) { - painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64)); + if (!SettingsManager::getInstance().hideTabX.get() && (_mouseOver || _selected)) { + if (_mouseOverX) { + painter.fillRect(getXRect(), QColor(0, 0, 0, 64)); - if (this->mouseDownX) { - painter.fillRect(this->getXRect(), QColor(0, 0, 0, 64)); + if (_mouseDownX) { + painter.fillRect(getXRect(), QColor(0, 0, 0, 64)); } } - painter.drawLine(this->getXRect().topLeft() + QPoint(4, 4), - this->getXRect().bottomRight() + QPoint(-4, -4)); - painter.drawLine(this->getXRect().topRight() + QPoint(-4, 4), - this->getXRect().bottomLeft() + QPoint(4, -4)); + painter.drawLine(getXRect().topLeft() + QPoint(4, 4), + getXRect().bottomRight() + QPoint(-4, -4)); + painter.drawLine(getXRect().topRight() + QPoint(-4, 4), + getXRect().bottomLeft() + QPoint(4, -4)); } } -void -NotebookTab::mousePressEvent(QMouseEvent *event) +void NotebookTab::mousePressEvent(QMouseEvent *event) { - this->mouseDown = true; - this->mouseDownX = this->getXRect().contains(event->pos()); + _mouseDown = true; + _mouseDownX = getXRect().contains(event->pos()); - this->update(); + update(); - this->notebook->select(page); + _notebook->select(page); } -void -NotebookTab::mouseReleaseEvent(QMouseEvent *event) +void NotebookTab::mouseReleaseEvent(QMouseEvent *event) { - this->mouseDown = false; + _mouseDown = false; - if (!Settings::getInstance().hideTabX.get() && this->mouseDownX && - this->getXRect().contains(event->pos())) { - this->mouseDownX = false; + if (!SettingsManager::getInstance().hideTabX.get() && _mouseDownX && + getXRect().contains(event->pos())) { + _mouseDownX = false; - this->notebook->removePage(this->page); + _notebook->removePage(page); } else { update(); } } -void -NotebookTab::enterEvent(QEvent *) +void NotebookTab::enterEvent(QEvent *) { - this->mouseOver = true; + _mouseOver = true; update(); } -void -NotebookTab::leaveEvent(QEvent *) +void NotebookTab::leaveEvent(QEvent *) { - this->mouseOverX = this->mouseOver = false; + _mouseOverX = _mouseOver = false; update(); } -void -NotebookTab::dragEnterEvent(QDragEnterEvent *) +void NotebookTab::dragEnterEvent(QDragEnterEvent *) { - this->notebook->select(page); + _notebook->select(page); } -void -NotebookTab::mouseMoveEvent(QMouseEvent *event) +void NotebookTab::mouseMoveEvent(QMouseEvent *event) { - bool overX = this->getXRect().contains(event->pos()); + bool overX = getXRect().contains(event->pos()); - if (overX != this->mouseOverX) { - this->mouseOverX = overX && !Settings::getInstance().hideTabX.get(); + if (overX != _mouseOverX) { + _mouseOverX = overX && !SettingsManager::getInstance().hideTabX.get(); - this->update(); + update(); } - if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) { - QPoint relPoint = this->mapToParent(event->pos()); + if (_mouseDown && !getDesiredRect().contains(event->pos())) { + QPoint relPoint = mapToParent(event->pos()); int index; - NotebookPage *page = notebook->tabAt(relPoint, index); + NotebookPage *page = _notebook->tabAt(relPoint, index); - if (page != nullptr && page != this->page) { - notebook->rearrangePage(this->page, index); + if (page != nullptr && page != page) { + _notebook->rearrangePage(page, index); } } } -void -NotebookTab::load(const boost::property_tree::ptree &tree) +void NotebookTab::load(const boost::property_tree::ptree &tree) { // Load tab title try { - this->setTitle(QString::fromStdString(tree.get("title"))); + setTitle(QString::fromStdString(tree.get("title"))); } catch (boost::property_tree::ptree_error) { } } -boost::property_tree::ptree -NotebookTab::save() +boost::property_tree::ptree NotebookTab::save() { boost::property_tree::ptree tree; - tree.put("title", this->getTitle().toStdString()); + tree.put("title", getTitle().toStdString()); return tree; } diff --git a/widgets/notebooktab.h b/widgets/notebooktab.h index fa90d9a99..acc7bf87e 100644 --- a/widgets/notebooktab.h +++ b/widgets/notebooktab.h @@ -18,72 +18,27 @@ class NotebookTab : public QWidget Q_OBJECT public: - enum HighlightStyle { - HighlightNone, - HighlightHighlighted, - HighlightNewMessage - }; + enum HighlightStyle { HighlightNone, HighlightHighlighted, HighlightNewMessage }; - explicit NotebookTab(Notebook *notebook); + explicit NotebookTab(Notebook *_notebook); ~NotebookTab(); void calcSize(); NotebookPage *page; - const QString & - getTitle() const - { - return this->title; - } + const QString &getTitle() const; + void setTitle(const QString &title); + bool getSelected(); + void setSelected(bool value); - void - setTitle(const QString &title) - { - this->title = title; - } - - bool - getSelected() - { - return this->selected; - } - - void - setSelected(bool value) - { - this->selected = value; - update(); - } - - HighlightStyle - getHighlightStyle() const - { - return this->highlightStyle; - } - - void - setHighlightStyle(HighlightStyle style) - { - this->highlightStyle = style; - update(); - } + HighlightStyle getHighlightStyle() const; + void setHighlightStyle(HighlightStyle style); void moveAnimated(QPoint pos, bool animated = true); -public: - QRect - getDesiredRect() const - { - return QRect(posAnimationDesired, this->size()); - } - - void - hideTabXChanged(bool) - { - calcSize(); - update(); - } + QRect getDesiredRect() const; + void hideTabXChanged(bool); protected: void paintEvent(QPaintEvent *) override; @@ -98,26 +53,25 @@ protected: void mouseMoveEvent(QMouseEvent *event) override; private: - boost::signals2::connection hideXConnection; + boost::signals2::connection _hideXConnection; - QPropertyAnimation posAnimation; - bool posAnimated; - QPoint posAnimationDesired; + QPropertyAnimation _posAnimation; + bool _posAnimated; + QPoint _posAnimationDesired; - Notebook *notebook; + Notebook *_notebook; - QString title; + QString _title; - bool selected; - bool mouseOver; - bool mouseDown; - bool mouseOverX; - bool mouseDownX; + bool _selected; + bool _mouseOver; + bool _mouseDown; + bool _mouseOverX; + bool _mouseDownX; - HighlightStyle highlightStyle; + HighlightStyle _highlightStyle; - QRect - getXRect() + QRect getXRect() { return QRect(this->width() - 20, 4, 16, 16); } diff --git a/widgets/resizingtextedit.h b/widgets/resizingtextedit.h index ef6b250dc..7ea4162b9 100644 --- a/widgets/resizingtextedit.h +++ b/widgets/resizingtextedit.h @@ -16,18 +16,15 @@ public: sizePolicy.setVerticalPolicy(QSizePolicy::Preferred); this->setSizePolicy(sizePolicy); - QObject::connect(this, &QTextEdit::textChanged, this, - &QWidget::updateGeometry); + QObject::connect(this, &QTextEdit::textChanged, this, &QWidget::updateGeometry); } - QSize - sizeHint() const override + QSize sizeHint() const override { return QSize(this->width(), this->heightForWidth(this->width())); } - bool - hasHeightForWidth() const override + bool hasHeightForWidth() const override { return true; } @@ -35,17 +32,14 @@ public: boost::signals2::signal keyPressed; protected: - int - heightForWidth(int) const override + int heightForWidth(int) const override { auto margins = this->contentsMargins(); - return margins.top() + document()->size().height() + margins.bottom() + - 5; + return margins.top() + document()->size().height() + margins.bottom() + 5; } - void - keyPressEvent(QKeyEvent *event) + void keyPressEvent(QKeyEvent *event) { event->ignore(); diff --git a/widgets/scrollbar.cpp b/widgets/scrollbar.cpp index 334661bcd..70e676614 100644 --- a/widgets/scrollbar.cpp +++ b/widgets/scrollbar.cpp @@ -11,35 +11,34 @@ namespace widgets { ScrollBar::ScrollBar(QWidget *widget) : QWidget(widget) - , mutex() - , currentValueAnimation(this, "currentValue") - , highlights(NULL) - , mouseOverIndex(-1) - , mouseDownIndex(-1) - , lastMousePosition() - , buttonHeight(16) - , trackHeight(100) - , thumbRect() - , maximum() - , minimum() - , largeChange() - , smallChange() - , desiredValue() - , currentValueChanged() - , currentValue() + , _mutex() + , _currentValueAnimation(this, "currentValue") + , _highlights(NULL) + , _mouseOverIndex(-1) + , _mouseDownIndex(-1) + , _lastMousePosition() + , _buttonHeight(16) + , _trackHeight(100) + , _thumbRect() + , _maximum() + , _minimum() + , _largeChange() + , _smallChange() + , _desiredValue() + , _currentValueChanged() + , _currentValue() { - this->resize(16, 100); + resize(16, 100); - this->currentValueAnimation.setDuration(300); - this->currentValueAnimation.setEasingCurve( - QEasingCurve(QEasingCurve::OutCubic)); + _currentValueAnimation.setDuration(250); + _currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic)); - this->setMouseTracking(true); + setMouseTracking(true); } ScrollBar::~ScrollBar() { - auto highlight = this->highlights; + auto highlight = _highlights; while (highlight != NULL) { auto tmp = highlight->next; @@ -48,18 +47,17 @@ ScrollBar::~ScrollBar() } } -void -ScrollBar::removeHighlightsWhere(std::function func) +void ScrollBar::removeHighlightsWhere(std::function func) { - this->mutex.lock(); + _mutex.lock(); ScrollBarHighlight *last = NULL; - ScrollBarHighlight *current = this->highlights; + ScrollBarHighlight *current = _highlights; while (current != NULL) { if (func(*current)) { if (last == NULL) { - this->highlights = current->next; + _highlights = current->next; } else { last->next = current->next; } @@ -73,150 +71,233 @@ ScrollBar::removeHighlightsWhere(std::function func) } } - this->mutex.unlock(); + _mutex.unlock(); } -void -ScrollBar::addHighlight(ScrollBarHighlight *highlight) +void ScrollBar::addHighlight(ScrollBarHighlight *highlight) { - this->mutex.lock(); + _mutex.lock(); - if (this->highlights == NULL) { - this->highlights = highlight; + if (_highlights == NULL) { + _highlights = highlight; } else { - highlight->next = this->highlights->next; - this->highlights->next = highlight; + highlight->next = _highlights->next; + _highlights->next = highlight; } - this->mutex.unlock(); + _mutex.unlock(); } -void -ScrollBar::paintEvent(QPaintEvent *) +void ScrollBar::setMaximum(qreal value) +{ + _maximum = value; + + updateScroll(); +} + +void ScrollBar::setMinimum(qreal value) +{ + _minimum = value; + + updateScroll(); +} + +void ScrollBar::setLargeChange(qreal value) +{ + _largeChange = value; + + updateScroll(); +} + +void ScrollBar::setSmallChange(qreal value) +{ + _smallChange = value; + + updateScroll(); +} + +void ScrollBar::setDesiredValue(qreal value, bool animated) +{ + value = std::max(_minimum, std::min(_maximum - _largeChange, value)); + + if (_desiredValue != value) { + if (animated) { + _currentValueAnimation.stop(); + _currentValueAnimation.setStartValue(_currentValue); + + _currentValueAnimation.setEndValue(value); + _currentValueAnimation.start(); + } else { + if (_currentValueAnimation.state() != QPropertyAnimation::Running) { + // currentValueAnimation.stop(); + + setCurrentValue(value); + } + } + } + + _desiredValue = value; +} + +qreal ScrollBar::getMaximum() const +{ + return _maximum; +} + +qreal ScrollBar::getMinimum() const +{ + return _minimum; +} + +qreal ScrollBar::getLargeChange() const +{ + return _largeChange; +} + +qreal ScrollBar::getSmallChange() const +{ + return _smallChange; +} + +qreal ScrollBar::getDesiredValue() const +{ + return _desiredValue; +} + +qreal ScrollBar::getCurrentValue() const +{ + return _currentValue; +} + +boost::signals2::signal &ScrollBar::getCurrentValueChanged() +{ + return _currentValueChanged; +} + +void ScrollBar::setCurrentValue(qreal value) +{ + value = std::max(_minimum, std::min(_maximum - _largeChange, value)); + + if (_currentValue != value) { + _currentValue = value; + + updateScroll(); + _currentValueChanged(); + + update(); + } +} + +void ScrollBar::paintEvent(QPaintEvent *) { QPainter painter(this); painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG); - painter.fillRect(QRect(0, 0, width(), this->buttonHeight), - QColor(255, 0, 0)); - painter.fillRect( - QRect(0, height() - this->buttonHeight, width(), this->buttonHeight), - QColor(255, 0, 0)); + painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0)); + painter.fillRect(QRect(0, height() - _buttonHeight, width(), _buttonHeight), QColor(255, 0, 0)); - painter.fillRect(this->thumbRect, QColor(0, 255, 255)); + painter.fillRect(_thumbRect, QColor(0, 255, 255)); - ScrollBarHighlight *highlight = this->highlights; + // ScrollBarHighlight *highlight = highlights; - this->mutex.lock(); + _mutex.lock(); // do { // painter.fillRect(); // } while ((highlight = highlight->next()) != NULL); - this->mutex.unlock(); + _mutex.unlock(); } -void -ScrollBar::mouseMoveEvent(QMouseEvent *event) +void ScrollBar::mouseMoveEvent(QMouseEvent *event) { - if (this->mouseDownIndex == -1) { + if (_mouseDownIndex == -1) { int y = event->pos().y(); - auto oldIndex = this->mouseOverIndex; + auto oldIndex = _mouseOverIndex; - if (y < this->buttonHeight) { - this->mouseOverIndex = 0; - } else if (y < this->thumbRect.y()) { - this->mouseOverIndex = 1; - } else if (this->thumbRect.contains(2, y)) { - this->mouseOverIndex = 2; - } else if (y < height() - this->buttonHeight) { - this->mouseOverIndex = 3; + if (y < _buttonHeight) { + _mouseOverIndex = 0; + } else if (y < _thumbRect.y()) { + _mouseOverIndex = 1; + } else if (_thumbRect.contains(2, y)) { + _mouseOverIndex = 2; + } else if (y < height() - _buttonHeight) { + _mouseOverIndex = 3; } else { - this->mouseOverIndex = 4; + _mouseOverIndex = 4; } - if (oldIndex != this->mouseOverIndex) { - this->update(); + if (oldIndex != _mouseOverIndex) { + update(); } - } else if (this->mouseDownIndex == 2) { - int delta = event->pos().y() - lastMousePosition.y(); + } else if (_mouseDownIndex == 2) { + int delta = event->pos().y() - _lastMousePosition.y(); - this->setDesiredValue(this->desiredValue + - (qreal)delta / this->trackHeight * maximum); + setDesiredValue(_desiredValue + (qreal)delta / _trackHeight * _maximum); } - this->lastMousePosition = event->pos(); + _lastMousePosition = event->pos(); } -void -ScrollBar::mousePressEvent(QMouseEvent *event) +void ScrollBar::mousePressEvent(QMouseEvent *event) { int y = event->pos().y(); - if (y < this->buttonHeight) { - this->mouseDownIndex = 0; - } else if (y < this->thumbRect.y()) { - this->mouseDownIndex = 1; - } else if (this->thumbRect.contains(2, y)) { - this->mouseDownIndex = 2; - } else if (y < height() - this->buttonHeight) { - this->mouseDownIndex = 3; + if (y < _buttonHeight) { + _mouseDownIndex = 0; + } else if (y < _thumbRect.y()) { + _mouseDownIndex = 1; + } else if (_thumbRect.contains(2, y)) { + _mouseDownIndex = 2; + } else if (y < height() - _buttonHeight) { + _mouseDownIndex = 3; } else { - this->mouseDownIndex = 4; + _mouseDownIndex = 4; } } -void -ScrollBar::mouseReleaseEvent(QMouseEvent *event) +void ScrollBar::mouseReleaseEvent(QMouseEvent *event) { int y = event->pos().y(); - if (y < this->buttonHeight) { - if (this->mouseDownIndex == 0) { - this->setDesiredValue(this->desiredValue - this->smallChange, true); + if (y < _buttonHeight) { + if (_mouseDownIndex == 0) { + setDesiredValue(_desiredValue - _smallChange, true); } - } else if (y < this->thumbRect.y()) { - if (this->mouseDownIndex == 1) { - this->setDesiredValue(this->desiredValue - this->smallChange, true); + } else if (y < _thumbRect.y()) { + if (_mouseDownIndex == 1) { + setDesiredValue(_desiredValue - _smallChange, true); } - } else if (this->thumbRect.contains(2, y)) { + } else if (_thumbRect.contains(2, y)) { // do nothing - } else if (y < height() - this->buttonHeight) { - if (this->mouseDownIndex == 3) { - this->setDesiredValue(this->desiredValue + this->smallChange, true); + } else if (y < height() - _buttonHeight) { + if (_mouseDownIndex == 3) { + setDesiredValue(_desiredValue + _smallChange, true); } } else { - if (this->mouseDownIndex == 4) { - this->setDesiredValue(this->desiredValue + this->smallChange, true); + if (_mouseDownIndex == 4) { + setDesiredValue(_desiredValue + _smallChange, true); } } - this->mouseDownIndex = -1; + _mouseDownIndex = -1; update(); } -void -ScrollBar::leaveEvent(QEvent *) +void ScrollBar::leaveEvent(QEvent *) { - this->mouseOverIndex = -1; + _mouseOverIndex = -1; update(); } -void -ScrollBar::updateScroll() +void ScrollBar::updateScroll() { - this->trackHeight = height() - this->buttonHeight - this->buttonHeight - - MIN_THUMB_HEIGHT - 1; + _trackHeight = height() - _buttonHeight - _buttonHeight - MIN_THUMB_HEIGHT - 1; - this->thumbRect = - QRect(0, - (int)(this->currentValue / this->maximum * this->trackHeight) + - 1 + this->buttonHeight, - width(), - (int)(this->largeChange / this->maximum * this->trackHeight) + - MIN_THUMB_HEIGHT); + _thumbRect = QRect(0, (int)(_currentValue / _maximum * _trackHeight) + 1 + _buttonHeight, + width(), (int)(_largeChange / _maximum * _trackHeight) + MIN_THUMB_HEIGHT); update(); } diff --git a/widgets/scrollbar.h b/widgets/scrollbar.h index f4e1d074d..cebdc3583 100644 --- a/widgets/scrollbar.h +++ b/widgets/scrollbar.h @@ -23,128 +23,29 @@ public: void removeHighlightsWhere(std::function func); void addHighlight(ScrollBarHighlight *highlight); - Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue) + Q_PROPERTY(qreal _desiredValue READ getDesiredValue WRITE setDesiredValue) - void - setMaximum(qreal value) - { - this->maximum = value; - - this->updateScroll(); - } - - void - setMinimum(qreal value) - { - this->minimum = value; - - this->updateScroll(); - } - - void - setLargeChange(qreal value) - { - this->largeChange = value; - - this->updateScroll(); - } - - void - setSmallChange(qreal value) - { - this->smallChange = value; - - this->updateScroll(); - } - - void - setDesiredValue(qreal value, bool animated = false) - { - value = std::max(this->minimum, - std::min(this->maximum - this->largeChange, value)); - - if (this->desiredValue != value) { - if (animated) { - this->currentValueAnimation.stop(); - this->currentValueAnimation.setStartValue(this->currentValue); - - this->currentValueAnimation.setEndValue(value); - this->currentValueAnimation.start(); - } else { - this->currentValueAnimation.stop(); - - this->setCurrentValue(value); - } - } - - this->desiredValue = value; - } - - qreal - getMaximum() const - { - return this->maximum; - } - - qreal - getMinimum() const - { - return this->minimum; - } - - qreal - getLargeChange() const - { - return this->largeChange; - } - - qreal - getSmallChange() const - { - return this->smallChange; - } - - qreal - getDesiredValue() const - { - return this->desiredValue; - } - - qreal - getCurrentValue() const - { - return this->currentValue; - } - - boost::signals2::signal & - getCurrentValueChanged() - { - return currentValueChanged; - } - - void - setCurrentValue(qreal value) - { - value = std::max(this->minimum, - std::min(this->maximum - this->largeChange, value)); - - if (this->currentValue != value) { - this->currentValue = value; - - this->updateScroll(); - this->currentValueChanged(); - - this->update(); - } - } + void setMaximum(qreal value); + void setMinimum(qreal value); + void setLargeChange(qreal value); + void setSmallChange(qreal value); + void setDesiredValue(qreal value, bool animated = false); + qreal getMaximum() const; + qreal getMinimum() const; + qreal getLargeChange() const; + qreal getSmallChange() const; + qreal getDesiredValue() const; + qreal getCurrentValue() const; + boost::signals2::signal &getCurrentValueChanged(); + void setCurrentValue(qreal value); private: - Q_PROPERTY(qreal currentValue READ getCurrentValue WRITE setCurrentValue) + Q_PROPERTY(qreal _currentValue READ getCurrentValue WRITE setCurrentValue) - QMutex mutex; - ScrollBarHighlight *highlights; + QMutex _mutex; + ScrollBarHighlight *_highlights; - QPropertyAnimation currentValueAnimation; + QPropertyAnimation _currentValueAnimation; void paintEvent(QPaintEvent *); void mouseMoveEvent(QMouseEvent *event); @@ -152,23 +53,23 @@ private: void mouseReleaseEvent(QMouseEvent *event); void leaveEvent(QEvent *); - int mouseOverIndex; - int mouseDownIndex; - QPoint lastMousePosition; + int _mouseOverIndex; + int _mouseDownIndex; + QPoint _lastMousePosition; - int buttonHeight; - int trackHeight; + int _buttonHeight; + int _trackHeight; - QRect thumbRect; + QRect _thumbRect; - qreal maximum; - qreal minimum; - qreal largeChange; - qreal smallChange; - qreal desiredValue; - qreal currentValue; + qreal _maximum; + qreal _minimum; + qreal _largeChange; + qreal _smallChange; + qreal _desiredValue; + qreal _currentValue; - boost::signals2::signal currentValueChanged; + boost::signals2::signal _currentValueChanged; void updateScroll(); }; diff --git a/widgets/scrollbarhighlight.cpp b/widgets/scrollbarhighlight.cpp index 0591110b5..96e0b395a 100644 --- a/widgets/scrollbarhighlight.cpp +++ b/widgets/scrollbarhighlight.cpp @@ -4,13 +4,11 @@ namespace chatterino { namespace widgets { -ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, - Style style, QString tag) - : position(position) - , colorIndex(std::max( - 0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex))) - , style(style) - , tag(tag) +ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag) + : _position(position) + , _colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex))) + , _style(style) + , _tag(tag) , next(NULL) { } diff --git a/widgets/scrollbarhighlight.h b/widgets/scrollbarhighlight.h index 7d058a2ab..0fd08a3c9 100644 --- a/widgets/scrollbarhighlight.h +++ b/widgets/scrollbarhighlight.h @@ -11,40 +11,36 @@ class ScrollBarHighlight public: enum Style { Default, Left, Right, SingleLine }; - ScrollBarHighlight(float getPosition, int getColorIndex, - Style getStyle = Default, QString tag = ""); + ScrollBarHighlight(float getPosition, int getColorIndex, Style getStyle = Default, + QString _tag = ""); - Style - getStyle() + Style getStyle() { - return style; + return _style; } - float - getPosition() + float getPosition() { - return position; + return _position; } - int - getColorIndex() + int getColorIndex() { - return colorIndex; + return _colorIndex; } - QString - getTag() + QString getTag() { - return tag; + return _tag; } ScrollBarHighlight *next; private: - Style style; - float position; - int colorIndex; - QString tag; + Style _style; + float _position; + int _colorIndex; + QString _tag; }; } } diff --git a/widgets/settingsdialog.cpp b/widgets/settingsdialog.cpp index dc1603774..3510f9851 100644 --- a/widgets/settingsdialog.cpp +++ b/widgets/settingsdialog.cpp @@ -1,6 +1,6 @@ #include "widgets/settingsdialog.h" #include "widgets/settingsdialogtab.h" -#include "windows.h" +#include "windowmanager.h" #include #include @@ -14,7 +14,7 @@ namespace chatterino { namespace widgets { SettingsDialog::SettingsDialog() - : snapshot(Settings::getInstance().createSnapshot()) + : _snapshot(SettingsManager::getInstance().createSnapshot()) { QFile file(":/qss/settings.qss"); file.open(QFile::ReadOnly); @@ -25,44 +25,41 @@ SettingsDialog::SettingsDialog() palette.setColor(QPalette::Background, QColor("#444")); setPalette(palette); - pageStack.setObjectName("pages"); + _pageStack.setObjectName("pages"); - setLayout(&vbox); + setLayout(&_vbox); - vbox.addLayout(&hbox); + _vbox.addLayout(&_hbox); - vbox.addWidget(&buttonBox); + _vbox.addWidget(&_buttonBox); auto tabWidget = new QWidget(); tabWidget->setObjectName("tabWidget"); - tabWidget->setLayout(&tabs); + tabWidget->setLayout(&_tabs); tabWidget->setFixedWidth(200); - hbox.addWidget(tabWidget); - hbox.addLayout(&pageStack); + _hbox.addWidget(tabWidget); + _hbox.addLayout(&_pageStack); - buttonBox.addButton(&okButton, QDialogButtonBox::ButtonRole::AcceptRole); - buttonBox.addButton(&cancelButton, - QDialogButtonBox::ButtonRole::RejectRole); + _buttonBox.addButton(&_okButton, QDialogButtonBox::ButtonRole::AcceptRole); + _buttonBox.addButton(&_cancelButton, QDialogButtonBox::ButtonRole::RejectRole); - QObject::connect(&okButton, &QPushButton::clicked, this, - &SettingsDialog::okButtonClicked); - QObject::connect(&cancelButton, &QPushButton::clicked, this, + QObject::connect(&_okButton, &QPushButton::clicked, this, &SettingsDialog::okButtonClicked); + QObject::connect(&_cancelButton, &QPushButton::clicked, this, &SettingsDialog::cancelButtonClicked); - okButton.setText("OK"); - cancelButton.setText("Cancel"); + _okButton.setText("OK"); + _cancelButton.setText("Cancel"); resize(600, 500); addTabs(); } -void -SettingsDialog::addTabs() +void SettingsDialog::addTabs() { - Settings &settings = Settings::getInstance(); + SettingsManager &settings = SettingsManager::getInstance(); QVBoxLayout *vbox; @@ -77,11 +74,9 @@ SettingsDialog::addTabs() auto slider = new QSlider(Qt::Horizontal); auto font = new QPushButton("select"); auto compactTabs = createCheckbox("Hide tab X", settings.hideTabX); - auto hidePreferencesButton = - createCheckbox("Hide preferences button (ctrl+p to show)", - settings.hidePreferencesButton); - auto hideUserButton = - createCheckbox("Hide user button", settings.hideUserButton); + auto hidePreferencesButton = createCheckbox("Hide preferences button (ctrl+p to show)", + settings.hidePreferencesButton); + auto hideUserButton = createCheckbox("Hide user button", settings.hideUserButton); form->addRow("Theme:", combo); form->addRow("Theme color:", slider); @@ -110,9 +105,7 @@ SettingsDialog::addTabs() } QObject::connect(combo, &QComboBox::currentTextChanged, this, - [this, &settings](const QString &value) { - settings.theme.set(value); - }); + [this, &settings](const QString &value) { settings.theme.set(value); }); // theme hue slider->setMinimum(0); @@ -120,14 +113,12 @@ SettingsDialog::addTabs() float hue = settings.themeHue.get(); - slider->setValue(std::min(std::max(hue, (float)0.0), (float)1.0) * - 1000); + slider->setValue(std::min(std::max(hue, (float)0.0), (float)1.0) * 1000); - QObject::connect(slider, &QSlider::valueChanged, this, - [this, &settings](int value) { - settings.themeHue.set(value / 1000.0); - Windows::updateAll(); - }); + QObject::connect(slider, &QSlider::valueChanged, this, [this, &settings](int value) { + settings.themeHue.set(value / 1000.0); + WindowManager::updateAll(); + }); group->setLayout(form); @@ -139,15 +130,11 @@ SettingsDialog::addTabs() auto v = new QVBoxLayout(); v->addWidget(createCheckbox("Show timestamp", settings.showTimestamps)); - v->addWidget(createCheckbox("Show seconds in timestamp", - settings.showTimestampSeconds)); - v->addWidget(createCheckbox( - "Allow sending duplicate messages (add a space at the end)", - settings.allowDouplicateMessages)); - v->addWidget( - createCheckbox("Seperate messages", settings.seperateMessages)); - v->addWidget( - createCheckbox("Show message length", settings.showMessageLength)); + v->addWidget(createCheckbox("Show seconds in timestamp", settings.showTimestampSeconds)); + v->addWidget(createCheckbox("Allow sending duplicate messages (add a space at the end)", + settings.allowDouplicateMessages)); + v->addWidget(createCheckbox("Seperate messages", settings.seperateMessages)); + v->addWidget(createCheckbox("Show message length", settings.showMessageLength)); group->setLayout(v); @@ -161,15 +148,12 @@ SettingsDialog::addTabs() // Behaviour vbox = new QVBoxLayout(); + vbox->addWidget(createCheckbox("Hide input box if empty", settings.hideEmptyInput)); vbox->addWidget( - createCheckbox("Hide input box if empty", settings.hideEmptyInput)); + createCheckbox("Mention users with a @ (except in commands)", settings.mentionUsersWithAt)); + vbox->addWidget(createCheckbox("Window always on top", settings.windowTopMost)); vbox->addWidget( - createCheckbox("Mention users with a @ (except in commands)", - settings.mentionUsersWithAt)); - vbox->addWidget( - createCheckbox("Window always on top", settings.windowTopMost)); - vbox->addWidget(createCheckbox("Show last read message indicator", - settings.showLastMessageIndicator)); + createCheckbox("Show last read message indicator", settings.showLastMessageIndicator)); { auto v = new QVBoxLayout(); @@ -197,17 +181,13 @@ SettingsDialog::addTabs() // Emotes vbox = new QVBoxLayout(); - vbox->addWidget( - createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes)); - vbox->addWidget( - createCheckbox("Enable BetterTTV Emotes", settings.enableBttvEmotes)); - vbox->addWidget( - createCheckbox("Enable FrankerFaceZ Emotes", settings.enableFfzEmotes)); + vbox->addWidget(createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes)); + vbox->addWidget(createCheckbox("Enable BetterTTV Emotes", settings.enableBttvEmotes)); + vbox->addWidget(createCheckbox("Enable FrankerFaceZ Emotes", settings.enableFfzEmotes)); vbox->addWidget(createCheckbox("Enable Gif Emotes", settings.enableGifs)); vbox->addWidget(createCheckbox("Enable Emojis", settings.enableEmojis)); - vbox->addWidget( - createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes)); + vbox->addWidget(createCheckbox("Enable Twitch Emotes", settings.enableTwitchEmotes)); vbox->addStretch(1); addTab(vbox, "Emotes", ":/images/Emoji_Color_1F60A_19.png"); @@ -215,8 +195,7 @@ SettingsDialog::addTabs() // Ignored Users vbox = new QVBoxLayout(); vbox->addStretch(1); - addTab(vbox, "Ignored Users", - ":/images/StatusAnnotations_Blocked_16xLG_color.png"); + addTab(vbox, "Ignored Users", ":/images/StatusAnnotations_Blocked_16xLG_color.png"); // Ignored Messages vbox = new QVBoxLayout(); @@ -244,11 +223,10 @@ SettingsDialog::addTabs() addTab(vbox, "Whispers", ":/images/Message_16xLG.png"); // Add stretch - tabs.addStretch(1); + _tabs.addStretch(1); } -void -SettingsDialog::addTab(QLayout *layout, QString title, QString imageRes) +void SettingsDialog::addTab(QLayout *layout, QString title, QString imageRes) { auto widget = new QWidget(); @@ -258,33 +236,31 @@ SettingsDialog::addTab(QLayout *layout, QString title, QString imageRes) tab->setWidget(widget); - tabs.addWidget(tab, 0, Qt::AlignTop); + _tabs.addWidget(tab, 0, Qt::AlignTop); - pageStack.addWidget(widget); + _pageStack.addWidget(widget); - if (tabs.count() == 1) { + if (_tabs.count() == 1) { select(tab); } } -void -SettingsDialog::select(SettingsDialogTab *tab) +void SettingsDialog::select(SettingsDialogTab *tab) { - pageStack.setCurrentWidget(tab->getWidget()); + _pageStack.setCurrentWidget(tab->getWidget()); - if (selectedTab != NULL) { - selectedTab->setSelected(false); - selectedTab->setStyleSheet("color: #FFF"); + if (_selectedTab != NULL) { + _selectedTab->setSelected(false); + _selectedTab->setStyleSheet("color: #FFF"); } tab->setSelected(true); tab->setStyleSheet("background: #555; color: #FFF"); - selectedTab = tab; + _selectedTab = tab; } /// Widget creation helpers -QCheckBox * -SettingsDialog::createCheckbox(const QString &title, Setting &setting) +QCheckBox *SettingsDialog::createCheckbox(const QString &title, Setting &setting) { auto checkbox = new QCheckBox(title); @@ -297,16 +273,14 @@ SettingsDialog::createCheckbox(const QString &title, Setting &setting) return checkbox; } -void -SettingsDialog::okButtonClicked() +void SettingsDialog::okButtonClicked() { this->close(); } -void -SettingsDialog::cancelButtonClicked() +void SettingsDialog::cancelButtonClicked() { - snapshot.apply(); + _snapshot.apply(); this->close(); } diff --git a/widgets/settingsdialog.h b/widgets/settingsdialog.h index ff60c40ab..edd74b6e0 100644 --- a/widgets/settingsdialog.h +++ b/widgets/settingsdialog.h @@ -1,7 +1,7 @@ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H -#include "settings.h" +#include "settingsmanager.h" #include "settingssnapshot.h" #include "widgets/settingsdialogtab.h" @@ -27,21 +27,21 @@ public: void select(SettingsDialogTab *tab); private: - SettingsSnapshot snapshot; + SettingsSnapshot _snapshot; - QVBoxLayout tabs; - QVBoxLayout vbox; - QHBoxLayout hbox; - QStackedLayout pageStack; - QDialogButtonBox buttonBox; - QPushButton okButton; - QPushButton cancelButton; + QVBoxLayout _tabs; + QVBoxLayout _vbox; + QHBoxLayout _hbox; + QStackedLayout _pageStack; + QDialogButtonBox _buttonBox; + QPushButton _okButton; + QPushButton _cancelButton; void addTab(QLayout *layout, QString title, QString imageRes); void addTabs(); - SettingsDialogTab *selectedTab = NULL; + SettingsDialogTab *_selectedTab = NULL; /// Widget creation helpers QCheckBox *createCheckbox(const QString &title, Setting &setting); diff --git a/widgets/settingsdialogtab.cpp b/widgets/settingsdialogtab.cpp index a46fc9c8e..5732e5eff 100644 --- a/widgets/settingsdialogtab.cpp +++ b/widgets/settingsdialogtab.cpp @@ -7,13 +7,12 @@ namespace chatterino { namespace widgets { -SettingsDialogTab::SettingsDialogTab(SettingsDialog *dialog, QString label, - QString imageRes) - : image(QImage(imageRes)) +SettingsDialogTab::SettingsDialogTab(SettingsDialog *dialog, QString label, QString imageRes) + : _label(label) + , _image(QImage(imageRes)) + , _dialog(dialog) + , _selected(false) { - this->dialog = dialog; - - this->label = label; setFixedHeight(32); setCursor(QCursor(Qt::PointingHandCursor)); @@ -21,8 +20,31 @@ SettingsDialogTab::SettingsDialogTab(SettingsDialog *dialog, QString label, setStyleSheet("color: #FFF"); } -void -SettingsDialogTab::paintEvent(QPaintEvent *) +void SettingsDialogTab::setSelected(bool selected) +{ + if (_selected == selected) + return; + + _selected = selected; + emit selectedChanged(selected); +} + +bool SettingsDialogTab::getSelected() const +{ + return _selected; +} + +QWidget *SettingsDialogTab::getWidget() +{ + return _widget; +} + +void SettingsDialogTab::setWidget(QWidget *widget) +{ + _widget = widget; +} + +void SettingsDialogTab::paintEvent(QPaintEvent *) { QPainter painter(this); @@ -31,23 +53,22 @@ SettingsDialogTab::paintEvent(QPaintEvent *) style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); - int a = (height() - image.width()) / 2; + int a = (height() - _image.width()) / 2; - painter.drawImage(a, a, image); + painter.drawImage(a, a, _image); - a = a + a + image.width(); + a = a + a + _image.width(); - painter.drawText(QRect(a, 0, width() - a, height()), label, + painter.drawText(QRect(a, 0, width() - a, height()), _label, QTextOption(Qt::AlignLeft | Qt::AlignVCenter)); } -void -SettingsDialogTab::mouseReleaseEvent(QMouseEvent *event) +void SettingsDialogTab::mouseReleaseEvent(QMouseEvent *event) { if (event->button() != Qt::LeftButton) return; - dialog->select(this); + _dialog->select(this); } } } diff --git a/widgets/settingsdialogtab.h b/widgets/settingsdialogtab.h index f6a6ad8dc..ac495cbc3 100644 --- a/widgets/settingsdialogtab.h +++ b/widgets/settingsdialogtab.h @@ -12,39 +12,15 @@ class SettingsDialog; class SettingsDialogTab : public QWidget { Q_OBJECT - Q_PROPERTY(bool getSelected READ getSelected WRITE setSelected NOTIFY - selectedChanged) + Q_PROPERTY(bool getSelected READ getSelected WRITE setSelected NOTIFY selectedChanged) public: - SettingsDialogTab(SettingsDialog *dialog, QString label, QString imageRes); + SettingsDialogTab(SettingsDialog *_dialog, QString _label, QString imageRes); - void - setSelected(bool selected) - { - if (this->selected == selected) - return; - - this->selected = selected; - emit selectedChanged(selected); - } - - bool - getSelected() const - { - return this->selected; - } - - QWidget * - getWidget() - { - return this->widget; - } - - void - setWidget(QWidget *widget) - { - this->widget = widget; - } + void setSelected(bool selected); + bool getSelected() const; + QWidget *getWidget(); + void setWidget(QWidget *widget); signals: void selectedChanged(bool); @@ -53,13 +29,13 @@ private: void paintEvent(QPaintEvent *); void mouseReleaseEvent(QMouseEvent *event); - QWidget *widget; - QString label; - QImage image; + QWidget *_widget; + QString _label; + QImage _image; - SettingsDialog *dialog = NULL; + SettingsDialog *_dialog; - bool selected = false; + bool _selected; }; } } diff --git a/widgets/signallabel.h b/widgets/signallabel.h index 2b52aa1c3..849383641 100644 --- a/widgets/signallabel.h +++ b/widgets/signallabel.h @@ -24,14 +24,12 @@ signals: void mouseUp(); protected: - virtual void - mouseDoubleClickEvent(QMouseEvent *ev) override + virtual void mouseDoubleClickEvent(QMouseEvent *ev) override { emit this->mouseDoubleClick(ev); } - virtual void - mousePressEvent(QMouseEvent *event) override + virtual void mousePressEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { emit mouseDown(); @@ -40,8 +38,7 @@ protected: event->ignore(); } - void - mouseReleaseEvent(QMouseEvent *event) override + void mouseReleaseEvent(QMouseEvent *event) override { if (event->button() == Qt::LeftButton) { emit mouseUp(); @@ -50,8 +47,7 @@ protected: event->ignore(); } - virtual void - mouseMoveEvent(QMouseEvent *event) override + virtual void mouseMoveEvent(QMouseEvent *event) override { event->ignore(); } diff --git a/widgets/textinputdialog.cpp b/widgets/textinputdialog.cpp index 68092b7a4..9c2319130 100644 --- a/widgets/textinputdialog.cpp +++ b/widgets/textinputdialog.cpp @@ -6,38 +6,34 @@ namespace widgets { TextInputDialog::TextInputDialog(QWidget *parent) : QDialog(parent) - , vbox(this) - , lineEdit() - , buttonBox() - , okButton("OK") - , cancelButton("Cancel") + , _vbox(this) + , _lineEdit() + , _buttonBox() + , _okButton("OK") + , _cancelButton("Cancel") { - this->vbox.addWidget(&this->lineEdit); - this->vbox.addLayout(&this->buttonBox); - this->buttonBox.addStretch(1); - this->buttonBox.addWidget(&this->okButton); - this->buttonBox.addWidget(&this->cancelButton); + _vbox.addWidget(&_lineEdit); + _vbox.addLayout(&_buttonBox); + _buttonBox.addStretch(1); + _buttonBox.addWidget(&_okButton); + _buttonBox.addWidget(&_cancelButton); - QObject::connect(&this->okButton, SIGNAL(clicked()), this, - SLOT(okButtonClicked())); - QObject::connect(&this->cancelButton, SIGNAL(clicked()), this, - SLOT(cancelButtonClicked())); + QObject::connect(&_okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked())); + QObject::connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(cancelButtonClicked())); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - setWindowFlags((windowFlags() & ~(Qt::WindowContextHelpButtonHint)) | - Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); + setWindowFlags((windowFlags() & ~(Qt::WindowContextHelpButtonHint)) | Qt::Dialog | + Qt::MSWindowsFixedSizeDialogHint); } -void -TextInputDialog::okButtonClicked() +void TextInputDialog::okButtonClicked() { accept(); close(); } -void -TextInputDialog::cancelButtonClicked() +void TextInputDialog::cancelButtonClicked() { reject(); close(); diff --git a/widgets/textinputdialog.h b/widgets/textinputdialog.h index 6f8f5544b..9354b9a6f 100644 --- a/widgets/textinputdialog.h +++ b/widgets/textinputdialog.h @@ -18,24 +18,22 @@ class TextInputDialog : public QDialog public: TextInputDialog(QWidget *parent = NULL); - QString - getText() const + QString getText() const { - return lineEdit.text(); + return _lineEdit.text(); } - void - setText(const QString &text) + void setText(const QString &text) { - lineEdit.setText(text); + _lineEdit.setText(text); } private: - QVBoxLayout vbox; - QLineEdit lineEdit; - QHBoxLayout buttonBox; - QPushButton okButton; - QPushButton cancelButton; + QVBoxLayout _vbox; + QLineEdit _lineEdit; + QHBoxLayout _buttonBox; + QPushButton _okButton; + QPushButton _cancelButton; private slots: void okButtonClicked(); diff --git a/widgets/titlebar.cpp b/widgets/titlebar.cpp new file mode 100644 index 000000000..c898ec3b5 --- /dev/null +++ b/widgets/titlebar.cpp @@ -0,0 +1,11 @@ +#include "titlebar.h" + +namespace chatterino { +namespace widgets { +TitleBar::TitleBar(QWidget *parent) + : QWidget(parent) +{ + setFixedHeight(32); +} +} +} diff --git a/widgets/titlebar.h b/widgets/titlebar.h new file mode 100644 index 000000000..ab72d8803 --- /dev/null +++ b/widgets/titlebar.h @@ -0,0 +1,16 @@ +#ifndef TITLEBAR_H +#define TITLEBAR_H + +#include + +namespace chatterino { +namespace widgets { +class TitleBar : public QWidget +{ +public: + TitleBar(QWidget *parent = nullptr); +}; +} +} + +#endif // TITLEBAR_H diff --git a/widgets/userpopupwidget.cpp b/widgets/userpopupwidget.cpp new file mode 100644 index 000000000..4cea7877f --- /dev/null +++ b/widgets/userpopupwidget.cpp @@ -0,0 +1,41 @@ +#include "widgets/userpopupwidget.h" +#include "channel.h" +#include "ui_userpopup.h" + +#include + +namespace chatterino { +namespace widgets { + +UserPopupWidget::UserPopupWidget(std::shared_ptr &&channel) + : QWidget(nullptr) + , _ui(new Ui::UserPopup) + , _channel(std::move(channel)) +{ + _ui->setupUi(this); + + resize(0, 0); + + setWindowFlags(Qt::FramelessWindowHint); + + // Close button + connect(_ui->btnClose, &QPushButton::clicked, [=]() { + hide(); // + }); + + connect(_ui->btnPurge, &QPushButton::clicked, [=]() { + qDebug() << "xD: " << _channel->getName(); + /* + _channel->sendMessage( + QString(".timeout %1 0").arg(_ui->lblUsername->text())); + */ + }); +} + +void UserPopupWidget::setName(const QString &name) +{ + _ui->lblUsername->setText(name); +} + +} // namespace widgets +} // namespace chatterino diff --git a/widgets/userpopupwidget.h b/widgets/userpopupwidget.h new file mode 100644 index 000000000..7979ea997 --- /dev/null +++ b/widgets/userpopupwidget.h @@ -0,0 +1,35 @@ +#ifndef USERPOPUPWIDGET_H +#define USERPOPUPWIDGET_H + +#include + +#include + +namespace Ui { +class UserPopup; +} + +namespace chatterino { + +class Channel; + +namespace widgets { + +class UserPopupWidget : public QWidget +{ + Q_OBJECT +public: + UserPopupWidget(std::shared_ptr &&_channel); + + void setName(const QString &name); + +private: + Ui::UserPopup *_ui; + + std::shared_ptr _channel; +}; + +} // namespace widgets +} // namespace chatterino + +#endif // USERPOPUPWIDGET_H diff --git a/windows.cpp b/windowmanager.cpp similarity index 57% rename from windows.cpp rename to windowmanager.cpp index 0d298dbe4..eb3ac41b4 100644 --- a/windows.cpp +++ b/windowmanager.cpp @@ -1,4 +1,4 @@ -#include "windows.h" +#include "windowmanager.h" #include #include @@ -7,55 +7,48 @@ namespace chatterino { -static const std::string & -getSettingsPath() +static const std::string &getSettingsPath() { static std::string path = - (QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + - "/windows.json") + (QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/windows.json") .toStdString(); return path; } -QMutex Windows::windowMutex; +QMutex WindowManager::windowMutex; -widgets::MainWindow *Windows::mainWindow(nullptr); +widgets::MainWindow *WindowManager::mainWindow(nullptr); -void -Windows::layoutVisibleChatWidgets(Channel *channel) +void WindowManager::layoutVisibleChatWidgets(Channel *channel) { - if (Windows::mainWindow != nullptr) { - Windows::mainWindow->layoutVisibleChatWidgets(channel); + if (WindowManager::mainWindow != nullptr) { + WindowManager::mainWindow->layoutVisibleChatWidgets(channel); } } -void -Windows::repaintVisibleChatWidgets(Channel *channel) +void WindowManager::repaintVisibleChatWidgets(Channel *channel) { - if (Windows::mainWindow != nullptr) { - Windows::mainWindow->repaintVisibleChatWidgets(channel); + if (WindowManager::mainWindow != nullptr) { + WindowManager::mainWindow->repaintVisibleChatWidgets(channel); } } -void -Windows::repaintGifEmotes() +void WindowManager::repaintGifEmotes() { - if (Windows::mainWindow != nullptr) { - Windows::mainWindow->repaintGifEmotes(); + if (WindowManager::mainWindow != nullptr) { + WindowManager::mainWindow->repaintGifEmotes(); } } -void -Windows::updateAll() +void WindowManager::updateAll() { - if (Windows::mainWindow != nullptr) { - Windows::mainWindow->update(); + if (WindowManager::mainWindow != nullptr) { + WindowManager::mainWindow->update(); } } -void -Windows::load() +void WindowManager::load() { const auto &settingsPath = getSettingsPath(); boost::property_tree::ptree tree; @@ -63,10 +56,9 @@ Windows::load() try { boost::property_tree::read_json(settingsPath, tree); } catch (const boost::property_tree::json_parser_error &ex) { - qDebug() << "Error using property_tree::readJson: " - << QString::fromStdString(ex.message()); + qDebug() << "Error using property_tree::readJson: " << QString::fromStdString(ex.message()); - Windows::getMainWindow().loadDefaults(); + WindowManager::getMainWindow().loadDefaults(); return; } @@ -79,7 +71,7 @@ Windows::load() const auto &type = v.second.get("type", "unknown"); if (type == "main") { - Windows::getMainWindow().load(v.second); + WindowManager::getMainWindow().load(v.second); } else { qDebug() << "Unhandled window type: " << type.c_str(); } @@ -89,15 +81,14 @@ Windows::load() } // if the main window was not loaded properly, load defaults - if (!Windows::getMainWindow().isLoaded()) { - Windows::getMainWindow().loadDefaults(); + if (!WindowManager::getMainWindow().isLoaded()) { + WindowManager::getMainWindow().loadDefaults(); } // If there are no windows, create a default main window } -void -Windows::save() +void WindowManager::save() { const auto &settingsPath = getSettingsPath(); boost::property_tree::ptree tree; @@ -107,7 +98,7 @@ Windows::save() { // save main window - auto child = Windows::getMainWindow().save(); + auto child = WindowManager::getMainWindow().save(); windows.push_back(std::make_pair("", child)); } diff --git a/windows.h b/windowmanager.h similarity index 84% rename from windows.h rename to windowmanager.h index 49aa73c04..b0f82b556 100644 --- a/windows.h +++ b/windowmanager.h @@ -7,7 +7,7 @@ namespace chatterino { -class Windows +class WindowManager { public: static void layoutVisibleChatWidgets(Channel *channel = NULL); @@ -15,11 +15,10 @@ public: static void repaintGifEmotes(); static void updateAll(); - static widgets::MainWindow & - getMainWindow() + static widgets::MainWindow &getMainWindow() { windowMutex.lock(); - if (mainWindow == NULL) { + if (mainWindow == nullptr) { mainWindow = new widgets::MainWindow(); } windowMutex.unlock(); @@ -31,7 +30,7 @@ public: static void save(); private: - Windows() + WindowManager() { }