diff --git a/chatterino.pro b/chatterino.pro index 535ef437e..9331902d2 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -61,7 +61,10 @@ SOURCES += main.cpp\ appsettings.cpp \ emojis.cpp \ wordpart.cpp \ - resources.cpp + resources.cpp \ + windows.cpp \ + chatwidgetheaderbutton.cpp \ + chatwidgetheaderbuttonlabel.cpp HEADERS += mainwindow.h \ chatwidget.h \ @@ -96,7 +99,10 @@ HEADERS += mainwindow.h \ emojis.h \ wordpart.h \ common.h \ - resources.h + resources.h \ + windows.h \ + chatwidgetheaderbutton.h \ + chatwidgetheaderbuttonlabel.h PRECOMPILED_HEADER = diff --git a/chatwidgetheader.cpp b/chatwidgetheader.cpp index 7abf92271..d2cd71014 100644 --- a/chatwidgetheader.cpp +++ b/chatwidgetheader.cpp @@ -1,16 +1,82 @@ #include "chatwidgetheader.h" -#include "QByteArray" -#include "QDrag" -#include "QMimeData" -#include "QPainter" #include "chatwidget.h" #include "colorscheme.h" #include "notebookpage.h" +#include +#include +#include +#include + ChatWidgetHeader::ChatWidgetHeader() : QWidget() + , m_dragStart() + , m_dragging(false) + , leftLabel() + , middleLabel() + , rightLabel() + , leftMenu(this) + , rightMenu(this) { 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( + ""); + + 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 @@ -26,17 +92,17 @@ ChatWidgetHeader::paintEvent(QPaintEvent *) void ChatWidgetHeader::mousePressEvent(QMouseEvent *event) { - dragging = true; + m_dragging = true; - dragStart = event->pos(); + m_dragStart = event->pos(); } void ChatWidgetHeader::mouseMoveEvent(QMouseEvent *event) { - if (dragging) { - if (std::abs(dragStart.x() - event->pos().x()) > 12 || - std::abs(dragStart.y() - event->pos().y()) > 12) { + if (m_dragging) { + if (std::abs(m_dragStart.x() - event->pos().x()) > 12 || + std::abs(m_dragStart.y() - event->pos().y()) > 12) { auto chatWidget = getChatWidget(); auto page = static_cast(chatWidget->parentWidget()); @@ -72,3 +138,56 @@ ChatWidgetHeader::getChatWidget() { return static_cast(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() +{ +} diff --git a/chatwidgetheader.h b/chatwidgetheader.h index 05c196150..3bedf36c0 100644 --- a/chatwidgetheader.h +++ b/chatwidgetheader.h @@ -1,10 +1,16 @@ #ifndef CHATWIDGETHEADER_H #define CHATWIDGETHEADER_H -#include "QMouseEvent" -#include "QPaintEvent" -#include "QPoint" -#include "QWidget" +#include "chatwidgetheaderbutton.h" + +#include +#include +#include +#include +#include +#include +#include +#include class ChatWidget; @@ -15,6 +21,7 @@ class ChatWidgetHeader : public QWidget public: ChatWidgetHeader(); ChatWidget *getChatWidget(); + void updateColors(); protected: void paintEvent(QPaintEvent *); @@ -22,8 +29,31 @@ protected: void mouseMoveEvent(QMouseEvent *event); private: - QPoint dragStart; - bool dragging = false; + QPoint m_dragStart; + 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 diff --git a/chatwidgetheaderbutton.cpp b/chatwidgetheaderbutton.cpp new file mode 100644 index 000000000..e6ce5f947 --- /dev/null +++ b/chatwidgetheaderbutton.cpp @@ -0,0 +1,99 @@ +#include "chatwidgetheaderbutton.h" +#include "colorscheme.h" + +#include +#include + +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(); +} diff --git a/chatwidgetheaderbutton.h b/chatwidgetheaderbutton.h new file mode 100644 index 000000000..759f709f3 --- /dev/null +++ b/chatwidgetheaderbutton.h @@ -0,0 +1,47 @@ +#ifndef CHATWIDGETHEADERBUTTON_H +#define CHATWIDGETHEADERBUTTON_H + +#include "chatwidgetheaderbuttonlabel.h" + +#include +#include +#include +#include + +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 diff --git a/chatwidgetheaderbuttonlabel.cpp b/chatwidgetheaderbuttonlabel.cpp new file mode 100644 index 000000000..d43bb40b7 --- /dev/null +++ b/chatwidgetheaderbuttonlabel.cpp @@ -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(); + } +} diff --git a/chatwidgetheaderbuttonlabel.h b/chatwidgetheaderbuttonlabel.h new file mode 100644 index 000000000..c053ea44d --- /dev/null +++ b/chatwidgetheaderbuttonlabel.h @@ -0,0 +1,23 @@ +#ifndef CHATWIDGETHEADERBUTTONLABEL_H +#define CHATWIDGETHEADERBUTTONLABEL_H + +#include +#include + +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 diff --git a/chatwidgetview.cpp b/chatwidgetview.cpp index 3405cf32d..3a65a5113 100644 --- a/chatwidgetview.cpp +++ b/chatwidgetview.cpp @@ -15,7 +15,7 @@ ChatWidgetView::ChatWidgetView() scroll->scrollTo(QPointF(0, 100)); - m_channel = Channel::getChannel("ian678"); + m_channel = Channel::getChannel("fourtf"); } void @@ -42,6 +42,8 @@ ChatWidgetView::paintEvent(QPaintEvent *) { QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + auto c = channel(); if (c == NULL) @@ -49,7 +51,7 @@ ChatWidgetView::paintEvent(QPaintEvent *) auto messages = c->getMessagesClone(); - int y = 32; + int y = 0; for (std::shared_ptr const &message : messages) { for (WordPart const &wordPart : message.get()->wordParts()) { @@ -77,8 +79,7 @@ ChatWidgetView::paintEvent(QPaintEvent *) painter.drawText( QRectF(wordPart.x(), wordPart.y() + y, 10000, 10000), - wordPart.getText(), - QTextOption(Qt::AlignLeft | Qt::AlignTop)); + wordPart.text(), QTextOption(Qt::AlignLeft | Qt::AlignTop)); } } diff --git a/emotes.cpp b/emotes.cpp index 17d75cebd..5509592cd 100644 --- a/emotes.cpp +++ b/emotes.cpp @@ -13,6 +13,8 @@ ConcurrentMap Emotes::m_ffzChannelEmoteFromCaches; ConcurrentMap Emotes::m_twitchEmoteFromCache; ConcurrentMap Emotes::m_miscImageFromCache; +int Emotes::m_generation = 0; + Emotes::Emotes() { } diff --git a/emotes.h b/emotes.h index 87d911da8..71564c8af 100644 --- a/emotes.h +++ b/emotes.h @@ -66,6 +66,18 @@ public: static LazyLoadedImage *getTwitchEmoteById(const QString &name, long int id); + static int + generation() + { + return m_generation; + } + + static void + incGeneration() + { + m_generation++; + } + private: Emotes(); @@ -83,6 +95,8 @@ private: static ConcurrentMap m_miscImageFromCache; static QString getTwitchEmoteLink(long id, qreal &scale); + + static int m_generation; }; #endif // EMOTES_H diff --git a/fonts.cpp b/fonts.cpp index 3f49b9132..c85f8dc49 100644 --- a/fonts.cpp +++ b/fonts.cpp @@ -5,7 +5,7 @@ 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, 12); +QFont *Fonts::small = new QFont(DEFAULT_FONT, 10); QFont *Fonts::large = new QFont(DEFAULT_FONT, 16); 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::metricsVeryLarge = new QFontMetrics(*veryLarge); +int Fonts::m_generation = 0; + Fonts::Fonts() { } diff --git a/fonts.h b/fonts.h index 37c4a5a1c..737fb203d 100644 --- a/fonts.h +++ b/fonts.h @@ -19,6 +19,18 @@ public: static QFont &getFont(Type type); static QFontMetrics &getFontMetrics(Type type); + static int + generation() + { + return m_generation; + } + + static void + incGeneration() + { + m_generation++; + } + private: Fonts(); @@ -35,6 +47,8 @@ private: static QFontMetrics *metricsSmall; static QFontMetrics *metricsLarge; static QFontMetrics *metricsVeryLarge; + + static int m_generation; }; #endif // FONTS_H diff --git a/images/button_ban.png b/images/button_ban.png new file mode 100644 index 000000000..a983d1c87 Binary files /dev/null and b/images/button_ban.png differ diff --git a/images/button_timeout.png b/images/button_timeout.png new file mode 100644 index 000000000..a087ed123 Binary files /dev/null and b/images/button_timeout.png differ diff --git a/images/tool_moreCollapser_off16.png b/images/tool_moreCollapser_off16.png index 7c74540d8..717fc75e2 100644 Binary files a/images/tool_moreCollapser_off16.png and b/images/tool_moreCollapser_off16.png differ diff --git a/ircmanager.cpp b/ircmanager.cpp index 44a2edeb9..f11da49bc 100644 --- a/ircmanager.cpp +++ b/ircmanager.cpp @@ -120,8 +120,6 @@ IrcManager::beginConnecting() c->setNickName("justinfan123"); c->setRealName("justinfan123"); 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/tags")); diff --git a/lazyloadedimage.cpp b/lazyloadedimage.cpp index 83da3b5ff..c6d8c3961 100644 --- a/lazyloadedimage.cpp +++ b/lazyloadedimage.cpp @@ -1,6 +1,7 @@ #include "lazyloadedimage.h" #include "asyncexec.h" +#include "emotes.h" #include "ircmanager.h" #include @@ -56,6 +57,7 @@ LazyLoadedImage::loadImage() } m_pixmap = pixmap; + Emotes::incGeneration(); }); //})); } diff --git a/link.h b/link.h index 0109eabfc..6539c8249 100644 --- a/link.h +++ b/link.h @@ -11,6 +11,8 @@ public: Url, CloseCurrentSplit, UserInfo, + UserTimeout, + UserBan, InsertText, ShowMessage, }; diff --git a/main.cpp b/main.cpp index 082c6c8a0..1d0baf451 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include "ircmanager.h" #include "mainwindow.h" #include "resources.h" +#include "windows.h" #include #include @@ -17,7 +18,7 @@ main(int argc, char *argv[]) ColorScheme::instance().setColors(0, -0.8); - MainWindow w; + MainWindow &w = Windows::mainWindow(); w.show(); Channel::addChannel("ian678"); diff --git a/message.cpp b/message.cpp index d868e0c5b..b76433999 100644 --- a/message.cpp +++ b/message.cpp @@ -23,14 +23,14 @@ QRegularExpression *Message::cheerRegex = new QRegularExpression("cheer[1-9][0-9]*"); Message::Message(const QString &text) - : m_wordParts(new std::list()) + : m_wordParts(new std::vector()) { } Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, bool enablePingSound, bool isReceivedWhisper, bool isSentWhisper, bool includeChannel) - : m_wordParts(new std::list()) + : m_wordParts(new std::vector()) { m_parseTime = std::chrono::system_clock::now(); @@ -70,6 +70,17 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, ColorScheme::instance().SystemMessageColor, 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 iterator = tags.find("badges"); @@ -239,10 +250,8 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, for (QString split : splits) { // twitch emote - if (currentTwitchEmote == twitchEmotes.end()) - break; - - if (currentTwitchEmote->first == i) { + if (currentTwitchEmote != twitchEmotes.end() && + currentTwitchEmote->first == i) { words.push_back(Word(currentTwitchEmote->second, Word::TwitchEmoteImage, currentTwitchEmote->second->name(), @@ -264,10 +273,10 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, std::vector> parsed; Emojis::parseEmojis(parsed, split); - for (const std::tuple &tuple : parsed) { LazyLoadedImage *image = std::get<0>(tuple); - if (image == NULL) { + + if (image == NULL) { // is text QString string = std::get<1>(tuple); // cheers @@ -344,7 +353,7 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, // bttv / ffz emotes LazyLoadedImage *bttvEmote; -#pragma message WARN("xD ignored emotes") +#pragma message WARN("ignored emotes") if (Emotes::bttvEmotes().tryGet(string, bttvEmote) || channel.bttvChannelEmotes().tryGet(string, bttvEmote) || Emotes::ffzEmotes().tryGet(string, bttvEmote) || @@ -364,6 +373,13 @@ Message::Message(const IrcPrivateMessage &ircMessage, const Channel &channel, 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->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; - 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; for (auto &word : m_words) { if (word.isImage()) { - if (m_recalculateImages) { + if (recalculateImages) { auto &image = word.getImage(); qreal w = image.width(); @@ -413,16 +435,13 @@ Message::layout(int width, bool enableEmoteMargins) } } } else { - if (m_recalculateText) { + if (recalculateText) { QFontMetrics &metrics = word.getFontMetrics(); word.setSize(metrics.width(word.getText()), metrics.height()); } } } - - m_recalculateImages = false; - m_recalculateText = false; } if (!redraw) { @@ -434,20 +453,17 @@ Message::layout(int width, bool enableEmoteMargins) int right = width - MARGIN_RIGHT - MARGIN_LEFT; - std::list *parts = new std::list(); + std::vector *parts = new std::vector(); - auto lineStart = parts->begin(); + int lineStart = 0; int lineHeight = 0; + bool first = true; auto alignParts = [&lineStart, &lineHeight, &parts, this] { - for (auto it2 = lineStart; true; it2++) { - WordPart &wordPart2 = *it2; + for (int i = lineStart; i < parts->size(); i++) { + WordPart &wordPart2 = parts->at(i); 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 &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 - if (x + word.width() + xOffset <= right) { + else if (first || x + word.width() + xOffset <= right) { parts->push_back( WordPart(word, x, y - word.height(), word.copyText())); x += word.width() + xOffset; - - if (word.hasTrailingSpace()) { - x += spaceWidth; - } + x += spaceWidth; lineHeight = std::max(word.height(), lineHeight); - } - // else if (word.isText() && word.getText().length() - // > 2) - // { - // } + first = false; + } // doesn't fit in the line else { @@ -499,15 +561,12 @@ Message::layout(int width, bool enableEmoteMargins) parts->push_back(WordPart(word, MARGIN_LEFT, y - word.height(), word.copyText())); - lineStart = parts->end(); + lineStart = parts->size() - 1; lineHeight = word.height(); - x = word.width(); - - if (word.hasTrailingSpace()) { - x += spaceWidth; - } + x = word.width() + MARGIN_LEFT; + x += spaceWidth; } } diff --git a/message.h b/message.h index 5d76195fd..f9feb45f5 100644 --- a/message.h +++ b/message.h @@ -60,7 +60,7 @@ public: return m_words; } - const std::list + const std::vector wordParts() const { return *m_wordParts; @@ -91,16 +91,6 @@ public: { m_relayoutRequested = true; } - void - requestTextRecalculation() - { - m_recalculateText = true; - } - void - requestImageRecalculation() - { - m_recalculateImages = true; - } private: static LazyLoadedImage *badgeStaff; @@ -126,12 +116,12 @@ private: int m_height = 0; std::vector m_words; - std::list *m_wordParts; + std::vector *m_wordParts; long m_currentLayoutWidth = -1; bool m_relayoutRequested = true; - bool m_recalculateText = true; - bool m_recalculateImages = true; + int m_fontGeneration = -1; + int m_emoteGeneration = -1; static QString matchLink(const QString &string); diff --git a/notebooktab.cpp b/notebooktab.cpp index e7504e074..d81150a7b 100644 --- a/notebooktab.cpp +++ b/notebooktab.cpp @@ -1,49 +1,27 @@ #include "notebooktab.h" -#include #include "colorscheme.h" #include "notebook.h" +#include + NotebookTab::NotebookTab(Notebook *notebook) : QWidget(notebook) + , m_notebook(notebook) + , m_text("") + , m_selected(false) + , m_mouseOver(false) + , m_mouseDown(false) + , m_highlightStyle(HighlightNone) { - this->notebook = notebook; - text = ""; - calcSize(); 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 NotebookTab::calcSize() { - resize(fontMetrics().width(text) + 8, 24); + resize(fontMetrics().width(m_text) + 8, 24); } void @@ -55,16 +33,16 @@ NotebookTab::paintEvent(QPaintEvent *) auto colorScheme = ColorScheme::instance(); - if (selected) { + if (m_selected) { painter.fillRect(rect(), colorScheme.TabSelectedBackground); fg = colorScheme.TabSelectedText; - } else if (mouseOver) { + } else if (m_mouseOver) { painter.fillRect(rect(), colorScheme.TabHoverBackground); fg = colorScheme.TabHoverText; - } else if (highlightStyle == HighlightHighlighted) { + } else if (m_highlightStyle == HighlightHighlighted) { painter.fillRect(rect(), colorScheme.TabHighlightedBackground); fg = colorScheme.TabHighlightedText; - } else if (highlightStyle == HighlightNewMessage) { + } else if (m_highlightStyle == HighlightNewMessage) { painter.fillRect(rect(), colorScheme.TabNewMessageBackground); fg = colorScheme.TabHighlightedText; } else { @@ -73,23 +51,23 @@ NotebookTab::paintEvent(QPaintEvent *) } painter.setPen(fg); - painter.drawText(4, (height() + fontMetrics().height()) / 2, text); + painter.drawText(4, (height() + fontMetrics().height()) / 2, m_text); } void NotebookTab::mousePressEvent(QMouseEvent *) { - mouseDown = true; + m_mouseDown = true; repaint(); - notebook->select(page); + m_notebook->select(page); } void NotebookTab::mouseReleaseEvent(QMouseEvent *) { - mouseDown = false; + m_mouseDown = false; repaint(); } @@ -97,7 +75,7 @@ NotebookTab::mouseReleaseEvent(QMouseEvent *) void NotebookTab::enterEvent(QEvent *) { - mouseOver = true; + m_mouseOver = true; repaint(); } @@ -105,7 +83,7 @@ NotebookTab::enterEvent(QEvent *) void NotebookTab::leaveEvent(QEvent *) { - mouseOver = false; + m_mouseOver = false; repaint(); } @@ -113,5 +91,5 @@ NotebookTab::leaveEvent(QEvent *) void NotebookTab::dragEnterEvent(QDragEnterEvent *event) { - notebook->select(page); + m_notebook->select(page); } diff --git a/notebooktab.h b/notebooktab.h index 607bc9864..98c8d7902 100644 --- a/notebooktab.h +++ b/notebooktab.h @@ -1,7 +1,7 @@ #ifndef NOTEBOOKTAB_H #define NOTEBOOKTAB_H -#include "QWidget" +#include class Notebook; class NotebookPage; @@ -11,22 +11,55 @@ class NotebookTab : public QWidget Q_OBJECT public: - NotebookTab(Notebook *notebook); + enum HighlightStyle { + HighlightNone, + HighlightHighlighted, + HighlightNewMessage + }; + + NotebookTab(Notebook *m_notebook); void calcSize(); NotebookPage *page; - QString text; - bool getSelected(); - void setSelected(bool value); + const QString & + text() const + { + return m_text; + } - int getHighlightStyle(); - void setHighlightStyle(int style); + void + setText(const QString &text) + { + m_text = text; + } - static const int HighlightNone = 0; - static const int HighlightHighlighted = 1; - static const int HighlightNewMessage = 2; + bool + selected() + { + 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: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; @@ -39,12 +72,14 @@ protected: void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; private: - Notebook *notebook; - bool selected = false; + Notebook *m_notebook; - bool mouseOver = false; - bool mouseDown = false; - int highlightStyle; + QString m_text; + + bool m_selected; + bool m_mouseOver; + bool m_mouseDown; + HighlightStyle m_highlightStyle; }; #endif // NOTEBOOKTAB_H diff --git a/resources.cpp b/resources.cpp index 30ac9b47e..b18dd1680 100644 --- a/resources.cpp +++ b/resources.cpp @@ -16,6 +16,9 @@ LazyLoadedImage *Resources::m_cheerBadge1000; LazyLoadedImage *Resources::m_cheerBadge100; LazyLoadedImage *Resources::m_cheerBadge1; +LazyLoadedImage *Resources::m_buttonBan; +LazyLoadedImage *Resources::m_buttonTimeout; + Resources::Resources() { } @@ -37,10 +40,17 @@ Resources::load() new LazyLoadedImage(new QPixmap(":/images/twitchprime_bg.png")); // cheer badges - m_cheerBadge100000 = new LazyLoadedImage(new QPixmap(":/cheer100000")); - m_cheerBadge10000 = new LazyLoadedImage(new QPixmap(":/cheer10000")); - m_cheerBadge5000 = new LazyLoadedImage(new QPixmap(":/cheer5000")); - m_cheerBadge1000 = new LazyLoadedImage(new QPixmap(":/cheer1000")); - m_cheerBadge100 = new LazyLoadedImage(new QPixmap(":/cheer100")); - m_cheerBadge1 = new LazyLoadedImage(new QPixmap(":/cheer1")); + m_cheerBadge100000 = + new LazyLoadedImage(new QPixmap(":/images/cheer100000")); + m_cheerBadge10000 = new LazyLoadedImage(new QPixmap(":/images/cheer10000")); + m_cheerBadge5000 = new LazyLoadedImage(new QPixmap(":/images/cheer5000")); + m_cheerBadge1000 = new LazyLoadedImage(new QPixmap(":/images/cheer1000")); + 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); } diff --git a/resources.h b/resources.h index 94ffbdde7..dbd32e8c3 100644 --- a/resources.h +++ b/resources.h @@ -88,6 +88,18 @@ public: return m_cheerBadge1; } + static LazyLoadedImage * + buttonBan() + { + return m_buttonBan; + } + + static LazyLoadedImage * + buttonTimeout() + { + return m_buttonTimeout; + } + private: Resources(); @@ -105,6 +117,9 @@ private: static LazyLoadedImage *m_cheerBadge1000; static LazyLoadedImage *m_cheerBadge100; static LazyLoadedImage *m_cheerBadge1; + + static LazyLoadedImage *m_buttonBan; + static LazyLoadedImage *m_buttonTimeout; }; #endif // RESOURCES_H diff --git a/resources.qrc b/resources.qrc index 774b93bb3..489d0d6d3 100644 --- a/resources.qrc +++ b/resources.qrc @@ -25,5 +25,7 @@ images/staff_bg.png images/turbo_bg.png emojidata.txt + images/button_ban.png + images/button_timeout.png diff --git a/windows.cpp b/windows.cpp new file mode 100644 index 000000000..b2b4ad7ae --- /dev/null +++ b/windows.cpp @@ -0,0 +1,10 @@ +#include "windows.h" + +QMutex Windows::m_windowMutex; + +MainWindow *Windows::m_mainWindow(NULL); + +void +Windows::invalidateEmotes() +{ +} diff --git a/windows.h b/windows.h new file mode 100644 index 000000000..0dd022262 --- /dev/null +++ b/windows.h @@ -0,0 +1,35 @@ +#ifndef WINDOWS_H +#define WINDOWS_H + +#include "mainwindow.h" + +#include + +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 diff --git a/word.cpp b/word.cpp index b39d6dcdb..f2c6c122e 100644 --- a/word.cpp +++ b/word.cpp @@ -5,12 +5,13 @@ Word::Word(LazyLoadedImage *image, Type type, const QString ©text, const QString &tooltip, const Link &link) : m_image(image) , m_text() + , m_color() , m_isImage(true) , m_type(type) , m_copyText(copytext) , m_tooltip(tooltip) - , m_color() , m_link(link) + , m_characterWidthCache() { image->width(); // professional segfault test } @@ -20,11 +21,12 @@ Word::Word(const QString &text, Type type, const QColor &color, const QString ©text, const QString &tooltip, const Link &link) : m_image(NULL) , m_text(text) + , m_color(color) , m_isImage(false) , m_type(type) , m_copyText(copytext) , m_tooltip(tooltip) - , m_color(color) , m_link(link) + , m_characterWidthCache() { } diff --git a/word.h b/word.h index 7a54a3990..bd2e94b1f 100644 --- a/word.h +++ b/word.h @@ -49,9 +49,16 @@ public: Username = 0x800000, BitsAmount = 0x1000000, + ButtonBan = 0x2000000, + ButtonTimeout = 0x4000000, + + EmojiImage = 0x8000000, + EmojiText = 0x10000000, + Default = TimestampNoSeconds | Badges | Username | Bits | FfzEmoteImage | BttvEmoteImage | BttvGifEmoteImage | - TwitchEmoteImage | BitsAmount | Text + TwitchEmoteImage | BitsAmount | Text | ButtonBan | + ButtonTimeout }; explicit Word(LazyLoadedImage *m_image, Type type, const QString ©text, @@ -170,6 +177,12 @@ public: m_yOffset = std::max(0, yOffset); } + std::vector & + characterWidthCache() + { + return m_characterWidthCache; + } + private: LazyLoadedImage *m_image; QString m_text; @@ -188,6 +201,8 @@ private: bool m_hasTrailingSpace; Fonts::Type m_font = Fonts::Medium; Link m_link; + + std::vector m_characterWidthCache; }; #endif // WORD_H diff --git a/wordpart.cpp b/wordpart.cpp index e759a475e..9317d7926 100644 --- a/wordpart.cpp +++ b/wordpart.cpp @@ -10,11 +10,20 @@ WordPart::WordPart(Word &word, int x, int y, const QString ©Text, , m_width(word.width()) , m_height(word.height()) , m_trailingSpace(word.hasTrailingSpace() & allowTrailingSpace) + , m_text(word.isText() ? m_word.getText() : QString()) { } -const QString & -WordPart::getText() const +WordPart::WordPart(Word &word, int x, int y, int width, int height, + const QString ©Text, 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(); } diff --git a/wordpart.h b/wordpart.h index 5da733fde..bea7ef5f6 100644 --- a/wordpart.h +++ b/wordpart.h @@ -12,6 +12,10 @@ public: WordPart(Word &word, int x, int y, const QString ©Text, bool allowTrailingSpace = true); + WordPart(Word &word, int x, int y, int width, int height, + const QString ©Text, const QString &customText, + bool allowTrailingSpace = true); + const Word & word() const { @@ -85,12 +89,17 @@ public: return m_trailingSpace; } - const QString &getText() const; + const QString & + text() const + { + return m_text; + } private: Word &m_word; QString m_copyText; + QString m_text; int m_x; int m_y;