From a2cbe6377dabd59c025d5bdf46c956a334b274a6 Mon Sep 17 00:00:00 2001 From: nerix Date: Tue, 23 Jul 2024 23:38:17 +0200 Subject: [PATCH 1/6] chore: improve link parser and its tests a bit (#5522) --- src/common/LinkParser.cpp | 34 ++++++++++++++++++---------------- tests/src/LinkParser.cpp | 11 +++++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/common/LinkParser.cpp b/src/common/LinkParser.cpp index 6858ff9022b..c6ec1ab2df7 100644 --- a/src/common/LinkParser.cpp +++ b/src/common/LinkParser.cpp @@ -1,17 +1,24 @@ #define QT_NO_CAST_FROM_ASCII // avoids unexpected implicit casts #include "common/LinkParser.hpp" +#include "util/QCompareCaseInsensitive.hpp" + #include -#include #include #include #include +#include + namespace { -QSet &tlds() +using namespace chatterino; + +using TldSet = std::set; + +TldSet &tlds() { - static QSet tlds = [] { + static TldSet tlds = [] { QFile file(QStringLiteral(":/tlds.txt")); file.open(QFile::ReadOnly); QTextStream stream(&file); @@ -21,19 +28,12 @@ QSet &tlds() #else stream.setCodec("UTF-8"); #endif - int safetyMax = 20000; - QSet set; + TldSet set; while (!stream.atEnd()) { - auto line = stream.readLine(); - set.insert(line); - - if (safetyMax-- == 0) - { - break; - } + set.emplace(stream.readLine()); } return set; @@ -43,7 +43,7 @@ QSet &tlds() bool isValidTld(QStringView tld) { - return tlds().contains(tld.toString().toLower()); + return tlds().contains(tld); } bool isValidIpv4(QStringView host) @@ -166,6 +166,8 @@ namespace chatterino::linkparser { std::optional parse(const QString &source) noexcept { + using SizeType = QString::size_type; + std::optional result; // This is not implemented with a regex to increase performance. @@ -201,11 +203,11 @@ std::optional parse(const QString &source) noexcept QStringView host = remaining; QStringView rest; bool lastWasDot = true; - int lastDotPos = -1; - int nDots = 0; + SizeType lastDotPos = -1; + SizeType nDots = 0; // Extract the host - for (int i = 0; i < remaining.size(); i++) + for (SizeType i = 0; i < remaining.size(); i++) { char16_t currentChar = remaining[i].unicode(); if (currentChar == u'.') diff --git a/tests/src/LinkParser.cpp b/tests/src/LinkParser.cpp index 67fdff0b08c..32a5ad340fd 100644 --- a/tests/src/LinkParser.cpp +++ b/tests/src/LinkParser.cpp @@ -72,6 +72,8 @@ TEST(LinkParser, parseDomainLinks) {"", "chatterino.com", ":80"}, {"", "wiki.chatterino.com", ":80"}, {"", "wiki.chatterino.com", ":80/foo/bar"}, + {"", "wiki.chatterino.com", ":80?foo"}, + {"", "wiki.chatterino.com", ":80#foo"}, {"", "wiki.chatterino.com", "/:80?foo/bar"}, {"", "wiki.chatterino.com", "/127.0.0.1"}, {"", "a.b.c.chatterino.com"}, @@ -156,6 +158,7 @@ TEST(LinkParser, parseIpv4Links) TEST(LinkParser, doesntParseInvalidIpv4Links) { const QStringList inputs = { + "196.162.a.1", // U+0660 - in category "number digits" QStringLiteral("٠.٠.٠.٠"), "https://127.0.0.", @@ -186,6 +189,10 @@ TEST(LinkParser, doesntParseInvalidIpv4Links) "196.162.8.1(", "196.162.8.1(!", "127.1.1;.com", + "127.0.-.1", + "127...", + "1.1.1.", + "1.1.1.:80", }; for (const auto &input : inputs) @@ -223,6 +230,10 @@ TEST(LinkParser, doesntParseInvalidLinks) "https://pn./", "pn./", "pn.", + "pn.:80", + "pn./foo", + "pn.#foo", + "pn.?foo", "http/chatterino.com", "http/wiki.chatterino.com", "http:cat.com", From ff7cc09f8b6acf6156a9a3f925520380d8a15cf7 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 27 Jul 2024 13:19:26 +0200 Subject: [PATCH 2/6] chore: cleanup, document, and test some RTL code (#5473) --- CHANGELOG.md | 1 + src/messages/Image.cpp | 33 +-- src/messages/MessageElement.cpp | 2 - src/messages/layouts/MessageLayout.hpp | 4 +- .../layouts/MessageLayoutContainer.cpp | 136 +++++----- .../layouts/MessageLayoutContainer.hpp | 107 ++++++-- tests/CMakeLists.txt | 1 + tests/src/MessageLayoutContainer.cpp | 237 ++++++++++++++++++ 8 files changed, 415 insertions(+), 106 deletions(-) create mode 100644 tests/src/MessageLayoutContainer.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 131f2016835..714681d098f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Dev: Deprecate Qt 5.12. (#5396) - Dev: The running Qt version is now shown in the about page if it differs from the compiled version. (#5501) - Dev: `FlagsEnum` is now `constexpr`. (#5510) +- Dev: Documented and added tests to RTL handling. (#5473) ## 2.5.1 diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index f74bba16444..59be1995756 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -53,24 +53,27 @@ Frames::Frames(QList &&frames) getApp()->getEmotes()->getGIFTimer().signal.connect([this] { this->advance(); }); - } - auto totalLength = std::accumulate(this->items_.begin(), this->items_.end(), - 0UL, [](auto init, auto &&frame) { - return init + frame.duration; - }); + auto totalLength = + std::accumulate(this->items_.begin(), this->items_.end(), 0UL, + [](auto init, auto &&frame) { + return init + frame.duration; + }); - if (totalLength == 0) - { - this->durationOffset_ = 0; - } - else - { - this->durationOffset_ = std::min( - int(getApp()->getEmotes()->getGIFTimer().position() % totalLength), - 60000); + if (totalLength == 0) + { + this->durationOffset_ = 0; + } + else + { + this->durationOffset_ = std::min( + int(getApp()->getEmotes()->getGIFTimer().position() % + totalLength), + 60000); + } + this->processOffset(); } - this->processOffset(); + DebugCount::increase("image bytes", this->memoryUsage()); DebugCount::increase("image bytes (ever loaded)", this->memoryUsage()); } diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index 8d994ef225c..a8beed9c011 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -593,8 +593,6 @@ void SingleLineTextElement::addToContainer(MessageLayoutContainer &container, // once we encounter an emote or reach the end of the message text. */ QString currentText; - container.first = FirstWord::Neutral; - bool firstIteration = true; for (Word &word : this->words_) { diff --git a/src/messages/layouts/MessageLayout.hpp b/src/messages/layouts/MessageLayout.hpp index 01958ddf2ab..12a8b153a76 100644 --- a/src/messages/layouts/MessageLayout.hpp +++ b/src/messages/layouts/MessageLayout.hpp @@ -119,9 +119,9 @@ class MessageLayout QPixmap *ensureBuffer(QPainter &painter, int width); // variables - MessagePtr message_; + const MessagePtr message_; MessageLayoutContainer container_; - std::unique_ptr buffer_{}; + std::unique_ptr buffer_; bool bufferValid_ = false; int height_ = 0; diff --git a/src/messages/layouts/MessageLayoutContainer.cpp b/src/messages/layouts/MessageLayoutContainer.cpp index a4abee58d70..3c2bd122d09 100644 --- a/src/messages/layouts/MessageLayoutContainer.cpp +++ b/src/messages/layouts/MessageLayoutContainer.cpp @@ -1,4 +1,4 @@ -#include "MessageLayoutContainer.hpp" +#include "messages/layouts/MessageLayoutContainer.hpp" #include "Application.hpp" #include "messages/layouts/MessageLayoutContext.hpp" @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -55,7 +56,6 @@ void MessageLayoutContainer::beginLayout(int width, float scale, this->currentWordId_ = 0; this->canAddMessages_ = true; this->isCollapsed_ = false; - this->wasPrevReversed_ = false; } void MessageLayoutContainer::endLayout() @@ -71,7 +71,7 @@ void MessageLayoutContainer::endLayout() QSize(this->dotdotdotWidth_, this->textLineHeight_), QColor("#00D80A"), FontStyle::ChatMediumBold, this->scale_); - if (this->first == FirstWord::RTL) + if (this->isRTL()) { // Shift all elements in the next line to the left for (auto i = this->lines_.back().startIndex; @@ -125,9 +125,9 @@ void MessageLayoutContainer::addElementNoLineBreak( void MessageLayoutContainer::breakLine() { - if (this->containsRTL) + if (this->lineContainsRTL_ || this->isRTL()) { - for (int i = 0; i < this->elements_.size(); i++) + for (size_t i = 0; i < this->elements_.size(); i++) { if (this->elements_[i]->getFlags().has( MessageElementFlag::Username)) @@ -136,6 +136,7 @@ void MessageLayoutContainer::breakLine() break; } } + this->lineContainsRTL_ = false; } int xOffset = 0; @@ -404,7 +405,7 @@ size_t MessageLayoutContainer::getSelectionIndex(QPoint point) const size_t index = 0; - for (auto i = 0; i < lineEnd; i++) + for (size_t i = 0; i < lineEnd; i++) { auto &&element = this->elements_[i]; @@ -565,30 +566,37 @@ int MessageLayoutContainer::nextWordId() void MessageLayoutContainer::addElement(MessageLayoutElement *element, const bool forceAdd, - const int prevIndex) + const qsizetype prevIndex) { if (!this->canAddElements() && !forceAdd) { + assert(prevIndex == -2 && + "element is still referenced in this->elements_"); delete element; return; } - bool isRTLMode = this->first == FirstWord::RTL && prevIndex != -2; bool isAddingMode = prevIndex == -2; - - // This lambda contains the logic for when to step one 'space width' back for compact x emotes - auto shouldRemoveSpaceBetweenEmotes = [this, prevIndex]() -> bool { + bool isRTLAdjusting = this->isRTL() && !isAddingMode; + + /// Returns `true` if a previously added `spaceWidth_` should be removed + /// before the to be added emote. The space was inserted by the + /// previous element but isn't desired as "removeSpacesBetweenEmotes" is + /// enabled and both elements are emotes. + auto shouldRemoveSpaceBetweenEmotes = [this, prevIndex, + isAddingMode]() -> bool { if (prevIndex == -1 || this->elements_.empty()) { // No previous element found return false; } - const auto &lastElement = prevIndex == -2 ? this->elements_.back() - : this->elements_[prevIndex]; + const auto &lastElement = + isAddingMode ? this->elements_.back() : this->elements_[prevIndex]; if (!lastElement) { + assert(false && "Empty element in container found"); return false; } @@ -608,23 +616,24 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, return lastElement->getFlags().has(MessageElementFlag::EmoteImages); }; - if (element->getText().isRightToLeft()) + bool isRTLElement = element->getText().isRightToLeft(); + if (isRTLElement) { - this->containsRTL = true; + this->lineContainsRTL_ = true; } // check the first non-neutral word to see if we should render RTL or LTR - if (isAddingMode && this->first == FirstWord::Neutral && + if (isAddingMode && this->isNeutral() && element->getFlags().has(MessageElementFlag::Text) && !element->getFlags().has(MessageElementFlag::RepliedMessage)) { - if (element->getText().isRightToLeft()) + if (isRTLElement) { - this->first = FirstWord::RTL; + this->textDirection_ = TextDirection::RTL; } - else if (!isNeutral(element->getText())) + else if (!chatterino::isNeutral(element->getText())) { - this->first = FirstWord::LTR; + this->textDirection_ = TextDirection::LTR; } } @@ -665,7 +674,7 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, shouldRemoveSpaceBetweenEmotes()) { // Move cursor one 'space width' to the left (right in case of RTL) to combine hug the previous emote - if (isRTLMode) + if (isRTLAdjusting) { this->currentX_ += this->spaceWidth_; } @@ -675,7 +684,7 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, } } - if (isRTLMode) + if (isRTLAdjusting) { // shift by width since we are calculating according to top right in RTL mode // but setPosition wants top left @@ -697,7 +706,7 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, } // set current x - if (isRTLMode) + if (isRTLAdjusting) { this->currentX_ -= element->getRect().width(); } @@ -708,7 +717,7 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, if (element->hasTrailingSpace()) { - if (isRTLMode) + if (isRTLAdjusting) { this->currentX_ -= this->spaceWidth_; } @@ -719,15 +728,15 @@ void MessageLayoutContainer::addElement(MessageLayoutElement *element, } } -void MessageLayoutContainer::reorderRTL(int firstTextIndex) +void MessageLayoutContainer::reorderRTL(size_t firstTextIndex) { if (this->elements_.empty()) { return; } - int startIndex = static_cast(this->lineStart_); - int endIndex = static_cast(this->elements_.size()) - 1; + size_t startIndex = this->lineStart_; + size_t endIndex = this->elements_.size() - 1; if (firstTextIndex >= endIndex || startIndex >= this->elements_.size()) { @@ -735,64 +744,53 @@ void MessageLayoutContainer::reorderRTL(int firstTextIndex) } startIndex = std::max(startIndex, firstTextIndex); - std::vector correctSequence; - std::stack swappedSequence; - - // we reverse a sequence of words if it's opposite to the text direction - // the second condition below covers the possible three cases: - // 1 - if we are in RTL mode (first non-neutral word is RTL) - // we render RTL, reversing LTR sequences, - // 2 - if we are in LTR mode (first non-neutral word is LTR or all words are neutral) - // we render LTR, reversing RTL sequences - // 3 - neutral words follow previous words, we reverse a neutral word when the previous word was reversed - - // the first condition checks if a neutral word is treated as a RTL word - // this is used later to add U+202B (RTL embedding) character signal to - // fix punctuation marks and mixing embedding LTR in an RTL word - // this can happen in two cases: - // 1 - in RTL mode, the previous word should be RTL (i.e. not reversed) - // 2 - in LTR mode, the previous word should be RTL (i.e. reversed) - for (int i = startIndex; i <= endIndex; i++) + QVarLengthArray correctSequence; + // temporary buffer to store elements in opposite order + QVarLengthArray swappedSequence; + + bool isReversing = false; + for (size_t i = startIndex; i <= endIndex; i++) { auto &element = this->elements_[i]; - const auto neutral = isNeutral(element->getText()); + const auto neutral = chatterino::isNeutral(element->getText()); const auto neutralOrUsername = neutral || element->getFlags().has(MessageElementFlag::Mention); + // check if neutral words are treated as RTL to add U+202B (RTL + // embedding) which fixes punctuation marks if (neutral && - ((this->first == FirstWord::RTL && !this->wasPrevReversed_) || - (this->first == FirstWord::LTR && this->wasPrevReversed_))) + ((this->isRTL() && !isReversing) || (this->isLTR() && isReversing))) { element->reversedNeutral = true; } - if (((element->getText().isRightToLeft() != - (this->first == FirstWord::RTL)) && + + if ((element->getText().isRightToLeft() != this->isRTL() && !neutralOrUsername) || - (neutralOrUsername && this->wasPrevReversed_)) + (neutralOrUsername && isReversing)) { - swappedSequence.push(i); - this->wasPrevReversed_ = true; + swappedSequence.append(i); + isReversing = true; } else { while (!swappedSequence.empty()) { - correctSequence.push_back(swappedSequence.top()); - swappedSequence.pop(); + correctSequence.push_back(swappedSequence.last()); + swappedSequence.pop_back(); } correctSequence.push_back(i); - this->wasPrevReversed_ = false; + isReversing = false; } } while (!swappedSequence.empty()) { - correctSequence.push_back(swappedSequence.top()); - swappedSequence.pop(); + correctSequence.push_back(swappedSequence.last()); + swappedSequence.pop_back(); } // render right to left if we are in RTL mode, otherwise LTR - if (this->first == FirstWord::RTL) + if (this->isRTL()) { this->currentX_ = this->elements_[endIndex]->getRect().right(); } @@ -806,10 +804,11 @@ void MessageLayoutContainer::reorderRTL(int firstTextIndex) this->addElement(this->elements_[correctSequence[0]].get(), false, -1); } - for (int i = 1; i < correctSequence.size() && this->canAddElements(); i++) + for (qsizetype i = 1; i < correctSequence.size() && this->canAddElements(); + i++) { this->addElement(this->elements_[correctSequence[i]].get(), false, - correctSequence[i - 1]); + static_cast(correctSequence[i - 1])); } } @@ -992,4 +991,19 @@ bool MessageLayoutContainer::canCollapse() const this->flags_.has(MessageFlag::Collapsed); } +bool MessageLayoutContainer::isRTL() const noexcept +{ + return this->textDirection_ == TextDirection::RTL; +} + +bool MessageLayoutContainer::isLTR() const noexcept +{ + return this->textDirection_ == TextDirection::LTR; +} + +bool MessageLayoutContainer::isNeutral() const noexcept +{ + return this->textDirection_ == TextDirection::Neutral; +} + } // namespace chatterino diff --git a/src/messages/layouts/MessageLayoutContainer.hpp b/src/messages/layouts/MessageLayoutContainer.hpp index dde3f4d452f..7cbcaf9fbef 100644 --- a/src/messages/layouts/MessageLayoutContainer.hpp +++ b/src/messages/layouts/MessageLayoutContainer.hpp @@ -10,12 +10,21 @@ #include #include +#if __has_include() +# include +#endif + class QPainter; namespace chatterino { +enum class TextDirection : uint8_t { + Neutral, + RTL, + LTR, +}; + enum class MessageFlag : int64_t; -enum class FirstWord { Neutral, RTL, LTR }; using MessageFlags = FlagsEnum; class MessageLayoutElement; struct Selection; @@ -24,8 +33,6 @@ struct MessagePaintContext; struct MessageLayoutContainer { MessageLayoutContainer() = default; - FirstWord first = FirstWord::Neutral; - /** * Begin the layout process of this message * @@ -212,24 +219,54 @@ struct MessageLayoutContainer { QRect rect; }; - /* - addElement is called at two stages. first stage is the normal one where we want to add message layout elements to the container. - If we detect an RTL word in the message, reorderRTL will be called, which is the second stage, where we call _addElement - again for each layout element, but in the correct order this time, without adding the elemnt to the this->element_ vector. - Due to compact emote logic, we need the previous element to check if we should change the spacing or not. - in stage one, this is simply elements_.back(), but in stage 2 that's not the case due to the reordering, and we need to pass the - index of the reordered previous element. - In stage one we don't need that and we pass -2 to indicate stage one (i.e. adding mode) - In stage two, we pass -1 for the first element, and the index of the oredered privous element for the rest. - */ + /// @brief Attempts to add @a element to this container + /// + /// This can be called in two scenarios. + /// + /// 1. **Regular**: In this scenario, @a element is positioned and added + /// to the internal container. + /// This is active iff @a prevIndex is `-2`. + /// During this stage, if there isn't any @a textDirection_ detected yet, + /// the added element is checked if it contains RTL/LTR text to infer the + /// direction. Only upon calling @a breakLine, the elements will be + /// visually reorderd. + /// + /// 2. **Repositioning**: In this scenario, @a element is already added to + /// the container thus it's only repositioned. + /// This is active iff @a prevIndex is not `-2`. + /// @a prevIndex is used to handle compact emotes. `-1` is used to + /// indicate no predecessor. + /// + /// @param element[in] The element to add. This must be non-null and + /// allocated with `new`. Ownership is transferred + /// into this container. + /// @param forceAdd When enabled, @a element will be added regardless of + /// `canAddElements`. If @a element won't be added it will + /// be `delete`d. + /// @param prevIndex Controls the "scenario" (see above). `-2` indicates + /// "regular" mode; other values indicate "repositioning". + /// In case of repositioning, this contains the index of + /// the precesding element (visually, according to + /// @a textDirection_ [RTL/LTR]). void addElement(MessageLayoutElement *element, bool forceAdd, - int prevIndex); - - // this method is called when a message has an RTL word - // we need to reorder the words to be shown properly - // however we don't we to reorder non-text elements like badges, timestamps, username - // firstTextIndex is the index of the first text element that we need to start the reordering from - void reorderRTL(int firstTextIndex); + qsizetype prevIndex); + + /// @brief Reorders the last line according to @a textDirection_ + /// + /// If a line contains RTL or the text direction is RTL, elements need to be + /// reordered (see @a lineContainsRTL_ and @a isRTL respectively). + /// This method reverses sequences of text in the opposite direction for it + /// to remain in its intended direction when rendered. Non-text elements + /// won't be reordered. + /// + /// For example, in an RTL container, the sequence + /// "1_R 2_R 3_N 4_R 5_L 6_L 7_N 8_R" will be (visually) reordered to + /// "8_R 5_L 6_L 7_N 4_R 3_N 2_R 1_R" (x_{L,N,R} indicates the element with + /// id x which is in the direction {LTR,Neutral,RTL}). + /// + /// @param firstTextIndex The index of the first element of the message + /// (i.e. the index after the username). + void reorderRTL(size_t firstTextIndex); /** * Paint a selection rectangle over the given line @@ -274,6 +311,15 @@ struct MessageLayoutContainer { */ bool canCollapse() const; + /// @returns true, if @a textDirection_ is RTL + [[nodiscard]] bool isRTL() const noexcept; + + /// @returns true, if @a textDirection_ is LTR + [[nodiscard]] bool isLTR() const noexcept; + + /// @returns true, if @a textDirection_ is Neutral + [[nodiscard]] bool isNeutral() const noexcept; + // variables float scale_ = 1.F; /** @@ -304,13 +350,18 @@ struct MessageLayoutContainer { int currentWordId_ = 0; bool canAddMessages_ = true; bool isCollapsed_ = false; - bool wasPrevReversed_ = false; - /** - * containsRTL indicates whether or not any of the text in this message - * contains any right-to-left characters (e.g. arabic) - */ - bool containsRTL = false; + /// @brief True if the current line contains any RTL text. + /// + /// If the line contains any RTL, it needs to be reordered after a + /// linebreak after which it's reset to `false`. + bool lineContainsRTL_ = false; + + /// @brief The direction of the text in this container. + /// + /// This starts off as neutral until an element is encountered that is + /// either LTR or RTL (afterwards this remains constant). + TextDirection textDirection_ = TextDirection::Neutral; std::vector> elements_; @@ -320,6 +371,10 @@ struct MessageLayoutContainer { * These lines hold no relation to the elements that are in this */ std::vector lines_; + +#ifdef FRIEND_TEST + FRIEND_TEST(MessageLayoutContainerTest, RtlReordering); +#endif }; } // namespace chatterino diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 79c490fa947..9f79b77ebdb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -46,6 +46,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/Scrollbar.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Commands.cpp ${CMAKE_CURRENT_LIST_DIR}/src/FlagsEnum.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/MessageLayoutContainer.cpp # Add your new file above this line! ) diff --git a/tests/src/MessageLayoutContainer.cpp b/tests/src/MessageLayoutContainer.cpp new file mode 100644 index 00000000000..b72934c7688 --- /dev/null +++ b/tests/src/MessageLayoutContainer.cpp @@ -0,0 +1,237 @@ +#include "messages/layouts/MessageLayoutContainer.hpp" + +#include "common/Literals.hpp" +#include "messages/Emote.hpp" +#include "messages/layouts/MessageLayoutElement.hpp" +#include "messages/Message.hpp" +#include "messages/MessageElement.hpp" +#include "mocks/BaseApplication.hpp" +#include "singletons/Fonts.hpp" +#include "singletons/Resources.hpp" +#include "singletons/Theme.hpp" +#include "Test.hpp" + +#include +#include + +using namespace chatterino; +using namespace literals; + +namespace { + +class MockApplication : mock::BaseApplication +{ +public: + MockApplication() + : theme(this->paths_) + , fonts(this->settings) + { + } + Theme *getThemes() override + { + return &this->theme; + } + + Fonts *getFonts() override + { + return &this->fonts; + } + + Theme theme; + Fonts fonts; +}; + +std::vector> makeElements(const QString &text) +{ + std::vector> elements; + bool seenUsername = false; + for (const auto &word : text.split(' ')) + { + if (word.startsWith('@')) + { + if (seenUsername) + { + elements.emplace_back(std::make_shared( + word, word, MessageColor{}, MessageColor{})); + } + else + { + elements.emplace_back(std::make_shared( + word, MessageElementFlag::Username, MessageColor{}, + FontStyle::ChatMediumBold)); + seenUsername = true; + } + continue; + } + + if (word.startsWith('!')) + { + auto emote = std::make_shared(Emote{ + .name = EmoteName{word}, + .images = ImageSet{Image::fromResourcePixmap( + getResources().buttons.addSplit)}, + .tooltip = {}, + .homePage = {}, + .id = {}, + .author = {}, + .baseName = {}, + }); + elements.emplace_back(std::make_shared( + emote, MessageElementFlag::TwitchEmote)); + continue; + } + + elements.emplace_back(std::make_shared( + word, MessageElementFlag::Text, MessageColor{}, + FontStyle::ChatMedium)); + } + + return elements; +} + +using TestParam = std::tuple; + +} // namespace + +namespace chatterino { + +class MessageLayoutContainerTest : public ::testing::TestWithParam +{ +public: + MessageLayoutContainerTest() = default; + + MockApplication mockApplication; +}; + +TEST_P(MessageLayoutContainerTest, RtlReordering) +{ + auto [inputText, expected, expectedDirection] = GetParam(); + MessageLayoutContainer container; + container.beginLayout(10000, 1.0F, 1.0F, {MessageFlag::Collapsed}); + + auto elements = makeElements(inputText); + for (const auto &element : elements) + { + element->addToContainer(container, { + MessageElementFlag::Text, + MessageElementFlag::Username, + MessageElementFlag::TwitchEmote, + }); + } + container.breakLine(); + ASSERT_EQ(container.line_, 1) << "unexpected linebreak"; + + // message layout elements ordered by x position + std::vector ordered; + ordered.reserve(container.elements_.size()); + for (const auto &el : container.elements_) + { + ordered.push_back(el.get()); + } + + std::ranges::sort(ordered, [](auto *a, auto *b) { + return a->getRect().x() < b->getRect().x(); + }); + + QString got; + for (const auto &el : ordered) + { + if (!got.isNull()) + { + got.append(' '); + } + + if (dynamic_cast(el)) + { + el->addCopyTextToString(got); + ASSERT_TRUE(got.endsWith(' ')); + got.chop(1); + } + else + { + got.append(el->getText()); + } + } + + ASSERT_EQ(got, expected) << got; + ASSERT_EQ(container.textDirection_, expectedDirection) << got; +} + +INSTANTIATE_TEST_SUITE_P( + MessageLayoutContainer, MessageLayoutContainerTest, + testing::Values( + TestParam{ + u"@aliens foo bar baz @foo qox !emote1 !emote2"_s, + u"@aliens foo bar baz @foo qox !emote1 !emote2"_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens ! foo bar baz @foo qox !emote1 !emote2"_s, + u"@aliens ! foo bar baz @foo qox !emote1 !emote2"_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens ."_s, + u"@aliens ."_s, + TextDirection::Neutral, + }, + // RTL + TestParam{ + u"@aliens و غير دارت إعادة, بل كما وقام قُدُماً. قام تم الجوي بوابة, خلاف أراض هو بلا. عن وحتّى ميناء غير"_s, + u"@aliens غير ميناء وحتّى عن بلا. هو أراض خلاف بوابة, الجوي تم قام قُدُماً. وقام كما بل إعادة, دارت غير و"_s, + TextDirection::RTL, + }, + TestParam{ + u"@aliens و غير دارت إعادة, بل ض هو my LTR 123 بلا. عن 123 456 وحتّى ميناء غير"_s, + u"@aliens غير ميناء وحتّى 456 123 عن بلا. my LTR 123 هو ض بل إعادة, دارت غير و"_s, + TextDirection::RTL, + }, + TestParam{ + u"@aliens ور دارت إ @user baz bar عاد هو my LTR 123 بلا. عن 123 456 وحتّ غير"_s, + u"@aliens غير وحتّ 456 123 عن بلا. my LTR 123 هو عاد baz bar @user إ دارت ور"_s, + TextDirection::RTL, + }, + TestParam{ + u"@aliens ور !emote1 !emote2 !emote3 دارت إ @user baz bar عاد هو my LTR 123 بلا. عن 123 456 وحتّ غير"_s, + u"@aliens غير وحتّ 456 123 عن بلا. my LTR 123 هو عاد baz bar @user إ دارت !emote3 !emote2 !emote1 ور"_s, + TextDirection::RTL, + }, + TestParam{ + u"@aliens ور !emote1 !emote2 LTR text !emote3 !emote4 غير"_s, + u"@aliens غير LTR text !emote3 !emote4 !emote2 !emote1 ور"_s, + TextDirection::RTL, + }, + + TestParam{ + u"@aliens !!! ور !emote1 !emote2 LTR text !emote3 !emote4 غير"_s, + u"@aliens غير LTR text !emote3 !emote4 !emote2 !emote1 ور !!!"_s, + TextDirection::RTL, + }, + // LTR + TestParam{ + u"@aliens LTR و غير دا ميناء غير"_s, + u"@aliens LTR غير ميناء دا غير و"_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens LTR و غير د ض هو my LTR 123 بلا. عن 123 456 وحتّى مير"_s, + u"@aliens LTR هو ض د غير و my LTR 123 مير وحتّى 456 123 عن بلا."_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens LTR ور دارت إ @user baz bar عاد هو my LTR 123 بلا. عن 123 456 وحتّ غير"_s, + u"@aliens LTR @user إ دارت ور baz bar هو عاد my LTR 123 غير وحتّ 456 123 عن بلا."_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens LTR ور !emote1 !emote2 !emote3 دارت إ @user baz bar عاد هو my LTR 123 بلا. عن 123 456 وحتّ غير"_s, + u"@aliens LTR @user إ دارت !emote3 !emote2 !emote1 ور baz bar هو عاد my LTR 123 غير وحتّ 456 123 عن بلا."_s, + TextDirection::LTR, + }, + TestParam{ + u"@aliens LTR غير وحتّ !emote1 !emote2 LTR text !emote3 !emote4 عاد هو"_s, + u"@aliens LTR !emote2 !emote1 وحتّ غير LTR text !emote3 !emote4 هو عاد"_s, + TextDirection::LTR, + })); + +} // namespace chatterino From 5fc4309e0efe2013445947839a1a841b816dc55b Mon Sep 17 00:00:00 2001 From: cmp Date: Sun, 28 Jul 2024 05:02:20 -0500 Subject: [PATCH 3/6] Handle panning touch gestures (#5524) --- CHANGELOG.md | 1 + src/widgets/helper/ChannelView.cpp | 66 ++++++++++++++++++++++++++++++ src/widgets/helper/ChannelView.hpp | 5 +++ 3 files changed, 72 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 714681d098f..deec23cb2f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Minor: Support more Firefox variants for incognito link opening. (#5503) - Minor: Replying to a message will now display the message being replied to. (#4350, #5519) - Minor: Links can now have prefixes and suffixes such as parentheses. (#5486, #5515) +- Minor: Added support for scrolling in splits with touchscreen panning gestures. (#5524) - Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426) - Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378) - Bugfix: Fixed restricted users usernames not being clickable. (#5405) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 7430614a947..be91e2342df 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -369,6 +370,8 @@ ChannelView::ChannelView(InternalCtor /*tag*/, QWidget *parent, Split *split, this->scrollUpdateRequested(); }); + this->grabGesture(Qt::PanGesture); + // TODO: Figure out if we need this, and if so, why // StrongFocus means we can focus this event through clicking it // and tabbing to it from another widget. I don't currently know @@ -1786,8 +1789,71 @@ void ChannelView::leaveEvent(QEvent * /*event*/) this->unpause(PauseReason::Mouse); } +bool ChannelView::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) + { + if (const auto *gestureEvent = dynamic_cast(event)) + { + return this->gestureEvent(gestureEvent); + } + } + + return BaseWidget::event(event); +} + +bool ChannelView::gestureEvent(const QGestureEvent *event) +{ + if (QGesture *pan = event->gesture(Qt::PanGesture)) + { + if (const auto *gesture = dynamic_cast(pan)) + { + switch (gesture->state()) + { + case Qt::GestureStarted: { + this->isPanning_ = true; + // Remove any selections and hide tooltip while panning + this->clearSelection(); + this->tooltipWidget_->hide(); + if (this->isScrolling_) + { + this->disableScrolling(); + } + } + break; + + case Qt::GestureUpdated: { + if (this->scrollBar_->isVisible()) + { + this->scrollBar_->offset(-gesture->delta().y() * 0.1); + } + } + break; + + case Qt::GestureFinished: + case Qt::GestureCanceled: + default: { + this->clearSelection(); + this->isPanning_ = false; + } + break; + } + + return true; + } + } + + return false; +} + void ChannelView::mouseMoveEvent(QMouseEvent *event) { + if (this->isPanning_) + { + // Don't do any text selection, hovering, etc while panning + return; + } + /// Pause on hover if (float pauseTime = getSettings()->pauseOnHoverDuration; pauseTime > 0.001F) diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index e6cb7597ee8..a670d6e3074 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -10,6 +10,7 @@ #include "widgets/TooltipWidget.hpp" #include +#include #include #include #include @@ -216,6 +217,9 @@ class ChannelView final : public BaseWidget #endif void leaveEvent(QEvent * /*event*/) override; + bool event(QEvent *event) override; + bool gestureEvent(const QGestureEvent *event); + void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; @@ -374,6 +378,7 @@ class ChannelView final : public BaseWidget QTimer clickTimer_; bool isScrolling_ = false; + bool isPanning_ = false; QPointF lastMiddlePressPosition_; QPointF currentMousePosition_; QTimer scrollTimer_; From 5ee5abf5b20547bdcf991009a9d8583c5e319254 Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 28 Jul 2024 12:29:29 +0200 Subject: [PATCH 4/6] fix: sort elements after RTL reordering (#5525) --- CHANGELOG.md | 1 + .../layouts/MessageLayoutContainer.cpp | 14 +++++++++++ .../layouts/MessageLayoutContainer.hpp | 3 +++ tests/src/MessageLayoutContainer.cpp | 23 ++++++++----------- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deec23cb2f6..fe4463eb38d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - Bugfix: Fixed `/clearmessages` not working with more than one window. (#5489) - Bugfix: Fixed splits staying paused after unfocusing Chatterino in certain configurations. (#5504) - Bugfix: Links with invalid characters in the domain are no longer detected. (#5509) +- Bugfix: Fixed janky selection for messages with RTL segments (selection is still wrong, but consistently wrong). (#5525) - Dev: Update Windows build from Qt 6.5.0 to Qt 6.7.1. (#5420) - Dev: Update vcpkg build Qt from 6.5.0 to 6.7.0, boost from 1.83.0 to 1.85.0, openssl from 3.1.3 to 3.3.0. (#5422) - Dev: Unsingletonize `ISoundController`. (#5462) diff --git a/src/messages/layouts/MessageLayoutContainer.cpp b/src/messages/layouts/MessageLayoutContainer.cpp index 3c2bd122d09..0f8aa498f99 100644 --- a/src/messages/layouts/MessageLayoutContainer.cpp +++ b/src/messages/layouts/MessageLayoutContainer.cpp @@ -56,6 +56,8 @@ void MessageLayoutContainer::beginLayout(int width, float scale, this->currentWordId_ = 0; this->canAddMessages_ = true; this->isCollapsed_ = false; + this->lineContainsRTL_ = false; + this->anyReorderingDone_ = false; } void MessageLayoutContainer::endLayout() @@ -105,6 +107,17 @@ void MessageLayoutContainer::endLayout() { this->elements_.back()->setTrailingSpace(false); } + + if (this->anyReorderingDone_) + { + std::ranges::sort(this->elements_, [](const auto &a, const auto &b) { + if (a->getLine() == b->getLine()) + { + return a->getRect().x() < b->getRect().x(); + } + return a->getLine() < b->getLine(); + }); + } } void MessageLayoutContainer::addElement(MessageLayoutElement *element) @@ -137,6 +150,7 @@ void MessageLayoutContainer::breakLine() } } this->lineContainsRTL_ = false; + this->anyReorderingDone_ = true; } int xOffset = 0; diff --git a/src/messages/layouts/MessageLayoutContainer.hpp b/src/messages/layouts/MessageLayoutContainer.hpp index 7cbcaf9fbef..7c14faf40af 100644 --- a/src/messages/layouts/MessageLayoutContainer.hpp +++ b/src/messages/layouts/MessageLayoutContainer.hpp @@ -357,6 +357,9 @@ struct MessageLayoutContainer { /// linebreak after which it's reset to `false`. bool lineContainsRTL_ = false; + /// True if there was any RTL/LTR reordering done in this container + bool anyReorderingDone_ = false; + /// @brief The direction of the text in this container. /// /// This starts off as neutral until an element is encountered that is diff --git a/tests/src/MessageLayoutContainer.cpp b/tests/src/MessageLayoutContainer.cpp index b72934c7688..b0aa4f30e4a 100644 --- a/tests/src/MessageLayoutContainer.cpp +++ b/tests/src/MessageLayoutContainer.cpp @@ -118,34 +118,31 @@ TEST_P(MessageLayoutContainerTest, RtlReordering) MessageElementFlag::TwitchEmote, }); } - container.breakLine(); + container.endLayout(); ASSERT_EQ(container.line_, 1) << "unexpected linebreak"; - // message layout elements ordered by x position - std::vector ordered; - ordered.reserve(container.elements_.size()); + int x = -1; for (const auto &el : container.elements_) { - ordered.push_back(el.get()); + ASSERT_LT(x, el->getRect().x()); + x = el->getRect().x(); } - std::ranges::sort(ordered, [](auto *a, auto *b) { - return a->getRect().x() < b->getRect().x(); - }); - QString got; - for (const auto &el : ordered) + for (const auto &el : container.elements_) { if (!got.isNull()) { got.append(' '); } - if (dynamic_cast(el)) + if (dynamic_cast(el.get())) { el->addCopyTextToString(got); - ASSERT_TRUE(got.endsWith(' ')); - got.chop(1); + if (el->hasTrailingSpace()) + { + got.chop(1); + } } else { From aed55ac1ba765aa21a74c79d7493fdd3fd79a657 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 3 Aug 2024 12:00:58 +0200 Subject: [PATCH 5/6] fix: replace defines with constexpr/const and use more absolute paths for includes (#5527) bye bye nuuls --- CHANGELOG.md | 1 + src/common/ChannelChatters.cpp | 2 +- src/common/Common.hpp | 33 ++-- src/common/Credentials.cpp | 19 ++- src/common/Modes.cpp | 2 +- src/common/Version.hpp | 19 +-- src/controllers/accounts/Account.cpp | 2 +- src/controllers/accounts/AccountModel.cpp | 2 +- src/controllers/commands/Command.cpp | 2 +- src/controllers/highlights/HighlightBadge.cpp | 2 +- .../notifications/NotificationModel.cpp | 2 +- src/controllers/pings/MutedChannelModel.cpp | 2 +- src/debug/Benchmark.cpp | 2 +- src/messages/Emote.cpp | 2 +- src/messages/MessageBuilder.cpp | 2 +- src/messages/MessageColor.cpp | 2 +- src/messages/layouts/MessageLayout.cpp | 6 - .../layouts/MessageLayoutContainer.cpp | 21 ++- src/messages/search/RegexPredicate.cpp | 2 +- src/messages/search/RegexPredicate.hpp | 2 +- src/providers/IvrApi.cpp | 2 +- src/providers/IvrApi.hpp | 4 +- src/providers/irc/AbstractIrcServer.cpp | 2 +- src/providers/irc/Irc2.cpp | 2 +- src/providers/irc/IrcAccount.cpp | 2 +- src/providers/irc/IrcChannel2.cpp | 2 +- src/providers/irc/IrcCommands.cpp | 2 +- src/providers/irc/IrcConnection2.cpp | 4 +- src/providers/irc/IrcServer.cpp | 2 +- src/providers/twitch/ChannelPointReward.cpp | 21 ++- src/providers/twitch/ChannelPointReward.hpp | 6 +- src/providers/twitch/TwitchEmotes.cpp | 4 +- src/providers/twitch/TwitchEmotes.hpp | 9 +- src/providers/twitch/api/Helix.hpp | 4 +- src/singletons/ImageUploader.cpp | 6 +- src/singletons/Theme.cpp | 1 - src/singletons/Toasts.cpp | 12 +- src/singletons/Updates.cpp | 38 +++-- src/util/DisplayBadge.cpp | 2 +- src/util/FunctionEventFilter.cpp | 2 +- src/util/FuzzyConvert.cpp | 2 +- src/util/Helpers.cpp | 2 +- src/util/RatelimitBucket.cpp | 2 +- src/util/SampleData.cpp | 2 +- src/widgets/AccountSwitchWidget.cpp | 2 +- src/widgets/TooltipWidget.cpp | 6 +- src/widgets/Window.cpp | 6 +- src/widgets/dialogs/BadgePickerDialog.cpp | 2 +- src/widgets/dialogs/EmotePopup.cpp | 2 +- src/widgets/dialogs/IrcConnectionEditor.cpp | 2 +- src/widgets/dialogs/QualityPopup.cpp | 2 +- src/widgets/dialogs/SelectChannelDialog.cpp | 8 +- .../dialogs/SelectChannelFiltersDialog.cpp | 2 +- src/widgets/dialogs/UserInfoPopup.cpp | 154 +++++++++--------- src/widgets/dialogs/WelcomeDialog.cpp | 2 +- src/widgets/helper/Button.cpp | 2 +- src/widgets/helper/ChannelView.cpp | 6 +- src/widgets/helper/ComboBoxItemDelegate.cpp | 2 +- src/widgets/helper/CommonTexts.hpp | 21 ++- src/widgets/helper/EditableModelView.cpp | 2 +- src/widgets/helper/NotebookButton.cpp | 2 - src/widgets/helper/NotebookTab.hpp | 2 +- src/widgets/helper/SearchPopup.cpp | 2 +- src/widgets/helper/TitlebarButton.cpp | 2 +- src/widgets/listview/GenericListItem.cpp | 2 +- src/widgets/listview/GenericListModel.cpp | 2 +- src/widgets/settingspages/AboutPage.cpp | 19 +-- src/widgets/settingspages/AccountsPage.cpp | 2 +- src/widgets/settingspages/CommandPage.cpp | 16 +- .../settingspages/ExternalToolsPage.cpp | 8 +- src/widgets/settingspages/FiltersPage.cpp | 12 +- src/widgets/settingspages/GeneralPage.cpp | 101 ++++++------ .../settingspages/HighlightingPage.cpp | 10 +- src/widgets/settingspages/IgnoresPage.cpp | 10 +- src/widgets/settingspages/ModerationPage.cpp | 2 +- src/widgets/settingspages/NicknamesPage.cpp | 2 +- .../settingspages/NotificationPage.cpp | 2 +- src/widgets/settingspages/SettingsPage.cpp | 2 +- src/widgets/splits/ClosedSplits.cpp | 2 +- src/widgets/splits/Split.cpp | 47 +++--- src/widgets/splits/SplitContainer.cpp | 10 +- src/widgets/splits/SplitOverlay.cpp | 2 +- tests/src/Updates.cpp | 4 +- 83 files changed, 376 insertions(+), 370 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe4463eb38d..d9abb6df1cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ - Dev: The running Qt version is now shown in the about page if it differs from the compiled version. (#5501) - Dev: `FlagsEnum` is now `constexpr`. (#5510) - Dev: Documented and added tests to RTL handling. (#5473) +- Dev: Refactored a few `#define`s into `const(expr)` and cleaned includes. (#5527) ## 2.5.1 diff --git a/src/common/ChannelChatters.cpp b/src/common/ChannelChatters.cpp index 3bb785a0ae2..b66a3cc4598 100644 --- a/src/common/ChannelChatters.cpp +++ b/src/common/ChannelChatters.cpp @@ -1,4 +1,4 @@ -#include "ChannelChatters.hpp" +#include "common/ChannelChatters.hpp" #include "common/Channel.hpp" #include "messages/Message.hpp" diff --git a/src/common/Common.hpp b/src/common/Common.hpp index 8d6097473bc..f1ce967549f 100644 --- a/src/common/Common.hpp +++ b/src/common/Common.hpp @@ -5,17 +5,17 @@ #include #include -#include -#include - -#define LINK_CHATTERINO_WIKI "https://wiki.chatterino.com" -#define LINK_CHATTERINO_DISCORD "https://discord.gg/7Y5AYhAK4z" -#define LINK_CHATTERINO_SOURCE "https://github.com/Chatterino/chatterino2" namespace chatterino { -const inline auto TWITCH_PLAYER_URL = - QStringLiteral("https://player.twitch.tv/?channel=%1&parent=twitch.tv"); +constexpr QStringView LINK_CHATTERINO_WIKI = u"https://wiki.chatterino.com"; +constexpr QStringView LINK_CHATTERINO_DISCORD = + u"https://discord.gg/7Y5AYhAK4z"; +constexpr QStringView LINK_CHATTERINO_SOURCE = + u"https://github.com/Chatterino/chatterino2"; + +constexpr QStringView TWITCH_PLAYER_URL = + u"https://player.twitch.tv/?channel=%1&parent=twitch.tv"; enum class HighlightState { None, @@ -23,21 +23,14 @@ enum class HighlightState { NewMessage, }; -const Qt::KeyboardModifiers showSplitOverlayModifiers = +constexpr Qt::KeyboardModifiers SHOW_SPLIT_OVERLAY_MODIFIERS = Qt::ControlModifier | Qt::AltModifier; -const Qt::KeyboardModifiers showAddSplitRegions = +constexpr Qt::KeyboardModifiers SHOW_ADD_SPLIT_REGIONS = Qt::ControlModifier | Qt::AltModifier; -const Qt::KeyboardModifiers showResizeHandlesModifiers = Qt::ControlModifier; - -#ifndef ATTR_UNUSED -# ifdef Q_OS_WIN -# define ATTR_UNUSED -# else -# define ATTR_UNUSED __attribute__((unused)) -# endif -#endif +constexpr Qt::KeyboardModifiers SHOW_RESIZE_HANDLES_MODIFIERS = + Qt::ControlModifier; -static const char *ANONYMOUS_USERNAME_LABEL ATTR_UNUSED = " - anonymous - "; +constexpr const char *ANONYMOUS_USERNAME_LABEL = " - anonymous - "; template std::weak_ptr weakOf(T *element) diff --git a/src/common/Credentials.cpp b/src/common/Credentials.cpp index 68c9cdd5989..3c1c0ce043f 100644 --- a/src/common/Credentials.cpp +++ b/src/common/Credentials.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -26,16 +27,16 @@ # endif #endif -#define FORMAT_NAME \ - ([&] { \ - assert(!provider.contains(":")); \ - return QString("chatterino:%1:%2").arg(provider).arg(name_); \ - })() - namespace { using namespace chatterino; +QString formatName(const QString &provider, const QString &name) +{ + assert(!provider.contains(":")); + return u"chatterino:" % provider % u':' % name; +} + bool useKeyring() { #ifdef NO_QTKEYCHAIN @@ -184,7 +185,7 @@ void Credentials::get(const QString &provider, const QString &name_, { assertInGuiThread(); - auto name = FORMAT_NAME; + auto name = formatName(provider, name_); if (useKeyring()) { @@ -219,7 +220,7 @@ void Credentials::set(const QString &provider, const QString &name_, /// On linux, we try to use a keychain but show a message to disable it when it fails. /// XXX: add said message - auto name = FORMAT_NAME; + auto name = formatName(provider, name_); if (useKeyring()) { @@ -242,7 +243,7 @@ void Credentials::erase(const QString &provider, const QString &name_) { assertInGuiThread(); - auto name = FORMAT_NAME; + auto name = formatName(provider, name_); if (useKeyring()) { diff --git a/src/common/Modes.cpp b/src/common/Modes.cpp index ead79dea942..588a304f10a 100644 --- a/src/common/Modes.cpp +++ b/src/common/Modes.cpp @@ -1,4 +1,4 @@ -#include "Modes.hpp" +#include "common/Modes.hpp" #include "util/CombinePath.hpp" diff --git a/src/common/Version.hpp b/src/common/Version.hpp index fbe536a69b8..8d9ef54a0d4 100644 --- a/src/common/Version.hpp +++ b/src/common/Version.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include + +namespace chatterino { /** * Valid version formats, in order of latest to oldest @@ -24,21 +25,7 @@ * - 2.4.0-alpha.2 * - 2.4.0-alpha **/ -#define CHATTERINO_VERSION "2.5.1" - -#if defined(Q_OS_WIN) -# define CHATTERINO_OS "win" -#elif defined(Q_OS_MACOS) -# define CHATTERINO_OS "macos" -#elif defined(Q_OS_LINUX) -# define CHATTERINO_OS "linux" -#elif defined(Q_OS_FREEBSD) -# define CHATTERINO_OS "freebsd" -#else -# define CHATTERINO_OS "unknown" -#endif - -namespace chatterino { +inline const QString CHATTERINO_VERSION = QStringLiteral("2.5.1"); class Version { diff --git a/src/controllers/accounts/Account.cpp b/src/controllers/accounts/Account.cpp index 9cf9ceefa9f..1d9f0ab1ca9 100644 --- a/src/controllers/accounts/Account.cpp +++ b/src/controllers/accounts/Account.cpp @@ -1,4 +1,4 @@ -#include "Account.hpp" +#include "controllers/accounts/Account.hpp" #include diff --git a/src/controllers/accounts/AccountModel.cpp b/src/controllers/accounts/AccountModel.cpp index cf2b1c79f21..f0598318c42 100644 --- a/src/controllers/accounts/AccountModel.cpp +++ b/src/controllers/accounts/AccountModel.cpp @@ -1,4 +1,4 @@ -#include "AccountModel.hpp" +#include "controllers/accounts/AccountModel.hpp" #include "controllers/accounts/Account.hpp" #include "util/StandardItemHelper.hpp" diff --git a/src/controllers/commands/Command.cpp b/src/controllers/commands/Command.cpp index e7113db9c21..5ac0feb5cbf 100644 --- a/src/controllers/commands/Command.cpp +++ b/src/controllers/commands/Command.cpp @@ -1,4 +1,4 @@ -#include "Command.hpp" +#include "controllers/commands/Command.hpp" namespace chatterino { diff --git a/src/controllers/highlights/HighlightBadge.cpp b/src/controllers/highlights/HighlightBadge.cpp index b4452a22b14..7958c8faaba 100644 --- a/src/controllers/highlights/HighlightBadge.cpp +++ b/src/controllers/highlights/HighlightBadge.cpp @@ -1,4 +1,4 @@ -#include "HighlightBadge.hpp" +#include "controllers/highlights/HighlightBadge.hpp" #include "messages/SharedMessageBuilder.hpp" #include "providers/twitch/TwitchBadge.hpp" diff --git a/src/controllers/notifications/NotificationModel.cpp b/src/controllers/notifications/NotificationModel.cpp index 5de8bc903e7..b68a50441fc 100644 --- a/src/controllers/notifications/NotificationModel.cpp +++ b/src/controllers/notifications/NotificationModel.cpp @@ -1,4 +1,4 @@ -#include "NotificationModel.hpp" +#include "controllers/notifications/NotificationModel.hpp" #include "Application.hpp" #include "singletons/Settings.hpp" diff --git a/src/controllers/pings/MutedChannelModel.cpp b/src/controllers/pings/MutedChannelModel.cpp index fc14735069d..df32f53d548 100644 --- a/src/controllers/pings/MutedChannelModel.cpp +++ b/src/controllers/pings/MutedChannelModel.cpp @@ -1,4 +1,4 @@ -#include "MutedChannelModel.hpp" +#include "controllers/pings/MutedChannelModel.hpp" #include "Application.hpp" #include "singletons/Settings.hpp" diff --git a/src/debug/Benchmark.cpp b/src/debug/Benchmark.cpp index 5c93ac8e62f..0aca0a61de3 100644 --- a/src/debug/Benchmark.cpp +++ b/src/debug/Benchmark.cpp @@ -1,4 +1,4 @@ -#include "Benchmark.hpp" +#include "debug/Benchmark.hpp" #include "common/QLogging.hpp" diff --git a/src/messages/Emote.cpp b/src/messages/Emote.cpp index 6281277fcc3..f19fc3027b5 100644 --- a/src/messages/Emote.cpp +++ b/src/messages/Emote.cpp @@ -1,4 +1,4 @@ -#include "Emote.hpp" +#include "messages/Emote.hpp" #include diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index 3e91d04dd67..ae78ab71120 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -1,4 +1,4 @@ -#include "MessageBuilder.hpp" +#include "messages/MessageBuilder.hpp" #include "Application.hpp" #include "common/IrcColors.hpp" diff --git a/src/messages/MessageColor.cpp b/src/messages/MessageColor.cpp index bd784197aaf..6e2f01c6737 100644 --- a/src/messages/MessageColor.cpp +++ b/src/messages/MessageColor.cpp @@ -1,4 +1,4 @@ -#include "MessageColor.hpp" +#include "messages/MessageColor.hpp" #include "singletons/Theme.hpp" diff --git a/src/messages/layouts/MessageLayout.cpp b/src/messages/layouts/MessageLayout.cpp index 10397d93c5f..e1a3d8a3c48 100644 --- a/src/messages/layouts/MessageLayout.cpp +++ b/src/messages/layouts/MessageLayout.cpp @@ -19,12 +19,6 @@ #include #include -#define MARGIN_LEFT (int)(8 * this->scale) -#define MARGIN_RIGHT (int)(8 * this->scale) -#define MARGIN_TOP (int)(4 * this->scale) -#define MARGIN_BOTTOM (int)(4 * this->scale) -#define COMPACT_EMOTES_OFFSET 6 - namespace chatterino { namespace { diff --git a/src/messages/layouts/MessageLayoutContainer.cpp b/src/messages/layouts/MessageLayoutContainer.cpp index 0f8aa498f99..0e84d325201 100644 --- a/src/messages/layouts/MessageLayoutContainer.cpp +++ b/src/messages/layouts/MessageLayoutContainer.cpp @@ -18,13 +18,17 @@ #include -#define COMPACT_EMOTES_OFFSET 4 -#define MAX_UNCOLLAPSED_LINES \ - (getSettings()->collpseMessagesMinLines.getValue()) - namespace { -constexpr const QMargins MARGIN{8, 4, 8, 4}; +using namespace chatterino; + +constexpr QMargins MARGIN{8, 4, 8, 4}; +constexpr int COMPACT_EMOTES_OFFSET = 4; + +int maxUncollapsedLines() +{ + return getSettings()->collpseMessagesMinLines.getValue(); +} } // namespace @@ -208,7 +212,7 @@ void MessageLayoutContainer::breakLine() this->lineStart_ = this->elements_.size(); // this->currentX = (int)(this->scale * 8); - if (this->canCollapse() && this->line_ + 1 >= MAX_UNCOLLAPSED_LINES) + if (this->canCollapse() && this->line_ + 1 >= maxUncollapsedLines()) { this->canAddMessages_ = false; return; @@ -568,8 +572,9 @@ int MessageLayoutContainer::remainingWidth() const { return (this->width_ - int(MARGIN.left() * this->scale_) - int(MARGIN.right() * this->scale_) - - (this->line_ + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth_ - : 0)) - + (static_cast(this->line_ + 1) == maxUncollapsedLines() + ? this->dotdotdotWidth_ + : 0)) - this->currentX_; } diff --git a/src/messages/search/RegexPredicate.cpp b/src/messages/search/RegexPredicate.cpp index 02eeec6c849..733949afa9e 100644 --- a/src/messages/search/RegexPredicate.cpp +++ b/src/messages/search/RegexPredicate.cpp @@ -1,4 +1,4 @@ -#include "RegexPredicate.hpp" +#include "messages/search/RegexPredicate.hpp" #include "messages/Message.hpp" diff --git a/src/messages/search/RegexPredicate.hpp b/src/messages/search/RegexPredicate.hpp index 63e68cabd68..e1cc6e15e25 100644 --- a/src/messages/search/RegexPredicate.hpp +++ b/src/messages/search/RegexPredicate.hpp @@ -1,8 +1,8 @@ #pragma once #include "messages/search/MessagePredicate.hpp" -#include "QRegularExpression" +#include #include namespace chatterino { diff --git a/src/providers/IvrApi.cpp b/src/providers/IvrApi.cpp index 9991661b7b8..02d733b18bd 100644 --- a/src/providers/IvrApi.cpp +++ b/src/providers/IvrApi.cpp @@ -1,4 +1,4 @@ -#include "IvrApi.hpp" +#include "providers/IvrApi.hpp" #include "common/network/NetworkResult.hpp" #include "common/QLogging.hpp" diff --git a/src/providers/IvrApi.hpp b/src/providers/IvrApi.hpp index f8cc72b76a1..8084718971a 100644 --- a/src/providers/IvrApi.hpp +++ b/src/providers/IvrApi.hpp @@ -64,9 +64,7 @@ struct IvrEmote { : code(root.value("code").toString()) , id(root.value("id").toString()) , setId(root.value("setID").toString()) - , url(QString(TWITCH_EMOTE_TEMPLATE) - .replace("{id}", this->id) - .replace("{scale}", "3.0")) + , url(TWITCH_EMOTE_TEMPLATE.arg(this->id, u"3.0")) , emoteType(root.value("type").toString()) , imageType(root.value("assetType").toString()) { diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 948a2962db6..599a8e0551c 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -1,4 +1,4 @@ -#include "AbstractIrcServer.hpp" +#include "providers/irc/AbstractIrcServer.hpp" #include "common/Channel.hpp" #include "common/QLogging.hpp" diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index c51a5f32340..48b3cd0a130 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -1,4 +1,4 @@ -#include "Irc2.hpp" +#include "providers/irc/Irc2.hpp" #include "Application.hpp" #include "common/Credentials.hpp" diff --git a/src/providers/irc/IrcAccount.cpp b/src/providers/irc/IrcAccount.cpp index 96486b7eac3..53030252154 100644 --- a/src/providers/irc/IrcAccount.cpp +++ b/src/providers/irc/IrcAccount.cpp @@ -1,4 +1,4 @@ -#include "IrcAccount.hpp" +#include "providers/irc/IrcAccount.hpp" // namespace chatterino { // diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp index 2a2a3be93e7..76feb901b16 100644 --- a/src/providers/irc/IrcChannel2.cpp +++ b/src/providers/irc/IrcChannel2.cpp @@ -1,4 +1,4 @@ -#include "IrcChannel2.hpp" +#include "providers/irc/IrcChannel2.hpp" #include "common/Channel.hpp" #include "debug/AssertInGuiThread.hpp" diff --git a/src/providers/irc/IrcCommands.cpp b/src/providers/irc/IrcCommands.cpp index cd5f7d5e628..b1b2e8fc3c2 100644 --- a/src/providers/irc/IrcCommands.cpp +++ b/src/providers/irc/IrcCommands.cpp @@ -1,4 +1,4 @@ -#include "IrcCommands.hpp" +#include "providers/irc/IrcCommands.hpp" #include "messages/MessageBuilder.hpp" #include "providers/irc/IrcChannel2.hpp" diff --git a/src/providers/irc/IrcConnection2.cpp b/src/providers/irc/IrcConnection2.cpp index 19f327c24d0..3f9d57c97be 100644 --- a/src/providers/irc/IrcConnection2.cpp +++ b/src/providers/irc/IrcConnection2.cpp @@ -1,4 +1,4 @@ -#include "IrcConnection2.hpp" +#include "providers/irc/IrcConnection2.hpp" #include "common/QLogging.hpp" #include "common/Version.hpp" @@ -11,7 +11,7 @@ namespace chatterino { namespace { - const auto payload = QString("chatterino/" CHATTERINO_VERSION); + const auto payload = "chatterino/" + CHATTERINO_VERSION; } // namespace diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index a5148c9c763..c88109ad439 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -1,4 +1,4 @@ -#include "IrcServer.hpp" +#include "providers/irc/IrcServer.hpp" #include "Application.hpp" #include "common/QLogging.hpp" diff --git a/src/providers/twitch/ChannelPointReward.cpp b/src/providers/twitch/ChannelPointReward.cpp index 658d498ff68..8849b8b62a0 100644 --- a/src/providers/twitch/ChannelPointReward.cpp +++ b/src/providers/twitch/ChannelPointReward.cpp @@ -1,8 +1,18 @@ -#include "ChannelPointReward.hpp" +#include "providers/twitch/ChannelPointReward.hpp" -#include "common/QLogging.hpp" #include "messages/Image.hpp" +#include + +namespace { + +QString twitchChannelPointRewardUrl(const QString &file) +{ + return u"https://static-cdn.jtvnw.net/custom-reward-images/default-" % file; +} + +} // namespace + namespace chatterino { ChannelPointReward::ChannelPointReward(const QJsonObject &redemption) @@ -94,11 +104,10 @@ ChannelPointReward::ChannelPointReward(const QJsonObject &redemption) else { static const ImageSet defaultImage{ - Image::fromUrl({TWITCH_CHANNEL_POINT_REWARD_URL("1.png")}, 1, - baseSize), - Image::fromUrl({TWITCH_CHANNEL_POINT_REWARD_URL("2.png")}, 0.5, + Image::fromUrl({twitchChannelPointRewardUrl("1.png")}, 1, baseSize), + Image::fromUrl({twitchChannelPointRewardUrl("2.png")}, 0.5, baseSize * 2), - Image::fromUrl({TWITCH_CHANNEL_POINT_REWARD_URL("4.png")}, 0.25, + Image::fromUrl({twitchChannelPointRewardUrl("4.png")}, 0.25, baseSize * 4)}; this->image = defaultImage; } diff --git a/src/providers/twitch/ChannelPointReward.hpp b/src/providers/twitch/ChannelPointReward.hpp index d4f428e92f5..6fdd985b6e7 100644 --- a/src/providers/twitch/ChannelPointReward.hpp +++ b/src/providers/twitch/ChannelPointReward.hpp @@ -1,15 +1,11 @@ #pragma once -#include "common/Aliases.hpp" #include "messages/ImageSet.hpp" #include -#define TWITCH_CHANNEL_POINT_REWARD_URL(x) \ - QString("https://static-cdn.jtvnw.net/custom-reward-images/default-%1") \ - .arg(x) - namespace chatterino { + struct ChannelPointReward { ChannelPointReward(const QJsonObject &redemption); ChannelPointReward() = delete; diff --git a/src/providers/twitch/TwitchEmotes.cpp b/src/providers/twitch/TwitchEmotes.cpp index 4baa13f2027..b7ba9f54a00 100644 --- a/src/providers/twitch/TwitchEmotes.cpp +++ b/src/providers/twitch/TwitchEmotes.cpp @@ -11,9 +11,7 @@ using namespace chatterino; Url getEmoteLink(const EmoteId &id, const QString &emoteScale) { - return {QString(TWITCH_EMOTE_TEMPLATE) - .replace("{id}", id.string) - .replace("{scale}", emoteScale)}; + return {TWITCH_EMOTE_TEMPLATE.arg(id.string, emoteScale)}; } QSize getEmoteExpectedBaseSize(const EmoteId &id) diff --git a/src/providers/twitch/TwitchEmotes.hpp b/src/providers/twitch/TwitchEmotes.hpp index 17e50b11fcb..f7691131cbc 100644 --- a/src/providers/twitch/TwitchEmotes.hpp +++ b/src/providers/twitch/TwitchEmotes.hpp @@ -10,12 +10,15 @@ #include #include +namespace chatterino { + // NB: "default" can be replaced with "static" to always get a non-animated // variant -#define TWITCH_EMOTE_TEMPLATE \ - "https://static-cdn.jtvnw.net/emoticons/v2/{id}/default/dark/{scale}" +/// %1 <-> {id} +/// %2 <-> {scale} (1.0, 2.0, 3.0) +constexpr QStringView TWITCH_EMOTE_TEMPLATE = + u"https://static-cdn.jtvnw.net/emoticons/v2/%1/default/dark/%2"; -namespace chatterino { struct Emote; using EmotePtr = std::shared_ptr; diff --git a/src/providers/twitch/api/Helix.hpp b/src/providers/twitch/api/Helix.hpp index 346c9f3c375..aa95f1e32d1 100644 --- a/src/providers/twitch/api/Helix.hpp +++ b/src/providers/twitch/api/Helix.hpp @@ -276,9 +276,7 @@ struct HelixChannelEmote { , name(jsonObject.value("name").toString()) , type(jsonObject.value("emote_type").toString()) , setId(jsonObject.value("emote_set_id").toString()) - , url(QString(TWITCH_EMOTE_TEMPLATE) - .replace("{id}", this->emoteId) - .replace("{scale}", "3.0")) + , url(TWITCH_EMOTE_TEMPLATE.arg(this->emoteId, u"3.0")) { } }; diff --git a/src/singletons/ImageUploader.cpp b/src/singletons/ImageUploader.cpp index b5fe9c43a95..ebf39a958ba 100644 --- a/src/singletons/ImageUploader.cpp +++ b/src/singletons/ImageUploader.cpp @@ -24,11 +24,11 @@ #include -#define UPLOAD_DELAY 2000 -// Delay between uploads in milliseconds - namespace { +// Delay between uploads in milliseconds +constexpr int UPLOAD_DELAY = 2000; + std::optional convertToPng(const QImage &image) { QByteArray imageData; diff --git a/src/singletons/Theme.cpp b/src/singletons/Theme.cpp index bbd29abf948..8f1045e0ca2 100644 --- a/src/singletons/Theme.cpp +++ b/src/singletons/Theme.cpp @@ -1,4 +1,3 @@ - #include "singletons/Theme.hpp" #include "Application.hpp" diff --git a/src/singletons/Toasts.cpp b/src/singletons/Toasts.cpp index 26a82543a43..7e21dc2c315 100644 --- a/src/singletons/Toasts.cpp +++ b/src/singletons/Toasts.cpp @@ -1,4 +1,4 @@ -#include "Toasts.hpp" +#include "singletons/Toasts.hpp" #include "Application.hpp" #include "common/Common.hpp" @@ -83,19 +83,17 @@ bool Toasts::isEnabled() QString Toasts::findStringFromReaction(const ToastReaction &reaction) { - // The constants are macros right now, but we want to avoid ASCII casts, - // so we're concatenating them with a QString literal - effectively making them part of it. switch (reaction) { case ToastReaction::OpenInBrowser: - return OPEN_IN_BROWSER u""_s; + return OPEN_IN_BROWSER; case ToastReaction::OpenInPlayer: - return OPEN_PLAYER_IN_BROWSER u""_s; + return OPEN_PLAYER_IN_BROWSER; case ToastReaction::OpenInStreamlink: - return OPEN_IN_STREAMLINK u""_s; + return OPEN_IN_STREAMLINK; case ToastReaction::DontOpen: default: - return DONT_OPEN u""_s; + return DONT_OPEN; } } diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index 0a7351491b1..35b625659fe 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -1,12 +1,13 @@ -#include "Updates.hpp" +#include "singletons/Updates.hpp" +#include "common/Literals.hpp" #include "common/Modes.hpp" #include "common/network/NetworkRequest.hpp" #include "common/network/NetworkResult.hpp" #include "common/QLogging.hpp" #include "common/Version.hpp" -#include "Settings.hpp" #include "singletons/Paths.hpp" +#include "singletons/Settings.hpp" #include "util/CombinePath.hpp" #include "util/PostToThread.hpp" @@ -15,16 +16,34 @@ #include #include #include +#include #include -namespace chatterino { namespace { - QString currentBranch() - { - return getSettings()->betaUpdates ? "beta" : "stable"; - } + +using namespace chatterino; +using namespace literals; + +QString currentBranch() +{ + return getSettings()->betaUpdates ? "beta" : "stable"; +} + +#if defined(Q_OS_WIN) +const QString CHATTERINO_OS = u"win"_s; +#elif defined(Q_OS_MACOS) +const QString CHATTERINO_OS = u"macos"_s; +#elif defined(Q_OS_LINUX) +const QString CHATTERINO_OS = u"linux"_s; +#elif defined(Q_OS_FREEBSD) +const QString CHATTERINO_OS = u"freebsd"_s; +#else +const QString CHATTERINO_OS = u"unknown"_s; +#endif +; } // namespace +namespace chatterino { Updates::Updates(const Paths &paths_) : paths(paths_) @@ -262,9 +281,8 @@ void Updates::checkForUpdates() return; } - QString url = - "https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/" + - currentBranch(); + QString url = "https://notitia.chatterino.com/version/chatterino/" % + CHATTERINO_OS % "/" % currentBranch(); NetworkRequest(url) .timeout(60000) diff --git a/src/util/DisplayBadge.cpp b/src/util/DisplayBadge.cpp index cf56634fa95..73363333987 100644 --- a/src/util/DisplayBadge.cpp +++ b/src/util/DisplayBadge.cpp @@ -1,4 +1,4 @@ -#include "DisplayBadge.hpp" +#include "util/DisplayBadge.hpp" namespace chatterino { DisplayBadge::DisplayBadge(QString displayName, QString badgeName) diff --git a/src/util/FunctionEventFilter.cpp b/src/util/FunctionEventFilter.cpp index 5071e6e9b87..33a41a2c6fd 100644 --- a/src/util/FunctionEventFilter.cpp +++ b/src/util/FunctionEventFilter.cpp @@ -1,4 +1,4 @@ -#include "FunctionEventFilter.hpp" +#include "util/FunctionEventFilter.hpp" namespace chatterino { diff --git a/src/util/FuzzyConvert.cpp b/src/util/FuzzyConvert.cpp index 2d62e5648bc..0870b711a1b 100644 --- a/src/util/FuzzyConvert.cpp +++ b/src/util/FuzzyConvert.cpp @@ -1,4 +1,4 @@ -#include "FuzzyConvert.hpp" +#include "util/FuzzyConvert.hpp" #include diff --git a/src/util/Helpers.cpp b/src/util/Helpers.cpp index 836214d05a9..88ba17fe1c9 100644 --- a/src/util/Helpers.cpp +++ b/src/util/Helpers.cpp @@ -1,4 +1,4 @@ -#include "Helpers.hpp" +#include "util/Helpers.hpp" #include "providers/twitch/TwitchCommon.hpp" diff --git a/src/util/RatelimitBucket.cpp b/src/util/RatelimitBucket.cpp index c33f3a30a18..75bf8e344ef 100644 --- a/src/util/RatelimitBucket.cpp +++ b/src/util/RatelimitBucket.cpp @@ -1,4 +1,4 @@ -#include "RatelimitBucket.hpp" +#include "util/RatelimitBucket.hpp" #include diff --git a/src/util/SampleData.cpp b/src/util/SampleData.cpp index 0b976f1900c..7b12cd31cba 100644 --- a/src/util/SampleData.cpp +++ b/src/util/SampleData.cpp @@ -1,4 +1,4 @@ -#include "SampleData.hpp" +#include "util/SampleData.hpp" namespace chatterino { diff --git a/src/widgets/AccountSwitchWidget.cpp b/src/widgets/AccountSwitchWidget.cpp index 19dbc678b50..01fc161bc39 100644 --- a/src/widgets/AccountSwitchWidget.cpp +++ b/src/widgets/AccountSwitchWidget.cpp @@ -1,4 +1,4 @@ -#include "AccountSwitchWidget.hpp" +#include "widgets/AccountSwitchWidget.hpp" #include "Application.hpp" #include "common/Common.hpp" diff --git a/src/widgets/TooltipWidget.cpp b/src/widgets/TooltipWidget.cpp index 14328e91f54..64b3bec8218 100644 --- a/src/widgets/TooltipWidget.cpp +++ b/src/widgets/TooltipWidget.cpp @@ -7,11 +7,11 @@ #include -// number of columns in grid mode -#define GRID_NUM_COLS 3 - namespace { +// number of columns in grid mode +constexpr int GRID_NUM_COLS = 3; + #ifdef Q_OS_WIN template inline constexpr T *tooltipParentFor(T * /*desiredParent*/) diff --git a/src/widgets/Window.cpp b/src/widgets/Window.cpp index ee836d1857e..4374881fdb2 100644 --- a/src/widgets/Window.cpp +++ b/src/widgets/Window.cpp @@ -737,19 +737,19 @@ void Window::addMenuBar() // Help->Chatterino Wiki item QAction *helpWiki = helpMenu->addAction(QString("Chatterino Wiki")); connect(helpWiki, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_WIKI)); + QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_WIKI.toString())); }); // Help->Chatterino Github QAction *helpGithub = helpMenu->addAction(QString("Chatterino GitHub")); connect(helpGithub, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_SOURCE)); + QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_SOURCE.toString())); }); // Help->Chatterino Discord QAction *helpDiscord = helpMenu->addAction(QString("Chatterino Discord")); connect(helpDiscord, &QAction::triggered, this, []() { - QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_DISCORD)); + QDesktopServices::openUrl(QUrl(LINK_CHATTERINO_DISCORD.toString())); }); } diff --git a/src/widgets/dialogs/BadgePickerDialog.cpp b/src/widgets/dialogs/BadgePickerDialog.cpp index f45dbbbf158..e90c66b5155 100644 --- a/src/widgets/dialogs/BadgePickerDialog.cpp +++ b/src/widgets/dialogs/BadgePickerDialog.cpp @@ -1,4 +1,4 @@ -#include "BadgePickerDialog.hpp" +#include "widgets/dialogs/BadgePickerDialog.hpp" #include "Application.hpp" #include "providers/twitch/TwitchBadges.hpp" diff --git a/src/widgets/dialogs/EmotePopup.cpp b/src/widgets/dialogs/EmotePopup.cpp index eb789249a2a..8936549e6eb 100644 --- a/src/widgets/dialogs/EmotePopup.cpp +++ b/src/widgets/dialogs/EmotePopup.cpp @@ -1,4 +1,4 @@ -#include "EmotePopup.hpp" +#include "widgets/dialogs/EmotePopup.hpp" #include "Application.hpp" #include "common/QLogging.hpp" diff --git a/src/widgets/dialogs/IrcConnectionEditor.cpp b/src/widgets/dialogs/IrcConnectionEditor.cpp index 63e6bfa2d19..8ebfd8f9552 100644 --- a/src/widgets/dialogs/IrcConnectionEditor.cpp +++ b/src/widgets/dialogs/IrcConnectionEditor.cpp @@ -1,4 +1,4 @@ -#include "IrcConnectionEditor.hpp" +#include "widgets/dialogs/IrcConnectionEditor.hpp" #include "ui_IrcConnectionEditor.h" diff --git a/src/widgets/dialogs/QualityPopup.cpp b/src/widgets/dialogs/QualityPopup.cpp index e08d25208b5..9feec4f3370 100644 --- a/src/widgets/dialogs/QualityPopup.cpp +++ b/src/widgets/dialogs/QualityPopup.cpp @@ -1,4 +1,4 @@ -#include "QualityPopup.hpp" +#include "widgets/dialogs/QualityPopup.hpp" #include "Application.hpp" #include "common/QLogging.hpp" diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 1eb33c17276..728caac8b3f 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -1,4 +1,4 @@ -#include "SelectChannelDialog.hpp" +#include "widgets/dialogs/SelectChannelDialog.hpp" #include "Application.hpp" #include "common/QLogging.hpp" @@ -25,11 +25,11 @@ #include #include -#define TAB_TWITCH 0 -#define TAB_IRC 1 - namespace chatterino { +constexpr int TAB_TWITCH = 0; +constexpr int TAB_IRC = 1; + SelectChannelDialog::SelectChannelDialog(QWidget *parent) : BaseWindow( { diff --git a/src/widgets/dialogs/SelectChannelFiltersDialog.cpp b/src/widgets/dialogs/SelectChannelFiltersDialog.cpp index 78c597c02ba..e0acb8523f7 100644 --- a/src/widgets/dialogs/SelectChannelFiltersDialog.cpp +++ b/src/widgets/dialogs/SelectChannelFiltersDialog.cpp @@ -1,4 +1,4 @@ -#include "SelectChannelFiltersDialog.hpp" +#include "widgets/dialogs/SelectChannelFiltersDialog.hpp" #include "controllers/filters/FilterRecord.hpp" #include "singletons/Settings.hpp" diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index 89d43da3798..a25cf7633a2 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -38,101 +38,104 @@ #include #include #include +#include -const QString TEXT_FOLLOWERS("Followers: %1"); -const QString TEXT_CREATED("Created: %1"); -const QString TEXT_TITLE("%1's Usercard - #%2"); -#define TEXT_USER_ID "ID: " -#define TEXT_UNAVAILABLE "(not available)" - -namespace chatterino { namespace { - Label *addCopyableLabel(LayoutCreator box, const char *tooltip, - Button **copyButton = nullptr) + +constexpr QStringView TEXT_FOLLOWERS = u"Followers: %1"; +constexpr QStringView TEXT_CREATED = u"Created: %1"; +constexpr QStringView TEXT_TITLE = u"%1's Usercard - #%2"; +constexpr QStringView TEXT_USER_ID = u"ID: "; +constexpr QStringView TEXT_UNAVAILABLE = u"(not available)"; + +using namespace chatterino; + +Label *addCopyableLabel(LayoutCreator box, const char *tooltip, + Button **copyButton = nullptr) +{ + auto label = box.emplace