diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..2ff033dd8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/libcommuni"] + path = lib/libcommuni + url = https://github.com/communi/libcommuni diff --git a/channel.cpp b/channel.cpp index 2716da312..a35b673e1 100644 --- a/channel.cpp +++ b/channel.cpp @@ -3,6 +3,8 @@ const Channel Channel::whispers = Channel(QString("/whispers")); const Channel Channel::mentions = Channel(QString("/mentions")); +QMap Channel::channels = QMap(); + Channel::Channel(QString channel) { name = (channel.length() > 0 && channel[0] == '#') ? channel.mid(1) : channel; @@ -11,6 +13,55 @@ Channel::Channel(QString channel) popoutPlayerLink = "https://player.twitch.tv/?channel=" + name; } +Channel* Channel::addChannel(const QString &channel) +{ + auto c = getChannel(channel); + + if (c == NULL) { + c = new Channel(channel); + channels.insert(channel, c); + + return c; + } + + c->referenceCount++; + + return c; +} + +Channel* Channel::getChannel(const QString &channel) +{ + if (channel == "/whispers") { + return const_cast(&whispers); + } + + if (channel == "/mentions") { + return const_cast(&mentions); + } + + auto a = channels.find(channel); + + if (a == channels.end()) { + return *a; + } + + return NULL; +} + +void Channel::removeChannel(const QString &channel) +{ + auto c = getChannel(channel); + + if (c == NULL) return; + + c->referenceCount--; + + if (c->referenceCount == 0) { + channels.remove(channel); + delete c; + } +} + QString Channel::getSubLink() { return subLink ; } QString Channel::getChannelLink() { return channelLink ; } QString Channel::getPopoutPlayerLink() { return popoutPlayerLink ; } diff --git a/channel.h b/channel.h index 1be23bd52..9fb5111af 100644 --- a/channel.h +++ b/channel.h @@ -2,6 +2,7 @@ #define CHANNEL_H #include "QString" +#include "QMap" class Channel { @@ -9,8 +10,9 @@ public: static const Channel whispers; static const Channel mentions; - static Channel addChannel(QString channel); - static void removeChannel(QString channel); + static Channel* addChannel(const QString &channel); + static Channel* getChannel(const QString &channel); + static void removeChannel(const QString &channel); public: QString getSubLink(); @@ -25,7 +27,9 @@ public: private: Channel(QString channel); - int referenceCount = 0; + static QMap channels; + + int referenceCount = 1; QString name; diff --git a/chatterino.pro b/chatterino.pro index 3d00946f7..d3701e6b4 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -4,7 +4,11 @@ # #------------------------------------------------- -QT += core gui +QT += core gui network +CONFIG += communi c++11 +COMMUNI += core model util + +include(lib/libcommuni/src/src.pri) greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -38,7 +42,11 @@ SOURCES += main.cpp\ channel.cpp \ dialog.cpp \ settingsdialog.cpp \ - settingsdialogtab.cpp + settingsdialogtab.cpp \ + scrollbar.cpp \ + scrollbarhighlight.cpp \ + ircmanager.cpp \ + lambdaqrunnable.cpp HEADERS += mainwindow.h \ chatwidget.h \ @@ -54,7 +62,11 @@ HEADERS += mainwindow.h \ channel.h \ dialog.h \ settingsdialog.h \ - settingsdialogtab.h + settingsdialogtab.h \ + scrollbar.h \ + scrollbarhighlight.h \ + ircmanager.h \ + lambdaqrunnable.h FORMS += \ dialog.ui diff --git a/chatwidget.cpp b/chatwidget.cpp index 3d308d4b2..adaa5c9c6 100644 --- a/chatwidget.cpp +++ b/chatwidget.cpp @@ -15,9 +15,6 @@ ChatWidget::ChatWidget(QWidget *parent) vbox.addWidget(&header); vbox.addWidget(&view); vbox.addWidget(&input); - -// QFont font("Segoe UI", 15, QFont::Normal, false); -// this->font = font; } ChatWidget::~ChatWidget() @@ -30,14 +27,4 @@ void ChatWidget::paintEvent(QPaintEvent *) QPainter painter (this); painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground); - -// QColor color (255, 0, 0); - -// painter.setPen(color); - -// painter.setFont(this->font); -// painter.drawRect(0, 0, width() - 1, height() - 1); - -// QString text = "test text"; -// painter.drawText(20, 20, text); } diff --git a/chatwidget.h b/chatwidget.h index f53e7e7b2..c78c0ef7f 100644 --- a/chatwidget.h +++ b/chatwidget.h @@ -7,6 +7,7 @@ #include "chatwidgetheader.h" #include "chatwidgetview.h" #include "chatwidgetinput.h" +#include "channel.h" class ChatWidget : public QWidget { @@ -25,6 +26,9 @@ private: ChatWidgetHeader header; ChatWidgetView view; ChatWidgetInput input; + + + Channel* channel = NULL; }; #endif // CHATWIDGET_H diff --git a/chatwidgetview.cpp b/chatwidgetview.cpp index e874276cb..a0f47ee09 100644 --- a/chatwidgetview.cpp +++ b/chatwidgetview.cpp @@ -1,7 +1,14 @@ #include "chatwidgetview.h" ChatWidgetView::ChatWidgetView() - : QWidget() + : QWidget(), + scrollbar(this) { } + +void ChatWidgetView::resizeEvent(QResizeEvent *) +{ + scrollbar.resize(scrollbar.width(), height()); + scrollbar.move(width() - scrollbar.width(), 0); +} diff --git a/chatwidgetview.h b/chatwidgetview.h index b270c4994..31018eeb8 100644 --- a/chatwidgetview.h +++ b/chatwidgetview.h @@ -2,6 +2,7 @@ #define CHATVIEW_H #include +#include "scrollbar.h" class ChatWidgetView : public QWidget { @@ -9,6 +10,12 @@ class ChatWidgetView : public QWidget public: ChatWidgetView(); + +protected: + void resizeEvent(QResizeEvent *); + +private: + ScrollBar scrollbar; }; #endif // CHATVIEW_H diff --git a/colorscheme.h b/colorscheme.h index d828399f3..78405f0cb 100644 --- a/colorscheme.h +++ b/colorscheme.h @@ -51,6 +51,9 @@ public: QColor TabSelectedText; QColor TabHighlightedText; + const int HighlightColorCount = 3; + QColor HighlightColors[3]; + static ColorScheme& getInstance() { static ColorScheme instance; diff --git a/ircmanager.cpp b/ircmanager.cpp new file mode 100644 index 000000000..0ee586235 --- /dev/null +++ b/ircmanager.cpp @@ -0,0 +1,81 @@ +#include "ircmanager.h" +#include "ircconnection.h" +#include "irccommand.h" +#include "future" +#include "QThreadPool" +#include "QRunnable" +#include "lambdaqrunnable.h" + +IrcConnection* IrcManager::connection = NULL; +QMutex* IrcManager::connectionMutex = new QMutex(); +long IrcManager::connectionIteration = 0; + +IrcManager::IrcManager() +{ + +} + +void IrcManager::connect() +{ + disconnect(); + + QThreadPool::globalInstance()->start(new LambdaQRunnable([]{ beginConnecting(); return false; })); +} + +void IrcManager::beginConnecting() +{ + int iteration = ++connectionIteration; + + auto c = new IrcConnection(); + + QObject::connect(c, + &IrcConnection::messageReceived, + &messageReceived); + QObject::connect(c, + &IrcConnection::privateMessageReceived, + &privateMessageReceived); + + c->setHost("irc.chat.twitch.tv"); + c->setPort(6667); + + c->setUserName("justinfan123"); + c->setNickName("justinfan123"); + c->setRealName("justinfan123"); + c->sendRaw("JOIN #hsdogdog"); + + c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands")); + c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags")); + + c->open(); + + connectionMutex->lock(); + if (iteration == connectionIteration) { + connection = c; + } + else { + delete c; + } + connectionMutex->unlock(); +} + +void IrcManager::disconnect() +{ + connectionMutex->lock(); + + if (connection != NULL) { + delete connection; + connection = NULL; + } + + connectionMutex->unlock(); +} + +void IrcManager::messageReceived(IrcMessage *message) +{ +// qInfo(message->()); +} + +void IrcManager::privateMessageReceived(IrcPrivateMessage *message) +{ + qInfo(message->content().toStdString().c_str()); +} diff --git a/ircmanager.h b/ircmanager.h new file mode 100644 index 000000000..0f8419287 --- /dev/null +++ b/ircmanager.h @@ -0,0 +1,28 @@ +#ifndef IRCMANAGER_H +#define IRCMANAGER_H + +#define TWITCH_MAX_MESSAGELENGTH 500 + +#include "IrcMessage" +#include "QMutex" + +class IrcManager +{ +public: + static void connect(); + static void disconnect(); + +private: + IrcManager(); + + static void beginConnecting(); + + static IrcConnection* connection; + static QMutex* connectionMutex; + static long connectionIteration; + + static void messageReceived(IrcMessage* message); + static void privateMessageReceived(IrcPrivateMessage* message); +}; + +#endif // IRCMANAGER_H diff --git a/lambdaqrunnable.cpp b/lambdaqrunnable.cpp new file mode 100644 index 000000000..4051b4afd --- /dev/null +++ b/lambdaqrunnable.cpp @@ -0,0 +1,11 @@ +#include "lambdaqrunnable.h" + +LambdaQRunnable::LambdaQRunnable(std::function action) +{ + this->action = action; +} + +void LambdaQRunnable::run() +{ + action(); +} diff --git a/lambdaqrunnable.h b/lambdaqrunnable.h new file mode 100644 index 000000000..289097c0f --- /dev/null +++ b/lambdaqrunnable.h @@ -0,0 +1,18 @@ +#ifndef LAMBDAQRUNNABLE_H +#define LAMBDAQRUNNABLE_H + +#include "QRunnable" +#include "functional" + +class LambdaQRunnable : public QRunnable +{ +public: + LambdaQRunnable(std::function action); + + void run(); + +private: + std::function action; +}; + +#endif // LAMBDAQRUNNABLE_H diff --git a/lib/libcommuni b/lib/libcommuni new file mode 160000 index 000000000..1c7ebef19 --- /dev/null +++ b/lib/libcommuni @@ -0,0 +1 @@ +Subproject commit 1c7ebef19662ba5b09a78e01fc77dd901e302947 diff --git a/main.cpp b/main.cpp index 5a8fc32bc..26678c1b2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,6 +1,7 @@ #include #include "mainwindow.h" #include "colorscheme.h" +#include "ircmanager.h" int main(int argc, char *argv[]) { @@ -11,5 +12,7 @@ int main(int argc, char *argv[]) MainWindow w; w.show(); + IrcManager::connect(); + return a.exec(); } diff --git a/qss/settings.qss b/qss/settings.qss index eb1e52820..4dfdf32fb 100644 --- a/qss/settings.qss +++ b/qss/settings.qss @@ -2,6 +2,10 @@ background-color: #333; } +* { + font-size: 14px; +} + SettingsDialogTab { color: #FFF; } @@ -10,6 +14,6 @@ SettingsDialogTab:hover { border: 1px solid grey; } -QLabel { +QLabel, QCheckBox, QGroupBox { color: white; } diff --git a/scrollbar.cpp b/scrollbar.cpp new file mode 100644 index 000000000..aed41f15d --- /dev/null +++ b/scrollbar.cpp @@ -0,0 +1,83 @@ +#include "QPainter" +#include "scrollbar.h" +#include "colorscheme.h" + +ScrollBar::ScrollBar(QWidget* widget) + : QWidget(widget), + mutex() +{ + resize(16, 100); +} + +ScrollBar::~ScrollBar() +{ + auto highlight = highlights; + + while (highlight != NULL) + { + auto tmp = highlight->next; + delete highlight; + highlight = tmp; + } +} + +void ScrollBar::removeHighlightsWhere(std::function func) +{ + mutex.lock(); + + ScrollBarHighlight* last = NULL; + ScrollBarHighlight* current = highlights; + + while (current != NULL) + { + if (func(*current)) + { + if (last == NULL) + { + highlights = current->next; + } + else + { + last->next = current->next; + } + + auto oldCurrent = current; + + current = current->next; + last = current; + + delete oldCurrent; + } + } + + mutex.unlock(); +} + +void ScrollBar::addHighlight(ScrollBarHighlight* highlight) +{ + mutex.lock(); + + if (highlights == NULL) + { + highlights = highlight; + } + else + { + highlight->next = highlights->next; + highlights->next = highlight; + } + + mutex.unlock(); +} + +void ScrollBar::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG); + + mutex.lock(); + + + + mutex.unlock(); +} diff --git a/scrollbar.h b/scrollbar.h new file mode 100644 index 000000000..81cdc1fe5 --- /dev/null +++ b/scrollbar.h @@ -0,0 +1,28 @@ +#ifndef SCROLLBAR_H +#define SCROLLBAR_H + +#include +#include "QWidget" +#include "QMutex" +#include "scrollbarhighlight.h" + +class ScrollBar : public QWidget +{ + Q_OBJECT + +public: + ScrollBar(QWidget* parent = 0); + ~ScrollBar(); + + void removeHighlightsWhere(std::function func); + void addHighlight(ScrollBarHighlight* highlight); + +private: + QMutex mutex; + ScrollBarHighlight* highlights = NULL; + void paintEvent(QPaintEvent *); + + QRect thumbRect; +}; + +#endif // SCROLLBAR_H diff --git a/scrollbarhighlight.cpp b/scrollbarhighlight.cpp new file mode 100644 index 000000000..9ab92e272 --- /dev/null +++ b/scrollbarhighlight.cpp @@ -0,0 +1,12 @@ +#include "scrollbarhighlight.h" +#include "colorscheme.h" + +ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag) + : m_position(position), + m_colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex))), + m_style(style), + m_tag(tag), + next(NULL) +{ + +} diff --git a/scrollbarhighlight.h b/scrollbarhighlight.h new file mode 100644 index 000000000..7b3cb83f6 --- /dev/null +++ b/scrollbarhighlight.h @@ -0,0 +1,43 @@ +#ifndef SCROLLBARHIGHLIGHT_H +#define SCROLLBARHIGHLIGHT_H + +#include "QString" + +class ScrollBarHighlight +{ +public: + enum Style { + Default, + Left, + Right, + SingleLine + }; + + ScrollBarHighlight(float position, int colorIndex, Style style = Default, QString tag = ""); + + Style style() { + return m_style; + } + + float position() { + return m_position; + } + + int colorIndex() { + return m_colorIndex; + } + + QString getTag() { + return m_tag; + } + + ScrollBarHighlight* next; + +private: + Style m_style; + float m_position; + int m_colorIndex; + QString m_tag; +}; + +#endif // SCROLLBARHIGHLIGHT_H diff --git a/settingsdialog.cpp b/settingsdialog.cpp index bf6badb48..4a483ee31 100644 --- a/settingsdialog.cpp +++ b/settingsdialog.cpp @@ -7,6 +7,7 @@ #include "QLabel" #include "QFormLayout" #include "QComboBox" +#include "QGroupBox" SettingsDialog::SettingsDialog() { @@ -54,22 +55,63 @@ void SettingsDialog::addTabs() vbox = new QVBoxLayout(); { + auto group = new QGroupBox("Application"); + auto form = new QFormLayout(); auto combo = new QComboBox(); auto slider = new QSlider(Qt::Horizontal); auto font = new QPushButton("select"); form->addRow("Theme:", combo); - form->addRow("Theme Hue:", slider); + form->addRow("Theme color:", slider); form->addRow("Font:", font); - vbox->addLayout(form); + group->setLayout(form); + + vbox->addWidget(group); + } + + { + auto group = new QGroupBox("Messages"); + + auto v = new QVBoxLayout(); + v->addWidget(createCheckbox("Show timestamp", "")); + v->addWidget(createCheckbox("Show seconds in timestamp", "")); + v->addWidget(createCheckbox("Allow sending duplicate messages (add a space at the end)", "")); + v->addWidget(createCheckbox("Seperate messages", "")); + v->addWidget(createCheckbox("Show message length", "")); + + group->setLayout(v); + + vbox->addWidget(group); } vbox->addStretch(1); addTab(vbox, "Appearance", ":/images/AppearanceEditorPart_16x.png"); + // Behaviour + vbox = new QVBoxLayout(); + + vbox->addWidget(createCheckbox("Hide input box if empty", "")); + vbox->addWidget(createCheckbox("Mention users with a @ (except in commands)", "")); + vbox->addWidget(createCheckbox("Window always on top", "")); + vbox->addWidget(createCheckbox("Show last read message indicator", "")); + + { + auto v = new QVBoxLayout(); + v->addWidget(new QLabel("Mouse scroll speed")); + + auto scroll = new QSlider(Qt::Horizontal); + + v->addWidget(scroll); + v->addStretch(1); + } + + vbox->addStretch(1); + + addTab(vbox, "Behaviour", ":/images/AppearanceEditorPart_16x.png"); + // Commands vbox = new QVBoxLayout(); @@ -83,6 +125,11 @@ void SettingsDialog::addTabs() tabs.addStretch(1); } +QCheckBox* SettingsDialog::createCheckbox(QString title, QString settingsId) +{ + return new QCheckBox(title); +} + void SettingsDialog::addTab(QLayout* layout, QString title, QString imageRes) { auto widget = new QWidget(); diff --git a/settingsdialog.h b/settingsdialog.h index 59cc79407..76fc25150 100644 --- a/settingsdialog.h +++ b/settingsdialog.h @@ -11,6 +11,7 @@ #include "QDialogButtonBox" #include "QStackedLayout" #include "settingsdialogtab.h" +#include "QCheckBox" class SettingsDialog : public QWidget { @@ -33,6 +34,8 @@ private: void addTabs(); SettingsDialogTab* selectedTab = NULL; + + QCheckBox* createCheckbox(QString title, QString settingsId); }; #endif // SETTINGSDIALOG_H