updated messages

This commit is contained in:
fourtf 2017-01-15 16:38:30 +01:00
parent 4dbe2b0254
commit 801e7b8a1e
32 changed files with 698 additions and 148 deletions

View file

@ -61,7 +61,10 @@ SOURCES += main.cpp\
appsettings.cpp \ appsettings.cpp \
emojis.cpp \ emojis.cpp \
wordpart.cpp \ wordpart.cpp \
resources.cpp resources.cpp \
windows.cpp \
chatwidgetheaderbutton.cpp \
chatwidgetheaderbuttonlabel.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
chatwidget.h \ chatwidget.h \
@ -96,7 +99,10 @@ HEADERS += mainwindow.h \
emojis.h \ emojis.h \
wordpart.h \ wordpart.h \
common.h \ common.h \
resources.h resources.h \
windows.h \
chatwidgetheaderbutton.h \
chatwidgetheaderbuttonlabel.h
PRECOMPILED_HEADER = PRECOMPILED_HEADER =

View file

@ -1,16 +1,82 @@
#include "chatwidgetheader.h" #include "chatwidgetheader.h"
#include "QByteArray"
#include "QDrag"
#include "QMimeData"
#include "QPainter"
#include "chatwidget.h" #include "chatwidget.h"
#include "colorscheme.h" #include "colorscheme.h"
#include "notebookpage.h" #include "notebookpage.h"
#include <QByteArray>
#include <QDrag>
#include <QMimeData>
#include <QPainter>
ChatWidgetHeader::ChatWidgetHeader() ChatWidgetHeader::ChatWidgetHeader()
: QWidget() : QWidget()
, m_dragStart()
, m_dragging(false)
, leftLabel()
, middleLabel()
, rightLabel()
, leftMenu(this)
, rightMenu(this)
{ {
setFixedHeight(32); setFixedHeight(32);
updateColors();
setLayout(&hbox);
hbox.setMargin(0);
hbox.addWidget(&leftLabel);
hbox.addWidget(&middleLabel, 1);
hbox.addWidget(&rightLabel);
// left
leftLabel.label().setTextFormat(Qt::RichText);
leftLabel.label().setText(
"<img src=':/images/tool_moreCollapser_off16.png' />");
QObject::connect(&leftLabel, ChatWidgetHeaderButton::clicked, this,
leftButtonClicked);
leftMenu.addAction("Add new split", this, ChatWidgetHeader::menuAddSplit,
QKeySequence(tr("Ctrl+T")));
leftMenu.addAction("Close split", this, ChatWidgetHeader::menuCloseSplit,
QKeySequence(tr("Ctrl+W")));
leftMenu.addAction("Move split", this, ChatWidgetHeader::menuMoveSplit);
leftMenu.addSeparator();
leftMenu.addAction("Change channel", this,
ChatWidgetHeader::menuChangeChannel,
QKeySequence(tr("Ctrl+R")));
leftMenu.addAction("Clear chat", this, ChatWidgetHeader::menuClearChat);
leftMenu.addAction("Open channel", this, ChatWidgetHeader::menuOpenChannel);
leftMenu.addAction("Open pop-out player", this,
ChatWidgetHeader::menuPopupPlayer);
leftMenu.addSeparator();
leftMenu.addAction("Reload channel emotes", this,
ChatWidgetHeader::menuReloadChannelEmotes);
leftMenu.addAction("Manual reconnect", this,
ChatWidgetHeader::menuManualReconnect);
leftMenu.addSeparator();
leftMenu.addAction("Show changelog", this,
ChatWidgetHeader::menuShowChangelog);
// middle
middleLabel.setAlignment(Qt::AlignCenter);
middleLabel.setText("textString");
// right
rightLabel.setMinimumWidth(height());
rightLabel.label().setTextFormat(Qt::RichText);
rightLabel.label().setText("ayy");
}
void
ChatWidgetHeader::updateColors()
{
QPalette palette;
palette.setColor(QPalette::Foreground, ColorScheme::instance().Text);
leftLabel.setPalette(palette);
middleLabel.setPalette(palette);
rightLabel.setPalette(palette);
} }
void void
@ -26,17 +92,17 @@ ChatWidgetHeader::paintEvent(QPaintEvent *)
void void
ChatWidgetHeader::mousePressEvent(QMouseEvent *event) ChatWidgetHeader::mousePressEvent(QMouseEvent *event)
{ {
dragging = true; m_dragging = true;
dragStart = event->pos(); m_dragStart = event->pos();
} }
void void
ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event) ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event)
{ {
if (dragging) { if (m_dragging) {
if (std::abs(dragStart.x() - event->pos().x()) > 12 || if (std::abs(m_dragStart.x() - event->pos().x()) > 12 ||
std::abs(dragStart.y() - event->pos().y()) > 12) { std::abs(m_dragStart.y() - event->pos().y()) > 12) {
auto chatWidget = getChatWidget(); auto chatWidget = getChatWidget();
auto page = static_cast<NotebookPage *>(chatWidget->parentWidget()); auto page = static_cast<NotebookPage *>(chatWidget->parentWidget());
@ -72,3 +138,56 @@ ChatWidgetHeader::getChatWidget()
{ {
return static_cast<ChatWidget *>(parentWidget()); return static_cast<ChatWidget *>(parentWidget());
} }
void
ChatWidgetHeader::leftButtonClicked()
{
leftMenu.move(leftLabel.mapToGlobal(QPoint(0, leftLabel.height())));
leftMenu.show();
}
void
ChatWidgetHeader::rightButtonClicked()
{
}
void
ChatWidgetHeader::menuAddSplit()
{
}
void
ChatWidgetHeader::menuCloseSplit()
{
}
void
ChatWidgetHeader::menuMoveSplit()
{
}
void
ChatWidgetHeader::menuChangeChannel()
{
}
void
ChatWidgetHeader::menuClearChat()
{
}
void
ChatWidgetHeader::menuOpenChannel()
{
}
void
ChatWidgetHeader::menuPopupPlayer()
{
}
void
ChatWidgetHeader::menuReloadChannelEmotes()
{
}
void
ChatWidgetHeader::menuManualReconnect()
{
}
void
ChatWidgetHeader::menuShowChangelog()
{
}

View file

@ -1,10 +1,16 @@
#ifndef CHATWIDGETHEADER_H #ifndef CHATWIDGETHEADER_H
#define CHATWIDGETHEADER_H #define CHATWIDGETHEADER_H
#include "QMouseEvent" #include "chatwidgetheaderbutton.h"
#include "QPaintEvent"
#include "QPoint" #include <QAction>
#include "QWidget" #include <QHBoxLayout>
#include <QLabel>
#include <QMenu>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QPoint>
#include <QWidget>
class ChatWidget; class ChatWidget;
@ -15,6 +21,7 @@ class ChatWidgetHeader : public QWidget
public: public:
ChatWidgetHeader(); ChatWidgetHeader();
ChatWidget *getChatWidget(); ChatWidget *getChatWidget();
void updateColors();
protected: protected:
void paintEvent(QPaintEvent *); void paintEvent(QPaintEvent *);
@ -22,8 +29,31 @@ protected:
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
private: private:
QPoint dragStart; QPoint m_dragStart;
bool dragging = false; bool m_dragging;
QHBoxLayout hbox;
ChatWidgetHeaderButton leftLabel;
QLabel middleLabel;
ChatWidgetHeaderButton rightLabel;
QMenu leftMenu;
QMenu rightMenu;
void leftButtonClicked();
void rightButtonClicked();
void menuAddSplit();
void menuCloseSplit();
void menuMoveSplit();
void menuChangeChannel();
void menuClearChat();
void menuOpenChannel();
void menuPopupPlayer();
void menuReloadChannelEmotes();
void menuManualReconnect();
void menuShowChangelog();
}; };
#endif // CHATWIDGETHEADER_H #endif // CHATWIDGETHEADER_H

View file

@ -0,0 +1,99 @@
#include "chatwidgetheaderbutton.h"
#include "colorscheme.h"
#include <QBrush>
#include <QPainter>
ChatWidgetHeaderButton::ChatWidgetHeaderButton()
: QWidget()
, hbox()
, m_label()
, m_mouseOver(false)
, m_mouseDown(false)
{
setLayout(&hbox);
hbox.setMargin(0);
hbox.addSpacing(6);
hbox.addWidget(&m_label);
hbox.addSpacing(6);
QObject::connect(&m_label, ChatWidgetHeaderButtonLabel::mouseUp, this,
labelMouseUp);
QObject::connect(&m_label, ChatWidgetHeaderButtonLabel::mouseDown, this,
labelMouseDown);
}
void
ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QBrush brush(ColorScheme::instance().IsLightTheme
? QColor(0, 0, 0, 32)
: QColor(255, 255, 255, 32));
if (m_mouseDown) {
painter.fillRect(rect(), brush);
}
if (m_mouseOver) {
painter.fillRect(rect(), brush);
}
}
void
ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_mouseDown = true;
repaint();
}
}
void
ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
m_mouseDown = false;
repaint();
emit clicked();
}
}
void
ChatWidgetHeaderButton::enterEvent(QEvent *)
{
m_mouseOver = true;
repaint();
}
void
ChatWidgetHeaderButton::leaveEvent(QEvent *)
{
m_mouseOver = false;
repaint();
}
void
ChatWidgetHeaderButton::labelMouseUp()
{
m_mouseDown = false;
repaint();
emit clicked();
}
void
ChatWidgetHeaderButton::labelMouseDown()
{
m_mouseDown = true;
repaint();
}

47
chatwidgetheaderbutton.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef CHATWIDGETHEADERBUTTON_H
#define CHATWIDGETHEADERBUTTON_H
#include "chatwidgetheaderbuttonlabel.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QPaintEvent>
#include <QWidget>
class ChatWidgetHeaderButton : public QWidget
{
Q_OBJECT
public:
ChatWidgetHeaderButton();
ChatWidgetHeaderButtonLabel &
label()
{
return m_label;
}
signals:
void clicked();
protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
void enterEvent(QEvent *) Q_DECL_OVERRIDE;
void leaveEvent(QEvent *) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
private:
QHBoxLayout hbox;
ChatWidgetHeaderButtonLabel m_label;
bool m_mouseOver;
bool m_mouseDown;
void labelMouseUp();
void labelMouseDown();
};
#endif // CHATWIDGETHEADERBUTTON_H

View file

@ -0,0 +1,21 @@
#include "chatwidgetheaderbuttonlabel.h"
ChatWidgetHeaderButtonLabel::ChatWidgetHeaderButtonLabel()
{
}
void
ChatWidgetHeaderButtonLabel::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
emit mouseDown();
}
}
void
ChatWidgetHeaderButtonLabel::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
emit mouseUp();
}
}

View file

@ -0,0 +1,23 @@
#ifndef CHATWIDGETHEADERBUTTONLABEL_H
#define CHATWIDGETHEADERBUTTONLABEL_H
#include <QLabel>
#include <QMouseEvent>
class ChatWidgetHeaderButtonLabel : public QLabel
{
Q_OBJECT
public:
ChatWidgetHeaderButtonLabel();
signals:
void mouseDown();
void mouseUp();
protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
};
#endif // CHATWIDGETHEADERBUTTONLABEL_H

View file

@ -15,7 +15,7 @@ ChatWidgetView::ChatWidgetView()
scroll->scrollTo(QPointF(0, 100)); scroll->scrollTo(QPointF(0, 100));
m_channel = Channel::getChannel("ian678"); m_channel = Channel::getChannel("fourtf");
} }
void void
@ -42,6 +42,8 @@ ChatWidgetView::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
auto c = channel(); auto c = channel();
if (c == NULL) if (c == NULL)
@ -49,7 +51,7 @@ ChatWidgetView::paintEvent(QPaintEvent *)
auto messages = c->getMessagesClone(); auto messages = c->getMessagesClone();
int y = 32; int y = 0;
for (std::shared_ptr<Message> const &message : messages) { for (std::shared_ptr<Message> const &message : messages) {
for (WordPart const &wordPart : message.get()->wordParts()) { for (WordPart const &wordPart : message.get()->wordParts()) {
@ -77,8 +79,7 @@ ChatWidgetView::paintEvent(QPaintEvent *)
painter.drawText( painter.drawText(
QRectF(wordPart.x(), wordPart.y() + y, 10000, 10000), QRectF(wordPart.x(), wordPart.y() + y, 10000, 10000),
wordPart.getText(), wordPart.text(), QTextOption(Qt::AlignLeft | Qt::AlignTop));
QTextOption(Qt::AlignLeft | Qt::AlignTop));
} }
} }

View file

@ -13,6 +13,8 @@ ConcurrentMap<QString, LazyLoadedImage *> Emotes::m_ffzChannelEmoteFromCaches;
ConcurrentMap<long, LazyLoadedImage *> Emotes::m_twitchEmoteFromCache; ConcurrentMap<long, LazyLoadedImage *> Emotes::m_twitchEmoteFromCache;
ConcurrentMap<QString, LazyLoadedImage *> Emotes::m_miscImageFromCache; ConcurrentMap<QString, LazyLoadedImage *> Emotes::m_miscImageFromCache;
int Emotes::m_generation = 0;
Emotes::Emotes() Emotes::Emotes()
{ {
} }

View file

@ -66,6 +66,18 @@ public:
static LazyLoadedImage *getTwitchEmoteById(const QString &name, static LazyLoadedImage *getTwitchEmoteById(const QString &name,
long int id); long int id);
static int
generation()
{
return m_generation;
}
static void
incGeneration()
{
m_generation++;
}
private: private:
Emotes(); Emotes();
@ -83,6 +95,8 @@ private:
static ConcurrentMap<QString, LazyLoadedImage *> m_miscImageFromCache; static ConcurrentMap<QString, LazyLoadedImage *> m_miscImageFromCache;
static QString getTwitchEmoteLink(long id, qreal &scale); static QString getTwitchEmoteLink(long id, qreal &scale);
static int m_generation;
}; };
#endif // EMOTES_H #endif // EMOTES_H

View file

@ -5,7 +5,7 @@
QFont *Fonts::medium = new QFont(DEFAULT_FONT, 14); QFont *Fonts::medium = new QFont(DEFAULT_FONT, 14);
QFont *Fonts::mediumBold = new QFont(DEFAULT_FONT, 14); QFont *Fonts::mediumBold = new QFont(DEFAULT_FONT, 14);
QFont *Fonts::mediumItalic = new QFont(DEFAULT_FONT, 14); QFont *Fonts::mediumItalic = new QFont(DEFAULT_FONT, 14);
QFont *Fonts::small = new QFont(DEFAULT_FONT, 12); QFont *Fonts::small = new QFont(DEFAULT_FONT, 10);
QFont *Fonts::large = new QFont(DEFAULT_FONT, 16); QFont *Fonts::large = new QFont(DEFAULT_FONT, 16);
QFont *Fonts::veryLarge = new QFont(DEFAULT_FONT, 18); QFont *Fonts::veryLarge = new QFont(DEFAULT_FONT, 18);
@ -16,6 +16,8 @@ QFontMetrics *Fonts::metricsSmall = new QFontMetrics(*small);
QFontMetrics *Fonts::metricsLarge = new QFontMetrics(*large); QFontMetrics *Fonts::metricsLarge = new QFontMetrics(*large);
QFontMetrics *Fonts::metricsVeryLarge = new QFontMetrics(*veryLarge); QFontMetrics *Fonts::metricsVeryLarge = new QFontMetrics(*veryLarge);
int Fonts::m_generation = 0;
Fonts::Fonts() Fonts::Fonts()
{ {
} }

14
fonts.h
View file

@ -19,6 +19,18 @@ public:
static QFont &getFont(Type type); static QFont &getFont(Type type);
static QFontMetrics &getFontMetrics(Type type); static QFontMetrics &getFontMetrics(Type type);
static int
generation()
{
return m_generation;
}
static void
incGeneration()
{
m_generation++;
}
private: private:
Fonts(); Fonts();
@ -35,6 +47,8 @@ private:
static QFontMetrics *metricsSmall; static QFontMetrics *metricsSmall;
static QFontMetrics *metricsLarge; static QFontMetrics *metricsLarge;
static QFontMetrics *metricsVeryLarge; static QFontMetrics *metricsVeryLarge;
static int m_generation;
}; };
#endif // FONTS_H #endif // FONTS_H

BIN
images/button_ban.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
images/button_timeout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 279 B

View file

@ -120,8 +120,6 @@ IrcManager::beginConnecting()
c->setNickName("justinfan123"); c->setNickName("justinfan123");
c->setRealName("justinfan123"); c->setRealName("justinfan123");
c->sendRaw("JOIN #fourtf"); c->sendRaw("JOIN #fourtf");
c->sendRaw("JOIN #ian678");
c->sendRaw("JOIN #nuuls");
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands")); c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/commands"));
c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags")); c->sendCommand(IrcCommand::createCapability("REQ", "twitch.tv/tags"));

View file

@ -1,6 +1,7 @@
#include "lazyloadedimage.h" #include "lazyloadedimage.h"
#include "asyncexec.h" #include "asyncexec.h"
#include "emotes.h"
#include "ircmanager.h" #include "ircmanager.h"
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
@ -56,6 +57,7 @@ LazyLoadedImage::loadImage()
} }
m_pixmap = pixmap; m_pixmap = pixmap;
Emotes::incGeneration();
}); });
//})); //}));
} }

2
link.h
View file

@ -11,6 +11,8 @@ public:
Url, Url,
CloseCurrentSplit, CloseCurrentSplit,
UserInfo, UserInfo,
UserTimeout,
UserBan,
InsertText, InsertText,
ShowMessage, ShowMessage,
}; };

View file

@ -3,6 +3,7 @@
#include "ircmanager.h" #include "ircmanager.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "resources.h" #include "resources.h"
#include "windows.h"
#include <QApplication> #include <QApplication>
#include <QClipboard> #include <QClipboard>
@ -17,7 +18,7 @@ main(int argc, char *argv[])
ColorScheme::instance().setColors(0, -0.8); ColorScheme::instance().setColors(0, -0.8);
MainWindow w; MainWindow &w = Windows::mainWindow();
w.show(); w.show();
Channel::addChannel("ian678"); Channel::addChannel("ian678");

View file

@ -23,14 +23,14 @@ QRegularExpression *Message::cheerRegex =
new QRegularExpression("cheer[1-9][0-9]*"); new QRegularExpression("cheer[1-9][0-9]*");
Message::Message(const QString &text) Message::Message(const QString &text)
: m_wordParts(new std::list<WordPart>()) : m_wordParts(new std::vector<WordPart>())
{ {
} }
Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
bool enablePingSound, bool isReceivedWhisper, bool enablePingSound, bool isReceivedWhisper,
bool isSentWhisper, bool includeChannel) bool isSentWhisper, bool includeChannel)
: m_wordParts(new std::list<WordPart>()) : m_wordParts(new std::vector<WordPart>())
{ {
m_parseTime = std::chrono::system_clock::now(); m_parseTime = std::chrono::system_clock::now();
@ -70,6 +70,17 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
ColorScheme::instance().SystemMessageColor, QString(), ColorScheme::instance().SystemMessageColor, QString(),
QString())); QString()));
// mod buttons
static QString buttonBanTooltip("Ban user");
static QString buttonTimeoutTooltip("Timeout user");
words.push_back(Word(Resources::buttonBan(), Word::ButtonBan, QString(),
buttonBanTooltip,
Link(Link::UserBan, ircMessage.account())));
words.push_back(Word(Resources::buttonTimeout(), Word::ButtonTimeout,
QString(), buttonTimeoutTooltip,
Link(Link::UserTimeout, ircMessage.account())));
// badges // badges
iterator = tags.find("badges"); iterator = tags.find("badges");
@ -239,10 +250,8 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
for (QString split : splits) { for (QString split : splits) {
// twitch emote // twitch emote
if (currentTwitchEmote == twitchEmotes.end()) if (currentTwitchEmote != twitchEmotes.end() &&
break; currentTwitchEmote->first == i) {
if (currentTwitchEmote->first == i) {
words.push_back(Word(currentTwitchEmote->second, words.push_back(Word(currentTwitchEmote->second,
Word::TwitchEmoteImage, Word::TwitchEmoteImage,
currentTwitchEmote->second->name(), currentTwitchEmote->second->name(),
@ -264,10 +273,10 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed; std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
Emojis::parseEmojis(parsed, split); Emojis::parseEmojis(parsed, split);
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) { for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
LazyLoadedImage *image = std::get<0>(tuple); LazyLoadedImage *image = std::get<0>(tuple);
if (image == NULL) {
if (image == NULL) { // is text
QString string = std::get<1>(tuple); QString string = std::get<1>(tuple);
// cheers // cheers
@ -344,7 +353,7 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
// bttv / ffz emotes // bttv / ffz emotes
LazyLoadedImage *bttvEmote; LazyLoadedImage *bttvEmote;
#pragma message WARN("xD ignored emotes") #pragma message WARN("ignored emotes")
if (Emotes::bttvEmotes().tryGet(string, bttvEmote) || if (Emotes::bttvEmotes().tryGet(string, bttvEmote) ||
channel.bttvChannelEmotes().tryGet(string, bttvEmote) || channel.bttvChannelEmotes().tryGet(string, bttvEmote) ||
Emotes::ffzEmotes().tryGet(string, bttvEmote) || Emotes::ffzEmotes().tryGet(string, bttvEmote) ||
@ -364,6 +373,13 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel,
words.push_back( words.push_back(
Word(string, Word::Text, textColor, string, QString(), Word(string, Word::Text, textColor, string, QString(),
link.isEmpty() ? Link() : Link(Link::Url, link))); link.isEmpty() ? Link() : Link(Link::Url, link)));
} else { // is emoji
static QString emojiTooltip("Emoji");
words.push_back(
Word(image, Word::EmojiImage, image->name(), emojiTooltip));
Word(image->name(), Word::EmojiText, textColor, image->name(),
emojiTooltip);
} }
} }
@ -390,12 +406,18 @@ Message::layout(int width, bool enableEmoteMargins)
bool redraw = width != m_currentLayoutWidth || m_relayoutRequested; bool redraw = width != m_currentLayoutWidth || m_relayoutRequested;
if (m_recalculateImages || m_recalculateText) { bool recalculateImages = m_emoteGeneration != Emotes::generation();
bool recalculateText = m_fontGeneration != Fonts::generation();
if (recalculateImages || recalculateText) {
m_emoteGeneration = Emotes::generation();
m_fontGeneration = Fonts::generation();
redraw = true; redraw = true;
for (auto &word : m_words) { for (auto &word : m_words) {
if (word.isImage()) { if (word.isImage()) {
if (m_recalculateImages) { if (recalculateImages) {
auto &image = word.getImage(); auto &image = word.getImage();
qreal w = image.width(); qreal w = image.width();
@ -413,16 +435,13 @@ Message::layout(int width, bool enableEmoteMargins)
} }
} }
} else { } else {
if (m_recalculateText) { if (recalculateText) {
QFontMetrics &metrics = word.getFontMetrics(); QFontMetrics &metrics = word.getFontMetrics();
word.setSize(metrics.width(word.getText()), word.setSize(metrics.width(word.getText()),
metrics.height()); metrics.height());
} }
} }
} }
m_recalculateImages = false;
m_recalculateText = false;
} }
if (!redraw) { if (!redraw) {
@ -434,20 +453,17 @@ Message::layout(int width, bool enableEmoteMargins)
int right = width - MARGIN_RIGHT - MARGIN_LEFT; int right = width - MARGIN_RIGHT - MARGIN_LEFT;
std::list<WordPart> *parts = new std::list<WordPart>(); std::vector<WordPart> *parts = new std::vector<WordPart>();
auto lineStart = parts->begin(); int lineStart = 0;
int lineHeight = 0; int lineHeight = 0;
bool first = true;
auto alignParts = [&lineStart, &lineHeight, &parts, this] { auto alignParts = [&lineStart, &lineHeight, &parts, this] {
for (auto it2 = lineStart; true; it2++) { for (int i = lineStart; i < parts->size(); i++) {
WordPart &wordPart2 = *it2; WordPart &wordPart2 = parts->at(i);
wordPart2.setY(wordPart2.y() + lineHeight); wordPart2.setY(wordPart2.y() + lineHeight);
if (it2 == parts->end()) {
break;
}
} }
}; };
@ -471,24 +487,70 @@ Message::layout(int width, bool enableEmoteMargins)
} }
} }
// word wrapping
if (word.isText() && word.width() + MARGIN_LEFT > right) {
alignParts();
y += lineHeight;
const QString &text = word.getText();
int start = 0;
QFontMetrics &metrics = word.getFontMetrics();
int width = 0;
std::vector<short> &charWidths = word.characterWidthCache();
if (charWidths.size() == 0) {
charWidths.reserve(text.length());
for (int i = 0; i < text.length(); i++) {
charWidths.push_back(metrics.charWidth(text, i));
}
}
for (int i = 2; i <= text.length(); i++) {
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
QString mid = text.mid(start, i - start - 1);
parts->push_back(WordPart(word, MARGIN_LEFT, y, width,
word.height(), mid, mid));
y += metrics.height();
start = i - 1;
width = 0;
}
}
QString mid(text.mid(start));
width = metrics.width(mid);
parts->push_back(WordPart(word, MARGIN_LEFT, y - word.height(),
width, word.height(), mid, mid));
x = width + MARGIN_LEFT + spaceWidth;
lineHeight = word.height();
lineStart = parts->size() - 1;
first = false;
}
// fits in the line // fits in the line
if (x + word.width() + xOffset <= right) { else if (first || x + word.width() + xOffset <= right) {
parts->push_back( parts->push_back(
WordPart(word, x, y - word.height(), word.copyText())); WordPart(word, x, y - word.height(), word.copyText()));
x += word.width() + xOffset; x += word.width() + xOffset;
if (word.hasTrailingSpace()) {
x += spaceWidth; x += spaceWidth;
}
lineHeight = std::max(word.height(), lineHeight); lineHeight = std::max(word.height(), lineHeight);
}
// else if (word.isText() && word.getText().length() first = false;
// > 2) }
// {
// }
// doesn't fit in the line // doesn't fit in the line
else { else {
@ -499,17 +561,14 @@ Message::layout(int width, bool enableEmoteMargins)
parts->push_back(WordPart(word, MARGIN_LEFT, y - word.height(), parts->push_back(WordPart(word, MARGIN_LEFT, y - word.height(),
word.copyText())); word.copyText()));
lineStart = parts->end(); lineStart = parts->size() - 1;
lineHeight = word.height(); lineHeight = word.height();
x = word.width(); x = word.width() + MARGIN_LEFT;
if (word.hasTrailingSpace()) {
x += spaceWidth; x += spaceWidth;
} }
} }
}
alignParts(); alignParts();

View file

@ -60,7 +60,7 @@ public:
return m_words; return m_words;
} }
const std::list<WordPart> const std::vector<WordPart>
wordParts() const wordParts() const
{ {
return *m_wordParts; return *m_wordParts;
@ -91,16 +91,6 @@ public:
{ {
m_relayoutRequested = true; m_relayoutRequested = true;
} }
void
requestTextRecalculation()
{
m_recalculateText = true;
}
void
requestImageRecalculation()
{
m_recalculateImages = true;
}
private: private:
static LazyLoadedImage *badgeStaff; static LazyLoadedImage *badgeStaff;
@ -126,12 +116,12 @@ private:
int m_height = 0; int m_height = 0;
std::vector<Word> m_words; std::vector<Word> m_words;
std::list<WordPart> *m_wordParts; std::vector<WordPart> *m_wordParts;
long m_currentLayoutWidth = -1; long m_currentLayoutWidth = -1;
bool m_relayoutRequested = true; bool m_relayoutRequested = true;
bool m_recalculateText = true; int m_fontGeneration = -1;
bool m_recalculateImages = true; int m_emoteGeneration = -1;
static QString matchLink(const QString &string); static QString matchLink(const QString &string);

View file

@ -1,49 +1,27 @@
#include "notebooktab.h" #include "notebooktab.h"
#include <QPainter>
#include "colorscheme.h" #include "colorscheme.h"
#include "notebook.h" #include "notebook.h"
#include <QPainter>
NotebookTab::NotebookTab(Notebook *notebook) NotebookTab::NotebookTab(Notebook *notebook)
: QWidget(notebook) : QWidget(notebook)
, m_notebook(notebook)
, m_text("<no title>")
, m_selected(false)
, m_mouseOver(false)
, m_mouseDown(false)
, m_highlightStyle(HighlightNone)
{ {
this->notebook = notebook;
text = "<no title>";
calcSize(); calcSize();
setAcceptDrops(true); setAcceptDrops(true);
} }
int
NotebookTab::getHighlightStyle()
{
return highlightStyle;
}
void
NotebookTab::setHighlightStyle(int style)
{
highlightStyle = style;
repaint();
}
void
NotebookTab::setSelected(bool value)
{
selected = value;
repaint();
}
bool
NotebookTab::getSelected()
{
return selected;
}
void void
NotebookTab::calcSize() NotebookTab::calcSize()
{ {
resize(fontMetrics().width(text) + 8, 24); resize(fontMetrics().width(m_text) + 8, 24);
} }
void void
@ -55,16 +33,16 @@ NotebookTab::paintEvent(QPaintEvent *)
auto colorScheme = ColorScheme::instance(); auto colorScheme = ColorScheme::instance();
if (selected) { if (m_selected) {
painter.fillRect(rect(), colorScheme.TabSelectedBackground); painter.fillRect(rect(), colorScheme.TabSelectedBackground);
fg = colorScheme.TabSelectedText; fg = colorScheme.TabSelectedText;
} else if (mouseOver) { } else if (m_mouseOver) {
painter.fillRect(rect(), colorScheme.TabHoverBackground); painter.fillRect(rect(), colorScheme.TabHoverBackground);
fg = colorScheme.TabHoverText; fg = colorScheme.TabHoverText;
} else if (highlightStyle == HighlightHighlighted) { } else if (m_highlightStyle == HighlightHighlighted) {
painter.fillRect(rect(), colorScheme.TabHighlightedBackground); painter.fillRect(rect(), colorScheme.TabHighlightedBackground);
fg = colorScheme.TabHighlightedText; fg = colorScheme.TabHighlightedText;
} else if (highlightStyle == HighlightNewMessage) { } else if (m_highlightStyle == HighlightNewMessage) {
painter.fillRect(rect(), colorScheme.TabNewMessageBackground); painter.fillRect(rect(), colorScheme.TabNewMessageBackground);
fg = colorScheme.TabHighlightedText; fg = colorScheme.TabHighlightedText;
} else { } else {
@ -73,23 +51,23 @@ NotebookTab::paintEvent(QPaintEvent *)
} }
painter.setPen(fg); painter.setPen(fg);
painter.drawText(4, (height() + fontMetrics().height()) / 2, text); painter.drawText(4, (height() + fontMetrics().height()) / 2, m_text);
} }
void void
NotebookTab::mousePressEvent(QMouseEvent *) NotebookTab::mousePressEvent(QMouseEvent *)
{ {
mouseDown = true; m_mouseDown = true;
repaint(); repaint();
notebook->select(page); m_notebook->select(page);
} }
void void
NotebookTab::mouseReleaseEvent(QMouseEvent *) NotebookTab::mouseReleaseEvent(QMouseEvent *)
{ {
mouseDown = false; m_mouseDown = false;
repaint(); repaint();
} }
@ -97,7 +75,7 @@ NotebookTab::mouseReleaseEvent(QMouseEvent *)
void void
NotebookTab::enterEvent(QEvent *) NotebookTab::enterEvent(QEvent *)
{ {
mouseOver = true; m_mouseOver = true;
repaint(); repaint();
} }
@ -105,7 +83,7 @@ NotebookTab::enterEvent(QEvent *)
void void
NotebookTab::leaveEvent(QEvent *) NotebookTab::leaveEvent(QEvent *)
{ {
mouseOver = false; m_mouseOver = false;
repaint(); repaint();
} }
@ -113,5 +91,5 @@ NotebookTab::leaveEvent(QEvent *)
void void
NotebookTab::dragEnterEvent(QDragEnterEvent *event) NotebookTab::dragEnterEvent(QDragEnterEvent *event)
{ {
notebook->select(page); m_notebook->select(page);
} }

View file

@ -1,7 +1,7 @@
#ifndef NOTEBOOKTAB_H #ifndef NOTEBOOKTAB_H
#define NOTEBOOKTAB_H #define NOTEBOOKTAB_H
#include "QWidget" #include <QWidget>
class Notebook; class Notebook;
class NotebookPage; class NotebookPage;
@ -11,22 +11,55 @@ class NotebookTab : public QWidget
Q_OBJECT Q_OBJECT
public: public:
NotebookTab(Notebook *notebook); enum HighlightStyle {
HighlightNone,
HighlightHighlighted,
HighlightNewMessage
};
NotebookTab(Notebook *m_notebook);
void calcSize(); void calcSize();
NotebookPage *page; NotebookPage *page;
QString text;
bool getSelected(); const QString &
void setSelected(bool value); text() const
{
return m_text;
}
int getHighlightStyle(); void
void setHighlightStyle(int style); setText(const QString &text)
{
m_text = text;
}
static const int HighlightNone = 0; bool
static const int HighlightHighlighted = 1; selected()
static const int HighlightNewMessage = 2; {
return m_selected;
}
void
setSelected(bool value)
{
m_selected = value;
repaint();
}
HighlightStyle
highlightStyle() const
{
return m_highlightStyle;
}
void
setHighlightStyle(HighlightStyle style)
{
m_highlightStyle = style;
repaint();
}
protected: protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
@ -39,12 +72,14 @@ protected:
void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
private: private:
Notebook *notebook; Notebook *m_notebook;
bool selected = false;
bool mouseOver = false; QString m_text;
bool mouseDown = false;
int highlightStyle; bool m_selected;
bool m_mouseOver;
bool m_mouseDown;
HighlightStyle m_highlightStyle;
}; };
#endif // NOTEBOOKTAB_H #endif // NOTEBOOKTAB_H

View file

@ -16,6 +16,9 @@ LazyLoadedImage *Resources::m_cheerBadge1000;
LazyLoadedImage *Resources::m_cheerBadge100; LazyLoadedImage *Resources::m_cheerBadge100;
LazyLoadedImage *Resources::m_cheerBadge1; LazyLoadedImage *Resources::m_cheerBadge1;
LazyLoadedImage *Resources::m_buttonBan;
LazyLoadedImage *Resources::m_buttonTimeout;
Resources::Resources() Resources::Resources()
{ {
} }
@ -37,10 +40,17 @@ Resources::load()
new LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png")); new LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png"));
// cheer badges // cheer badges
m_cheerBadge100000 = new LazyLoadedImage(new QPixmap(":/cheer100000")); m_cheerBadge100000 =
m_cheerBadge10000 = new LazyLoadedImage(new QPixmap(":/cheer10000")); new LazyLoadedImage(new QPixmap(":/images/cheer100000"));
m_cheerBadge5000 = new LazyLoadedImage(new QPixmap(":/cheer5000")); m_cheerBadge10000 = new LazyLoadedImage(new QPixmap(":/images/cheer10000"));
m_cheerBadge1000 = new LazyLoadedImage(new QPixmap(":/cheer1000")); m_cheerBadge5000 = new LazyLoadedImage(new QPixmap(":/images/cheer5000"));
m_cheerBadge100 = new LazyLoadedImage(new QPixmap(":/cheer100")); m_cheerBadge1000 = new LazyLoadedImage(new QPixmap(":/images/cheer1000"));
m_cheerBadge1 = new LazyLoadedImage(new QPixmap(":/cheer1")); m_cheerBadge100 = new LazyLoadedImage(new QPixmap(":/images/cheer100"));
m_cheerBadge1 = new LazyLoadedImage(new QPixmap(":/images/cheer1"));
// button
m_buttonBan =
new LazyLoadedImage(new QPixmap(":/images/button_ban.png"), 0.25);
m_buttonTimeout =
new LazyLoadedImage(new QPixmap(":/images/button_timeout.png"), 0.25);
} }

View file

@ -88,6 +88,18 @@ public:
return m_cheerBadge1; return m_cheerBadge1;
} }
static LazyLoadedImage *
buttonBan()
{
return m_buttonBan;
}
static LazyLoadedImage *
buttonTimeout()
{
return m_buttonTimeout;
}
private: private:
Resources(); Resources();
@ -105,6 +117,9 @@ private:
static LazyLoadedImage *m_cheerBadge1000; static LazyLoadedImage *m_cheerBadge1000;
static LazyLoadedImage *m_cheerBadge100; static LazyLoadedImage *m_cheerBadge100;
static LazyLoadedImage *m_cheerBadge1; static LazyLoadedImage *m_cheerBadge1;
static LazyLoadedImage *m_buttonBan;
static LazyLoadedImage *m_buttonTimeout;
}; };
#endif // RESOURCES_H #endif // RESOURCES_H

View file

@ -25,5 +25,7 @@
<file>images/staff_bg.png</file> <file>images/staff_bg.png</file>
<file>images/turbo_bg.png</file> <file>images/turbo_bg.png</file>
<file>emojidata.txt</file> <file>emojidata.txt</file>
<file>images/button_ban.png</file>
<file>images/button_timeout.png</file>
</qresource> </qresource>
</RCC> </RCC>

10
windows.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "windows.h"
QMutex Windows::m_windowMutex;
MainWindow *Windows::m_mainWindow(NULL);
void
Windows::invalidateEmotes()
{
}

35
windows.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef WINDOWS_H
#define WINDOWS_H
#include "mainwindow.h"
#include <QMutex>
class Windows
{
public:
static void invalidateEmotes();
static MainWindow &
mainWindow()
{
m_windowMutex.lock();
if (m_mainWindow == NULL) {
m_mainWindow = new MainWindow();
}
m_windowMutex.unlock();
return *m_mainWindow;
}
private:
Windows()
{
}
static QMutex m_windowMutex;
static MainWindow *m_mainWindow;
};
#endif // WINDOWS_H

View file

@ -5,12 +5,13 @@ Word::Word(LazyLoadedImage *image, Type type, const QString &copytext,
const QString &tooltip, const Link &link) const QString &tooltip, const Link &link)
: m_image(image) : m_image(image)
, m_text() , m_text()
, m_color()
, m_isImage(true) , m_isImage(true)
, m_type(type) , m_type(type)
, m_copyText(copytext) , m_copyText(copytext)
, m_tooltip(tooltip) , m_tooltip(tooltip)
, m_color()
, m_link(link) , m_link(link)
, m_characterWidthCache()
{ {
image->width(); // professional segfault test image->width(); // professional segfault test
} }
@ -20,11 +21,12 @@ Word::Word(const QString &text, Type type, const QColor &color,
const QString &copytext, const QString &tooltip, const Link &link) const QString &copytext, const QString &tooltip, const Link &link)
: m_image(NULL) : m_image(NULL)
, m_text(text) , m_text(text)
, m_color(color)
, m_isImage(false) , m_isImage(false)
, m_type(type) , m_type(type)
, m_copyText(copytext) , m_copyText(copytext)
, m_tooltip(tooltip) , m_tooltip(tooltip)
, m_color(color)
, m_link(link) , m_link(link)
, m_characterWidthCache()
{ {
} }

17
word.h
View file

@ -49,9 +49,16 @@ public:
Username = 0x800000, Username = 0x800000,
BitsAmount = 0x1000000, BitsAmount = 0x1000000,
ButtonBan = 0x2000000,
ButtonTimeout = 0x4000000,
EmojiImage = 0x8000000,
EmojiText = 0x10000000,
Default = TimestampNoSeconds | Badges | Username | Bits | Default = TimestampNoSeconds | Badges | Username | Bits |
FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage | FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage |
TwitchEmoteImage | BitsAmount | Text TwitchEmoteImage | BitsAmount | Text | ButtonBan |
ButtonTimeout
}; };
explicit Word(LazyLoadedImage *m_image, Type type, const QString &copytext, explicit Word(LazyLoadedImage *m_image, Type type, const QString &copytext,
@ -170,6 +177,12 @@ public:
m_yOffset = std::max(0, yOffset); m_yOffset = std::max(0, yOffset);
} }
std::vector<short> &
characterWidthCache()
{
return m_characterWidthCache;
}
private: private:
LazyLoadedImage *m_image; LazyLoadedImage *m_image;
QString m_text; QString m_text;
@ -188,6 +201,8 @@ private:
bool m_hasTrailingSpace; bool m_hasTrailingSpace;
Fonts::Type m_font = Fonts::Medium; Fonts::Type m_font = Fonts::Medium;
Link m_link; Link m_link;
std::vector<short> m_characterWidthCache;
}; };
#endif // WORD_H #endif // WORD_H

View file

@ -10,11 +10,20 @@ WordPart::WordPart(Word &word, int x, int y, const QString &copyText,
, m_width(word.width()) , m_width(word.width())
, m_height(word.height()) , m_height(word.height())
, m_trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) , m_trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
, m_text(word.isText() ? m_word.getText() : QString())
{ {
} }
const QString & WordPart::WordPart(Word &word, int x, int y, int width, int height,
WordPart::getText() const const QString &copyText, const QString &customText,
bool allowTrailingSpace)
: m_word(word)
, m_copyText(copyText)
, m_x(x)
, m_y(y)
, m_width(width)
, m_height(height)
, m_trailingSpace(word.hasTrailingSpace() & allowTrailingSpace)
, m_text(customText)
{ {
return m_word.getText();
} }

View file

@ -12,6 +12,10 @@ public:
WordPart(Word &word, int x, int y, const QString &copyText, WordPart(Word &word, int x, int y, const QString &copyText,
bool allowTrailingSpace = true); bool allowTrailingSpace = true);
WordPart(Word &word, int x, int y, int width, int height,
const QString &copyText, const QString &customText,
bool allowTrailingSpace = true);
const Word & const Word &
word() const word() const
{ {
@ -85,12 +89,17 @@ public:
return m_trailingSpace; return m_trailingSpace;
} }
const QString &getText() const; const QString &
text() const
{
return m_text;
}
private: private:
Word &m_word; Word &m_word;
QString m_copyText; QString m_copyText;
QString m_text;
int m_x; int m_x;
int m_y; int m_y;