diff --git a/CHANGELOG.md b/CHANGELOG.md index 72faba7e7..62c460398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - Minor: Migrated /untimeout to Helix API. (#4026) - Minor: Migrated /unban to Helix API. (#4026) - Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716) +- Bugfix: Fixed `Smooth scrolling on new messages` setting sometimes hiding messages. (#4028) - Bugfix: Fixed a crash that can occur when closing and quickly reopening a split, then running a command. (#3852) - Bugfix: Fixed a crash that can occur when changing channels. (#3799) - Bugfix: Fixed viewers list search not working when used before loading finishes. (#3774) @@ -71,8 +72,8 @@ - Bugfix: Fixed crash related to logging IRC channels (#3918) - Bugfix: Mentions of "You" in timeouts will link to your own user now instead of the user "You". (#3922) - Bugfix: Fixed emoji popup not being shown in IRC channels (#4021) +- Bugfix: Display sent IRC messages like received ones (#4027) - Bugfix: Fixed non-global FrankerFaceZ emotes from being loaded as global emotes. (#3921) -- Bugfix: Fixed `Smooth scrolling on new messages` setting sometimes hiding messages. (#4028) - Dev: Removed official support for QMake. (#3839, #3883) - Dev: Rewrote LimitedQueue (#3798) - Dev: Overhauled highlight system by moving all checks into a Controller allowing for easier tests. (#3399, #3801, #3835) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 18f2b9102..4eab00cf7 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -1,6 +1,7 @@ #include "MessageBuilder.hpp" #include "Application.hpp" +#include "common/IrcColors.hpp" #include "common/LinkParser.hpp" #include "controllers/accounts/AccountController.hpp" #include "messages/Image.hpp" @@ -16,6 +17,14 @@ #include +namespace { + +QRegularExpression IRC_COLOR_PARSE_REGEX( + "(\u0003(\\d{1,2})?(,(\\d{1,2}))?|\u000f)", + QRegularExpression::UseUnicodePropertiesOption); + +} // namespace + namespace chatterino { MessagePtr makeSystemMessage(const QString &text) @@ -579,6 +588,163 @@ void MessageBuilder::addLink(const QString &origLink, }); } +void MessageBuilder::addIrcMessageText(const QString &text) +{ + this->message().messageText = text; + + auto words = text.split(' '); + MessageColor defaultColorType = MessageColor::Text; + const auto &defaultColor = defaultColorType.getColor(*getApp()->themes); + QColor textColor = defaultColor; + int fg = -1; + int bg = -1; + + for (const auto &word : words) + { + if (word.isEmpty()) + { + continue; + } + + auto string = QString(word); + + // Actually just text + auto linkString = this->matchLink(string); + auto link = Link(); + + if (!linkString.isEmpty()) + { + this->addLink(string, linkString); + continue; + } + + // Does the word contain a color changer? If so, split on it. + // Add color indicators, then combine into the same word with the color being changed + + auto i = IRC_COLOR_PARSE_REGEX.globalMatch(string); + + if (!i.hasNext()) + { + this->addIrcWord(string, textColor); + continue; + } + + int lastPos = 0; + + while (i.hasNext()) + { + auto match = i.next(); + + if (lastPos != match.capturedStart() && match.capturedStart() != 0) + { + if (fg >= 0 && fg <= 98) + { + textColor = IRC_COLORS[fg]; + getApp()->themes->normalizeColor(textColor); + } + else + { + textColor = defaultColor; + } + this->addIrcWord( + string.mid(lastPos, match.capturedStart() - lastPos), + textColor, false); + lastPos = match.capturedStart() + match.capturedLength(); + } + if (!match.captured(1).isEmpty()) + { + fg = -1; + bg = -1; + } + + if (!match.captured(2).isEmpty()) + { + fg = match.captured(2).toInt(nullptr); + } + else + { + fg = -1; + } + if (!match.captured(4).isEmpty()) + { + bg = match.captured(4).toInt(nullptr); + } + else if (fg == -1) + { + bg = -1; + } + + lastPos = match.capturedStart() + match.capturedLength(); + } + + if (fg >= 0 && fg <= 98) + { + textColor = IRC_COLORS[fg]; + getApp()->themes->normalizeColor(textColor); + } + else + { + textColor = defaultColor; + } + this->addIrcWord(string.mid(lastPos), textColor); + } + + this->message().elements.back()->setTrailingSpace(false); +} + +void MessageBuilder::addTextOrEmoji(EmotePtr emote) +{ + this->emplace(emote, MessageElementFlag::EmojiAll); +} + +void MessageBuilder::addTextOrEmoji(const QString &string_) +{ + auto string = QString(string_); + + // Actually just text + auto linkString = this->matchLink(string); + auto link = Link(); + + auto &&textColor = this->textColor_; + if (linkString.isEmpty()) + { + if (string.startsWith('@')) + { + this->emplace(string, MessageElementFlag::BoldUsername, + textColor, FontStyle::ChatMediumBold); + this->emplace( + string, MessageElementFlag::NonBoldUsername, textColor); + } + else + { + this->emplace(string, MessageElementFlag::Text, + textColor); + } + } + else + { + this->addLink(string, linkString); + } +} + +void MessageBuilder::addIrcWord(const QString &text, const QColor &color, + bool addSpace) +{ + this->textColor_ = color; + for (auto &variant : getApp()->emotes->emojis.parse(text)) + { + boost::apply_visitor( + [&](auto &&arg) { + this->addTextOrEmoji(arg); + }, + variant); + if (!addSpace) + { + this->message().elements.back()->setTrailingSpace(false); + } + } +} + TextElement *MessageBuilder::emplaceSystemTextAndUpdate(const QString &text, QString &toUpdate) { diff --git a/src/messages/MessageBuilder.hpp b/src/messages/MessageBuilder.hpp index c217cf1ed..7d725b13b 100644 --- a/src/messages/MessageBuilder.hpp +++ b/src/messages/MessageBuilder.hpp @@ -64,6 +64,13 @@ public: QString matchLink(const QString &string); void addLink(const QString &origLink, const QString &matchedLink); + /** + * Adds the text, applies irc colors, adds links, + * and updates the message's messageText. + * See https://modern.ircdocs.horse/formatting.html + */ + void addIrcMessageText(const QString &text); + template // clang-format off // clang-format can be enabled once clang-format v11+ has been installed in CI @@ -79,6 +86,12 @@ public: return pointer; } +protected: + virtual void addTextOrEmoji(EmotePtr emote); + virtual void addTextOrEmoji(const QString &value); + + MessageColor textColor_ = MessageColor::Text; + private: // Helper method that emplaces some text stylized as system text // and then appends that text to the QString parameter "toUpdate". @@ -86,6 +99,17 @@ private: TextElement *emplaceSystemTextAndUpdate(const QString &text, QString &toUpdate); + /** + * This will add the text and replace any emojis + * with an emoji emote-element. + * + * @param text Text to add + * @param color Color of the text + * @param addSpace true if a trailing space should be added after emojis + */ + void addIrcWord(const QString &text, const QColor &color, + bool addSpace = true); + std::shared_ptr message_; }; diff --git a/src/messages/SharedMessageBuilder.cpp b/src/messages/SharedMessageBuilder.cpp index f059a6708..49508c0b7 100644 --- a/src/messages/SharedMessageBuilder.cpp +++ b/src/messages/SharedMessageBuilder.cpp @@ -181,41 +181,6 @@ void SharedMessageBuilder::parseHighlights() } } -void SharedMessageBuilder::addTextOrEmoji(EmotePtr emote) -{ - this->emplace(emote, MessageElementFlag::EmojiAll); -} - -void SharedMessageBuilder::addTextOrEmoji(const QString &string_) -{ - auto string = QString(string_); - - // Actually just text - auto linkString = this->matchLink(string); - auto link = Link(); - auto &&textColor = this->textColor_; - - if (linkString.isEmpty()) - { - if (string.startsWith('@')) - { - this->emplace(string, MessageElementFlag::BoldUsername, - textColor, FontStyle::ChatMediumBold); - this->emplace( - string, MessageElementFlag::NonBoldUsername, textColor); - } - else - { - this->emplace(string, MessageElementFlag::Text, - textColor); - } - } - else - { - this->addLink(string, linkString); - } -} - void SharedMessageBuilder::appendChannelName() { QString channelName("#" + this->channel->getName()); diff --git a/src/messages/SharedMessageBuilder.hpp b/src/messages/SharedMessageBuilder.hpp index 62adb45df..421339418 100644 --- a/src/messages/SharedMessageBuilder.hpp +++ b/src/messages/SharedMessageBuilder.hpp @@ -53,9 +53,6 @@ protected: // parseHighlights only updates the visual state of the message, but leaves the playing of alerts and sounds to the triggerHighlights function virtual void parseHighlights(); - virtual void addTextOrEmoji(EmotePtr emote); - virtual void addTextOrEmoji(const QString &value); - void appendChannelName(); Channel *channel; @@ -67,7 +64,6 @@ protected: const bool action_{}; QColor usernameColor_ = {153, 153, 153}; - MessageColor textColor_ = MessageColor::Text; bool highlightAlert_ = false; bool highlightSound_ = false; diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp index 123ae738d..19140b4ee 100644 --- a/src/providers/irc/IrcChannel2.cpp +++ b/src/providers/irc/IrcChannel2.cpp @@ -4,7 +4,9 @@ #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/irc/IrcCommands.hpp" +#include "providers/irc/IrcMessageBuilder.hpp" #include "providers/irc/IrcServer.hpp" +#include "util/Helpers.hpp" namespace chatterino { @@ -33,20 +35,42 @@ void IrcChannel::sendMessage(const QString &message) } else { - if (this->server()) + if (this->server() != nullptr) + { this->server()->sendMessage(this->getName(), message); - MessageBuilder builder; - builder.emplace(); - const auto &nick = this->server()->nick(); - builder.emplace(nick + ":", MessageElementFlag::Username) - ->setLink({Link::UserInfo, nick}); - builder.emplace(message, MessageElementFlag::Text); - builder.message().messageText = message; - builder.message().searchText = nick + ": " + message; - builder.message().loginName = nick; - builder.message().displayName = nick; - this->addMessage(builder.release()); + MessageBuilder builder; + + builder + .emplace("#" + this->getName(), + MessageElementFlag::ChannelName, + MessageColor::System) + ->setLink({Link::JumpToChannel, this->getName()}); + + auto now = QDateTime::currentDateTime(); + builder.emplace(now.time()); + builder.message().serverReceivedTime = now; + + auto username = this->server()->nick(); + builder + .emplace( + username + ":", MessageElementFlag::Username, + getRandomColor(username), FontStyle::ChatMediumBold) + ->setLink({Link::UserInfo, username}); + builder.message().loginName = username; + builder.message().displayName = username; + + // message + builder.addIrcMessageText(message); + builder.message().messageText = message; + builder.message().searchText = username + ": " + message; + + this->addMessage(builder.release()); + } + else + { + this->addMessage(makeSystemMessage("You are not connected.")); + } } } diff --git a/src/providers/irc/IrcMessageBuilder.cpp b/src/providers/irc/IrcMessageBuilder.cpp index 77d36fc5a..4109a4eb8 100644 --- a/src/providers/irc/IrcMessageBuilder.cpp +++ b/src/providers/irc/IrcMessageBuilder.cpp @@ -16,14 +16,6 @@ #include "util/IrcHelpers.hpp" #include "widgets/Window.hpp" -namespace { - -QRegularExpression IRC_COLOR_PARSE_REGEX( - "(\u0003(\\d{1,2})?(,(\\d{1,2}))?|\u000f)", - QRegularExpression::UseUnicodePropertiesOption); - -} // namespace - namespace chatterino { IrcMessageBuilder::IrcMessageBuilder( @@ -63,10 +55,9 @@ MessagePtr IrcMessageBuilder::build() this->appendUsername(); - // words - this->addWords(this->originalMessage_.split(' ')); + // message + this->addIrcMessageText(this->originalMessage_); - this->message().messageText = this->originalMessage_; this->message().searchText = this->message().localizedName + " " + this->userName + ": " + this->originalMessage_; @@ -82,125 +73,6 @@ MessagePtr IrcMessageBuilder::build() return this->release(); } -void IrcMessageBuilder::addWords(const QStringList &words) -{ - MessageColor defaultColorType = this->textColor_; - auto defaultColor = defaultColorType.getColor(*getApp()->themes); - QColor textColor = defaultColor; - int fg = -1; - int bg = -1; - - for (auto word : words) - { - if (word.isEmpty()) - { - continue; - } - - auto string = QString(word); - - // Actually just text - auto linkString = this->matchLink(string); - auto link = Link(); - - if (!linkString.isEmpty()) - { - this->addLink(string, linkString); - continue; - } - - // Does the word contain a color changer? If so, split on it. - // Add color indicators, then combine into the same word with the color being changed - - auto i = IRC_COLOR_PARSE_REGEX.globalMatch(string); - - if (!i.hasNext()) - { - this->addText(string, textColor); - continue; - } - - int lastPos = 0; - - while (i.hasNext()) - { - auto match = i.next(); - - if (lastPos != match.capturedStart() && match.capturedStart() != 0) - { - if (fg >= 0 && fg <= 98) - { - textColor = IRC_COLORS[fg]; - getApp()->themes->normalizeColor(textColor); - } - else - { - textColor = defaultColor; - } - this->addText( - string.mid(lastPos, match.capturedStart() - lastPos), - textColor, false); - lastPos = match.capturedStart() + match.capturedLength(); - } - if (!match.captured(1).isEmpty()) - { - fg = -1; - bg = -1; - } - - if (!match.captured(2).isEmpty()) - { - fg = match.captured(2).toInt(nullptr); - } - else - { - fg = -1; - } - if (!match.captured(4).isEmpty()) - { - bg = match.captured(4).toInt(nullptr); - } - else if (fg == -1) - { - bg = -1; - } - - lastPos = match.capturedStart() + match.capturedLength(); - } - - if (fg >= 0 && fg <= 98) - { - textColor = IRC_COLORS[fg]; - getApp()->themes->normalizeColor(textColor); - } - else - { - textColor = defaultColor; - } - this->addText(string.mid(lastPos), textColor); - } - - this->message().elements.back()->setTrailingSpace(false); -} - -void IrcMessageBuilder::addText(const QString &text, const QColor &color, - bool addSpace) -{ - this->textColor_ = color; - for (auto &variant : getApp()->emotes->emojis.parse(text)) - { - boost::apply_visitor( - [&](auto &&arg) { - this->addTextOrEmoji(arg); - }, - variant); - if (!addSpace) - { - this->message().elements.back()->setTrailingSpace(false); - } - } -} - void IrcMessageBuilder::appendUsername() { QString username = this->userName; diff --git a/src/providers/irc/IrcMessageBuilder.hpp b/src/providers/irc/IrcMessageBuilder.hpp index 82f295b32..ef1f089f3 100644 --- a/src/providers/irc/IrcMessageBuilder.hpp +++ b/src/providers/irc/IrcMessageBuilder.hpp @@ -40,10 +40,6 @@ public: private: void appendUsername(); - - void addWords(const QStringList &words); - void addText(const QString &text, const QColor &color, - bool addSpace = true); }; } // namespace chatterino